К основному содержанию
скилл Claude Code CX Codex Cursor GitHub Copilot

app-store-screenshots — скилл для Claude Code: скриншоты для App Store

Собирает редактор на Next.js и ShadCN, в котором вы верстаете и выгружаете скриншоты для App Store и Google Play.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add ParthJadhav/app-store-screenshots --skill app-store-screenshots -a claude-code
Открыть на GitHub

Что делает app-store-screenshots

Скилл разворачивает готовый шаблон редактора в вашей рабочей папке, ставит зависимости вашим пакетным менеджером и кладёт ваши скриншоты в public/screenshots/, а иконку приложения — в public/. Дальше запускается dev-сервер, и вы открываете редактор в браузере. Писать page.tsx, рамки устройств и логику экспорта руками не нужно: всё это уже лежит в шаблоне.

Внутри — живой предпросмотр в реальном разрешении холста, перетаскивание слайдов, правка текста на месте и переключатель раскладки для каждого экрана. Есть рамки телефонов и устройств, подписи и слои, которые переносятся между соседними экранами и выгружаются обрезанными кропами. Состояние сохраняется в app-store-screenshots.json в корне проекта, так что его можно держать в git. Пакетный экспорт PNG идёт через html-to-image во всех разрешениях, которые требуют Apple и Google.

Поддержаны iPhone, iPad, iPhone Duo, Apple TV, Apple Watch, CarPlay, Mac, Android-телефон и планшеты 7" и 10", плюс Feature Graphic 1024×500. Если в проекте уже лежит старая сборка генератора, скилл сначала спросит, переносить ли её на новый редактор, и при согласии сохранит ваши ассеты и скриншоты. Перед миграцией он копирует заменяемые файлы в бэкап вне репозитория — это повод проверить, что рабочая копия чистая, иначе разбираться с чужими правками придётся вручную.

Какие задачи решает

  • собрать скриншоты для карточки в App Store и Google Play
  • выгрузить PNG во всех разрешениях, которые требуют Apple и Google
  • сделать рекламный слайд вместо простого показа интерфейса
  • переключить одну колоду между iOS, Mac и Android
  • перенести старый проект генератора на новый редактор

Примеры запросов агенту

  • разверни редактор скриншотов для App Store в этом проекте
  • собери скриншоты для Google Play из моих файлов в public/screenshots
  • перенеси старый проект app-store-screenshots на новый connected-canvas редактор

Как установить app-store-screenshots

1Через Skills CLI (Vercel)

npx skills add ParthJadhav/app-store-screenshots --skill app-store-screenshots -a claude-code

2Вручную: положить файлы

~/.claude/skills/app-store-screenshots/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/app-store-screenshots/

Похожие

orca-emulator stablyai Управляет iOS Simulator внутри Orca с живым экраном устройства. Пригодится для тапов, жестов, поворота экрана и сбора доказательств работы iOS-приложения. Claude Code CX Codex Cursor +1 5 800установок orca-emulator-android stablyai Скилл ведёт Android-эмулятор или телефон по adb прямо из Orca: тапы, свайпы, установка APK, логи. Пригодится, когда надо прогнать сценарий на устройстве. Claude Code CX Codex Cursor +1 5 600установок imsg openclaw Позволяет агенту читать локальную историю сообщений, отправлять iMessage и SMS, а также управлять реакциями и опросами через утилиту imsg. Claude Code CX Codex Cursor +1 3 400установок liquid-glass-design affaan-m Скилл для Claude и других ИИ-редакторов: подсказывает агенту, как собрать интерфейс в стиле Liquid Glass из iOS 26. Пригодится, если вы пишете SwiftUI, UIKit или WidgetKit под iOS 26+. Claude Code CX Codex Cursor +1 5 200установок android-clean-architecture affaan-m Подсказывает агенту, как раскладывать Android- и KMP-проект по слоям: модули, зависимости, UseCase, репозитории, работа с Room, SQLDelight и Ktor. Claude Code CX Codex Cursor +1 5 200установок accessibility affaan-m Проверяет и настраивает доступность интерфейсов по стандарту WCAG 2.2 Level AA для Web, iOS и Android. Пригодится при верстке компонентов, разметке ARIA и адаптации под скринридеры. Claude Code CX Codex Cursor +1 5 000установок vercel-react-native-skills vercel Набор правил по React Native и Expo для ИИ-агента: списки, анимации, навигация. Пригодится, когда агент правит мобильный код и надо, чтобы он делал это одинаково. Claude Code CX Codex Cursor +1 253 900установок animate-expo emilkowalski Создаёт мобильные анимации для React Native и Expo на базе Reanimated и Gesture Handler. Помогает избежать просадок кадров на реальных устройствах. Claude Code CX Codex Cursor +1 135 900установок

