#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

Compare on GitHub

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.

Compare on GitHub

View on GitHubAdd to my skills

Verified commit 5386f227360bddb06c8beaff920a1a34c9f01932

7.8kGitHub stars
82stars in 7 days
1skill in this repository
11commits in 30 days

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

Aug 2, 2026Nov 6, 20267,831

Individual skills in this repository

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 tokens

Always available to the agent

SKILL.md

15,544 tokens

When the skill is activated

Reference documents

165,949 tokens

Loaded on request

Token counts are estimates. Scripts run separately; they are not counted as reference context.

The playbook

  1. Overview
  2. Core Principle
  3. What This Skill Does
  4. Step 0: Probe for Existing Screenshot Projects
  5. Step 1: Gather Input (Before Scaffolding)
  6. Step 2: Scaffold the Template
  7. Step 3: Coach the User on Copy
  8. Visual Design Principles
  9. Step 4: Localization
  10. Step 5: Export Time
  11. Step 6: Final QA Gate
  12. Common Mistakes
  13. Project Migration
  14. Template Reference
  15. Hand-off Behavior

Scripts executed separately

  • scripts/capture-iphone-duo.sh
  • template/e2e-army/chrome-provider.ts
  • template/e2e-army/server.cjs
  • template/e2e.config.ts
  • template/next.config.mjs
  • template/package.json
  • template/postcss.config.mjs
  • template/src/app/api/frames/route.ts
  • template/src/app/api/project/route.ts
  • template/src/app/api/upload-font/route.ts
  • template/src/app/api/upload/route.ts
  • template/src/app/fonts/imported/[filename]/route.ts
  • template/src/app/frames/[filename]/route.ts
  • template/src/app/layout.tsx
  • template/src/app/page.tsx
  • template/src/app/screenshots/uploaded/[filename]/route.ts
  • template/src/components/editor/background-controls.tsx
  • template/src/components/editor/callout-controls.tsx
  • template/src/components/editor/create-image-mask.ts
  • template/src/components/editor/device-frames.tsx
  • template/src/components/editor/font-importer.tsx
  • template/src/components/editor/image-element-canvas.tsx
  • template/src/components/editor/inspector.tsx
  • template/src/components/editor/preview-stage.tsx
  • template/src/components/editor/scene-layers.tsx
  • template/src/components/editor/scene-playground.tsx
  • template/src/components/editor/screenshot-editor.tsx
  • template/src/components/editor/screenshot-picker.tsx
  • template/src/components/editor/sidebar.tsx
  • template/src/components/editor/slide-canvas.tsx
  • template/src/components/editor/slide-thumb.tsx
  • template/src/components/editor/style-lab.tsx
  • template/src/components/editor/toolbar.tsx
  • template/src/components/ui/button.tsx
  • template/src/components/ui/card.tsx
  • template/src/components/ui/dialog.tsx
  • template/src/components/ui/dropdown-menu.tsx
  • template/src/components/ui/input.tsx
  • template/src/components/ui/label.tsx
  • template/src/components/ui/popover.tsx
  • template/src/components/ui/select.tsx
  • template/src/components/ui/tabs.tsx
  • template/src/components/ui/textarea.tsx
  • template/src/components/ui/tooltip.tsx
  • template/src/lib/clean-hex-color.ts
  • template/src/lib/clean-imported-font.ts
  • template/src/lib/constants.ts
  • template/src/lib/contrast.ts
  • template/src/lib/copy-ideas.ts
  • template/src/lib/defaults.ts
  • template/src/lib/elements.ts
  • template/src/lib/export-assets.ts
  • template/src/lib/export-render.ts
  • template/src/lib/frame-assets.ts
  • template/src/lib/image-cache.ts
  • template/src/lib/locale.ts
  • template/src/lib/measure-frame.ts
  • template/src/lib/png-encode.ts
  • template/src/lib/png-rgb.ts
  • template/src/lib/png-worker.ts
  • template/src/lib/project-validation.ts
  • template/src/lib/request-body.ts
  • template/src/lib/request-guard.ts
  • template/src/lib/scene.ts
  • template/src/lib/serve-asset.ts
  • template/src/lib/storage.ts
  • template/src/lib/style-lab.ts
  • template/src/lib/types.ts
  • template/src/lib/typography.ts
  • template/src/lib/utils.ts
  • template/src/lib/write-asset.ts
  • template/tailwind.config.ts
  • template/tests/editor.e2e.ts
  • template/tests/harness/api-bug-bash.cjs
  • template/tests/harness/bug-bash.cjs
  • template/tests/harness/migration-fixture.cjs
  • template/tests/harness/run.cjs
  • template/tests/harness/ui-bug-bash.cjs
  • template/tests/regression.e2e.ts
  • template/tests/scene-style-lab.e2e.ts
  • template/tests/targets.e2e.ts

