@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 CHANGED
@@ -1,9 +1,26 @@
1
- # open-assets
1
+ <p align="center">
2
+ <img src="public/img/banner/1280/banner.png" alt="Open Assets" width="100%" />
3
+ </p>
4
+
5
+ # Open Assets
6
+
7
+ The LLM first way to do graphics all from your git repo.
2
8
 
3
9
  Dev server and export tool for app marketing assets. Like Storybook, but for marketing assets.
4
10
 
5
11
  Design your App Store screenshots, app icons, logos, OG images, favicons, and more in HTML/CSS/SVG, preview them live in the browser, and export pixel-perfect PNGs at any size — including directly into your Xcode project.
6
12
 
13
+ ## Examples
14
+
15
+ <p><img src="public/img/examples/only-recipes-icon.png" width="14" height="14" /> <a href="https://onlyrecipes.app"><strong>Only Recipes</strong></a> (<a href="examples/only-recipes">examples</a>)</p>
16
+ <img src="public/img/examples/only-recipes.png" alt="Only Recipes screenshots" width="100%" />
17
+
18
+ <p><img src="public/img/examples/cat-iq-test-icon.png" width="14" height="14" /> <a href="https://apps.apple.com/us/app/cat-iq-test/id6759520249"><strong>Cat IQ Test</strong></a> (<a href="examples/cat-iq-test">examples</a>)</p>
19
+ <img src="public/img/examples/cat-iq-test.png" alt="Cat IQ Test screenshots" width="100%" />
20
+
21
+ <p><img src="public/img/examples/no-context-icon.png" width="14" height="14" /> <a href="https://nocontextbot.com"><strong>No Context Bot</strong></a> (<a href="examples/no-context">examples</a>)</p>
22
+ <img src="public/img/examples/no-context.png" alt="No Context Bot screenshots" width="100%" />
23
+
7
24
  ## Installation
8
25
 
9
26
  ```bash
@@ -30,6 +47,22 @@ npx open-assets init
30
47
  npx open-assets dev
31
48
  ```
32
49
 
50
+ ## Shell Alias
51
+
52
+ Add an alias to your shell profile for a shorter command:
53
+
54
+ ```bash
55
+ echo 'alias oa="npx open-assets"' >> ~/.zshrc && source ~/.zshrc
56
+ ```
57
+
58
+ Then use `oa` anywhere:
59
+
60
+ ```bash
61
+ oa dev
62
+ oa render --force
63
+ oa init
64
+ ```
65
+
33
66
  ## Concepts
34
67
 
35
68
  open-assets uses a simple, unified data model. Every asset type follows the same structure: **N templates × M export sizes**.
@@ -39,7 +72,6 @@ open-assets uses a simple, unified data model. Every asset type follows the same
39
72
  | **Collection** | A named group of related assets sharing the same source size and export sizes. One tab in the dev UI. |
40
73
  | **Template** | A single source file (HTML or SVG) that produces one image per export size. |
41
74
  | **Export Size** | A named output dimension that templates are rendered at (e.g., "iPhone 6.9" → 1320×2868). |
42
- | **Platform** | Optional grouping label for related export sizes (e.g., "App Store", "Play Store"). |
43
75
  | **Source Size** | The dimensions the HTML template is authored at. Puppeteer scales from source → export size. |
44
76
  | **Output** | An optional post-render action (e.g., write to Xcode `.appiconset`, copy SVG source). |
45
77
 
@@ -51,7 +83,7 @@ open-assets uses a simple, unified data model. Every asset type follows the same
51
83
  npx open-assets init
52
84
  ```
53
85
 
54
- Creates a `manifest.json`, sample HTML templates, and a `public/` directory for shared assets.
86
+ Creates a `assets.json`, sample HTML templates, and a `public/` directory for shared assets.
55
87
 
56
88
  ### 2. Design with Claude Code
57
89
 
@@ -74,13 +106,13 @@ your eye as you scroll. Use bold headlines with highlighted keywords, phone
74
106
  mockups with real app screenshots, and close with reviews + a CTA.
75
107
  ```
76
108
 
77
- Claude reads your `manifest.json` and `publicDir` to understand the project structure, then creates screenshot HTML files using the design patterns in the skill.
109
+ Claude reads your `assets.json` and `publicDir` to understand the project structure, then creates screenshot HTML files using the design patterns in the skill.
78
110
 
79
111
  ### 3. Preview
80
112
 