Вопросы о app-store-screenshots

Как установить app-store-screenshots в Claude?

Выполните npx skills add ParthJadhav/app-store-screenshots --skill app-store-screenshots -a claude-code. Скилл окажется в ~/.claude/skills/app-store-screenshots/SKILL.md.

Что делает app-store-screenshots?

Копирует шаблон редактора Next.js и ShadCN в ваш проект, ставит зависимости и запускает dev-сервер. В редакторе вы верстаете слайды и выгружаете PNG во всех нужных разрешениях.

Что будет со старым проектом генератора скриншотов?

Скилл распознаёт старую сборку и задаёт один вопрос: переносить её на новый редактор или нет. При согласии он обновляет шаблон на месте, сохраняя ваши скриншоты, иконку и app-store-screenshots.json.

Исходный текст

SKILL.md · MIT
---
name: app-store-screenshots
description: 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.
---

# 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
- A **Copy ideas** menu next to the headline field with formulas for hero, differentiator, feature, proof, and closer slides
- Per-slide custom background colors (caption colours stay readable automatically), a live screenshot font menu, and importing licensed WOFF2/WOFF/TTF/OTF fonts
- Image overlay elements (logos, badges, photos) with drag/resize/rotation/layering controls and directional edge fades
- Toolbar Undo/Redo (`⌘Z` / `⇧⌘Z`) over the last 50 edits of the session
- Guided in-place migration for older projects created by this skill; passive and explicit migrations keep legacy decks isolated until the user intentionally opts into connected canvas

Supported devices out of the box:
- **iPhone** (portrait) — Apple App Store
- **iPad** (portrait) — Apple App Store
- **iPhone Duo** outer and inner displays, each in portrait and landscape (four decks) — Apple App Store; see "iPhone Duo" under Step 5
- **App Store creative assets**: universal 5244×2950, product page header 3840×1646, search results 3:2 — Apple App Store; see "App Store creative assets" under Step 5
- **Apple TV** (landscape, 4K + HD) — Apple App Store
- **Apple Watch** (portrait, every Ultra/Series size) — Apple App Store
- **CarPlay** (landscape head unit) — uploaded into the **iPhone** slot; see "Apple TV, Apple Watch and CarPlay" under Step 5
- **Mac** (16:10 landscape, own **Mac** tab) — Mac App Store (`2880×1800`, `2560×1600`, `1440×900`, `1280×800`); see "Mac" under Step 5
- **Android Phone** (portrait) — Google Play
- **Android Tablet 7"** (portrait + landscape) — Google Play
- **Android Tablet 10"** (portrait + landscape) — Google Play
- **Feature Graphic** (1024×500 banner) — Google Play store listing header

## Core Principle

**Screenshots are advertisements, not documentation.** Every screenshot sells one idea. If you're showing UI, you're doing it wrong — you're selling a *feeling*, an *outcome*, or killing a *pain point*. Use this skill's interactive editor to iterate on copy and layout fast; do not hand-craft the page from scratch.

## What This Skill Does

1. **Copies a pre-built template** from `template/` (co-located with this `SKILL.md`) into the user's working directory.
2. Installs dependencies with the user's package manager.
3. Drops the user's screenshots into `public/screenshots/...` and their app icon into `public/`.
4. (Optionally) prefills `app-store-screenshots.json` with the user's app name, starting copy, screenshots, and connected-canvas preference so the first preview is meaningful.
5. Starts the dev server and tells the user to open the editor in the browser.

You should NOT write `page.tsx`, device frames, or export logic by hand. They live in the template.

## Step 0: Probe for Existing Screenshot Projects

Before asking the new-project questions in Step 1, always inspect the current working directory for an existing app-store-screenshots implementation.