Files

  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • SKILL.mdcore65,029 bytes
  • copy-ideas.mddoc9,079 bytes
  • iphone-duo.mddoc14,090 bytes
  • mockup.pngasset95,028 bytes
  • scripts/capture-iphone-duo.shruns7,148 bytes
  • style-prompts.mddoc10,484 bytes
  • style-prompts/01-retro-rubberhose-mascot.mddoc25,329 bytes
  • style-prompts/02-moody-curated-dating.mddoc27,637 bytes
  • style-prompts/03-paper-sticker-skeuomorphic.mddoc29,001 bytes
  • style-prompts/04-dreamy-pastel-couples.mddoc25,260 bytes
  • style-prompts/05-hand-drawn-editorial-tasks.mddoc24,102 bytes
  • style-prompts/06-glossy-3d-kbeauty-creator.mddoc21,620 bytes
  • style-prompts/07-liquid-glass-aurora.mddoc20,243 bytes
  • style-prompts/08-swiss-grid-bold.mddoc20,500 bytes
  • style-prompts/09-neon-athletic-night.mddoc22,884 bytes
  • style-prompts/10-magazine-cover-editorial.mddoc25,850 bytes
  • style-prompts/11-candy-pop-social.mddoc20,359 bytes
  • style-prompts/12-soft-clay-wellness.mddoc21,145 bytes
  • style-prompts/13-midnight-glow-pro.mddoc34,486 bytes
  • style-prompts/14-risograph-zine.mddoc35,853 bytes
  • style-prompts/15-bento-keynote-grid.mddoc32,047 bytes
  • style-prompts/16-toybox-primary.mddoc36,005 bytes
  • style-prompts/17-quiet-japandi.mddoc30,726 bytes
  • style-prompts/18-vintage-travel-poster.mddoc38,475 bytes
  • style-prompts/_QUALITY_BAR.mddoc13,959 bytes
  • template/.github/workflows/e2e.ymlasset783 bytes
  • template/.gitignoreasset470 bytes
  • template/README.mddoc24,709 bytes
  • template/app-store-screenshots.jsonasset8,540 bytes
  • template/bun.lockasset73,634 bytes
  • template/components.jsonasset449 bytes
  • template/docs/testing/e2e.mddoc24,064 bytes
  • template/e2e-army/chrome-provider.tsruns1,706 bytes
  • template/e2e-army/server.cjsruns1,610 bytes
  • template/e2e.config.tsruns824 bytes
  • template/next.config.mjsruns294 bytes
  • template/package.jsonruns1,596 bytes
  • template/postcss.config.mjsruns81 bytes
  • template/public/fonts/imported/.gitkeepasset1 bytes
  • template/public/frames/.gitkeepasset0 bytes
  • template/public/frames/duo-inner-landscape.pngasset795,922 bytes
  • template/public/frames/duo-inner-portrait.pngasset821,757 bytes
  • template/public/frames/duo-outer-landscape.pngasset380,872 bytes
  • template/public/frames/duo-outer-portrait.pngasset457,010 bytes
  • template/public/frames/ipad-portrait.pngasset314,484 bytes
  • template/public/frames/iphone-portrait.pngasset489,833 bytes
  • template/public/frames/mac.pngasset676,527 bytes
  • template/public/frames/tv.pngasset853,962 bytes
  • template/public/frames/watch.pngasset239,889 bytes
  • template/public/mockup.pngasset95,028 bytes
  • template/public/screenshots/android/phone/en/.gitkeepasset0 bytes
  • template/public/screenshots/apple/ipad/en/.gitkeepasset0 bytes
  • template/public/screenshots/apple/iphone/en/.gitkeepasset0 bytes
  • template/src/app/api/frames/route.tsruns1,532 bytes
  • template/src/app/api/project/route.tsruns3,522 bytes
  • template/src/app/api/upload-font/route.tsruns4,580 bytes
  • template/src/app/api/upload/route.tsruns3,263 bytes
  • template/src/app/fonts/imported/[filename]/route.tsruns359 bytes
  • template/src/app/frames/[filename]/route.tsruns1,349 bytes
  • template/src/app/globals.cssasset3,299 bytes
  • template/src/app/layout.tsxruns487 bytes
  • template/src/app/page.tsxruns141 bytes
  • template/src/app/screenshots/uploaded/[filename]/route.tsruns328 bytes
  • template/src/components/editor/background-controls.tsxruns2,972 bytes
  • template/src/components/editor/callout-controls.tsxruns8,067 bytes
  • template/src/components/editor/create-image-mask.tsruns586 bytes
  • template/src/components/editor/device-frames.tsxruns21,429 bytes
  • template/src/components/editor/font-importer.tsxruns3,121 bytes
  • template/src/components/editor/image-element-canvas.tsxruns1,400 bytes
  • template/src/components/editor/inspector.tsxruns41,841 bytes
  • template/src/components/editor/preview-stage.tsxruns10,678 bytes
  • template/src/components/editor/scene-layers.tsxruns14,593 bytes
  • template/src/components/editor/scene-playground.tsxruns10,785 bytes
  • template/src/components/editor/screenshot-editor.tsxruns38,529 bytes
  • template/src/components/editor/screenshot-picker.tsxruns7,328 bytes
  • template/src/components/editor/sidebar.tsxruns4,429 bytes
  • template/src/components/editor/slide-canvas.tsxruns56,174 bytes
  • template/src/components/editor/slide-thumb.tsxruns6,063 bytes
  • template/src/components/editor/style-lab.tsxruns17,791 bytes
  • template/src/components/editor/toolbar.tsxruns15,003 bytes
  • template/src/components/ui/button.tsxruns1,813 bytes
  • template/src/components/ui/card.tsxruns1,183 bytes
  • template/src/components/ui/dialog.tsxruns3,195 bytes
  • template/src/components/ui/dropdown-menu.tsxruns3,776 bytes
  • template/src/components/ui/input.tsxruns717 bytes
  • template/src/components/ui/label.tsxruns714 bytes
  • template/src/components/ui/popover.tsxruns1,028 bytes
  • template/src/components/ui/select.tsxruns3,780 bytes
  • template/src/components/ui/tabs.tsxruns1,904 bytes
  • template/src/components/ui/textarea.tsxruns644 bytes
  • template/src/components/ui/tooltip.tsxruns898 bytes
  • template/src/lib/clean-hex-color.tsruns225 bytes
  • template/src/lib/clean-imported-font.tsruns990 bytes
  • template/src/lib/constants.tsruns22,412 bytes
  • template/src/lib/contrast.tsruns3,058 bytes
  • template/src/lib/copy-ideas.tsruns3,012 bytes
  • template/src/lib/defaults.tsruns9,447 bytes
  • template/src/lib/elements.tsruns1,344 bytes
  • template/src/lib/export-assets.tsruns1,320 bytes
  • template/src/lib/export-render.tsruns9,158 bytes
  • template/src/lib/frame-assets.tsruns7,838 bytes
  • template/src/lib/image-cache.tsruns3,092 bytes
  • template/src/lib/locale.tsruns1,956 bytes
  • template/src/lib/measure-frame.tsruns3,732 bytes
  • template/src/lib/png-encode.tsruns3,003 bytes
  • template/src/lib/png-rgb.tsruns5,492 bytes
  • template/src/lib/png-worker.tsruns545 bytes
  • template/src/lib/project-validation.tsruns5,055 bytes
  • template/src/lib/request-body.tsruns1,755 bytes
  • template/src/lib/request-guard.tsruns2,072 bytes
  • template/src/lib/scene.tsruns4,833 bytes
  • template/src/lib/serve-asset.tsruns1,058 bytes
  • template/src/lib/storage.tsruns16,886 bytes
  • template/src/lib/style-lab.tsruns13,436 bytes
  • template/src/lib/types.tsruns7,604 bytes
  • template/src/lib/typography.tsruns2,027 bytes
  • template/src/lib/utils.tsruns169 bytes
  • template/src/lib/write-asset.tsruns448 bytes
  • template/tailwind.config.tsruns2,043 bytes
  • template/tests/editor.e2e.tsruns12,755 bytes
  • template/tests/harness/api-bug-bash.cjsruns9,737 bytes
  • template/tests/harness/bug-bash.cjsruns35,700 bytes
  • template/tests/harness/migration-fixture.cjsruns7,579 bytes
  • template/tests/harness/run.cjsruns466 bytes
  • template/tests/harness/ui-bug-bash.cjsruns15,216 bytes
  • template/tests/regression.e2e.tsruns1,504 bytes
  • template/tests/scene-style-lab.e2e.tsruns15,073 bytes
  • template/tests/targets.e2e.tsruns12,758 bytes
  • template/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 select