81
113
  ```bash
82
114
  # With Tailwind
83
- concurrently "npx @tailwindcss/cli -i src/styles.css -o dist/styles.css --watch" "npx open-assets dev"
115
+ concurrently "npx @tailwindcss/cli -i assets/styles.css -o dist/styles.css --watch" "npx open-assets dev"
84
116
 
85
117
  # Without Tailwind
86
118
  npx open-assets dev
@@ -91,7 +123,7 @@ Opens a live preview UI at `http://localhost:3200` with zoom/pan controls and ex
91
123
  ### 4. Export
92
124
 
93
125
  ```bash
94
- npx open-assets render --all
126
+ npx open-assets render --force
95
127
  ```
96
128
 
97
129
  Exports every template at every configured export size into `./exports/`, organized as `exports/{collection}/{size}/{template}.png`.
@@ -109,7 +141,7 @@ Capture real app screenshots via Playwright or XCTest, then use them inside your
109
141
  npx playwright test --project=screenshots
110
142
 
111
143
  # 2. Export marketing screenshots with those captures embedded
112
- npx open-assets render --all
144
+ npx open-assets render --force
113
145
  ```
114
146
 
115
147
  Reference captured screenshots in your HTML templates:
@@ -117,10 +149,10 @@ Reference captured screenshots in your HTML templates:
117
149
  <img src="../../public/screenshots/01-home.png" style="width: 100%; height: 100%; object-fit: cover;" />
118
150
  ```
119
151
 
120
- The `manifest.json` `command` field stores the export command so automation tools know what to run:
152
+ The `assets.json` `command` field stores the export command so automation tools know what to run:
121
153
  ```json
122
154
  {
123
- "command": "npx open-assets render --all"
155
+ "command": "npx open-assets render --force"
124
156
  }
125
157
  ```
126
158
 
@@ -139,7 +171,7 @@ open-assets dev --no-open # Don't auto-open browser
139
171
  open-assets dev --quiet # Suppress server logs
140
172
  open-assets dev --ci # CI mode (quiet + no browser)
141
173
  open-assets dev --static-dir ./shared # Serve additional static directories
142
- open-assets dev --manifest config.json # Use a custom manifest filename
174
+ open-assets dev --config config.json # Use a custom config filename
143
175
  ```
144
176
 
145
177
  Options:
@@ -150,7 +182,7 @@ Options:
150
182
  | `--no-open` | `OPEN_ASSETS_NO_OPEN` | — | Don't auto-open the browser |
151
183
  | `-q, --quiet` | `OPEN_ASSETS_QUIET` | `false` | Suppress server logs |
152
184
  | `--ci` | `CI` | `false` | CI mode: quiet + no browser |
153
- | `--manifest <path>` | `OPEN_ASSETS_MANIFEST` | `manifest.json` | Path to manifest file |
185
+ | `--config <path>` | `OPEN_ASSETS_CONFIG` | `assets.json` | Path to config file |
154
186
  | `--static-dir <dirs...>` | — | — | Additional static directories to serve |
155
187
  | `--render-timeout <ms>` | `OPEN_ASSETS_RENDER_TIMEOUT` | `30000` | Puppeteer render timeout |
156
188
 
@@ -160,13 +192,11 @@ Render assets headlessly via the command line, without opening a browser.
160
192
 
161
193
  ```bash
162
194
  open-assets render # Render all collections at source size
163
- open-assets render --all # Export at EVERY configured size
195
+ open-assets render --force # Export at every size, ignore cache
164
196
  open-assets render --collection screenshots # Render a specific collection
165
197
  open-assets render --template 01-hero # Render a single template
166
198
  open-assets render --size iphone-6.9 # Use a named export size
167
- open-assets render --platform "App Store" # Render all sizes for a platform
168
199
  open-assets render --width 1320 --height 2868 # Custom size
169
- open-assets render --force # Re-render everything, ignore cache
170
200
  open-assets render -o ./build # Custom output directory
171
201
  open-assets render --json # Output results as JSON (for CI)
172
202
  open-assets render --quiet # Suppress progress logs
@@ -177,14 +207,12 @@ Options:
177
207
  |------|---------|---------|-------------|
178
208
  | `--collection <id>` | — | all | Render only the collection with this ID |
179
209
  | `--template <name>` | — | all | Render only the template with this name |
180
- | `--size <name>` | — | — | Use a named export size from manifest |
181
- | `--platform <name>` | — | — | Render only sizes for this platform |
210
+ | `--size <name>` | — | — | Use a named export size from config |
182
211
  | `--width <px>` | — | — | Custom output width |