Run lightweight probes:

```bash
test -f package.json && sed -n '1,220p' package.json
test -f app-store-screenshots.json && sed -n '1,120p' app-store-screenshots.json
rg -n "app-store-screenshots|html-to-image|toPng|ScreenshotEditor|DeckCanvas|connectedCanvas|EXPORT_SIZES|mockup.png|PHONE_SCREEN" package.json src app public 2>/dev/null
find public -maxdepth 4 \( -path "*/screenshots*" -o -name "mockup.png" -o -name "app-icon.png" \) -print 2>/dev/null
```

Treat the project as an older implementation when any of these are true:

- `app-store-screenshots.json` exists but has no `schemaVersion`, has `schemaVersion < 2`, or lacks `connectedCanvas`.
- `src/components/editor/screenshot-editor.tsx` exists but the editor does not reference `DeckCanvas` or `connectedCanvas`.
- `src/app/page.tsx` contains a previous all-in-one generator (`html-to-image`, `toPng`, `EXPORT_SIZES`, `PHONE_SCREEN`, hardcoded slide arrays/themes).
- The repo contains the old screenshot asset layout (`public/mockup.png`, `public/screenshots...`) plus a screenshot generator package setup.

If an older implementation is detected, ask exactly one question before doing anything else:

> I found an older App Store screenshots project here. Do you want me to migrate this existing project to the new connected-canvas editor?
>
> 1. Yes — migrate the existing project to the new editor
> 2. No — set up or modify a project another way

If the user chooses **Yes**, do **not** ask the Step 1 questionnaire. Run the migration path below using the files already in the repo. If the user chooses **No**, continue to Step 1.

### Migration Path (When User Says Yes)

The goal is an in-place UI/template upgrade, not a redesign. Preserve the user's existing app name, copy, screenshot paths, app icon, uploaded assets, locales, and device decks wherever they already exist. Replace the old UI implementation with the current template. Keep legacy decks in isolated export mode unless the project already explicitly opted into connected canvas.

Migration rules:

1. **Do not ask further product/design questions.** The user already has a project. Infer from existing files and report any non-blocking gaps at the end.
2. **Never delete user assets.** Preserve `public/screenshots/`, `public/app-icon.png`, uploaded screenshots, and any existing `app-store-screenshots.json`.
3. **Preserve recoverability.** If the worktree is not clean, do not revert unrelated changes. Before overwriting template files, copy replaced project-state/assets/code snapshots to a temporary backup outside the repo (for example `/tmp/app-store-screenshots-migration-<timestamp>/`) and mention the path in the final response.
4. **Prefer structured migration.** Read and write `app-store-screenshots.json` with JSON tooling. Do not regex-edit JSON.
5. **Set `schemaVersion: 2` and keep legacy `connectedCanvas` safe.** If the existing project already has an explicit boolean `connectedCanvas`, preserve it. If the project is pre-v2 or lacks the flag, write `"connectedCanvas": false` so offscreen/clipped legacy mockups do not leak into neighboring exports. New projects still default to connected canvas.
6. **Keep screenshots pointed at existing files.** Do not rename screenshot files unless the old project already depended on numeric names and the migration needs them. Existing static paths are fine.
7. **Handle custom themes without asking.** If the old project references a custom `themeId`, merge the matching theme object into the new `src/lib/constants.ts` when it can be found. If it cannot be recovered, leave the `themeId` in project JSON; the editor will fall back to `clean-light` and warn, and you should note that a custom theme needs manual restoration.
8. **Merge package metadata when possible.** The template's dependencies and scripts must win for the screenshot editor, but preserve unrelated existing `dependencies`, `devDependencies`, and useful scripts unless they directly conflict.
9. **Do not import template sample decks into real migrations.** If the old project already has decks or screenshots, use the template for UI/code only. Keep template sample screenshots/decks out of the migrated project so the user's app does not inherit unrelated example content.
10. **Use a disposable copy for dogfooding.** If the user asks to test or review the migration instead of actually migrating their project, copy the app to a temp directory or worktree and run the migration there. Only touch the real checkout when the user explicitly asks for the real migration and answers **Yes**.

Recommended migration sequence:

```bash
# 1. Snapshot useful old files outside the repo.
STAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/tmp/app-store-screenshots-migration-$STAMP"
mkdir -p "$BACKUP_DIR"
cp -R app-store-screenshots.json public src package.json tailwind.config.ts next.config.mjs "$BACKUP_DIR/" 2>/dev/null || true

# 2. Preserve project state and assets that must survive template copy.
PRESERVE_DIR="$BACKUP_DIR/preserve"
mkdir -p "$PRESERVE_DIR"
cp app-store-screenshots.json "$PRESERVE_DIR/" 2>/dev/null || true
cp -R public/screenshots "$PRESERVE_DIR/screenshots" 2>/dev/null || true
cp public/app-icon.png "$PRESERVE_DIR/app-icon.png" 2>/dev/null || true

# 3. Copy the current template over the old UI implementation.
cp -R "<SKILL_DIR>/template/." "$PWD/"
cp app-store-screenshots.json "$BACKUP_DIR/template-app-store-screenshots.json" 2>/dev/null || true

# 4. Restore preserved user state/assets over template samples.
cp "$PRESERVE_DIR/app-store-screenshots.json" app-store-screenshots.json 2>/dev/null || true
mkdir -p public
if [ -d "$PRESERVE_DIR/screenshots" ]; then
  mkdir -p "$BACKUP_DIR/template-samples/public"
  mv public/screenshots "$BACKUP_DIR/template-samples/public/screenshots" 2>/dev/null || true
  cp -R "$PRESERVE_DIR/screenshots" public/screenshots
else
  mkdir -p public/screenshots
fi
cp "$PRESERVE_DIR/app-icon.png" public/app-icon.png 2>/dev/null || true
```

After copying, upgrade or create `app-store-screenshots.json`. If an existing project file exists, coerce it in place. If no project file exists but old slide data is embedded in `src/lib/defaults.ts` or `src/app/page.tsx`, extract it best-effort into the template's project JSON before falling back to starter slides. Prefer old arrays or objects named `slides`, `screens`, `features`, `defaultSlides`, `appName`, `tagline`, `theme`, and screenshot paths. If the old implementation only has image files, sort `public/screenshots/**` by path and seed slides from those files.

Use a small JSON script like this for the final project-state coercion:

```bash
BACKUP_DIR="$BACKUP_DIR" node <<'NODE'
const fs = require("fs");
const path = require("path");

const PROJECT_FILE = "app-store-screenshots.json";
const DEFAULT_LOCALE = "en";
const DEVICE_KEYS = ["iphone", "ipad", "duo-outer", "duo-outer-landscape", "duo-inner", "duo-inner-landscape", "tvos", "watchos", "carplay", "mac", "android", "android-7", "android-10", "feature-graphic", "creative-universal", "creative-header", "creative-search"];
const LAYOUTS = ["hero", "device-bottom", "device-top", "two-devices", "no-device", "split-landscape", "feature-graphic"];

function readJson(file) {
  try {
    return JSON.parse(fs.readFileSync(file, "utf8"));
  } catch {
    return null;
  }
}

const templateState =
  readJson(path.join(process.env.BACKUP_DIR || "", "template-app-store-screenshots.json")) ||
  readJson(PROJECT_FILE) ||
  {};
const existingState = readJson(PROJECT_FILE) || {};
const hasExplicitConnectedCanvas = typeof existingState.connectedCanvas === "boolean";
const existingDecks =
  existingState.slidesByDevice && typeof existingState.slidesByDevice === "object" && !Array.isArray(existingState.slidesByDevice)
    ? existingState.slidesByDevice
    : {};
const hasExistingDecks = Object.keys(existingDecks).length > 0;
const state = {
  ...templateState,
  ...existingState,
  slidesByDevice: hasExistingDecks ? existingDecks : templateState.slidesByDevice || {},
};

const legacySlides =
  Array.isArray(existingState.slides) ? existingState.slides :
  Array.isArray(existingState.screens) ? existingState.screens :
  Array.isArray(existingState.features) ? existingState.features :
  null;

if (legacySlides && !hasExistingDecks) {
  state.slidesByDevice = {
    iphone: legacySlides,
  };
}

function localized(value) {
  if (typeof value === "string") return { [DEFAULT_LOCALE]: value };
  if (value && typeof value === "object" && !Array.isArray(value)) {
    return Object.fromEntries(Object.entries(value).filter(([, text]) => typeof text === "string"));
  }
  return {};
}

function cleanTransform(value) {
  if (!value || typeof value !== "object") return undefined;
  const { x, y, width, height, rotation, zIndex } = value;
  if (![x, y, width, height].every((n) => typeof n === "number" && Number.isFinite(n))) return undefined;
  return {
    x,
    y,
    width: Math.max(1, width),
    height: Math.max(1, height),
    ...(typeof rotation === "number" && Number.isFinite(rotation) ? { rotation } : {}),
    ...(typeof zIndex === "number" && Number.isFinite(zIndex) ? { zIndex } : {}),
  };
}

function firstString(...values) {
  return values.find((value) => typeof value === "string") || "";
}

// The editor refuses to load a deck with empty or repeated screen ids.
function uniqueId(value, used) {
  let id = typeof value === "string" && value.trim() ? value : "";
  while (!id || used.has(id)) id = `migrated-${Math.random().toString(36).slice(2, 10)}`;
  used.add(id);
  return id;
}

function migrateSlide(slide, used) {
  if (!slide || typeof slide !== "object" || Array.isArray(slide)) return null;
  const transforms = {};
  const rawTransforms = slide.transforms && typeof slide.transforms === "object" ? slide.transforms : {};
  for (const [id, transform] of Object.entries(rawTransforms)) {
    const cleaned = cleanTransform(transform);
    if (["caption", "device", "deviceSecondary", "callout"].includes(id) && cleaned) transforms[id] = cleaned;
  }
  const textIds = new Set();
  const textElements = Array.isArray(slide.textElements)
    ? slide.textElements
        .map((element) => {
          if (!element || typeof element !== "object" || Array.isArray(element)) return null;
          const transform = cleanTransform(element.transform);
          if (!transform) return null;
          return {
            ...element,
            id: uniqueId(element.id, textIds),
            text: localized(element.text),
            transform,
            fontSize: Number.isFinite(element.fontSize) && element.fontSize > 0 ? element.fontSize : undefined,
            fontWeight: Number.isFinite(element.fontWeight) && element.fontWeight > 0 ? element.fontWeight : undefined,
          };
        })
        .filter(Boolean)
    : undefined;

  const imageIds = new Set();
  const imageElements = Array.isArray(slide.imageElements)
    ? slide.imageElements.map((element) => {
        if (!element || typeof element !== "object" || Array.isArray(element) || typeof element.src !== "string") return null;
        const transform = cleanTransform(element.transform);
        return transform ? { ...element, id: uniqueId(element.id, imageIds), transform } : null;
      }).filter(Boolean)
    : undefined;

  return {
    ...slide,
    id: uniqueId(slide.id, used),
    layout: LAYOUTS.includes(slide.layout) ? slide.layout : "device-bottom",
    label: localized(slide.label),
    headline: localized(slide.headline || slide.title || slide.caption || slide.copy),
    screenshot: firstString(slide.screenshot, slide.image, slide.src, slide.path),
    screenshotSecondary: typeof slide.screenshotSecondary === "string" ? slide.screenshotSecondary : undefined,
    inverted: typeof slide.inverted === "boolean" ? slide.inverted : undefined,
    ...(Object.keys(transforms).length ? { transforms } : { transforms: undefined }),
    ...(textElements && textElements.length ? { textElements } : { textElements: undefined }),
    ...(imageElements && imageElements.length ? { imageElements } : { imageElements: undefined }),
    // The editor clamps magnifier values on load; only a non-object would be rejected.
    callout: slide.callout && typeof slide.callout === "object" && !Array.isArray(slide.callout) ? slide.callout : undefined,
  };
}

state.schemaVersion = 2;
state.connectedCanvas = hasExplicitConnectedCanvas ? existingState.connectedCanvas : false;
// Unique codes like "en", "pt-BR", "zh_Hans"; anything else makes the editor refuse the file.
const LOCALE_CODE = /^[a-zA-Z0-9]+(?:[-_][a-zA-Z0-9]+)*$/;
state.locales = Array.isArray(state.locales)
  ? [...new Set(state.locales.filter((locale) => typeof locale === "string" && LOCALE_CODE.test(locale)))]
  : [];
if (!state.locales.length) state.locales = [DEFAULT_LOCALE];
state.locale = state.locales.includes(state.locale) ? state.locale : state.locales[0];
state.device = DEVICE_KEYS.includes(state.device) ? state.device : "iphone";
if (state.orientation !== "portrait" && state.orientation !== "landscape") delete state.orientation;
for (const key of ["appName", "themeId", "appIcon"]) {
  if (state[key] !== undefined && typeof state[key] !== "string") delete state[key];
}
// The feature graphic only shows an icon that `appIcon` points at.
if (!state.appIcon && fs.existsSync(path.join("public", "app-icon.png"))) state.appIcon = "/app-icon.png";

if (state.slidesByDevice && typeof state.slidesByDevice === "object") {
  for (const [device, slides] of Object.entries(state.slidesByDevice)) {
    // The editor only accepts known device decks; the backup keeps the original.
    if (!DEVICE_KEYS.includes(device)) {
      delete state.slidesByDevice[device];
      continue;
    }
    const used = new Set();
    state.slidesByDevice[device] = Array.isArray(slides) ? slides.map((slide) => migrateSlide(slide, used)).filter(Boolean) : [];
  }
}

if (!state.slidesByDevice[state.device]) {
  const firstDeviceWithSlides = DEVICE_KEYS.find((device) => state.slidesByDevice[device]?.length);
  if (firstDeviceWithSlides) state.device = firstDeviceWithSlides;
}

fs.writeFileSync(PROJECT_FILE, JSON.stringify(state, null, 2) + "\n");
NODE
```

