@open-assets/open-assets 0.1.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -120
- package/bin/open-assets.mjs +165 -319
- package/lib/add.mjs +476 -0
- package/lib/init.mjs +215 -107
- package/lib/list.mjs +64 -0
- package/lib/lockfile.mjs +53 -2
- package/lib/manifest.mjs +132 -0
- package/lib/render.mjs +219 -0
- package/lib/renderer.mjs +57 -7
- package/lib/server.mjs +58 -27
- package/lib/validate.mjs +93 -0
- package/package.json +7 -2
- package/public/img/banner/1280/banner.png +0 -0
- package/public/img/examples/cat-iq-test-icon.png +0 -0
- package/public/img/examples/cat-iq-test.png +0 -0
- package/public/img/examples/no-context-icon.png +0 -0
- package/public/img/examples/no-context.png +0 -0
- package/public/img/examples/only-recipes-icon.png +0 -0
- package/public/img/examples/only-recipes.png +0 -0
- package/public/img/icon/16/icon.png +0 -0
- package/public/img/icon/180/icon.png +0 -0
- package/public/img/icon/192/icon.png +0 -0
- package/public/img/icon/32/icon.png +0 -0
- package/public/img/icon/512/icon.png +0 -0
- package/public/viewer.html +59 -56
- package/skills/open-assets/SKILL.md +121 -76
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-assets/open-assets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Dev server and export tool for app screenshots, icons, and logos. Like Storybook for marketing assets.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"assets": "node bin/open-assets.mjs render --force -o ./public/img",
|
|
43
|
+
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
|
44
|
+
"release": "./scripts/release.sh"
|
|
45
|
+
},
|
|
41
46
|
"homepage": "https://openassets.dev",
|
|
42
47
|
"dependencies": {
|
|
43
48
|
"commander": "^13.0.0",
|
|
@@ -45,6 +50,6 @@
|
|
|
45
50
|
"puppeteer": "^24.37.5"
|
|
46
51
|
},
|
|
47
52
|
"devDependencies": {
|
|
48
|
-
"
|
|
53
|
+
"jest": "^30.3.0"
|
|
49
54
|
}
|
|
50
55
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/public/viewer.html
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Open Assets</title>
|
|
7
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/img/icon/32/icon.png" />
|
|
8
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/img/icon/16/icon.png" />
|
|
7
9
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
|
8
10
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
|
9
11
|
<style>
|
|
@@ -12,6 +14,7 @@
|
|
|
12
14
|
|
|
13
15
|
.toolbar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; height: 48px; display: flex; align-items: center; justify-content: space-between; padding: 0 20px; background: rgba(10,10,10,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,0.06); }
|
|
14
16
|
.toolbar-left { display: flex; align-items: center; gap: 12px; }
|
|
17
|
+
.toolbar-left img.toolbar-icon { width: 20px; height: 20px; border-radius: 4px; }
|
|
15
18
|
.toolbar-left h1 { font-size: 14px; font-weight: 700; color: #fff; }
|
|
16
19
|
.segmented { display: flex; background: rgba(255,255,255,0.06); border-radius: 6px; padding: 2px; gap: 1px; }
|
|
17
20
|
.seg-btn { padding: 4px 14px; border-radius: 5px; border: none; background: transparent; color: #888; font-size: 12px; font-weight: 600; cursor: pointer; transition: background 0.15s, color 0.15s; font-family: inherit; text-decoration: none; }
|
|
@@ -82,6 +85,7 @@
|
|
|
82
85
|
<body>
|
|
83
86
|
<div class="toolbar">
|
|
84
87
|
<div class="toolbar-left">
|
|
88
|
+
<img class="toolbar-icon" src="/img/icon/32/icon.png" alt="" />
|
|
85
89
|
<h1 id="app-title">Loading...</h1>
|
|
86
90
|
<div class="segmented" id="tab-bar"></div>
|
|
87
91
|
</div>
|
|
@@ -174,7 +178,7 @@
|
|
|
174
178
|
}
|
|
175
179
|
|
|
176
180
|
function flatSizes(col) {
|
|
177
|
-
return
|
|
181
|
+
return col.export || [];
|
|
178
182
|
}
|
|
179
183
|
|
|
180
184
|
// ---- Build collection tab bar ----
|
|
@@ -250,69 +254,68 @@
|
|
|
250
254
|
const sh = col.sourceSize.height;
|
|
251
255
|
const hasMultipleTemplates = col.templates.length > 1;
|
|
252
256
|
|
|
253
|
-
// Outputs (e.g., Xcode)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const btn = document.createElement('button');
|
|
264
|
-
btn.innerHTML = 'Export to Xcode <span class="dim">.appiconset</span>';
|
|
265
|
-
btn.addEventListener('click', () => runOutput(col.id, i));
|
|
266
|
-
exportDropdown.appendChild(btn);
|
|
267
|
-
|
|
268
|
-
exportDropdown.appendChild(document.createElement('hr'));
|
|
269
|
-
} else if (output.type === 'copy-source') {
|
|
270
|
-
const sectionLabel = document.createElement('div');
|
|
271
|
-
sectionLabel.className = 'section-label';
|
|
272
|
-
sectionLabel.textContent = output.format.toUpperCase();
|
|
273
|
-
exportDropdown.appendChild(sectionLabel);
|
|
274
|
-
|
|
275
|
-
// SVG download for each template
|
|
276
|
-
for (const template of col.templates) {
|
|
277
|
-
if (template.src.endsWith('.' + output.format)) {
|
|
278
|
-
const btn = document.createElement('button');
|
|
279
|
-
const displayName = hasMultipleTemplates ? template.label : `Download ${output.format.toUpperCase()}`;
|
|
280
|
-
btn.innerHTML = `${displayName} <span class="dim">${output.format}</span>`;
|
|
281
|
-
btn.addEventListener('click', () => downloadSource(template));
|
|
282
|
-
exportDropdown.appendChild(btn);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
257
|
+
// Outputs (e.g., Xcode, copy-source) — export entries with a type field
|
|
258
|
+
const allExports = col.export || [];
|
|
259
|
+
const outputs = allExports.filter(e => e.type);
|
|
260
|
+
for (let i = 0; i < outputs.length; i++) {
|
|
261
|
+
const output = outputs[i];
|
|
262
|
+
if (output.type === 'xcode') {
|
|
263
|
+
const sectionLabel = document.createElement('div');
|
|
264
|
+
sectionLabel.className = 'section-label';
|
|
265
|
+
sectionLabel.textContent = 'Xcode';
|
|
266
|
+
exportDropdown.appendChild(sectionLabel);
|
|
285
267
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
268
|
+
const btn = document.createElement('button');
|
|
269
|
+
btn.innerHTML = 'Export to Xcode <span class="dim">.appiconset</span>';
|
|
270
|
+
btn.addEventListener('click', () => runOutput(col.id, i));
|
|
271
|
+
exportDropdown.appendChild(btn);
|
|
290
272
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (group.platform) {
|
|
273
|
+
exportDropdown.appendChild(document.createElement('hr'));
|
|
274
|
+
} else if (output.type === 'copy-source') {
|
|
294
275
|
const sectionLabel = document.createElement('div');
|
|
295
276
|
sectionLabel.className = 'section-label';
|
|
296
|
-
sectionLabel.textContent =
|
|
277
|
+
sectionLabel.textContent = output.format.toUpperCase();
|
|
297
278
|
exportDropdown.appendChild(sectionLabel);
|
|
298
|
-
}
|
|
299
279
|
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
280
|
+
// SVG download for each template
|
|
281
|
+
for (const template of col.templates) {
|
|
282
|
+
if (template.src.endsWith('.' + output.format)) {
|
|
283
|
+
const btn = document.createElement('button');
|
|
284
|
+
const displayName = hasMultipleTemplates ? template.label : `Download ${output.format.toUpperCase()}`;
|
|
285
|
+
btn.innerHTML = `${displayName} <span class="dim">${output.format}</span>`;
|
|
286
|
+
btn.addEventListener('click', () => downloadSource(template));
|
|
287
|
+
exportDropdown.appendChild(btn);
|
|
308
288
|
}
|
|
309
|
-
}
|
|
310
|
-
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
exportDropdown.appendChild(document.createElement('hr'));
|
|
311
292
|
}
|
|
312
293
|
}
|
|
313
294
|
|
|
295
|
+
// Export sizes (entries without a type field)
|
|
296
|
+
const sizes = allExports.filter(e => !e.type);
|
|
297
|
+
if (sizes.length > 0) {
|
|
298
|
+
const sectionLabel = document.createElement('div');
|
|
299
|
+
sectionLabel.className = 'section-label';
|
|
300
|
+
sectionLabel.textContent = 'Sizes';
|
|
301
|
+
exportDropdown.appendChild(sectionLabel);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
for (const entry of sizes) {
|
|
305
|
+
const btn = document.createElement('button');
|
|
306
|
+
btn.innerHTML = `${entry.label} <span class="dim">${entry.size.width} × ${entry.size.height}</span>`;
|
|
307
|
+
btn.addEventListener('click', () => {
|
|
308
|
+
if (hasMultipleTemplates) {
|
|
309
|
+
exportZip(entry.size.width, entry.size.height, sw, sh, entry.name);
|
|
310
|
+
} else {
|
|
311
|
+
exportSinglePNG(col.templates[0], entry.size.width, entry.size.height, sw, sh, entry.name);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
exportDropdown.appendChild(btn);
|
|
315
|
+
}
|
|
316
|
+
|
|
314
317
|
// Custom size
|
|
315
|
-
if (col.
|
|
318
|
+
if (col.export && col.export.length > 0 || col.customExport) {
|
|
316
319
|
exportDropdown.appendChild(document.createElement('hr'));
|
|
317
320
|
|
|
318
321
|
const customLabel = document.createElement('div');
|
|
@@ -320,8 +323,8 @@
|
|
|
320
323
|
customLabel.textContent = 'Custom Size';
|
|
321
324
|
exportDropdown.appendChild(customLabel);
|
|
322
325
|
|
|
323
|
-
const defaultW = col.customExport?.defaultWidth || flatSizes(col)[0]?.width || sw;
|
|
324
|
-
const defaultH = col.customExport?.defaultHeight || flatSizes(col)[0]?.height || sh;
|
|
326
|
+
const defaultW = col.customExport?.defaultWidth || flatSizes(col)[0]?.size?.width || sw;
|
|
327
|
+
const defaultH = col.customExport?.defaultHeight || flatSizes(col)[0]?.size?.height || sh;
|
|
325
328
|
|
|
326
329
|
// Check if square (icon-like) for simplified input
|
|
327
330
|
const isSquare = sw === sh;
|
|
@@ -446,7 +449,7 @@
|
|
|
446
449
|
|
|
447
450
|
// ---- Init ----
|
|
448
451
|
async function init() {
|
|
449
|
-
const res = await fetch('
|
|
452
|
+
const res = await fetch('assets.json');
|
|
450
453
|
manifest = await res.json();
|
|
451
454
|
|
|
452
455
|
// Set title
|
|
@@ -15,15 +15,13 @@ open-assets is a dev server and export tool for app marketing assets — screens
|
|
|
15
15
|
open-assets dev
|
|
16
16
|
|
|
17
17
|
# Or with Tailwind
|
|
18
|
-
concurrently "npx @tailwindcss/cli -i
|
|
18
|
+
concurrently "npx @tailwindcss/cli -i assets/styles.css -o dist/styles.css --watch" "open-assets dev"
|
|
19
19
|
|
|
20
20
|
# Headless render
|
|
21
21
|
open-assets render --collection screenshots --size iphone-6.9
|
|
22
|
-
open-assets render --collection icon --
|
|
23
|
-
open-assets render --
|
|
24
|
-
open-assets render --template 01-hero --
|
|
25
|
-
open-assets render --platform "App Store"
|
|
26
|
-
open-assets render --force # ignore cache, re-render everything
|
|
22
|
+
open-assets render --collection icon --force
|
|
23
|
+
open-assets render --force
|
|
24
|
+
open-assets render --template 01-hero --force
|
|
27
25
|
```
|
|
28
26
|
|
|
29
27
|
## Concepts
|
|
@@ -33,20 +31,19 @@ open-assets render --force # ignore cache, re-render everything
|
|
|
33
31
|
| **Collection** | A named group of related assets sharing the same source size and export sizes. One tab in the dev UI. |
|
|
34
32
|
| **Template** | A single source file (HTML or SVG) that produces one image per export size. |
|
|
35
33
|
| **Export Size** | A named output dimension that templates are rendered at (e.g., "iPhone 6.9" → 1320×2868). |
|
|
36
|
-
| **Platform** | Optional grouping label for related export sizes (e.g., "App Store", "Play Store"). |
|
|
37
34
|
| **Source Size** | The dimensions the HTML template is authored at. Puppeteer scales from source → export size. |
|
|
38
35
|
| **Output** | An optional post-render action (e.g., write to Xcode `.appiconset`, copy SVG source). |
|
|
39
36
|
|
|
40
|
-
##
|
|
37
|
+
## assets.json
|
|
41
38
|
|
|
42
|
-
The `
|
|
39
|
+
The `assets.json` at the project root defines all asset collections. All collections follow the same unified schema — no `type` field needed.
|
|
43
40
|
|
|
44
41
|
```json
|
|
45
42
|
{
|
|
46
43
|
"version": 1,
|
|
47
44
|
"name": "App Display Name",
|
|
48
45
|
"publicDir": "public",
|
|
49
|
-
"command": "npx open-assets render --
|
|
46
|
+
"command": "npx open-assets render --force",
|
|
50
47
|
"collections": [
|
|
51
48
|
{
|
|
52
49
|
"id": "unique-id",
|
|
@@ -54,17 +51,11 @@ The `manifest.json` at the project root defines all asset collections. All colle
|
|
|
54
51
|
"sourceSize": { "width": 440, "height": 956 },
|
|
55
52
|
"borderRadius": 4,
|
|
56
53
|
"templates": [
|
|
57
|
-
{ "src": "
|
|
54
|
+
{ "src": "assets/screenshots/01-hero.html", "name": "01-hero", "label": "Hero" }
|
|
58
55
|
],
|
|
59
|
-
"
|
|
60
|
-
{
|
|
61
|
-
"platform": "App Store",
|
|
62
|
-
"sizes": [
|
|
63
|
-
{ "name": "iphone-6.9", "label": "iPhone 6.9\"", "width": 1320, "height": 2868 }
|
|
64
|
-
]
|
|
65
|
-
}
|
|
56
|
+
"export": [
|
|
57
|
+
{ "name": "iphone-6.9", "label": "iPhone 6.9\"", "size": { "width": 1320, "height": 2868 } }
|
|
66
58
|
],
|
|
67
|
-
"outputs": [],
|
|
68
59
|
"customExport": { "defaultWidth": 1320, "defaultHeight": 2868 }
|
|
69
60
|
}
|
|
70
61
|
]
|
|
@@ -80,12 +71,13 @@ The `manifest.json` at the project root defines all asset collections. All colle
|
|
|
80
71
|
| `sourceSize` | object | `{ width, height }` — dimensions templates are authored at |
|
|
81
72
|
| `borderRadius` | number | Border radius for preview frames (px) |
|
|
82
73
|
| `templates` | array | Source files: `{ src, name, label }` |
|
|
83
|
-
| `
|
|
84
|
-
| `outputs` | array | Post-render actions (optional) |
|
|
74
|
+
| `export` | array | Export sizes and output actions. Entries with a `type` field are post-render actions |
|
|
85
75
|
| `customExport` | object | Custom size defaults for UI (optional) |
|
|
86
76
|
|
|
87
77
|
### Output Types
|
|
88
78
|
|
|
79
|
+
Export entries with a `type` field are post-render actions (run with `--force`):
|
|
80
|
+
|
|
89
81
|
| Type | Config | Description |
|
|
90
82
|
|------|--------|-------------|
|
|
91
83
|
| `xcode` | `{ "type": "xcode", "path": "..." }` | Render icon and write to Xcode asset catalog |
|
|
@@ -101,17 +93,12 @@ The `manifest.json` at the project root defines all asset collections. All colle
|
|
|
101
93
|
"sourceSize": { "width": 440, "height": 956 },
|
|
102
94
|
"borderRadius": 4,
|
|
103
95
|
"templates": [
|
|
104
|
-
{ "src": "
|
|
105
|
-
{ "src": "
|
|
96
|
+
{ "src": "assets/screenshots/01-hero.html", "name": "01-hero", "label": "Hero" },
|
|
97
|
+
{ "src": "assets/screenshots/02-features.html", "name": "02-features", "label": "Features" }
|
|
106
98
|
],
|
|
107
|
-
"
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
"sizes": [
|
|
111
|
-
{ "name": "iphone-6.9", "label": "iPhone 6.9\"", "width": 1320, "height": 2868 },
|
|
112
|
-
{ "name": "iphone-6.7", "label": "iPhone 6.7\"", "width": 1290, "height": 2796 }
|
|
113
|
-
]
|
|
114
|
-
}
|
|
99
|
+
"export": [
|
|
100
|
+
{ "name": "iphone-6.9", "label": "iPhone 6.9\"", "size": { "width": 1320, "height": 2868 } },
|
|
101
|
+
{ "name": "iphone-6.7", "label": "iPhone 6.7\"", "size": { "width": 1290, "height": 2796 } }
|
|
115
102
|
]
|
|
116
103
|
}
|
|
117
104
|
```
|
|
@@ -124,18 +111,13 @@ The `manifest.json` at the project root defines all asset collections. All colle
|
|
|
124
111
|
"sourceSize": { "width": 1024, "height": 1024 },
|
|
125
112
|
"borderRadius": 224,
|
|
126
113
|
"templates": [
|
|
127
|
-
{ "src": "
|
|
114
|
+
{ "src": "assets/icon.html", "name": "icon", "label": "App Icon" }
|
|
128
115
|
],
|
|
129
|
-
"
|
|
130
|
-
{
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
{ "name": "180", "label": "180px", "width": 180, "height": 180 }
|
|
135
|
-
]
|
|
136
|
-
}
|
|
137
|
-
],
|
|
138
|
-
"outputs": [{ "type": "xcode", "path": "../MyApp/Assets.xcassets/AppIcon.appiconset" }]
|
|
116
|
+
"export": [
|
|
117
|
+
{ "name": "1024", "label": "1024px", "size": { "width": 1024, "height": 1024 } },
|
|
118
|
+
{ "name": "180", "label": "180px", "size": { "width": 180, "height": 180 } },
|
|
119
|
+
{ "type": "xcode", "path": "../MyApp/Assets.xcassets/AppIcon.appiconset" }
|
|
120
|
+
]
|
|
139
121
|
}
|
|
140
122
|
```
|
|
141
123
|
|
|
@@ -147,11 +129,11 @@ The `manifest.json` at the project root defines all asset collections. All colle
|
|
|
147
129
|
"sourceSize": { "width": 1024, "height": 1024 },
|
|
148
130
|
"borderRadius": 224,
|
|
149
131
|
"templates": [
|
|
150
|
-
{ "src": "
|
|
151
|
-
{ "src": "
|
|
132
|
+
{ "src": "assets/icons/concept-a.html", "name": "concept-a", "label": "Concept A" },
|
|
133
|
+
{ "src": "assets/icons/concept-b.html", "name": "concept-b", "label": "Concept B" }
|
|
152
134
|
],
|
|
153
|
-
"
|
|
154
|
-
{ "
|
|
135
|
+
"export": [
|
|
136
|
+
{ "name": "1024", "label": "1024px", "size": { "width": 1024, "height": 1024 } }
|
|
155
137
|
]
|
|
156
138
|
}
|
|
157
139
|
```
|
|
@@ -163,16 +145,14 @@ The `manifest.json` at the project root defines all asset collections. All colle
|
|
|
163
145
|
"label": "Logo",
|
|
164
146
|
"sourceSize": { "width": 940, "height": 940 },
|
|
165
147
|
"templates": [
|
|
166
|
-
{ "src": "
|
|
167
|
-
{ "src": "
|
|
148
|
+
{ "src": "assets/logo.svg", "name": "logo", "label": "Logo" },
|
|
149
|
+
{ "src": "assets/logo-dark.svg", "name": "logo-dark", "label": "Dark" }
|
|
168
150
|
],
|
|
169
|
-
"
|
|
170
|
-
{ "
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
],
|
|
175
|
-
"outputs": [{ "type": "copy-source", "format": "svg" }]
|
|
151
|
+
"export": [
|
|
152
|
+
{ "name": "1024", "label": "1024px", "size": { "width": 1024, "height": 1024 } },
|
|
153
|
+
{ "name": "512", "label": "512px", "size": { "width": 512, "height": 512 } },
|
|
154
|
+
{ "type": "copy-source", "format": "svg" }
|
|
155
|
+
]
|
|
176
156
|
}
|
|
177
157
|
```
|
|
178
158
|
|
|
@@ -183,35 +163,96 @@ The `manifest.json` at the project root defines all asset collections. All colle
|
|
|
183
163
|
"label": "Social Cards",
|
|
184
164
|
"sourceSize": { "width": 1200, "height": 630 },
|
|
185
165
|
"templates": [
|
|
186
|
-
{ "src": "
|
|
166
|
+
{ "src": "assets/og/default.html", "name": "default", "label": "Default OG" }
|
|
187
167
|
],
|
|
188
|
-
"
|
|
189
|
-
{ "
|
|
168
|
+
"export": [
|
|
169
|
+
{ "name": "og", "label": "OG Image", "size": { "width": 1200, "height": 630 } }
|
|
190
170
|
]
|
|
191
171
|
}
|
|
192
172
|
```
|
|
193
173
|
|
|
194
174
|
## CLI Reference
|
|
195
175
|
|
|
176
|
+
### All commands
|
|
177
|
+
|
|
178
|
+
| Command | Description |
|
|
179
|
+
|---------|-------------|
|
|
180
|
+
| `open-assets dev [dir]` | Start dev server with live preview and export UI |
|
|
181
|
+
| `open-assets render [dir]` | Render assets headlessly via CLI |
|
|
182
|
+
| `open-assets list [dir]` | List all collections and templates in the config |
|
|
183
|
+
| `open-assets validate [dir]` | Validate assets.json and check referenced files exist. Use `--fix` to interactively remove missing templates |
|
|
184
|
+
| `open-assets init [dir]` | Scaffold a new assets.json and example assets |
|
|
185
|
+
| `open-assets add collection [dir]` | Add a new collection (from presets or custom) |
|
|
186
|
+
| `open-assets add template [dir]` | Add a new template to an existing collection |
|
|
187
|
+
| `open-assets add size [dir]` | Add an export size to a collection |
|
|
188
|
+
| `open-assets skills [dir]` | Install Claude Code skills into a project |
|
|
189
|
+
|
|
196
190
|
### render command flags
|
|
197
191
|
|
|
198
192
|
| Flag | Description |
|
|
199
193
|
|------|-------------|
|
|
200
194
|
| `--collection <id>` | Render only the collection with this ID |
|
|
201
195
|
| `--template <name>` | Render only the template with this name |
|
|
202
|
-
| `--size <name>` | Use a named export size from
|
|
203
|
-
|
|
|
204
|
-
| `--all` | Export at every size defined in the manifest |
|
|
205
|
-
| `--force` | Re-render all assets even if unchanged |
|
|
196
|
+
| `--size <name>` | Use a named export size from config |
|
|
197
|
+
| `-f, --force` | Export at every size and re-render all (ignores cache) |
|
|
206
198
|
| `-o, --output <dir>` | Output directory (default: `./exports`) |
|
|
207
199
|
| `--json` | Output results as JSON |
|
|
208
200
|
| `-q, --quiet` | Suppress progress logs |
|
|
209
201
|
|
|
210
202
|
Flags compose naturally:
|
|
211
203
|
- `--collection screenshots --template 01-hero --size iphone-6.9` → single template, single size
|
|
212
|
-
- `--collection screenshots --template 01-hero --
|
|
204
|
+
- `--collection screenshots --template 01-hero --force` → one template, all sizes
|
|
213
205
|
- `--collection screenshots --size iphone-6.9` → all templates, one size
|
|
214
|
-
|
|
206
|
+
|
|
207
|
+
### add commands
|
|
208
|
+
|
|
209
|
+
Interactive commands for managing the config without editing JSON by hand. All support `--config <path>` for custom config filenames.
|
|
210
|
+
|
|
211
|
+
#### `open-assets add collection`
|
|
212
|
+
|
|
213
|
+
Add a new asset collection. Choose from presets or configure custom dimensions and format.
|
|
214
|
+
|
|
215
|
+
**Presets:**
|
|
216
|
+
| Preset | Source Size | Format |
|
|
217
|
+
|--------|-----------|--------|
|
|
218
|
+
| App Store Screenshots | 440x956 | HTML |
|
|
219
|
+
| App Icon | 1024x1024 | HTML |
|
|
220
|
+
| Logo | 800x800 | SVG |
|
|
221
|
+
| Wordmark | 1200x400 | SVG |
|
|
222
|
+
| Feature Graphic (Google Play) | 1024x500 | HTML |
|
|
223
|
+
| OG Image / Social | 1200x630 | HTML |
|
|
224
|
+
| Custom | user-defined | HTML or SVG |
|
|
225
|
+
|
|
226
|
+
Creates a starter template file and adds the collection to `assets.json` with appropriate export sizes.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
open-assets add collection # interactive preset picker
|
|
230
|
+
open-assets add collection ./my-assets # in a specific directory
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### `open-assets add template`
|
|
234
|
+
|
|
235
|
+
Add a new template (HTML or SVG) to an existing collection. Prompts for which collection, template name/label, and format. Creates the source file in the same directory as the collection's existing templates.
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
open-assets add template # interactive
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### `open-assets add size`
|
|
242
|
+
|
|
243
|
+
Add an export size to a collection. Choose from common device/platform presets or enter custom dimensions.
|
|
244
|
+
|
|
245
|
+
**Size presets include:**
|
|
246
|
+
- iPhone 6.9", 6.7", 6.5", 6.1"
|
|
247
|
+
- iPad 13", 12.9"
|
|
248
|
+
- Google Play Phone, 7" Tablet
|
|
249
|
+
- Mac App Store Retina
|
|
250
|
+
- OG Image, Twitter Card, Product Hunt Gallery
|
|
251
|
+
- Custom dimensions
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
open-assets add size # interactive
|
|
255
|
+
```
|
|
215
256
|
|
|
216
257
|
### Output structure
|
|
217
258
|
|
|
@@ -237,9 +278,11 @@ exports/
|
|
|
237
278
|
|
|
238
279
|
## Creating New Screenshot HTML Files
|
|
239
280
|
|
|
240
|
-
Each screenshot is a standalone HTML file at fixed dimensions.
|
|
281
|
+
Each screenshot is a standalone HTML file at fixed dimensions. You can create them manually or use `open-assets add template` to scaffold one.
|
|
241
282
|
|
|
242
|
-
|
|
283
|
+
Key rules:
|
|
284
|
+
|
|
285
|
+
1. Set `width` and `height` on the body to match `sourceSize` in the config
|
|
243
286
|
2. Set `overflow: hidden` on the body
|
|
244
287
|
3. Reference compiled Tailwind CSS via `<link rel="stylesheet" href="../dist/styles.css" />`
|
|
245
288
|
4. All paths are relative to the project root
|
|
@@ -263,11 +306,13 @@ Example template:
|
|
|
263
306
|
</html>
|
|
264
307
|
```
|
|
265
308
|
|
|
266
|
-
After creating the file, add it to the `templates` array in the relevant collection in `
|
|
309
|
+
After creating the file manually, add it to the `templates` array in the relevant collection in `assets.json`. Or use `open-assets add template` to do both steps at once.
|
|
267
310
|
|
|
268
311
|
## Adding a New Collection
|
|
269
312
|
|
|
270
|
-
|
|
313
|
+
Use `open-assets add collection` to interactively add a collection from presets or with custom dimensions and format (HTML/SVG). This creates the starter template file and updates `assets.json` automatically.
|
|
314
|
+
|
|
315
|
+
Alternatively, add a new object to the `collections` array in `assets.json` manually. Each collection needs a unique `id`, a `sourceSize`, `templates`, and `export`.
|
|
271
316
|
|
|
272
317
|
## Common Export Sizes
|
|
273
318
|
|
|
@@ -284,9 +329,9 @@ Add a new object to the `collections` array in `manifest.json`. Each collection
|
|
|
284
329
|
| Web | OG Image | 1200 | 630 |
|
|
285
330
|
| Mac App Store | Retina | 2880 | 1800 |
|
|
286
331
|
|
|
287
|
-
## Incremental Builds (
|
|
332
|
+
## Incremental Builds (assets.lock)
|
|
288
333
|
|
|
289
|
-
The `render` command maintains a `
|
|
334
|
+
The `render` command maintains a `assets.lock` file with SHA256 checksums of source files. On subsequent renders, unchanged assets are skipped automatically. Use `--force` to re-render everything.
|
|
290
335
|
|
|
291
336
|
Note: only source HTML/SVG files are checksummed. Changes to referenced assets (images in publicDir, compiled CSS) will not trigger re-renders — use `--force` when those change.
|
|
292
337
|
|
|
@@ -294,15 +339,15 @@ Note: only source HTML/SVG files are checksummed. Changes to referenced assets (
|
|
|
294
339
|
|
|
295
340
|
```
|
|
296
341
|
project/
|
|
297
|
-
|
|
298
|
-
|
|
342
|
+
assets.json
|
|
343
|
+
assets.lock # Auto-generated cache (commit this)
|
|
299
344
|
public/ # Shared assets (images, logos, icons, photos)
|
|
300
345
|
logo-round.png
|
|
301
346
|
social/
|
|
302
347
|
youtube.svg
|
|
303
348
|
screenshots/ # Real app screenshots for use in templates
|
|
304
349
|
01.png
|
|
305
|
-
|
|
350
|
+
assets/
|
|
306
351
|
styles.css # Tailwind input
|
|
307
352
|
icon.html # App icon template
|
|
308
353
|
icons/ # Icon concept explorations
|
|
@@ -490,8 +535,8 @@ colored background on the key word. Add the app rating below the logo. Make the
|
|
|
490
535
|
background food photo bleed off the bottom-right edge for visual impact.
|
|
491
536
|
```
|
|
492
537
|
|
|
493
|
-
**Adding a new
|
|
538
|
+
**Adding a new export size:**
|
|
494
539
|
```
|
|
495
|
-
Add Play Store screenshot sizes to the
|
|
496
|
-
|
|
540
|
+
Add Play Store screenshot sizes to the config. Use 1080x2400 as the export size.
|
|
541
|
+
The screenshots can share the same templates.
|
|
497
542
|
```
|