183
212
  | `--height <px>` | — | — | Custom output height |
184
- | `--all` | — | — | Export at every size defined in the manifest |
185
- | `--force` | — | — | Re-render all assets even if unchanged |
213
+ | `-f, --force` | — | — | Export at every size and re-render all (ignores cache) |
186
214
  | `-o, --output <dir>` | `OPEN_ASSETS_OUTPUT` | `./exports` | Output directory |
187
- | `--manifest <path>` | `OPEN_ASSETS_MANIFEST` | `manifest.json` | Path to manifest file |
215
+ | `--config <path>` | `OPEN_ASSETS_CONFIG` | `assets.json` | Path to config file |
188
216
  | `--parallel <count>` | `OPEN_ASSETS_PARALLEL` | `1` | Number of parallel renders |
189
217
  | `--render-timeout <ms>` | `OPEN_ASSETS_RENDER_TIMEOUT` | `30000` | Puppeteer render timeout |
190
218
  | `--json` | — | — | Output results as JSON |
@@ -196,13 +224,10 @@ Options:
196
224
  open-assets render --collection screenshots --template 01-hero --size iphone-6.9
197
225
 
198
226
  # One template at all device sizes
199
- open-assets render --collection screenshots --template 01-hero --all
227
+ open-assets render --collection screenshots --template 01-hero --force
200
228
 
201
229
  # All templates at one device size
202
230
  open-assets render --collection screenshots --size iphone-6.9
203
-
204
- # All templates for a specific platform
205
- open-assets render --collection screenshots --platform "App Store"
206
231
  ```
207
232
 
208
233
  **Output structure**: `exports/{collection}/{size}/{template}.png`
@@ -231,7 +256,7 @@ exports/
231
256
 
232
257
  ### `open-assets list [dir]`
233
258
 
234
- List all collections and templates defined in the manifest.
259
+ List all collections and templates defined in the config.
235
260
 
236
261
  ```bash
237
262
  open-assets list # Pretty-print asset tree
@@ -240,7 +265,7 @@ open-assets list --json # Output as JSON
240
265
 
241
266
  ### `open-assets validate [dir]`
242
267
 
243
- Validate the manifest and check that all referenced source files exist.
268
+ Validate the config and check that all referenced source files exist.
244
269
 
245
270
  ```bash
246
271
  open-assets validate # Check current directory
@@ -263,7 +288,7 @@ npx skills add https://github.com/Parra-Inc/open-assets --skill open-assets
263
288
 
264
289
  ### `open-assets init [dir]`
265
290
 
266
- Scaffold a new project with a `manifest.json`, sample HTML templates, and a `public/` directory.
291
+ Scaffold a new project with a `assets.json`, sample HTML templates, and a `public/` directory.
267
292
 
268
293
  ```bash
269
294
  open-assets init # Current directory
@@ -272,14 +297,14 @@ open-assets init ./my-assets # Specific directory
272
297
 
273
298
  ## Manifest Format
274
299
 
275
- Your project needs a `manifest.json` at its root. This file defines the collections shown in the viewer and all export options.
300
+ Your project needs a `assets.json` at its root. This file defines the collections shown in the viewer and all export options.
276
301
 
277
302
  ```json
278
303
  {
279
304
  "version": 1,
280
305
  "name": "My App",
281
306
  "publicDir": "public",
282
- "command": "npx open-assets render --all",
307
+ "command": "npx open-assets render --force",
283
308
  "collections": [ ... ]
284
309
  }
285
310
  ```
@@ -305,19 +330,12 @@ All collections follow the same structure. No `type` field needed.
305
330
  "sourceSize": { "width": 440, "height": 956 },
306
331
  "borderRadius": 4,
307
332
  "templates": [
308
- { "src": "src/screenshots/01-hero.html", "name": "01-hero", "label": "Hero" },
309
- { "src": "src/screenshots/02-features.html", "name": "02-features", "label": "Features" }
310
- ],
311
- "exportSizes": [
312
- {
313
- "platform": "App Store",
314
- "sizes": [
315
- { "name": "iphone-6.9", "label": "iPhone 6.9\"", "width": 1320, "height": 2868 },
316
- { "name": "iphone-6.7", "label": "iPhone 6.7\"", "width": 1290, "height": 2796 }
317
- ]
318
- }
333
+ { "src": "assets/screenshots/01-hero.html", "name": "01-hero", "label": "Hero" },
334
+ { "src": "assets/screenshots/02-features.html", "name": "02-features", "label": "Features" }
319
335
  ],