If `package.json` existed before the template copy, merge it after the project-state coercion instead of leaving a blind overwrite. Keep the template's `dev`, `build`, and `start` scripts and all editor dependencies, then add any old non-conflicting scripts and dependencies from the backed-up `package.json`.

```bash
BACKUP_DIR="$BACKUP_DIR" node <<'NODE'
const fs = require("fs");
const path = require("path");

function readJson(file) {
  try {
    return JSON.parse(fs.readFileSync(file, "utf8"));
  } catch {
    return null;
  }
}

const oldPkg = readJson(path.join(process.env.BACKUP_DIR || "", "package.json"));
const templatePkg = readJson("package.json");

if (oldPkg && templatePkg) {
  const merged = {
    ...oldPkg,
    ...templatePkg,
    scripts: {
      ...(oldPkg.scripts || {}),
      ...(templatePkg.scripts || {}),
    },
    dependencies: {
      ...(oldPkg.dependencies || {}),
      ...(templatePkg.dependencies || {}),
    },
    devDependencies: {
      ...(oldPkg.devDependencies || {}),
      ...(templatePkg.devDependencies || {}),
    },
  };

  fs.writeFileSync("package.json", JSON.stringify(merged, null, 2) + "\n");
}
NODE
```

Then install/update dependencies and verify:

```bash
bun install      # or pnpm install / yarn / npm install
set -o pipefail
bun run build 2>&1 | tee "$BACKUP_DIR/build.log"    # or the detected package-manager equivalent
```

Start the dev server and verify in the browser:

- The toolbar shows **Isolated** for migrated pre-v2 decks, unless the project file already explicitly had `"connectedCanvas": true`.
- Existing screens, copy, screenshot paths, and app icon are present.
- Referenced screenshot files exist for every configured locale, or the final report lists the missing paths.
- Device decks retained from the old project do not silently become template placeholders. If a retained deck has empty screenshots or lacks active-locale copy, report it as a follow-up instead of removing it.
- A bundle export succeeds for the active device.
- `app-store-screenshots.json` contains `"schemaVersion": 2` and a boolean `"connectedCanvas"` value.

## Step 1: Gather Input (Before Scaffolding)

Ask the user these. Do not proceed until you have answers:

### Required

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 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 with:
     ```bash
     npx skills add ParthJadhav/ios-marketing-capture
     ```
     Then have them run that skill first to generate the screenshots before continuing here.
   - If **no** and the app is **not iOS + Swift** (e.g. Android, React Native, Flutter, web): the capture skill won't work — the user needs to capture screenshots manually (simulator/device screenshots) before continuing.