View the complete source on GitHub

Skills 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

  1. #183 on Oct 25
  2. #184 from Oct 28 to Oct 29
  3. #185 on Oct 30
  4. #186 from Oct 31 to Nov 3
  5. #184 from Nov 4 to Nov 5
  6. #187 since Nov 6
69 earlier periods
  1. #164 on Aug 2
  2. #166 from Aug 7 to Aug 8
  3. #165 on Aug 9
  4. #169 from Aug 10 to Aug 11
  5. #168 on Aug 12
  6. #171 from Aug 13 to Aug 14
  7. #169 on Aug 15
  8. #170 on Aug 16
  9. #169 on Aug 17
  10. #172 on Aug 18
  11. #173 on Aug 19
  12. #171 on Aug 20
  13. #166 on Aug 21
  14. #161 on Aug 22
  15. #166 on Aug 23
  16. #165 on Aug 24
  17. #164 from Aug 25 to Aug 26
  18. #166 from Aug 27 to Aug 28
  19. #168 on Aug 29
  20. #167 on Aug 30
  21. #166 on Aug 31
  22. #172 on Sep 1
  23. #168 on Sep 2
  24. #173 on Sep 3
  25. #171 on Sep 4
  26. #173 on Sep 5
  27. #170 on Sep 6
  28. #173 from Sep 7 to Sep 8
  29. #174 on Sep 9
  30. #173 on Sep 10
  31. #172 on Sep 11
  32. #176 on Sep 12
  33. #177 on Sep 13
  34. #176 on Sep 14
  35. #177 on Sep 15
  36. #179 on Sep 16
  37. #181 on Sep 17
  38. #183 on Sep 18
  39. #181 on Sep 19
  40. #184 from Sep 20 to Sep 22
  41. #186 on Sep 23
  42. #185 from Sep 24 to Sep 26
  43. #183 on Sep 27
  44. #184 on Sep 28
  45. #183 on Sep 29
  46. #179 on Sep 30
  47. #183 on Oct 1
  48. #185 on Oct 2
  49. #182 on Oct 3
  50. #180 on Oct 4
  51. #183 on Oct 5
  52. #184 on Oct 6
  53. #188 on Oct 7
  54. #185 on Oct 8
  55. #187 on Oct 9
  56. #186 on Oct 10
  57. #187 on Oct 11
  58. #185 on Oct 12
  59. #187 on Oct 13
  60. #186 from Oct 14 to Oct 15
  61. #184 on Oct 16
  62. #185 on Oct 17
  63. #186 on Oct 18
  64. #184 on Oct 19
  65. #183 on Oct 20
  66. #185 on Oct 21
  67. #181 on Oct 22
  68. #180 on Oct 23
  69. #181 on Oct 24

