Du erstellst Store-Screenshot-Sets als Daten, nicht als generierte Bilder. Jedes Tile ist eine HTML-Leinwand mit mehreren Ebenen: Hintergrund, Überschrift, ein Geräterahmen mit einem echten Screenshot und Breakouts (Ausschnitte des Screenshots oder vektorbasierte Callouts), die in 3D aus dem Bild herausgehoben sind. Jeder Text lebt in locales/<locale>.json, sodass eine neue Sprache nur eine JSON-Datei plus Aufnahmen benötigt, und der Render-Vorgang ist deterministisch: gleiche Eingaben erzeugen die gleichen Pixel.
$S unten ist der Ordner, in dem diese SKILL.md liegt, $PY ist $S/.venv/bin/python. Wenn die virtuelle Umgebung fehlt, erstelle sie einmalig:
python3 -m venv $S/.venv && $S/.venv/bin/pip install -r $S/requirements.txt && $S/.venv/bin/playwright install chromium
Pipeline:
1 benefits -> 2 slides -> 3 capture.json -> 4 capture (simulators) -> 5 composition
-> 6 background -> 7 set.json + locales -> 8 render -> 9 review -> (more languages, devices)
$S/../../examples/demo/ im Repository ist ein vollständiges Projekt (fiktive App, zwei
Sprachen, fünf Geräte). Lies dessen set.json, bevor du eines schreibst.
Projektstruktur
<project>/
capture.json App, Aufnahmegeräte, zu erfassende Bildschirme
set.json Composition, die von allen Sprachen geteilt wird
locales/<locale>.json Jeder Text: Verb, Unterzeile, Callout-Texte
sim/<locale>/<slide>.png Screenshots (ein Ordner pro Aufnahmeset)
sim-tablet/ sim-duo/ sim-android/
fonts/ assets/ Optionale Schriftarten, Hintergrundbild, Objekte
out/<locale>/<device>/ Nur Store-Dateien
out/_sheets/ Kontaktbogen pro Sprache und Gerät
out/_captures/ Kontaktbogen pro Sprache nach der Aufnahme
Empfehle store-screenshots/ im Stammverzeichnis der App. Füge out/ und .cache/ zu dessen .gitignore hinzu.
1. Benefits (Vorteile)
Lies das Codebase: Navigation (Tabs, Routen, Bildschirme), Onboarding-Texte, Paywall-Texte, README, vorhandene Store-Listings. Liste die 5 bis 8 Dinge auf, die ein Benutzer erhält, sortiert danach, wie sehr sie eine Installation fördern würden. Ein Vorteil pro Slide.
Jede Slide-Überschrift besteht aus einem kurzen Verb in Großbuchstaben und einer Unterzeile von maximal zwei Zeilen: PLAN / YOUR WEEK IN MINUTES, SAVE / ON EVERY SHOP. Das Verb benennt den Vorteil, nicht die Funktion. Zeige die Liste mit dem Bildschirm, der jeden Vorteil belegt, und hole eine Bestätigung ein.
2. Slides
ID NN-name; die Zahl ist die Position im Store. Wähle den Bildschirm pro Slide und notiere den benötigten Zustand (eingeloggt, Daten vorhanden, ein Sheet offen). Neige bildlastige Bildschirme, behalte datenreiche Bildschirme gerade.
3. capture.json
Beginne mit $S/templates/capture.json.
| Schlüssel | Bedeutung |
|---|---|
app.ios | bundleId und app (Pfad zu einem Simulator-.app-Build) oder copyFrom (UDID eines Simulators, auf dem es installiert ist) |
app.android | package, optional activity, optional apk zum Installieren |
devices.<key> | platform ios oder android, dir (Aufnahmeordner), iOS: name (dedizierter Simulator, durch Setup erstellt) und type (xcrun simctl list devicetypes), optional runtime; Android: avd (emulator -list-avds) |
| Device-Extras | appearance (light), locale (überschreibt den untenstehenden Modus), displaySize (2853x2007 wählt das innere Display des iPhone Duo), rotate (Grad, für Aufnahmen, die seitlich erscheinen) |
locale | Pro Plattform. iOS reboot (Standard, zuverlässig), relaunch (kein Neustart, behält die Pose eines Foldables) oder launchArgs. Android app (Standard, sprachspezifisch ab Android 13, benötigt localeConfig der App) oder system (benötigt ein root-fähiges Emulator-Image) |
launch | wait Sekunden nach dem Start, optional url zum Öffnen anstelle des App-Starts (Expo-Dev-Client-URL), optional steps |
screens | Liste von {id, steps, ready, after, devices, copyFrom}, plus byDevice- / byLocale-Blöcke, die über den Screen gemerged werden |
Schritte werden der Reihe nach ausgeführt und warten jeweils (wait, Standard 1 bis 2 s):
| Schritt | Effekt |
|---|---|
{"open": "myapp://route"} | Deep Link. Bevorzuge dies gegenüber Taps: es funktioniert auf jedem Gerät und jeder Plattform |
{"tap": {"label": "Save"}} | Tippt auf das Element mit diesem Accessibility-Label (oder {"id": ...}), exakt dann Substring-Suche |
{"tap": {"at": [0.5, 0.9]}} | Tippt auf eine Position als Bruchteil des Bildschirms |
{"tap": {...}, "until": {"label": "Details"}} | Tippt erneut (bis zu 3 Mal), bis dieses Element erscheint; verwende es bei Taps auf Bildschirmen, die noch nicht stabil sind |
{"waitFor": {"label": "Done", "timeout": 20}} | Wartet, bis ein Element auf dem Bildschirm ist, vor Taps auf Bildschirmen, die laden oder animieren |
{"type": "pasta"}, {"key": "enter"}, {"key": "back"} | Text und Tasten |
{"swipe": [0.5, 0.8, 0.5, 0.3]} | Wischen, Bruchteile |
{"launch": true} | App neu starten |
ready: {"region": [x, y, w, h], "minStd": 20, "tries": 5, "delay": 3} wiederholt Aufnahmen, während dieser Bereich eine einfarbige Farbe zeigt (Skeleton-Zeilen, ein Spinner, ein noch ladendes Bild). iPhone-Aufnahmen werden automatisch wiederholt, wenn der Simulator die Dynamic Island in sie einbrennt.
copyFrom: "01-home" verwendet eine andere Aufnahme auf diesem Gerät (ein Tablet zeigt zwei Bildschirme in einer geteilten Ansicht). devices: ["phone"] beschränkt einen Bildschirm auf einige Geräte.
capture.py PROJEKT describe --device phone gibt tappbare Elemente mit Labels und Rahmen aus.
4. Aufnahme (Capture)
$PY $S/capture.py <project> doctor # Werkzeuge, Geräte, App-IDs
$PY $S/capture.py <project> setup # Erstellen/Starten dedizierter Simulatoren, Starten von AVDs, Installieren der App
$PY $S/capture.py <project> run --locale en-US # Jedes Gerät und jeder Bildschirm
$PY $S/capture.py <project> run --locale en-US --device phone --screen 03-search
- Aufnahmegeräte sind dediziert: iOS-Simulatoren werden mit Namen erstellt (
Capture iPhone...), Android verwendet die in capture.json benannten AVDs. Richte capture.json niemals auf den Simulator, in dem der Benutzer arbeitet, und frage nach, bevor du Geräte startest oder deren Sprache wechselst. - Nach
setupbenötigt die App normalerweise einen Demo-Zustand: Anmeldung bei einem Demo-Konto, Seed-Daten, erste Dialoge schließen (Passwort speichern, Benachrichtigungen, Bewertungsaufforderungen). Mache dies einmal pro Gerät; der Zustand bleibt über Sprachwechsel hinweg erhalten. Frage den Benutzer nach Demo-Anmeldedaten, schreibe sie niemals in capture.json. - Ein Dev-Build, der Debug-Overlays anzeigt (Dev-Menü-Buttons, Toasts), muss diese für die Aufnahme-Session verstecken. Entferne sie lokal und committe den Patch nicht.
- Überprüfe
out/_captures/<locale>.jpgTile für Tile: kein Text in einer anderen Sprache, kein Dialog, Toast, Spinner oder leerer Zustand. Korrigiere Daten oder Schritte und führe diesen Bildschirm erneut aus. Der Renderer zeigt die Aufnahme so, wie sie ist; nichts übermalt einen Fehler. - iPhone Duo: Entfalte den Simulator und lege ihn im Simulator oder DeviceHub ins Querformat; simctl kann nicht falten oder drehen. Verwende Deep Links (HID-Taps erreichen möglicherweise nicht das innere Display),
"locale": "relaunch"und"displaySize": "2853x2007".
Cross-Plattform-Apps (React Native, Flutter) können die Android-Aufnahme überspringen: Android-Tiles verwenden die iPhone- und iPad-Aufnahmen wieder, was die Standard-Geräte tun. Native Android-Apps nehmen ihre eigenen Bildschirme in sim-android/ auf, siehe Abschnitt 10.
5. Composition (Benutzer genehmigt vor jedem Rendern)
Skizziere jede Slide in ASCII: Überschrift, Geräteposition und -neigung, welches Element ausbricht und wo es landet. Hole eine Bestätigung ein, dann schreibe set.json.
+------------------------------+
| WATER | Verb, eine Zeile
| EVERY PLANT RIGHT | Unterzeile, max 2 Zeilen
| ON TIME |
| .--------------. |
| [* 3 plants today] | Callout, angehoben, ragt links heraus
| | Today ... | | Gerät geneigt y 15, x 4
+------------------------------+
6. Hintergrund (Background)
Ein Hintergrund pro Set, von jeder Sprache und jedem Gerät geteilt. Entweder CSS ("background": {"css": "radial-gradient(...), #1f4a37"}, das Demo verwendet dies) oder ein Bild ({"image": "assets/bg.png"}, Hintergrundgröße cover). Ein Bildgenerator eignet sich gut für eine weiche Platte; fordere ein nahtloses 9:16 Studio-Backdrop in der Markenfarbe mit weichem Licht an und gib explizit an: keine Objekte, kein Text, keine Geräte, keine Formen. Generiere zwei, der Benutzer wählt aus. Props (Sticker, Produktbilder) sind PNGs mit Alpha, die pro Slide platziert werden.
7. set.json und locales
Messe jedes Rechteck in Aufnahme-Pixeln mit measure.py, niemals nach Augenmaß:
$PY $S/measure.py sim/en-US/05-fill.png --at 700,790 # Füllt einen Button, Chip, Pill aus
$PY $S/measure.py sim/en-US/01-plan.png --ink 40,415,700,75 # Textbegrenzungen auf einer einfarbigen Fläche
$PY $S/measure.py sim/en-US/02-cook.png --grid 440,1510,340,110 # Zoom-Gitterbild im Temp-Verzeichnis
| Schlüssel | Bedeutung |
|---|---|
locales, devices | Was ein vollständiger Render abdeckt; Geräte-IDs aus $S/devices/devices.json |
background | {"image": ...} oder {"css": ...}, optional position |
fonts | {"family": "fonts/File.otf"}, Pfade relativ zum Projekt; Standard display ist die enthaltene Archivo Black |
headline | font, descFont, verbColor, descColor, shadow, verbTracking, descTracking, descWordSpacing |
capture | platform, statusBarBand (Aufnahmezeilen, die weggeblendet werden, iOS-Standard [66, 128]), sets ({"tablet": {"dir": "sim-tablet", "statusBarBand": [12, 56]}}), devices ({"android-phone": "android"}, um ein Gerät auf ein anderes Aufnahmeset zu verweisen) |
style | gloss, deviceShadow, thicknessRatio (Seitenwandtiefe), sideLayers |
statusBar | time, color der Statusleiste, die der Renderer zeichnet |
layout | Pro Geräte-ID, überschreibt das Layout der devices.json (deviceTop, deviceWidth, verbMax ...) |
baseline | {device: {locale: folder}} eines Live-Sets, für --compare |
slides | Liste, siehe unten |
Slide: id, sim (Aufnahmedatei, falls nicht <id>.png), tilt {x, y, z} Grad, offset
{x, y} Leinwand-Brüche, layout, breakouts, props (src, x, y, w Leinwand-Brüche, rotate, shadow, z: front oder back).
Breakout:
| Schlüssel | Bedeutung |
|---|---|
type | crop: die eigenen Pixel der Aufnahme; callout: vektorbasierte Tablette mit Locale-Text |
rect oder anchor | [x, y, w, h] in Aufnahme-Pixeln des Elements, von dem es stammt |
at oder dx/dy | Zielposition (at = neue obere linke Ecke in Aufnahme-Pixeln) oder Versatz |
scale, lift | Größe und z-Höhe in Pixeln (Standard 80) |
vacate, vacatePad | Fülle die ursprüngliche Stelle mit der umgebenden Farbe (verschoben, nicht kopiert) |
radius, shadow, origin | Eckradius in Aufnahme-Pixeln, Schattenstärke, Transformationsursprung |
text, icon, style | Callout: Schlüssel im callouts-Objekt des Locales, tag oder sparkles, und font, size, bg, fg, padX, padY, gap, radius, weight, tracking, iconScale, iconColor |
Jeder Wert innerhalb von slides kann pro Gerät und pro Sprache variieren:
{"byDevice": {"phone": ..., "tablet": ..., "default": ...}} (Schlüssel: Geräte-ID, Aufnahmeset,
default) und {"byLocale": {"de-DE": ..., "default": ...}}, beliebig verschachtelt. Rechtecke variieren fast immer pro Sprache, weil derselbe Chip auf Deutsch breiter ist.
Crops bleiben bis etwa 1,7x für große Elemente scharf. Kleine Chips und Texte werden zu Callouts, die vektorbasiert sind und in jeder Größe scharf bleiben.
Locales-Datei:
{
"captures": "en-US",
"dir": "ltr",
"fonts": {"display": "fonts/NotoSansJP-Black.otf"},
"slides": {
"01-plan": {"verb": "Plan", "desc": "Your week in minutes", "callouts": {"savings": "approx. €7.81 saved"}}
}
}
Nur slides ist erforderlich. captures veranlasst, dass ein Locale die Aufnahmen eines anderen Locales wiederverwendet (en-GB liest en-US). headline und statusBar überschreiben set.json. Archivo Black deckt nur Latein ab; Kyrillisch, Griechisch, CJK, Arabisch und Hebräisch benötigen einen fonts-Override, RTL benötigt "dir": "rtl".
8. Rendern (Render)
$PY $S/render.py <project> # Alles
$PY $S/render.py <project> --locale de-DE --device iphone-67 --slide 01-plan
$PY $S/render.py <project> --compare # Baseline vs. neue Blätter
Überschriftsgrößen werden über alle Slides einer Sprache und eines Geräts geteilt, sodass ein Set wie aus einem Guss wirkt. Der Renderer stoppt, wenn das Seitenverhältnis einer Aufnahme nicht mit der Bildschirmöffnung des Geräts übereinstimmt.
9. Überprüfung (Review)
Öffne out/_sheets/<locale>-<device>.jpg und zoome in jedes Breakout. Als CHECK gedruckte Zeilen nennen das Problem (Überschrift bei Mindestgröße, Überlappung). Eine Überschrift bei Mindestgröße bedeutet, dass der Text zu lang ist: Kürze ihn, senke die Grenzen nicht. Zeige die Blätter dem Benutzer.
10. Weitere Sprachen und Geräte
Füge eine Sprache hinzu:
$PY $S/add_locale.py <project> es-ES init --from en-US # Locale-Datei, byLocale-Platzhalter, Ordner
$PY $S/capture.py <project> run --locale es-ES # Aufnahmen
# Übersetze locales/es-ES.json: Store-Kopie, kurze Verben, Callouts passend zur Aufnahme
$PY $S/add_locale.py <project> es-ES measure --from en-US # Finde Rechtecke neu, überprüfe das Side-by-Side-Bild
$PY $S/add_locale.py <project> es-ES measure --from en-US --write
$PY $S/add_locale.py <project> es-ES check # 0 Fehler vor dem Rendern
$PY $S/render.py <project> --locale es-ES
Eine regionale Variante (en-GB, de-AT, fr-CA): init --share-captures, dann ändern sich nur Texte.
Geräte in $S/devices/devices.json:
| id | Leinwand | liest | Store-Slot |
|---|---|---|---|
iphone-67 | 1290x2796 | phone (1206x2622) | ASC APP_IPHONE_67 |
android-phone | 1080x1920 | phone | Play Phone Screenshots |
ipad-13 | 2064x2752 | tablet (2064x2752) | ASC APP_IPAD_PRO_3GEN_129 |
android-tablet | 1792x2400 | tablet | Play 7" and 10" Tablet |
iphone-duo | 2853x2007 | duo (2853x2007) | ASC iPhone Duo |
Die Rahmen sind generische Zeichnungen, die mit make_frame.py erstellt wurden, frei weiterverteilbar. Eine Aufnahmegröße ohne passenden Rahmen erhält einen:
# Native Android-Aufnahmen, 1080x2400, in sim-android/
$PY $S/make_frame.py --style android --screen 1080x2400 --device android-phone --captures android
# dann in set.json: "capture": {"sets": {"android": {"dir": "sim-android", "statusBarBand": [30, 105]}}}
Stile: ios, android, tablet, landscape. --canvas WxH setzt die Store-Größe für eine neue
Geräte-ID. Miss statusBarBand bei einer echten Aufnahme: die Zeilen der Statusleiste plus etwas
...