2. **App icon** — "Where is your app icon PNG?"
3. **App name** — "What's the app called?"
4. **Feature list** — "List your app's features in priority order. What's the #1 thing your app does?"
5. **Style direction** — "What style do you want? You can either (a) pick one of the named deep-spec styles, or (b) describe your own vibe in your own words (warm/organic, dark/moody, clean/minimal, bold/colorful, plus any reference apps you like) and I'll build a custom palette. The template also ships with palette presets in the toolbar theme picker: the generic `clean-light`, `dark-bold`, `warm-editorial`, `ocean-fresh`, and `bloom-roast`, plus one preset per named style (same id as the style slug). The named deep specs live in `style-prompts/` — see `style-prompts.md` for the full index. Currently available: Retro Rubberhose Mascot, Moody Curated Dating, Paper Sticker Skeuomorphic, Dreamy Pastel Couples, Hand-Drawn Editorial Tasks, Glossy 3D K-Beauty Creator, Liquid Glass Aurora, Swiss Grid Bold, Neon Athletic Night, Magazine Cover Editorial, Candy Pop Social, Soft Clay Wellness, Midnight Glow Pro, Risograph Zine, Bento Keynote Grid, Toybox Primary, Quiet Japandi, Vintage Travel Poster. If the user names one of these — or describes something that clearly matches one — read `style-prompts/_QUALITY_BAR.md` first, then the matching deep spec file, and apply its entire spec (palette, gradients, shadows, rotations, per-slide breakdown). If the user describes a fully custom style, fall back to the General Visual Design Principles below and pick the closest deep spec as a starting reference."

### Optional

6. **Target stores** — Apple App Store, Mac App Store, Google Play, or a mix? Determines which platform decks to seed.
7. **iPad / Mac / Android tablet screenshots** — If yes, what sizes and orientations?
8. **Apple TV / Apple Watch / CarPlay** — Does the app have a tvOS or watchOS app, or CarPlay support? Each gets its own deck.
9. **iPhone Duo** — Does the app support iPhone Duo? If so, read `iphone-duo.md` before planning. Ask: what does the app do differently on the inner display (two panes, sidebar, wider grid)? Which displays and orientations have their own layout? Do they have simulator captures from each display at the exact sizes? Apple's bezels for iPhone, iPad, Watch, TV, Mac and iPhone Duo ship with the template (see Step 5); ask only whether they want a different finish.
10. **App Store creative assets** — Do they want a product page header / search results asset? Default to one universal 5244×2950 asset; offer separate header and search assets only if they need different compositions. Ask for a short brand line for each; don't reuse a screenshot headline.
11. **Feature Graphic** — Want a 1024×500 Play Store banner too?
12. **Localized screenshots** — Languages? (e.g. en, de, es, pt, ja, ar, he)
13. **Number of slides** — Apple allows up to 10, Google Play up to 8.
14. **Brand colors / font** — If they want a custom theme beyond the built-in presets.
15. **Additional instructions** — Anything specific.

**IMPORTANT:** If the user gives instructions at any point, follow them. They override skill defaults.

## Step 2: Scaffold the Template

### Detect Package Manager

Priority: **bun > pnpm > yarn > npm**.

```bash
which bun && echo bun || which pnpm && echo pnpm || which yarn && echo yarn || echo npm
```

### Copy the Template

The template lives at `<this skill dir>/template/` — when the skill is installed, the whole folder is already on disk. Copy its contents (NOT the folder itself) into the user's working directory. The trailing `/.` copies dotfiles like `.gitignore` too.

```bash
# Replace <SKILL_DIR> with the absolute path to this skill (the directory containing SKILL.md).
cp -R "<SKILL_DIR>/template/." "$PWD/"
```

If the target directory already has a `package.json`, ask the user before overwriting during a new scaffold. If Step 0 detected an old implementation and the user chose **Yes**, do not ask this again; follow the migration path, preserve recoverability with the backup directory, and merge package metadata after the template copy.

### Install Dependencies

```bash
bun install      # or pnpm install / yarn / npm install
```

### Drop the User's Assets

Move the user's screenshots into the layout the template expects:

```
public/
├── app-icon.png                      # ← user's app icon
├── mockup.png                        # ← already copied by the template (iPhone bezel)
├── frames/                           # ← Apple's bezels, bundled; swap a file to change finish (Step 5)
└── screenshots/
    ├── apple/
    │   ├── iphone/{locale}/01.png … N.png
    │   ├── ipad/{locale}/01.png   … N.png
    │   ├── duo-outer/{portrait|landscape}/{locale}/01.png … N.png  # iPhone Duo outer display
    │   ├── duo-inner/{portrait|landscape}/{locale}/01.png … N.png  # iPhone Duo inner display
    │   ├── tvos/{locale}/01.png   … N.png   # Apple TV, 16:9
    │   ├── watchos/{locale}/01.png … N.png  # Apple Watch
    │   ├── carplay/{locale}/01.png … N.png  # CarPlay head-unit captures
    │   └── mac/{locale}/01.png    … N.png   # Mac, 16:10
    └── android/
        ├── phone/{locale}/01.png  … N.png
        ├── tablet-7/{portrait|landscape}/{locale}/...
        └── tablet-10/{portrait|landscape}/{locale}/...
```

The starter project state lives in `app-store-screenshots.json`, not `src/lib/defaults.ts`. If the user names their screenshots differently, either rename them or update the relevant slide `screenshot` fields in `app-store-screenshots.json` so the initial deck points at the right files. The user can also drag-drop files directly into the editor at runtime — those uploads are written to `public/screenshots/uploaded/<hash>.png` when the dev server is running.

### (Optional) Seed Initial Copy

If the user provided headlines, edit `app-store-screenshots.json` to set:
- `appName`
- `themeId` (one of `"clean-light" | "dark-bold" | "warm-editorial" | "ocean-fresh" | "bloom-roast"`, a named style slug such as `"swiss-grid-bold"` when the user picked that style, or add a matching entry to `THEMES` in `src/lib/constants.ts`). Themes may set `accentAlt` for the label color on inverted slides.
- `appIcon` — public path of the app icon (e.g. `"/app-icon.png"` after copying it to `public/app-icon.png`). The Play Store feature graphic shows it; blank uses the app's initial. The icon can also be picked in the feature-graphic inspector.
- `connectedCanvas` (`true` for new connected decks; migrated legacy decks should stay `false` until the user opts in)
- Starter slides per device with the user's `label` + `headline` + screenshot paths
- Optional `scene` for the whole project: `{ backdrop: "gradient"|"solid"|"aurora"|"spotlight"|"grid"|"dots"|"lines", span: boolean, decoration: "blobs"|"rings"|"sparkles"|"none", shadow: 0–100, glow: 0–100, tilt: -30–30, headlineWeight: 300–900, headlineCase: "as-typed"|"upper", captionAlign: "auto"|"left"|"center" }`. Omit it for the classic gradient + blobs look. Pick values that match the chosen style (e.g. `spotlight` + glow for dark pro styles, `lines` + left-aligned serif for editorial); the user can refine it in the editor's **Scene** popover or compare whole looks in **Style Lab**.
- Optional per-slide `callout: { focusX: 0–1, focusY: 0–1, zoom: 1.5–5, shape: "circle"|"rounded" }` to magnify one detail of the primary screenshot. It sits over the device's upper right unless `transforms.callout` places it.
- Optional per-slide `typography: { labelScale, headlineScale, appNameScale }` (0.5–2, default 1) when one headline is much longer or shorter than the rest of the deck. `appNameScale` only applies to the feature graphic, where `headlineScale` sizes the tagline.

Otherwise, leave the defaults — the user can rewrite copy in the editor.

### Start the Dev Server

```bash
bun dev    # → http://localhost:3000
```

Tell the user to open the URL and start editing. The editor auto-saves to **`app-store-screenshots.json`** at the project root (plus a `localStorage` mirror for instant paint). Uploaded screenshots land in `public/screenshots/uploaded/<hash>.png`. Both are git-trackable — committing them means another machine can `git clone` and resume the exact deck.

## Step 3: Coach the User on Copy

Inside the editor the user will write headlines themselves, but they often need guidance. Apply these rule...

© ParthJadhav · MIT · ParthJadhav/app-store-screenshots

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой