#187 by GitHub stars
ParthJadhav/app-store-screenshots
Use when building App Store or Google Play screenshot pages, generating exportable marketing screenshots for iOS, macOS, and/or Android apps, or scaffolding a screenshot editor with Next.js. Triggers on app store, mac app store, play store, screenshots, marketing assets, html-to-image, phone mockup,
History
Review weekly changes to SKILL.md before updating. Dates mark the weeks in which we observed each version; checks apply to the recorded commits.
View weekly changes
skills/app-store-screenshots/SKILL.md
Week of Nov 2, 2026 · +0 / −0 lines
Before Network or broad shell → After Network or broad shell
5386f227360b → 5386f227360b
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 5386f227360b
No earlier version was observed for comparison.
Heat
7.0- Star growth, last 7 daysAhead of 85% of tracked repositories
- Star growth, last 24 hoursAhead of 86% of tracked repositories
- Rank movement, last 7 daysAhead of 67% of tracked repositories
- Commits, last 30 daysAhead of 78% of tracked repositories
- Accounts asking about it through MCP, last 7 daysNot available
- Hot votes this weekAhead of 0% of tracked repositories
Heat never changes the order by stars, and sponsorship never touches it. How heat works
Stars over time
Individual skills in this repository
- app-store-screenshots
skills/app-store-screenshots
How it works
app-store-screenshots
When your agent uses it
Use when building App Store or Google Play screenshot pages, generating exportable marketing screenshots for iOS, macOS, and/or Android apps, or scaffolding a screenshot editor with Next.js. Triggers on app store, mac app store, play store, screenshots, marketing assets, html-to-image, phone mockup, mac mockup, android screenshots, feature graphic.
What it loads into context
Frontmatter
84 tokensAlways available to the agent
SKILL.md
15,544 tokensWhen the skill is activated
Reference documents
165,949 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Overview
- Core Principle
- What This Skill Does
- Step 0: Probe for Existing Screenshot Projects
- Step 1: Gather Input (Before Scaffolding)
- Step 2: Scaffold the Template
- Step 3: Coach the User on Copy
- Visual Design Principles
- Step 4: Localization
- Step 5: Export Time
- Step 6: Final QA Gate
- Common Mistakes
- Project Migration
- Template Reference
- Hand-off Behavior
Scripts executed separately
scripts/capture-iphone-duo.shtemplate/e2e-army/chrome-provider.tstemplate/e2e-army/server.cjstemplate/e2e.config.tstemplate/next.config.mjstemplate/package.jsontemplate/postcss.config.mjstemplate/src/app/api/frames/route.tstemplate/src/app/api/project/route.tstemplate/src/app/api/upload-font/route.tstemplate/src/app/api/upload/route.tstemplate/src/app/fonts/imported/[filename]/route.tstemplate/src/app/frames/[filename]/route.tstemplate/src/app/layout.tsxtemplate/src/app/page.tsxtemplate/src/app/screenshots/uploaded/[filename]/route.tstemplate/src/components/editor/background-controls.tsxtemplate/src/components/editor/callout-controls.tsxtemplate/src/components/editor/create-image-mask.tstemplate/src/components/editor/device-frames.tsxtemplate/src/components/editor/font-importer.tsxtemplate/src/components/editor/image-element-canvas.tsxtemplate/src/components/editor/inspector.tsxtemplate/src/components/editor/preview-stage.tsxtemplate/src/components/editor/scene-layers.tsxtemplate/src/components/editor/scene-playground.tsxtemplate/src/components/editor/screenshot-editor.tsxtemplate/src/components/editor/screenshot-picker.tsxtemplate/src/components/editor/sidebar.tsxtemplate/src/components/editor/slide-canvas.tsxtemplate/src/components/editor/slide-thumb.tsxtemplate/src/components/editor/style-lab.tsxtemplate/src/components/editor/toolbar.tsxtemplate/src/components/ui/button.tsxtemplate/src/components/ui/card.tsxtemplate/src/components/ui/dialog.tsxtemplate/src/components/ui/dropdown-menu.tsxtemplate/src/components/ui/input.tsxtemplate/src/components/ui/label.tsxtemplate/src/components/ui/popover.tsxtemplate/src/components/ui/select.tsxtemplate/src/components/ui/tabs.tsxtemplate/src/components/ui/textarea.tsxtemplate/src/components/ui/tooltip.tsxtemplate/src/lib/clean-hex-color.tstemplate/src/lib/clean-imported-font.tstemplate/src/lib/constants.tstemplate/src/lib/contrast.tstemplate/src/lib/copy-ideas.tstemplate/src/lib/defaults.tstemplate/src/lib/elements.tstemplate/src/lib/export-assets.tstemplate/src/lib/export-render.tstemplate/src/lib/frame-assets.tstemplate/src/lib/image-cache.tstemplate/src/lib/locale.tstemplate/src/lib/measure-frame.tstemplate/src/lib/png-encode.tstemplate/src/lib/png-rgb.tstemplate/src/lib/png-worker.tstemplate/src/lib/project-validation.tstemplate/src/lib/request-body.tstemplate/src/lib/request-guard.tstemplate/src/lib/scene.tstemplate/src/lib/serve-asset.tstemplate/src/lib/storage.tstemplate/src/lib/style-lab.tstemplate/src/lib/types.tstemplate/src/lib/typography.tstemplate/src/lib/utils.tstemplate/src/lib/write-asset.tstemplate/tailwind.config.tstemplate/tests/editor.e2e.tstemplate/tests/harness/api-bug-bash.cjstemplate/tests/harness/bug-bash.cjstemplate/tests/harness/migration-fixture.cjstemplate/tests/harness/run.cjstemplate/tests/harness/ui-bug-bash.cjstemplate/tests/regression.e2e.tstemplate/tests/scene-style-lab.e2e.tstemplate/tests/targets.e2e.ts
Files
SKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytesSKILL.mdcore65,029 bytescopy-ideas.mddoc9,079 bytesiphone-duo.mddoc14,090 bytesmockup.pngasset95,028 bytesscripts/capture-iphone-duo.shruns7,148 bytesstyle-prompts.mddoc10,484 bytesstyle-prompts/01-retro-rubberhose-mascot.mddoc25,329 bytesstyle-prompts/02-moody-curated-dating.mddoc27,637 bytesstyle-prompts/03-paper-sticker-skeuomorphic.mddoc29,001 bytesstyle-prompts/04-dreamy-pastel-couples.mddoc25,260 bytesstyle-prompts/05-hand-drawn-editorial-tasks.mddoc24,102 bytesstyle-prompts/06-glossy-3d-kbeauty-creator.mddoc21,620 bytesstyle-prompts/07-liquid-glass-aurora.mddoc20,243 bytesstyle-prompts/08-swiss-grid-bold.mddoc20,500 bytesstyle-prompts/09-neon-athletic-night.mddoc22,884 bytesstyle-prompts/10-magazine-cover-editorial.mddoc25,850 bytesstyle-prompts/11-candy-pop-social.mddoc20,359 bytesstyle-prompts/12-soft-clay-wellness.mddoc21,145 bytesstyle-prompts/13-midnight-glow-pro.mddoc34,486 bytesstyle-prompts/14-risograph-zine.mddoc35,853 bytesstyle-prompts/15-bento-keynote-grid.mddoc32,047 bytesstyle-prompts/16-toybox-primary.mddoc36,005 bytesstyle-prompts/17-quiet-japandi.mddoc30,726 bytesstyle-prompts/18-vintage-travel-poster.mddoc38,475 bytesstyle-prompts/_QUALITY_BAR.mddoc13,959 bytestemplate/.github/workflows/e2e.ymlasset783 bytestemplate/.gitignoreasset470 bytestemplate/README.mddoc24,709 bytestemplate/app-store-screenshots.jsonasset8,540 bytestemplate/bun.lockasset73,634 bytestemplate/components.jsonasset449 bytestemplate/docs/testing/e2e.mddoc24,064 bytestemplate/e2e-army/chrome-provider.tsruns1,706 bytestemplate/e2e-army/server.cjsruns1,610 bytestemplate/e2e.config.tsruns824 bytestemplate/next.config.mjsruns294 bytestemplate/package.jsonruns1,596 bytestemplate/postcss.config.mjsruns81 bytestemplate/public/fonts/imported/.gitkeepasset1 bytestemplate/public/frames/.gitkeepasset0 bytestemplate/public/frames/duo-inner-landscape.pngasset795,922 bytestemplate/public/frames/duo-inner-portrait.pngasset821,757 bytestemplate/public/frames/duo-outer-landscape.pngasset380,872 bytestemplate/public/frames/duo-outer-portrait.pngasset457,010 bytestemplate/public/frames/ipad-portrait.pngasset314,484 bytestemplate/public/frames/iphone-portrait.pngasset489,833 bytestemplate/public/frames/mac.pngasset676,527 bytestemplate/public/frames/tv.pngasset853,962 bytestemplate/public/frames/watch.pngasset239,889 bytestemplate/public/mockup.pngasset95,028 bytestemplate/public/screenshots/android/phone/en/.gitkeepasset0 bytestemplate/public/screenshots/apple/ipad/en/.gitkeepasset0 bytestemplate/public/screenshots/apple/iphone/en/.gitkeepasset0 bytestemplate/src/app/api/frames/route.tsruns1,532 bytestemplate/src/app/api/project/route.tsruns3,522 bytestemplate/src/app/api/upload-font/route.tsruns4,580 bytestemplate/src/app/api/upload/route.tsruns3,263 bytestemplate/src/app/fonts/imported/[filename]/route.tsruns359 bytestemplate/src/app/frames/[filename]/route.tsruns1,349 bytestemplate/src/app/globals.cssasset3,299 bytestemplate/src/app/layout.tsxruns487 bytestemplate/src/app/page.tsxruns141 bytestemplate/src/app/screenshots/uploaded/[filename]/route.tsruns328 bytestemplate/src/components/editor/background-controls.tsxruns2,972 bytestemplate/src/components/editor/callout-controls.tsxruns8,067 bytestemplate/src/components/editor/create-image-mask.tsruns586 bytestemplate/src/components/editor/device-frames.tsxruns21,429 bytestemplate/src/components/editor/font-importer.tsxruns3,121 bytestemplate/src/components/editor/image-element-canvas.tsxruns1,400 bytestemplate/src/components/editor/inspector.tsxruns41,841 bytestemplate/src/components/editor/preview-stage.tsxruns10,678 bytestemplate/src/components/editor/scene-layers.tsxruns14,593 bytestemplate/src/components/editor/scene-playground.tsxruns10,785 bytestemplate/src/components/editor/screenshot-editor.tsxruns38,529 bytestemplate/src/components/editor/screenshot-picker.tsxruns7,328 bytestemplate/src/components/editor/sidebar.tsxruns4,429 bytestemplate/src/components/editor/slide-canvas.tsxruns56,174 bytestemplate/src/components/editor/slide-thumb.tsxruns6,063 bytestemplate/src/components/editor/style-lab.tsxruns17,791 bytestemplate/src/components/editor/toolbar.tsxruns15,003 bytestemplate/src/components/ui/button.tsxruns1,813 bytestemplate/src/components/ui/card.tsxruns1,183 bytestemplate/src/components/ui/dialog.tsxruns3,195 bytestemplate/src/components/ui/dropdown-menu.tsxruns3,776 bytestemplate/src/components/ui/input.tsxruns717 bytestemplate/src/components/ui/label.tsxruns714 bytestemplate/src/components/ui/popover.tsxruns1,028 bytestemplate/src/components/ui/select.tsxruns3,780 bytestemplate/src/components/ui/tabs.tsxruns1,904 bytestemplate/src/components/ui/textarea.tsxruns644 bytestemplate/src/components/ui/tooltip.tsxruns898 bytestemplate/src/lib/clean-hex-color.tsruns225 bytestemplate/src/lib/clean-imported-font.tsruns990 bytestemplate/src/lib/constants.tsruns22,412 bytestemplate/src/lib/contrast.tsruns3,058 bytestemplate/src/lib/copy-ideas.tsruns3,012 bytestemplate/src/lib/defaults.tsruns9,447 bytestemplate/src/lib/elements.tsruns1,344 bytestemplate/src/lib/export-assets.tsruns1,320 bytestemplate/src/lib/export-render.tsruns9,158 bytestemplate/src/lib/frame-assets.tsruns7,838 bytestemplate/src/lib/image-cache.tsruns3,092 bytestemplate/src/lib/locale.tsruns1,956 bytestemplate/src/lib/measure-frame.tsruns3,732 bytestemplate/src/lib/png-encode.tsruns3,003 bytestemplate/src/lib/png-rgb.tsruns5,492 bytestemplate/src/lib/png-worker.tsruns545 bytestemplate/src/lib/project-validation.tsruns5,055 bytestemplate/src/lib/request-body.tsruns1,755 bytestemplate/src/lib/request-guard.tsruns2,072 bytestemplate/src/lib/scene.tsruns4,833 bytestemplate/src/lib/serve-asset.tsruns1,058 bytestemplate/src/lib/storage.tsruns16,886 bytestemplate/src/lib/style-lab.tsruns13,436 bytestemplate/src/lib/types.tsruns7,604 bytestemplate/src/lib/typography.tsruns2,027 bytestemplate/src/lib/utils.tsruns169 bytestemplate/src/lib/write-asset.tsruns448 bytestemplate/tailwind.config.tsruns2,043 bytestemplate/tests/editor.e2e.tsruns12,755 bytestemplate/tests/harness/api-bug-bash.cjsruns9,737 bytestemplate/tests/harness/bug-bash.cjsruns35,700 bytestemplate/tests/harness/migration-fixture.cjsruns7,579 bytestemplate/tests/harness/run.cjsruns466 bytestemplate/tests/harness/ui-bug-bash.cjsruns15,216 bytestemplate/tests/regression.e2e.tsruns1,504 bytestemplate/tests/scene-style-lab.e2e.tsruns15,073 bytestemplate/tests/targets.e2e.tsruns12,758 bytestemplate/tsconfig.jsonasset594 bytes
Read SKILL.md
# App Store & Google Play Screenshots Generator
## Overview
Scaffold a pre-built Next.js + ShadCN editor that lets the user design and export App Store **and** Google Play screenshots as **advertisements** (not UI showcases). The editor handles all the heavy lifting:
- Connected live preview at the canvas's true resolution (scaled to fit)
- Drag-to-reorder screens, inline text editing, layout switcher per screen
- Cross-screen mockups: phone/device frames, captions, and layered elements can be moved across adjacent screens, then exported as clipped crops
- Drop-target screenshot picker (file → saved to `public/screenshots/uploaded/<hash>.png`)
- Auto-save to **`app-store-screenshots.json`** at the project root (git-trackable) + `localStorage` mirror
- Easy iOS ↔ Mac ↔ Android platform switch — separate slide decks live side by side
- One-click bulk PNG export at every Apple/Google-required resolution via `html-to-image`
- Light/dark variant toggle per slide, a toolbar theme picker (one palette preset per named style), locale selectView the complete source on GitHubSkills included
- app-store-screenshots
Repository facts
- License
- MIT
- Created
- Mar 7, 2026
- Last pushed
- Oct 16, 2026
- Forks
- 557
- Open issues
- 7
- Project activity
- Active
Rank over the last 12 weeks
Now #187 · best #161 · down 2 in 30 days
- #183 on Oct 25
- #184 from Oct 28 to Oct 29
- #185 on Oct 30
- #186 from Oct 31 to Nov 3
- #184 from Nov 4 to Nov 5
- #187 since Nov 6
69 earlier periods
- #164 on Aug 2
- #166 from Aug 7 to Aug 8
- #165 on Aug 9
- #169 from Aug 10 to Aug 11
- #168 on Aug 12
- #171 from Aug 13 to Aug 14
- #169 on Aug 15
- #170 on Aug 16
- #169 on Aug 17
- #172 on Aug 18
- #173 on Aug 19
- #171 on Aug 20
- #166 on Aug 21
- #161 on Aug 22
- #166 on Aug 23
- #165 on Aug 24
- #164 from Aug 25 to Aug 26
- #166 from Aug 27 to Aug 28
- #168 on Aug 29
- #167 on Aug 30
- #166 on Aug 31
- #172 on Sep 1
- #168 on Sep 2
- #173 on Sep 3
- #171 on Sep 4
- #173 on Sep 5
- #170 on Sep 6
- #173 from Sep 7 to Sep 8
- #174 on Sep 9
- #173 on Sep 10
- #172 on Sep 11
- #176 on Sep 12
- #177 on Sep 13
- #176 on Sep 14
- #177 on Sep 15
- #179 on Sep 16
- #181 on Sep 17
- #183 on Sep 18
- #181 on Sep 19
- #184 from Sep 20 to Sep 22
- #186 on Sep 23
- #185 from Sep 24 to Sep 26
- #183 on Sep 27
- #184 on Sep 28
- #183 on Sep 29
- #179 on Sep 30
- #183 on Oct 1
- #185 on Oct 2
- #182 on Oct 3
- #180 on Oct 4
- #183 on Oct 5
- #184 on Oct 6
- #188 on Oct 7
- #185 on Oct 8
- #187 on Oct 9
- #186 on Oct 10
- #187 on Oct 11
- #185 on Oct 12
- #187 on Oct 13
- #186 from Oct 14 to Oct 15
- #184 on Oct 16
- #185 on Oct 17
- #186 on Oct 18
- #184 on Oct 19
- #183 on Oct 20
- #185 on Oct 21
- #181 on Oct 22
- #180 on Oct 23
- #181 on Oct 24
Before you install
Network or broad shellAutomated signals, not a guarantee
Checked Nov 2, 2026
Not fully analyzed: some files were too long, unreadable or missing. This never lowers the level.
Show 15 notes
skills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/app-store-screenshots/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzed
- No broad tool permissions
- 1 dangerous shell commandin 1 file: app-store-screenshots/scripts/capture-iphone-duo.sh:127
- 180 network referencesin 28 files: app-store-screenshots/template/tests/harness/api-bug-bash.cjs:1 app-store-screenshots/template/tests/editor.e2e.ts:134 app-store-screenshots/template/tests/harness/bug-bash.cjs:1 and 25 more
- 1 obfuscation signalin 1 file: app-store-screenshots/template/tests/targets.e2e.ts:153
- No instruction overrides
- 81 executable scriptsin 81 files: app-store-screenshots/scripts/capture-iphone-duo.sh:1 app-store-screenshots/template/e2e-army/chrome-provider.ts:1 app-store-screenshots/template/e2e-army/server.cjs:1 and 78 more
- No access to secrets
- No requests to vote in Hot
Show all 263 signals by file
skills/app-store-screenshots/SKILL.md
- Network accessline 373
1. **App screenshots** — "Do you already have screenshots of the devices?" - If **yes**: ask "Where are your app screenshots? (PNG files of actual device captures)" and proceed. - If **no** and the app is **iOS + Swift**: offer the co - Network accessline 374
- If **yes**: ask "Where are your app screenshots? (PNG files of actual device captures)" and proceed. - If **no** and the app is **iOS + Swift**: offer the companion capture skill — "Want to capture them automatically with the `ios-mark - Network accessline 375
- If **no** and the app is **iOS + Swift**: offer the companion capture skill — "Want to capture them automatically with the `ios-marketing-capture` skill (https://github.com/ParthJadhav/ios-marketing-capture)?" If they say yes, install it - Network accessline 472
bun dev # → http://localhost:3000 - Network accessline 866
1. **The server is running at `http://localhost:3000`** (or whichever port Next picked — read it from the dev server output and quote the actual URL). Tell them to open it in the browser. - Network accessline 869
bun install # only needed the first time, or after pulling new deps bun dev # → http://localhost:3000 - Network accessline 870
bun dev # → http://localhost:3000 - Network accessline 876
6. **Invite further edits:** say something like _"Feel free to ask me to make any changes you'd like to the screenshots — copy, layout, palette, anything. I can iterate with you."_ 7. **Showcase callout** (always include this, verbatim spir - Network accessline 878
> Check out apps generated by this skill here: https://www.parthjadhav.com/products/app-store-screenshots — and tag **@parthjadhav8** on Twitter if you want your app to be added to the showcase.
skills/app-store-screenshots/scripts/capture-iphone-duo.sh
skills/app-store-screenshots/style-prompts/03-paper-sticker-skeuomorphic.md
- Network accessline 201
### Sticky notes (Post-it) - Square 100-140px, sticky-note yellow `#FAE680`. - Slight curl on bottom-right corner — a triangle of `#E6CC54` revealed. - Network accessline 202
- Square 100-140px, sticky-note yellow `#FAE680`. - Slight curl on bottom-right corner — a triangle of `#E6CC54` revealed. - Tiny handwritten marker text in navy. - Network accessline 203
- Slight curl on bottom-right corner — a triangle of `#E6CC54` revealed.
skills/app-store-screenshots/style-prompts/05-hand-drawn-editorial-tasks.md
- Network accessline 247
- Wavy underline under "your whole day" (coral, ~5px stroke) - Small 5-point star top-right area (coral, ~40px) - Short squiggle/curl near upper-right corner (coral, ~3 loops) - Network accessline 248
- Small 5-point star top-right area (coral, ~40px) - Short squiggle/curl near upper-right corner (coral, ~3 loops) - **Phone:** tilt `-12°` (leans left), positioned bottom-right, bleeds off bottom edge ~25%, shows task list UI with checkb - Network accessline 249
- Short squiggle/curl near upper-right corner (coral, ~3 loops) - Network accessline 298
- **Headline:** the wordlist IS the headline. No separate title needed. - **Content:** vertical stack of feature names as specified in "Closer slide (feature wordlist) format" above — alternating script-coral and sans-navy, mixed sizes 40–8 - Network accessline 299
- **Content:** vertical stack of feature names as specified in "Closer slide (feature wordlist) format" above — alternating script-coral and sans-navy, mixed sizes 40–82pt, slight rotations, some bleeding off right edge - **Doodles:** 1–2 m - Network accessline 300
- **Doodles:** 1–2 minimal — perhaps a small star or curl somewhere in the lower-right negative space; do not over-decorate, the type IS the decoration
skills/app-store-screenshots/style-prompts/08-swiss-grid-bold.md
- Network accessline 85
**Fonts link:** `https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;800&family=JetBrains+Mono:wght@500&display=block`
skills/app-store-screenshots/style-prompts/10-magazine-cover-editorial.md
- Network accessline 63
- **Pull-quote:** Fraunces italic `opsz 96`, **64–80 px**, line-height 1.08, cream on oxblood or ink on paper. Always hand-break the lines. - **Sizes cheat-sheet (1320 wide):** masthead 26 px · hero roman 200–215 px · hero italic 240–265 px - Network accessline 65
- **Loading:** `<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT,WONK@0,9..144,100..900,0..100,0..1;1,9..144,100..900,0..100,0..1&display=block">`. In Next.js use `next/font/google` `Fraunces({ axes: ["opsz"
skills/app-store-screenshots/style-prompts/12-soft-clay-wellness.md
- Network accessline 69
- On sage: italic + `--clay-ink-deep #6E2F1D`. - On plum / moss: italic + `--clay-light #E6A585`. - Never underline it, never add a squiggle, never change size or weight beyond the italic cut. The soft italic's curl IS the decoration. - Network accessline 71
- Never underline it, never add a squiggle, never change size or weight beyond the italic cut. The soft italic's curl IS the decoration.
skills/app-store-screenshots/style-prompts/13-midnight-glow-pro.md
- Network accessline 63
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700&family=Inter:wght@400;500;600&family=Geist+Mono:wght@400;500&family=JetBrains+Mono:wght@400;500&display=block" rel="stylesheet">
skills/app-store-screenshots/style-prompts/14-risograph-zine.md
- Network accessline 74
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> - Network accessline 75
<link href="https://fonts.googleapis.com/css2?family=Anton&family=Archivo:wdth,wght@62..125,100..900&display=block" rel="stylesheet"> - Network accessline 155
.fibre{position:absolute;inset:0;pointer-events:none;mix-blend-mode:multiply;background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='600' height='600'><filter id='f'><feTurbulence type='fractalNoise' b - Network accessline 156
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='600' height='600'><filter id='f'><feTurbulence type='fractalNoise' baseFrequency='.025 .06' numOctaves='3' seed='5'/><feColorMatrix values='0 0 0 0 .62 0 0 0 0 .57 - Network accessline 157
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='500' height='500'><filter id='k'><feTurbulence type='fractalNoise' baseFrequency='.45' seed='11'/><feColorMatrix values='0 0 0 0 .55 0 0 0 0 .5 0 0 0 0 .44 0 0 0 14
skills/app-store-screenshots/style-prompts/15-bento-keynote-grid.md
- Network accessline 57
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@600;700&family=Inter:wght@500;600;700&display=block" rel="stylesheet">
skills/app-store-screenshots/style-prompts/16-toybox-primary.md
- Network accessline 59
<link href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@600;700;800&family=Nunito:wght@700;800;900&display=block" rel="stylesheet"> - Network accessline 125
.slide { background:#FFF6E6; } /* one hex, from the slide order */ .grain { position:absolute; inset:0; pointer-events:none; mix-blend-mode:overlay; opacity:.07; z-index:9; background-image:url("data:image/svg+xml;utf8,<svg xml - Network accessline 127
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='300'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0
skills/app-store-screenshots/style-prompts/17-quiet-japandi.md
- Network accessline 68
<link href="https://fonts.googleapis.com/css2?family=Shippori+Mincho:wght@400;500;600&family=Zen+Kaku+Gothic+New:wght@400;500;700&family=Yuji+Syuku&display=block" rel="stylesheet"> - Network accessline 135
background:linear-gradient(180deg,#E9D6B4 0,#E2CBA3 7px,#B8956A 8px,#CBAB80 12px,#C5A376 70%,#A7845A 100%); box-shadow:0 1px 0 rgba(90,64,36,.35),0 14px 22px -6px rgba(84,62,36,.26),0 50px 80px -24px rgba(84,62,36,.16)} .shelf::after{conte - Network accessline 137
.shelf::after{content:"";position:absolute;inset:8px 0 0;mix-blend-mode:multiply;opacity:.55;background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1360' height='24'><filter id='w'><feTurbulence type='fractal
skills/app-store-screenshots/style-prompts/18-vintage-travel-poster.md
- Network accessline 60
- **Commercial alternates:** Futura Condensed ExtraBold / Knockout 48 / Trade Gothic Bold Condensed No. 20 (display). Bello Script / Mission Script / Lust Script (script). Clarendon / Archer / Sentinel (slab). - **Google Fonts link:** `ht - Network accessline 62
`https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Yellowtail&family=Zilla+Slab:ital,wght@0,500;0,700;1,500;1,600&display=block`
skills/app-store-screenshots/template/README.md
- Network accessline 10
bun install # or pnpm / yarn / npm bun dev # http://localhost:3000 - Network accessline 11
bun dev # http://localhost:3000 - Network accessline 81
**Real bezels.** The template ships Apple's product bezels from [Apple Design Resources](https://developer.apple.com/design/resources/) (Product Bezels) in `public/frames/`. Apple licenses them for making mock-ups of apps for Apple platform - Network accessline 203
The scaffold includes [Tester Army](https://github.com/tester-army/e2e), pinned to `e2e@0.16.0`, `@e2e-dev/web@0.11.2` and `playwright@1.63.0`. Tests require Node.js 22.12 or newer and installed **Google Chrome**. No model account or API ke
skills/app-store-screenshots/template/components.json
skills/app-store-screenshots/template/docs/testing/e2e.md
- Network accessline 29
bun run test:regression http://localhost:4312 - Network accessline 30
BUG_BASH_FILTER='connected overlays' node tests/harness/bug-bash.cjs http://localhost:4312 - Network accessline 35
To exercise cold dev compilation after API-only traffic, start `node e2e-army/server.cjs 4312` from this template in one terminal, then run `node scripts/api-bug-bash.cjs http://localhost:4312` from the repository root in another. The serve - Network accessline 86
1. **Rapid structural actions erased preceding copy during undo.** Type a headline, immediately Add screen, then Undo. All ten direct deck regressions failed with original headline `First` instead of edited copy. The save/history hook previ - Network accessline 87
2. **Default screen selection jumped after keyboard reordering.** Initial rendering derived the selected slide from index zero but left its ID unset. Moving that initially selected slide changed the inspector to the new first slide. Hydrati - Network accessline 88
3. **Template framework dependency predates the September 30 security release.** Updated Next from 16.3.6 to 16.3.8 and regenerated the Bun lock. The [official release](https://github.com/vercel/next.js/releases/tag/v16.3.8) lists the repai
skills/app-store-screenshots/template/e2e-army/chrome-provider.ts
- Executable scriptline 1
Executable file - Network accessline 32
browsers.set(id, browser); request.log(`Google Chrome ${browser.version()} (channel: chrome)`); return { id, cdpEndpoint: `http://127.0.0.1:${port}` }; - Network accessline 33
request.log(`Google Chrome ${browser.version()} (channel: chrome)`); return { id, cdpEndpoint: `http://127.0.0.1:${port}` }; } catch (error) { - Network accessline 34
return { id, cdpEndpoint: `http://127.0.0.1:${port}` };
skills/app-store-screenshots/template/e2e-army/server.cjs
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/e2e.config.ts
- Executable scriptline 1
Executable file - Network accessline 16
engine: web({ browser: googleChrome(), viewport: { width: 1600, height: 1000 } }), app: { url: `http://localhost:${process.env.SCREENSHOTS_E2E_PORT ?? '4312'}`, - Network accessline 17
app: { url: `http://localhost:${process.env.SCREENSHOTS_E2E_PORT ?? '4312'}`, command: { executable: process.execPath, args: ['e2e-army/server.cjs', '{port}'], startupTimeout: 120_000, log: '.e2e/logs/app.log', env: { SCREENSHOT - Network accessline 18
url: `http://localhost:${process.env.SCREENSHOTS_E2E_PORT ?? '4312'}`,
skills/app-store-screenshots/template/next.config.mjs
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/package.json
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/postcss.config.mjs
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/api/frames/route.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/api/project/route.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/api/upload-font/route.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/api/upload/route.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/fonts/imported/[filename]/route.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/frames/[filename]/route.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/layout.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/page.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/app/screenshots/uploaded/[filename]/route.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/background-controls.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/callout-controls.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/create-image-mask.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/device-frames.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/font-importer.tsx
- Executable scriptline 1
Executable file - Network accessline 43
await new FontFace("Import validation", await file.arrayBuffer()).load(); if (request !== requestId.current) return; const response = await fetch("/api/upload-font", { - Network accessline 44
if (request !== requestId.current) return; const response = await fetch("/api/upload-font", { method: "POST", - Network accessline 45
const response = await fetch("/api/upload-font", {
skills/app-store-screenshots/template/src/components/editor/image-element-canvas.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/inspector.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/preview-stage.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/scene-layers.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/scene-playground.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/screenshot-editor.tsx
- Executable scriptline 1
Executable file - Network accessline 946
void (async () => { try { const resp = await fetch("/api/frames", { cache: "no-store", signal: AbortSignal.timeout(15000) }); - Network accessline 947
try { const resp = await fetch("/api/frames", { cache: "no-store", signal: AbortSignal.timeout(15000) }); const json = (await resp.json()) as { - Network accessline 948
const resp = await fetch("/api/frames", { cache: "no-store", signal: AbortSignal.timeout(15000) });
skills/app-store-screenshots/template/src/components/editor/screenshot-picker.tsx
- Executable scriptline 1
Executable file - Network accessline 27
async function uploadDataUrl(dataUrl: string): Promise<string | null> { try { const resp = await fetch("/api/upload", { - Network accessline 28
try { const resp = await fetch("/api/upload", { method: "POST", - Network accessline 29
const resp = await fetch("/api/upload", {
skills/app-store-screenshots/template/src/components/editor/sidebar.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/slide-canvas.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/slide-thumb.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/style-lab.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/editor/toolbar.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/button.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/card.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/dialog.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/dropdown-menu.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/input.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/label.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/popover.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/select.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/tabs.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/textarea.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/components/ui/tooltip.tsx
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/clean-hex-color.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/clean-imported-font.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/constants.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/contrast.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/copy-ideas.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/defaults.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/elements.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/export-assets.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/export-render.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/frame-assets.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/image-cache.ts
- Executable scriptline 1
Executable file - Network accessline 19
return { data: path, w: image.naturalWidth, h: image.naturalHeight }; } const resp = await fetch(path, { signal: AbortSignal.timeout(10000) }); - Network accessline 20
} const resp = await fetch(path, { signal: AbortSignal.timeout(10000) }); if (!resp.ok) return null; - Network accessline 21
const resp = await fetch(path, { signal: AbortSignal.timeout(10000) });
skills/app-store-screenshots/template/src/lib/locale.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/measure-frame.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/png-encode.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/png-rgb.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/png-worker.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/project-validation.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/request-body.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/request-guard.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/scene.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/serve-asset.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/storage.ts
- Executable scriptline 1
Executable file - Network accessline 207
if (typeof window === "undefined") return { ok: false, error: "Window is not available" }; try { const resp = await fetch("/api/project", { cache: "no-store", signal: AbortSignal.timeout(15000) }); - Network accessline 208
try { const resp = await fetch("/api/project", { cache: "no-store", signal: AbortSignal.timeout(15000) }); const json = (await resp.json()) as { ok: boolean; state: Partial<ProjectState> | null; error?: string }; - Network accessline 209
const resp = await fetch("/api/project", { cache: "no-store", signal: AbortSignal.timeout(15000) }); - Network accessline 231
async function saveToFile(state: ProjectState, revision: string | null): Promise<{ ok: true; revision: string | null } | { ok: false; error: string }> { try { const resp = await fetch("/api/project", { - Network accessline 232
try { const resp = await fetch("/api/project", { method: "POST", - Network accessline 233
const resp = await fetch("/api/project", {
skills/app-store-screenshots/template/src/lib/style-lab.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/types.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/typography.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/utils.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/src/lib/write-asset.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/tailwind.config.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/tests/editor.e2e.ts
- Executable scriptline 1
Executable file - Network accessline 134
test('real disk autosave reload and cache preserve edits', async ({ app, browser, screen }) => { const endpoint = new URL('/api/project', app.baseUrl); const original = (await (await fetch(endpoint)).json()).state; - Network accessline 135
const endpoint = new URL('/api/project', app.baseUrl); const original = (await (await fetch(endpoint)).json()).state; try { - Network accessline 136
const original = (await (await fetch(endpoint)).json()).state; - Network accessline 137
try { await fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(fixture()) }); await app.open('/'); await expect(screen.getByRole('textbox', 'Headline')).toHaveValue('First'); - Network accessline 138
await fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(fixture()) }); - Network accessline 143
await expect(browser.locator('[title="Project saved"]')).toBeVisible(); await browser.reload(); await expect(screen.getByRole('textbox', 'App name')).toHaveValue('Persisted army'); await expect((await (await fetch(endpoint)).json()) - Network accessline 144
await browser.reload(); await expect(screen.getByRole('textbox', 'App name')).toHaveValue('Persisted army'); await expect((await (await fetch(endpoint)).json()).state.appName).toBe('Persisted army'); await expect(await browser.evalu - Network accessline 145
await expect((await (await fetch(endpoint)).json()).state.appName).toBe('Persisted army'); - Network accessline 146
await expect(await browser.evaluate(() => Object.keys(localStorage).length > 0)).toBe(true); } finally { await fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(original) }); } }); - Network accessline 147
} finally { await fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(original) }); } - Network accessline 165
test('real API rejects invalid projects without changing disk', async ({ app }) => { const url = new URL('/api/project', app.baseUrl); const original = await fetch(url); const state = (await original.json()).state; - Network accessline 166
const url = new URL('/api/project', app.baseUrl); const original = await fetch(url); const state = (await original.json()).state; - Network accessline 167
for (const invalid of [null, {}, { ...state, schemaVersion: 999 }, { ...state, slidesByDevice: { unknown: [] } }]) { await expect((await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify( - Network accessline 168
await expect((await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(invalid) })).status).toBe(400); - Network accessline 169
} await expect((await (await fetch(url)).json()).state).toEqual(state); }); - Network accessline 170
await expect((await (await fetch(url)).json()).state).toEqual(state); - Network accessline 173
test('real API stale revisions conflict and keep the winning project', async ({ app }) => { const url = new URL('/api/project', app.baseUrl); const initial = await fetch(url); const state = (await initial.json()).state; const revision = i - Network accessline 174
const url = new URL('/api/project', app.baseUrl); const initial = await fetch(url); const state = (await initial.json()).state; const revision = initial.headers.get('etag')!; - Network accessline 175
const post = (appName: string) => fetch(url, { method: 'POST', headers: { 'content-type': 'application/json', 'if-match': revision }, body: JSON.stringify({ ...state, appName }) }); - Network accessline 177
const responses = await Promise.all([post('Winner A'), post('Winner B')]); await expect(responses.map(response => response.status).sort()).toEqual([200, 412]); await expect((await (await fetch(url)).json()).state.appName).toBe(respo - Network accessline 178
await expect(responses.map(response => response.status).sort()).toEqual([200, 412]); await expect((await (await fetch(url)).json()).state.appName).toBe(responses[0].status === 200 ? 'Winner A' : 'Winner B'); } finally { await fetch(ur - Network accessline 179
await expect((await (await fetch(url)).json()).state.appName).toBe(responses[0].status === 200 ? 'Winner A' : 'Winner B'); - Network accessline 180
} finally { await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(state) }); } - Network accessline 184
for (const [endpoint, data] of [['/api/upload', { dataUrl: 'data:image/png;base64,iVBORw0KGgo=' }], ['/api/upload-font', { data: 'd09GMg==' }]] as const) { const url = new URL(endpoint, app.baseUrl); const body = JSON.stringify(data); - Network accessline 185
const url = new URL(endpoint, app.baseUrl); const body = JSON.stringify(data); await expect((await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body })).status).toBe(400); await expect((await fetch(u - Network accessline 186
await expect((await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body })).status).toBe(400); - Network accessline 187
await expect((await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json', origin: 'https://foreign.example' }, body })).status).toBe(403);
skills/app-store-screenshots/template/tests/harness/api-bug-bash.cjs
- Network accessline 1
// Run only against a disposable template copy; this writes projects and assets. // node scripts/api-bug-bash.cjs http://localhost:3098 - Executable scriptline 1
Executable file - Network accessline 2
// node scripts/api-bug-bash.cjs http://localhost:3098 - Network accessline 8
const {execFileSync} = require('node:child_process'); const {randomBytes} = require('node:crypto'); const base = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); - Network accessline 9
const {randomBytes} = require('node:crypto'); const base = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); const projectURL = base + '/api/project'; - Network accessline 10
const base = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); const projectURL = base + '/api/project'; const post = (route, data, headers = {}) => fetch(base + route, { - Network accessline 11
const projectURL = base + '/api/project'; const post = (route, data, headers = {}) => fetch(base + route, { method: 'POST', headers: {'content-type': 'application/json', ...headers}, body: JSON.stringify(data), - Network accessline 12
const post = (route, data, headers = {}) => fetch(base + route, { - Network accessline 16
async function check(name, run) { await run(); console.log('PASS', name); passed++; } (async () => { const initial = await fetch(projectURL); - Network accessline 17
(async () => { const initial = await fetch(projectURL); assert.equal(initial.status, 200); - Network accessline 18
const initial = await fetch(projectURL); - Network accessline 30
.map(patch => ({...original, slidesByDevice:{watchos:[{...slide,...patch}]}}))]; for (const state of invalid) assert.equal((await post('/api/project',state)).status,400); assert.deepEqual((await (await fetch(projectURL)).json()) - Network accessline 31
for (const state of invalid) assert.equal((await post('/api/project',state)).status,400); assert.deepEqual((await (await fetch(projectURL)).json()).state, original); }); - Network accessline 32
assert.deepEqual((await (await fetch(projectURL)).json()).state, original); - Network accessline 35
for (const route of ['/api/project','/api/upload','/api/upload-font']) { for (const type of ['text/plain','application/json-invalid']) assert.equal((await post(route,{}, {'content-type':type})).status,415); for (const origin - Network accessline 36
for (const type of ['text/plain','application/json-invalid']) assert.equal((await post(route,{}, {'content-type':type})).status,415); for (const origin of ['null','https://evil.example','http://localhost:9999']) assert.equal((await - Network accessline 37
for (const origin of ['null','https://evil.example','http://localhost:9999']) assert.equal((await post(route,{}, {origin})).status,403); assert.equal((await post(route,{}, {'sec-fetch-site':'cross-site'})).status,403); } - Network accessline 40
for (const host of ['localhost','127.0.0.1']) { const url = new URL(projectURL); url.hostname = host; const response = await fetch(url, {method:'POST',headers:{'content-type':'application/json',origin:url.origin,'sec-fetch-s - Network accessline 41
const url = new URL(projectURL); url.hostname = host; const response = await fetch(url, {method:'POST',headers:{'content-type':'application/json',origin:url.origin,'sec-fetch-site':'same-origin'},body:JSON.stringify(original)}); - Network accessline 42
const response = await fetch(url, {method:'POST',headers:{'content-type':'application/json',origin:url.origin,'sec-fetch-site':'same-origin'},body:JSON.stringify(original)}); - Network accessline 45
}); await check('concurrent stale project writes conflict and preserve the winning state', async () => { const revision = (await fetch(projectURL)).headers.get('etag'); - Network accessline 46
await check('concurrent stale project writes conflict and preserve the winning state', async () => { const revision = (await fetch(projectURL)).headers.get('etag'); assert.ok(revision); - Network accessline 47
const revision = (await fetch(projectURL)).headers.get('etag'); - Network accessline 51
assert.deepEqual(responses.map(r=>r.status).sort(),[200,412]); const winning = responses.findIndex(r=>r.status===200); const current = await fetch(projectURL); - Network accessline 52
const winning = responses.findIndex(r=>r.status===200); const current = await fetch(projectURL); assert.deepEqual((await current.json()).state,candidates[winning]); - Network accessline 53
const current = await fetch(projectURL); - Network accessline 76
assert.equal(response.status,200); const result = await response.json(); if (path) assert.equal(result.path,path); path = result.path; const saved = await fetch(base+path); assert.equal(saved.status,200); - Network accessline 77
if (path) assert.equal(result.path,path); path = result.path; const saved = await fetch(base+path); assert.equal(saved.status,200); assert.deepEqual(Buffer.from(await saved.arrayBuffer()),bytes); - Network accessline 78
const saved = await fetch(base+path); assert.equal(saved.status,200); - Network accessline 85
const chunk = new Uint8Array(1024*1024).fill(32); for (const [route, megabytes] of [['/api/upload',13], ['/api/upload-font',24], ['/api/project',65]]) { const response = await fetch(base+route,{method:'POST',headers:{'content- - Network accessline 86
for (const [route, megabytes] of [['/api/upload',13], ['/api/upload-font',24], ['/api/project',65]]) { const response = await fetch(base+route,{method:'POST',headers:{'content-type':'application/json'},duplex:'half', body: - Network accessline 87
const response = await fetch(base+route,{method:'POST',headers:{'content-type':'application/json'},duplex:'half', - Network accessline 91
}); await check('valid WOFF2 uploads persist and truncated copies are rejected', async () => { const html = await (await fetch(base)).text(); - Network accessline 92
await check('valid WOFF2 uploads persist and truncated copies are rejected', async () => { const html = await (await fetch(base)).text(); const fontPath = html.match(/\/_next\/static\/media\/[^"\s<>]+\.woff2/)?.[0]; - Network accessline 93
const html = await (await fetch(base)).text(); - Network accessline 94
const fontPath = html.match(/\/_next\/static\/media\/[^"\s<>]+\.woff2/)?.[0]; assert.ok(fontPath,'Expected bundled Next font'); const bytes = Buffer.from(await (await fetch(base+fontPath)).arrayBuffer()); - Network accessline 96
const bytes = Buffer.from(await (await fetch(base+fontPath)).arrayBuffer()); - Network accessline 100
assert.equal(response.status,200); const {font}=await response.json(); assert.equal(font.format,'woff2'); assert.deepEqual(Buffer.from(await (await fetch(base+font.src)).arrayBuffer()),bytes); - Network accessline 101
assert.equal(font.format,'woff2'); assert.deepEqual(Buffer.from(await (await fetch(base+font.src)).arrayBuffer()),bytes); assert.equal((await post('/api/upload-font',{data:bytes.subarray(0,64).toString('base64')})).status,400); - Network accessline 102
assert.deepEqual(Buffer.from(await (await fetch(base+font.src)).arrayBuffer()),bytes); - Network accessline 106
for (const prefix of ['/screenshots/uploaded/','/fonts/imported/']) { for (const name of ['not-a-hash.png','0000000000000000.woff2','..%2f..%2fapp-store-screenshots.json']) { assert.ok([400,404].includes((await fetch(base+ - Network accessline 107
for (const name of ['not-a-hash.png','0000000000000000.woff2','..%2f..%2fapp-store-screenshots.json']) { assert.ok([400,404].includes((await fetch(base+prefix+name)).status)); } - Network accessline 108
assert.ok([400,404].includes((await fetch(base+prefix+name)).status));
skills/app-store-screenshots/template/tests/harness/bug-bash.cjs
- Network accessline 1
// Run against a disposable copy: this exercises uploads and /api/project. // PLAYWRIGHT_MODULE=/path/to/playwright node scripts/bug-bash.cjs http://localhost:3098 - Executable scriptline 1
Executable file - Network accessline 2
// PLAYWRIGHT_MODULE=/path/to/playwright node scripts/bug-bash.cjs http://localhost:3098 - Network accessline 4
const fs = require('node:fs/promises'); const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright'); const baseURL = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); - Network accessline 5
const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright'); const baseURL = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); const pause = ms => new Promise(resolve => setTimeout(resolve, ms)); - Network accessline 6
const baseURL = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); - Network accessline 347
}); await check('two real editor tabs cannot silently overwrite each other',async()=>{ const original=await (await fetch(baseURL+'/api/project')).json(); - Network accessline 348
await check('two real editor tabs cannot silently overwrite each other',async()=>{ const original=await (await fetch(baseURL+'/api/project')).json(); const save=state=>fetch(baseURL+'/api/project',{method:'POST',headers:{'content-type - Network accessline 349
const original=await (await fetch(baseURL+'/api/project')).json(); - Network accessline 350
const save=state=>fetch(baseURL+'/api/project',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(state)}); - Network accessline 360
await pages[1].getByRole('textbox',{name:'App name',exact:true}).fill('Keep my edits'); await pages[1].getByText(/Project changed in another tab or on disk/).waitFor(); assert.equal((await (await fetch(baseURL+'/api/project')).jso - Network accessline 361
await pages[1].getByText(/Project changed in another tab or on disk/).waitFor(); assert.equal((await (await fetch(baseURL+'/api/project')).json()).state.appName,'Winner'); assert.equal(await pages[1].getByRole('textbox',{name:'App - Network accessline 362
assert.equal((await (await fetch(baseURL+'/api/project')).json()).state.appName,'Winner'); - Network accessline 434
}); await check('real image and font uploads survive reload and export',async()=>{ const original=await (await fetch(baseURL+'/api/project')).json(); - Network accessline 435
await check('real image and font uploads survive reload and export',async()=>{ const original=await (await fetch(baseURL+'/api/project')).json(); const save=state=>fetch(baseURL+'/api/project',{method:'POST',headers:{'content-type':'a - Network accessline 436
const original=await (await fetch(baseURL+'/api/project')).json(); - Network accessline 437
const save=state=>fetch(baseURL+'/api/project',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(state)}); - Network accessline 449
await page.locator('input[type=file]').first().setInputFiles({name:'Runtimefont.woff2',mimeType:'font/woff2',buffer:fontBytes}); await page.getByText('Imported Runtimefont',{exact:true}).waitFor();await pause(1000); const disk=(aw - Network accessline 450
await page.getByText('Imported Runtimefont',{exact:true}).waitFor();await pause(1000); const disk=(await (await fetch(baseURL+'/api/project')).json()).state; assert.ok(disk.slidesByDevice.watchos[0].screenshot.startsWith('/screens - Network accessline 451
const disk=(await (await fetch(baseURL+'/api/project')).json()).state;
skills/app-store-screenshots/template/tests/harness/migration-fixture.cjs
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/tests/harness/run.cjs
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/tests/harness/ui-bug-bash.cjs
- Network accessline 1
// Interaction-focused Chrome bug bash. Uses disposable, mocked project state. // PLAYWRIGHT_MODULE=/path/to/playwright node scripts/ui-bug-bash.cjs http://localhost:3098 - Executable scriptline 1
Executable file - Network accessline 2
// PLAYWRIGHT_MODULE=/path/to/playwright node scripts/ui-bug-bash.cjs http://localhost:3098 - Network accessline 3
const assert = require('node:assert/strict'); const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright'); const baseURL = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); - Network accessline 4
const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright'); const baseURL = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, ''); const pause = ms => new Promise(resolve=>setTimeout(resolve,ms)); - Network accessline 5
const baseURL = (process.argv[2] || 'http://localhost:3098').replace(/\/$/, '');
skills/app-store-screenshots/template/tests/regression.e2e.ts
- Executable scriptline 1
Executable file
skills/app-store-screenshots/template/tests/scene-style-lab.e2e.ts
- Executable scriptline 1
Executable file - Network accessline 189
test('project API rejects malformed scene data and the editor cleans out-of-range values', async ({ app, browser, screen: ui }) => { const endpoint = new URL('/api/project', app.baseUrl); const post = (body: State) => fetch(endpoint, { - Network accessline 190
const endpoint = new URL('/api/project', app.baseUrl); const post = (body: State) => fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) }); await expect((await post(deck({ scene - Network accessline 191
const post = (body: State) => fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) }); - Network accessline 245
test('a magnifier with only some values is accepted and completed', async ({ app }) => { const endpoint = new URL('/api/project', app.baseUrl); const original = (await (await fetch(endpoint)).json()).state; - Network accessline 246
const endpoint = new URL('/api/project', app.baseUrl); const original = (await (await fetch(endpoint)).json()).state; try { - Network accessline 247
const original = (await (await fetch(endpoint)).json()).state; - Network accessline 248
try { const partial = deck({ slidesByDevice: { iphone: [{ ...screen('One'), callout: { focusX: 0.4 }, transforms: { callout: { x: 10, y: 20, width: 300, height: 300 } } }] } }); const response = await fetch(endpoint, { method: 'POST - Network accessline 249
const partial = deck({ slidesByDevice: { iphone: [{ ...screen('One'), callout: { focusX: 0.4 }, transforms: { callout: { x: 10, y: 20, width: 300, height: 300 } } }] } }); const response = await fetch(endpoint, { method: 'POST', headers - Network accessline 250
const response = await fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(partial) }); - Network accessline 251
await expect(response.status).toBe(200); } finally { await fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(original) }); } }); - Network accessline 252
} finally { await fetch(endpoint, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(original) }); }
skills/app-store-screenshots/template/tests/targets.e2e.ts
- Executable scriptline 1
Executable file - Possible obfuscationline 153
slide('Blank', 'no-device', { label: { en: '<U+200B>' }, headline: { en: '<U+200B>' } }), - Network accessline 178
const front = '<svg xmlns="http://www.w3.org/2000/svg" width="2853" height="2007"><rect width="2853" height="2007" fill="#4f46e5"/></svg>'; - Network accessline 200
const outerCapture = '<svg xmlns="http://www.w3.org/2000/svg" width="1398" height="2034"><rect width="1398" height="2034" fill="#0ea5e9"/></svg>';
How the signals fit together
Facts from a static read of the files at the pinned commit. They describe what the code does, not why.
Some files could not be read; the facts below still hold.
Signal confidence: 261 facts
- Network access in prose of the SKILL.mdapp-store-screenshots/SKILL.md:373Low confidence
- Network access in prose of the SKILL.mdapp-store-screenshots/SKILL.md:374Low confidence
- Network access in prose of the SKILL.mdapp-store-screenshots/SKILL.md:375Low confidence
- Network access in code of the SKILL.mdapp-store-screenshots/SKILL.md:472High confidence
- Network access in prose of the SKILL.mdapp-store-screenshots/SKILL.md:866Low confidence
- Network access in code of the SKILL.mdapp-store-screenshots/SKILL.md:869High confidence
- Network access in code of the SKILL.mdapp-store-screenshots/SKILL.md:870High confidence
- Network access in prose of the SKILL.mdapp-store-screenshots/SKILL.md:876Low confidence
- Network access in prose of the SKILL.mdapp-store-screenshots/SKILL.md:878Low confidence
- Executable script in code of a file that runsapp-store-screenshots/scripts/capture-iphone-duo.sh:1High confidence
- Dangerous shell operation in code of a file that runsapp-store-screenshots/scripts/capture-iphone-duo.sh:127High confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/03-paper-sticker-skeuomorphic.md:201Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/03-paper-sticker-skeuomorphic.md:202Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/03-paper-sticker-skeuomorphic.md:203Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/05-hand-drawn-editorial-tasks.md:247Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/05-hand-drawn-editorial-tasks.md:248Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/05-hand-drawn-editorial-tasks.md:249Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/05-hand-drawn-editorial-tasks.md:298Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/05-hand-drawn-editorial-tasks.md:299Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/05-hand-drawn-editorial-tasks.md:300Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/08-swiss-grid-bold.md:85Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/10-magazine-cover-editorial.md:63Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/10-magazine-cover-editorial.md:65Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/12-soft-clay-wellness.md:69Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/12-soft-clay-wellness.md:71Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/13-midnight-glow-pro.md:63Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/14-risograph-zine.md:74Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/14-risograph-zine.md:75Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/14-risograph-zine.md:155Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/14-risograph-zine.md:156Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/14-risograph-zine.md:157Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/15-bento-keynote-grid.md:57Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/16-toybox-primary.md:59Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/16-toybox-primary.md:125Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/16-toybox-primary.md:127Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/17-quiet-japandi.md:68Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/17-quiet-japandi.md:135Low confidence
- Network access in code of documentationapp-store-screenshots/style-prompts/17-quiet-japandi.md:137Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/18-vintage-travel-poster.md:60Low confidence
- Network access in prose of documentationapp-store-screenshots/style-prompts/18-vintage-travel-poster.md:62Low confidence
- Network access in code of documentationapp-store-screenshots/template/README.md:10Low confidence
- Network access in code of documentationapp-store-screenshots/template/README.md:11Low confidence
- Network access in prose of documentationapp-store-screenshots/template/README.md:81Low confidence
- Network access in prose of documentationapp-store-screenshots/template/README.md:203Low confidence
- Network access in code of documentationapp-store-screenshots/template/docs/testing/e2e.md:29Low confidence
- Network access in code of documentationapp-store-screenshots/template/docs/testing/e2e.md:30Low confidence
- Network access in prose of documentationapp-store-screenshots/template/docs/testing/e2e.md:35Low confidence
- Network access in prose of documentationapp-store-screenshots/template/docs/testing/e2e.md:86Low confidence
- Network access in prose of documentationapp-store-screenshots/template/docs/testing/e2e.md:87Low confidence
- Network access in prose of documentationapp-store-screenshots/template/docs/testing/e2e.md:88Low confidence
and 211 more
Works with
| Agent | Status | Evidence | Install to |
|---|---|---|---|
| Claude Code | Compatible | Standard SKILL.md skills/app-store-screenshots/SKILL.md | .claude/skills/<skill-name>/ |
| Codex | Compatible | Standard SKILL.md skills/app-store-screenshots/SKILL.md | .agents/skills/<skill-name>/ |
| Cursor | Compatible | Standard SKILL.md skills/app-store-screenshots/SKILL.md | .cursor/skills/<skill-name>/ |
| Gemini CLI | Compatible | Standard SKILL.md skills/app-store-screenshots/SKILL.md | .gemini/skills/<skill-name>/ |
| GitHub Copilot | Compatible | Standard SKILL.md skills/app-store-screenshots/SKILL.md | .github/skills/<skill-name>/ |
| OpenCode | Compatible | Standard SKILL.md skills/app-store-screenshots/SKILL.md | .opencode/skills/<skill-name>/ |
- Native The repository ships a manifest, folder or frontmatter field for this agent.
- Compatible A standard SKILL.md that this agent can read.
- Unknown No evidence either way.
Derived automatically from paths, manifests and frontmatter. Not tested on each agent.
Explore alternatives and comparisons
Add this badge to your README
It updates every night and links to this page.
[](https://hotskills.dev/skill/parthjadhav-app-store-screenshots/)