Before you install

Network or broad shell

Automated 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:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • skills/app-store-screenshots/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
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

  • Executable scriptline 1
    #!/bin/zsh
  • Dangerous shell operationline 127
    rm -f "$file"; echo "skip $name ($pose): the app keeps the outer display in portrait"; continue

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

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema.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

and 211 more

Works with

Agent compatibility, from the files in this repository
AgentStatusEvidenceInstall to
Claude CodeCompatibleStandard SKILL.md skills/app-store-screenshots/SKILL.md.claude/skills/<skill-name>/
CodexCompatibleStandard SKILL.md skills/app-store-screenshots/SKILL.md.agents/skills/<skill-name>/
CursorCompatibleStandard SKILL.md skills/app-store-screenshots/SKILL.md.cursor/skills/<skill-name>/
Gemini CLICompatibleStandard SKILL.md skills/app-store-screenshots/SKILL.md.gemini/skills/<skill-name>/
GitHub CopilotCompatibleStandard SKILL.md skills/app-store-screenshots/SKILL.md.github/skills/<skill-name>/
OpenCodeCompatibleStandard 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.

ParthJadhav/app-store-screenshots: #187 on hotskills, 7.8k stars

[![ParthJadhav/app-store-screenshots on hotskills](https://hotskills.dev/badge/parthjadhav-app-store-screenshots.svg)](https://hotskills.dev/skill/parthjadhav-app-store-screenshots/)