320
- "outputs": [
336
+ "export": [
337
+ { "name": "iphone-6.9", "label": "iPhone 6.9\"", "size": { "width": 1320, "height": 2868 } },
338
+ { "name": "iphone-6.7", "label": "iPhone 6.7\"", "size": { "width": 1290, "height": 2796 } },
321
339
  { "type": "xcode", "path": "../MyApp/Assets.xcassets/AppIcon.appiconset" }
322
340
  ]
323
341
  }
@@ -333,14 +351,18 @@ All collections follow the same structure. No `type` field needed.
333
351
  | `templates[].src` | string | Path to HTML/SVG file (relative to project root) |
334
352
  | `templates[].name` | string | Filename for exports (used by `--template` flag) |
335
353
  | `templates[].label` | string | Display label |
336
- | `exportSizes` | array | Grouped export size definitions |
337
- | `exportSizes[].platform` | string | Optional grouping label |
338
- | `exportSizes[].sizes[]` | object | `{ name, label, width, height }` |
339
- | `outputs` | array | Optional post-render actions |
354
+ | `export` | array | Array of export sizes and output actions |
355
+ | `export[].name` | string | Size identifier (used by `--size` flag) |
356
+ | `export[].label` | string | Display label |
357
+ | `export[].size` | object | `{ width, height }` — output dimensions in pixels |
358
+ | `export[].outFile` | string | Optional output path for this size (supports .png, .jpg) |
359
+ | `export[].type` | string | Output action type (`xcode`, `copy-source`). Entries with `type` are post-render actions, not sizes |
340
360
  | `customExport` | object | Optional `{ defaultWidth, defaultHeight }` for custom size UI |
341
361
 
342
362
  ### Output Types
343
363
 
364
+ Export entries with a `type` field are post-render actions (run with `--force`):
365
+
344
366
  | Type | Config | Description |
345
367
  |------|--------|-------------|
346
368
  | `xcode` | `{ "type": "xcode", "path": "..." }` | Render icon and write to Xcode asset catalog |
@@ -356,19 +378,14 @@ All collections follow the same structure. No `type` field needed.
356
378
  "sourceSize": { "width": 440, "height": 956 },
357
379
  "borderRadius": 4,
358
380
  "templates": [
359
- { "src": "src/screenshots/01-hero.html", "name": "01-hero", "label": "Hero" },
360
- { "src": "src/screenshots/02-import.html", "name": "02-import", "label": "Import" }
381
+ { "src": "assets/screenshots/01-hero.html", "name": "01-hero", "label": "Hero" },
382
+ { "src": "assets/screenshots/02-import.html", "name": "02-import", "label": "Import" }
361
383
  ],
362
- "exportSizes": [
363
- {
364
- "platform": "App Store",
365
- "sizes": [
366
- { "name": "iphone-6.9", "label": "iPhone 6.9\"", "width": 1320, "height": 2868 },
367
- { "name": "iphone-6.7", "label": "iPhone 6.7\"", "width": 1290, "height": 2796 },
368
- { "name": "iphone-6.5", "label": "iPhone 6.5\"", "width": 1284, "height": 2778 },
369
- { "name": "iphone-6.1", "label": "iPhone 6.1\"", "width": 1179, "height": 2556 }
370
- ]
371
- }
384
+ "export": [
385
+ { "name": "iphone-6.9", "label": "iPhone 6.9\"", "size": { "width": 1320, "height": 2868 } },
386
+ { "name": "iphone-6.7", "label": "iPhone 6.7\"", "size": { "width": 1290, "height": 2796 } },
387
+ { "name": "iphone-6.5", "label": "iPhone 6.5\"", "size": { "width": 1284, "height": 2778 } },
388
+ { "name": "iphone-6.1", "label": "iPhone 6.1\"", "size": { "width": 1179, "height": 2556 } }
372
389
  ]
373
390
  }
374
391
  ```
@@ -381,19 +398,12 @@ All collections follow the same structure. No `type` field needed.
381
398
  "sourceSize": { "width": 1024, "height": 1024 },
382
399
  "borderRadius": 224,
383
400
  "templates": [
384
- { "src": "src/icon.html", "name": "icon", "label": "App Icon" }
401
+ { "src": "assets/icon.html", "name": "icon", "label": "App Icon" }
385
402
  ],
386
- "exportSizes": [
387
- {
388
- "platform": "Apple",
389
- "sizes": [
390
- { "name": "1024", "label": "1024px", "width": 1024, "height": 1024 },
391
- { "name": "180", "label": "180px", "width": 180, "height": 180 },
392
- { "name": "120", "label": "120px", "width": 120, "height": 120 }
393
- ]
394
- }
395
- ],
396
- "outputs": [
403
+ "export": [
404
+ { "name": "1024", "label": "1024px", "size": { "width": 1024, "height": 1024 } },
405
+ { "name": "180", "label": "180px", "size": { "width": 180, "height": 180 } },
406
+ { "name": "120", "label": "120px", "size": { "width": 120, "height": 120 } },
397
407
  { "type": "xcode", "path": "../MyApp/Assets.xcassets/AppIcon.appiconset" }
398
408
  ]
399
409
  }
@@ -407,17 +417,13 @@ All collections follow the same structure. No `type` field needed.
407
417
  "sourceSize": { "width": 1024, "height": 1024 },
408
418
  "borderRadius": 224,
409
419
  "templates": [
410
- { "src": "src/icons/concept-a.html", "name": "concept-a", "label": "Concept A" },
411
- { "src": "src/icons/concept-b.html", "name": "concept-b", "label": "Concept B" },
412
- { "src": "src/icons/seasonal-winter.html", "name": "seasonal-winter", "label": "Winter" }
420
+ { "src": "assets/icons/concept-a.html", "name": "concept-a", "label": "Concept A" },
421
+ { "src": "assets/icons/concept-b.html", "name": "concept-b", "label": "Concept B" },
422
+ { "src": "assets/icons/seasonal-winter.html", "name": "seasonal-winter", "label": "Winter" }
413
423
  ],
414
- "exportSizes": [
415
- {
416
- "sizes": [
417
- { "name": "1024", "label": "1024px", "width": 1024, "height": 1024 },
418
- { "name": "512", "label": "512px", "width": 512, "height": 512 }
419
- ]
420
- }
424
+ "export": [
425
+ { "name": "1024", "label": "1024px", "size": { "width": 1024, "height": 1024 } },
426
+ { "name": "512", "label": "512px", "size": { "width": 512, "height": 512 } }
421
427
  ]
422
428
  }
423
429
  ```
@@ -429,20 +435,14 @@ All collections follow the same structure. No `type` field needed.
429
435
  "label": "Logo",
430
436
  "sourceSize": { "width": 940, "height": 940 },
431
437
  "templates": [
432
- { "src": "src/logo.svg", "name": "logo", "label": "Logo" },
433
- { "src": "src/logo-dark.svg", "name": "logo-dark", "label": "Dark" },
434
- { "src": "src/wordmark.svg", "name": "wordmark", "label": "Wordmark" }
435
- ],
436
- "exportSizes": [
437
- {
438
- "sizes": [
439
- { "name": "2048", "label": "2048px", "width": 2048, "height": 2048 },
440
- { "name": "1024", "label": "1024px", "width": 1024, "height": 1024 },
441
- { "name": "512", "label": "512px", "width": 512, "height": 512 }
442
- ]
443
- }
438
+ { "src": "assets/logo.svg", "name": "logo", "label": "Logo" },
439
+ { "src": "assets/logo-dark.svg", "name": "logo-dark", "label": "Dark" },
440
+ { "src": "assets/wordmark.svg", "name": "wordmark", "label": "Wordmark" }
444
441
  ],
445
- "outputs": [
442
+ "export": [
443
+ { "name": "2048", "label": "2048px", "size": { "width": 2048, "height": 2048 } },
444
+ { "name": "1024", "label": "1024px", "size": { "width": 1024, "height": 1024 } },
445
+ { "name": "512", "label": "512px", "size": { "width": 512, "height": 512 } },
446
446
  { "type": "copy-source", "format": "svg" }
447
447
  ]
448
448
  }
@@ -455,10 +455,10 @@ All collections follow the same structure. No `type` field needed.
455
455
  "label": "Social Cards",
456
456
  "sourceSize": { "width": 1200, "height": 630 },
457
457
  "templates": [
458
- { "src": "src/og/default.html", "name": "default", "label": "Default OG" }
458
+ { "src": "assets/og/default.html", "name": "default", "label": "Default OG" }
459
459
  ],
460
- "exportSizes": [
461
- { "sizes": [{ "name": "og", "label": "OG Image", "width": 1200, "height": 630 }] }
460
+ "export": [
461
+ { "name": "og", "label": "OG Image", "size": { "width": 1200, "height": 630 } }
462
462
  ]
463
463
  }
464
464
  ```
@@ -470,34 +470,24 @@ All collections follow the same structure. No `type` field needed.
470
470
  "label": "Favicons",
471
471
  "sourceSize": { "width": 512, "height": 512 },
472
472
  "templates": [
473
- { "src": "src/favicon.html", "name": "favicon", "label": "Favicon" }
473
+ { "src": "assets/favicon.html", "name": "favicon", "label": "Favicon" }
474
474
  ],
475
- "exportSizes": [
476
- {
477
- "platform": "Web",
478
- "sizes": [
479
- { "name": "512", "label": "512px", "width": 512, "height": 512 },
480
- { "name": "192", "label": "192px", "width": 192, "height": 192 },
481
- { "name": "32", "label": "32px", "width": 32, "height": 32 },
482
- { "name": "16", "label": "16px", "width": 16, "height": 16 }
483
- ]
484
- },
485
- {
486
- "platform": "Apple",
487
- "sizes": [
488
- { "name": "apple-touch", "label": "Apple Touch Icon", "width": 180, "height": 180 }
489
- ]
490
- }
475
+ "export": [
476
+ { "name": "512", "label": "512px", "size": { "width": 512, "height": 512 } },
477
+ { "name": "192", "label": "192px", "size": { "width": 192, "height": 192 } },
478
+ { "name": "32", "label": "32px", "size": { "width": 32, "height": 32 }, "outFile": "public/favicon.png" },
479
+ { "name": "16", "label": "16px", "size": { "width": 16, "height": 16 } },
480
+ { "name": "apple-touch", "label": "Apple Touch Icon", "size": { "width": 180, "height": 180 }, "outFile": "public/apple-touch-icon.png" }
491
481
  ]
492
482
  }
493
483
  ```
494
484
 
495
- ## Incremental Builds with manifest.lock
485
+ ## Incremental Builds with assets.lock
496
486
 
497
- The `render` command maintains a `manifest.lock` file that stores SHA256 checksums of each source HTML/SVG file. On subsequent renders, unchanged assets are skipped automatically:
487
+ The `render` command maintains a `assets.lock` file that stores SHA256 checksums of each source HTML/SVG file. On subsequent renders, unchanged assets are skipped automatically:
498
488
 
499
489
  ```
500
- $ open-assets render --all
490
+ $ open-assets render --force
501
491
  Skipping 01-hero at 1320x2868 (unchanged)
502
492
  Rendering 03-new-screen at 1320x2868...
503
493
  → exports/screenshots/iphone-6.9/03-new-screen.png
@@ -509,7 +499,7 @@ Use `--force` to re-render everything regardless of the cache.
509
499
 
510
500
  **Limitation**: Only source HTML/SVG files are checksummed. Changes to referenced assets (images in `publicDir`, compiled Tailwind CSS) won't trigger re-renders — use `--force` when those change.
511
501
 
512
- Add `manifest.lock` to your `.gitignore` (done automatically by `open-assets init`).
502
+ Commit `assets.lock` to your repository so teammates and CI benefit from the cache.
513
503
 
514
504
  ## CI/CD with GitHub Actions
515
505
 
@@ -521,7 +511,7 @@ on:
521
511
  push:
522
512
  paths:
523
513
  - 'marketing/screenshots/**'
524
- - 'marketing/screenshots/manifest.json'
514
+ - 'marketing/screenshots/assets.json'
525
515
  jobs:
526
516
  export:
527
517
  runs-on: ubuntu-latest
@@ -532,7 +522,7 @@ jobs:
532
522
  node-version: 20
533
523
  - run: npm ci
534
524
  - run: npx open-assets validate
535
- - run: npx open-assets render --all --json --quiet
525
+ - run: npx open-assets render --force --json --quiet
536
526
  - uses: actions/upload-artifact@v4
537
527
  with:
538
528
  name: screenshots
@@ -558,15 +548,15 @@ jobs:
558
548
 
559
549
  ```
560
550
  project/
561
- manifest.json
562
- manifest.lock # Auto-generated cache (gitignored)
551
+ assets.json
552
+ assets.lock # Auto-generated cache (commit this)
563
553
  public/ # Shared assets (configured via publicDir)
564
554
  logo-round.png
565
555
  social/
566
556
  youtube.svg
567
557
  screenshots/ # Real app screenshots from UI tests
568
558
  01-home.png
569
- src/
559
+ assets/
570
560
  styles.css # Tailwind input
571
561
  icon.html # App icon template
572
562
  logo.svg # Vector logo template