Construyes conjuntos de capturas de pantalla para la tienda como datos, no como imágenes generadas. Cada lienzo es un canvas HTML con capas: fondo, titular, un marco de dispositivo que sostiene una captura real y elementos emergentes (recortes de la captura o leyendas vectoriales) extraídos de la pantalla en 3D. Cada cadena de texto reside en locales/<locale>.json, por lo que un nuevo idioma es un archivo JSON más las capturas, y el renderizado es determinista: la misma entrada produce los mismos píxeles.
$S es la carpeta donde reside este SKILL.md, $PY es $S/.venv/bin/python. Si el entorno virtual falta, créalo una vez:
python3 -m venv $S/.venv && $S/.venv/bin/pip install -r $S/requirements.txt && $S/.venv/bin/playwright install chromium
Pipeline:
1 beneficios -> 2 diapositivas -> 3 capture.json -> 4 captura (simuladores) -> 5 composición
-> 6 fondo -> 7 set.json + configuración regional -> 8 renderizado -> 9 revisión -> (más idiomas, dispositivos)
$S/../../examples/demo/ en el repositorio es un proyecto completo (aplicación ficticia, dos idiomas, cinco dispositivos). Lee su set.json antes de escribir uno.
Estructura del proyecto
<proyecto>/
capture.json aplicación, dispositivos de captura, pantallas a capturar
set.json composición compartida por cada configuración regional
locales/<locale>.json cada cadena: verbo, subtítulo, textos de leyenda
sim/<locale>/<diapositiva>.png capturas de teléfono (una carpeta por conjunto de captura)
sim-tablet/ sim-duo/ sim-android/
fonts/ assets/ fuentes opcionales, imagen de fondo, accesorios
out/<locale>/<dispositivo>/ solo archivos de la tienda
out/_sheets/ hoja de contacto por configuración regional y dispositivo
out/_captures/ hoja de contacto por configuración regional después de la captura
Sugiere store-screenshots/ en la raíz del repositorio de la aplicación. Añade out/ y .cache/ a su .gitignore.
1. Beneficios
Lee el código base: navegación (pestañas, rutas, pantallas), texto de incorporación, texto de muro de pago, README, listado de tienda existente. Enumera de 5 a 8 cosas que el usuario obtiene, clasificadas por cuánto impulsarían una instalación. Un beneficio por diapositiva.
Cada titular de diapositiva es un verbo corto en mayúsculas más un subtítulo de como máximo dos líneas:
PLANIFICA / TU SEMANA EN MINUTOS, AHORRA / EN CADA COMPRA. El verbo nombra el beneficio, no la funcionalidad. Muestra la lista con la pantalla que prueba cada uno y obtén la aprobación.
2. Diapositivas
Id NN-nombre; el número es la posición en la tienda. Elige la pantalla por diapositiva y anota el estado que necesita (iniciado sesión, datos presentes, una hoja abierta). Inclina las pantallas con mucho contenido visual, mantén rectas las pantallas con muchos datos.
3. capture.json
Comienza desde $S/templates/capture.json.
| Clave | Significado |
|---|---|
app.ios | bundleId, y app (ruta a una compilación .app del simulador) o copyFrom (UDID de un simulador que la tenga instalada) |
app.android | package, opcional activity, opcional apk para instalar |
devices.<key> | platform ios o android, dir (carpeta de captura), iOS: name (simulador dedicado, creado por setup) y type (xcrun simctl list devicetypes), opcional runtime; Android: avd (emulator -list-avds) |
| extras del dispositivo | appearance (light), locale (anula el modo a continuación), displaySize (2853x2007 elige la pantalla interna del iPhone Duo), rotate (grados, para una captura que sale girada) |
locale | por plataforma. iOS reboot (por defecto, el más fiable), relaunch (sin reinicio, mantiene la pose de un plegable) o launchArgs. Android app (por defecto, idioma por aplicación en Android 13+, necesita localeConfig de la aplicación) o system (necesita una imagen de emulador con capacidad root) |
launch | wait segundos después del lanzamiento, opcional url para abrir en lugar de lanzar (URL del cliente de desarrollo Expo), opcional steps |
screens | lista de {id, steps, ready, after, devices, copyFrom}, más bloques byDevice / byLocale fusionados sobre la pantalla |
Los pasos se ejecutan en orden y cada uno espera (wait, por defecto 1 a 2 s):
| Paso | Efecto |
|---|---|
{"open": "myapp://route"} | enlace profundo. Prefiere esto a los toques: funciona en todos los dispositivos y plataformas |
{"tap": {"label": "Guardar"}} | toca el elemento con esa etiqueta de accesibilidad (o {"id": ...}), coincidencia exacta y luego coincidencia de subcadena |
{"tap": {"at": [0.5, 0.9]}} | toca en una fracción de la pantalla |
{"tap": {...}, "until": {"label": "Detalles"}} | toca de nuevo (hasta 3 veces) hasta que ese elemento aparezca; úsalo para toques en pantallas que aún se están estabilizando |
{"waitFor": {"label": "Listo", "timeout": 20}} | espera hasta que un elemento esté en pantalla, antes de toques en pantallas que cargan o animan |
{"type": "pasta"}, {"key": "enter"}, {"key": "back"} | texto y teclas |
{"swipe": [0.5, 0.8, 0.5, 0.3]} | deslizar, fracciones |
{"launch": true} | reiniciar la aplicación |
ready: {"region": [x, y, w, h], "minStd": 20, "tries": 5, "delay": 3} vuelve a tomar la captura mientras esa región sea de un color sólido (filas de esqueleto, un spinner, una imagen que aún se carga). Las capturas de iPhone se vuelven a tomar automáticamente cuando el simulador hornea la Isla Dinámica en ellas.
copyFrom: "01-home" reutiliza otra captura en ese dispositivo (una tableta que muestra dos pantallas en una vista dividida). devices: ["phone"] limita una pantalla a algunos dispositivos.
capture.py PROYECTO describe --device phone imprime los elementos táctiles con etiquetas y marcos.
4. Captura
$PY $S/capture.py <proyecto> doctor # herramientas, dispositivos, IDs de aplicación
$PY $S/capture.py <proyecto> setup # crear/iniciar simuladores dedicados, iniciar AVD, instalar la aplicación
$PY $S/capture.py <proyecto> run --locale es-ES # todos los dispositivos y pantallas
$PY $S/capture.py <proyecto> run --locale es-ES --device phone --screen 03-search
- Los dispositivos de captura son dedicados: los simuladores de iOS se crean por nombre (
Capture iPhone...), Android usa los AVD con nombre en capture.json. Nunca apuntes capture.json al simulador en el que el usuario trabaja, y pregunta antes de iniciar dispositivos o cambiar su idioma. - Después de
setup, la aplicación normalmente necesita un estado de demostración: iniciar sesión en una cuenta de demostración, sembrar datos, descartar diálogos de primera ejecución (guardar contraseña, notificaciones, solicitudes de reseña). Hazlo una vez por dispositivo; el estado sobrevive a los cambios de idioma. Pide al usuario credenciales de demostración, nunca las escribas en capture.json. - Una compilación de desarrollo que muestra superposiciones de depuración (botones de menú de desarrollo, toasts) necesita ocultarlas para la sesión de captura. Parchea localmente y no confirmes el parche.
- Revisa
out/_captures/<locale>.jpgbaldosa por baldosa: ninguna cadena en otro idioma, ningún diálogo, toast, spinner o estado vacío. Corrige los datos o los pasos y vuelve a ejecutar esa pantalla. El renderizador muestra la captura tal como es; nada pinta sobre un error. - iPhone Duo: despliega el simulador y ponlo en modo horizontal en Simulador o DeviceHub primero; simctl no puede plegar ni rotar. Usa enlaces profundos (los toques HID pueden no alcanzar la pantalla interna),
"locale": "relaunch"y"displaySize": "2853x2007".
Las aplicaciones multiplataforma (React Native, Flutter) pueden omitir la captura de Android: las baldosas de Android reutilizan las capturas de iPhone y iPad, que es lo que hacen los dispositivos predeterminados. Las aplicaciones nativas de Android capturan sus propias pantallas en sim-android/, consulta la sección 10.
5. Composición (el usuario aprueba antes de cualquier renderizado)
Dibuja cada diapositiva en ASCII: titular, posición e inclinación del dispositivo, qué elemento emerge y dónde aterriza. Obtén la aprobación, luego escribe set.json.
+------------------------------+
| AGUA | verbo, una línea
| CADA PLANTA EN SU | subtítulo, máximo 2 líneas
| MOMENTO |
| .--------------. |
| [* 3 plantas hoy] | leyenda, elevada, sobresale por la izquierda
| | Hoy ... | | dispositivo inclinado y 15, x 4
+------------------------------+
6. Fondo
Un fondo por conjunto, compartido por cada configuración regional y dispositivo. Ya sea CSS ("background": {"css": "radial-gradient(...), #1f4a37"}, el ejemplo usa esto) o una imagen ({"image": "assets/bg.png"}, background-size cover). Un generador de imágenes funciona bien para un fondo suave; solicita un fondo de estudio 9:16 sin costuras en el color de la marca con luz suave, y establece explícitamente: sin objetos, sin texto, sin dispositivos, sin formas. Genera dos, el usuario elige. Los accesorios (pegatinas, fotos de productos) son PNG con canal alfa colocados por diapositiva.
7. set.json y configuración regional
Mide cada rectángulo en píxeles de captura con measure.py, nunca a ojo:
$PY $S/measure.py sim/es-ES/05-fill.png --at 700,790 # rellena por inundación un botón, chip, píldora
$PY $S/measure.py sim/es-ES/01-plan.png --ink 40,415,700,75 # límites del texto en un área plana
$PY $S/measure.py sim/es-ES/02-cook.png --grid 440,1510,340,110 # imagen de cuadrícula ampliada en el directorio temporal
| Clave | Significado |
|---|---|
locales, devices | lo que cubre un renderizado completo; ids de dispositivo de $S/devices/devices.json |
background | {"image": ...} o {"css": ...}, opcional position |
fonts | {"family": "fonts/File.otf"}, rutas relativas al proyecto; por defecto display es el Archivo Black incluido |
headline | font, descFont, verbColor, descColor, shadow, verbTracking, descTracking, descWordSpacing |
capture | platform, statusBarBand (filas de captura difuminadas, iOS por defecto [66, 128]), sets ({"tablet": {"dir": "sim-tablet", "statusBarBand": [12, 56]}}), devices ({"android-phone": "android"} para apuntar un dispositivo a otro conjunto de captura) |
style | gloss, deviceShadow, thicknessRatio (profundidad de pared lateral), sideLayers |
statusBar | time, color de la barra de estado que dibuja el renderizador |
layout | por id de dispositivo, anula el diseño de devices.json (deviceTop, deviceWidth, verbMax ...) |
baseline | {device: {locale: folder}} de un conjunto en vivo, para --compare |
slides | lista, a continuación |
Diapositiva: id, sim (archivo de captura si no es <id>.png), tilt {x, y, z} grados, offset {x, y} fracciones del canvas, layout, breakouts, props (src, x, y, w fracciones del canvas, rotate, shadow, z: front o back).
Elemento emergente:
| Clave | Significado |
|---|---|
type | crop: los píxeles de la propia captura; callout: píldora vectorial con texto de la configuración regional |
rect o anchor | [x, y, w, h] en px de captura del elemento del que proviene |
at o dx/dy | posición de aterrizaje (at = nueva esquina superior izquierda en px de captura) o desplazamiento |
scale, lift | tamaño y elevación z en px (predeterminado 80) |
vacate, vacatePad | llenar el lugar original con el color circundante (movido, no copiado) |
radius, shadow, origin | radio de esquina en px de captura, intensidad de sombra, origen de transformación |
text, icon, style | leyenda: clave en callouts de la configuración regional, tag o sparkles, y font, size, bg, fg, padX, padY, gap, radius, weight, tracking, iconScale, iconColor |
Cualquier valor dentro de slides puede variar por dispositivo y por configuración regional:
{"byDevice": {"phone": ..., "tablet": ..., "default": ...}} (claves: id de dispositivo, conjunto de captura, default) y {"byLocale": {"es-ES": ..., "default": ...}}, anidados de cualquier forma. Los rectángulos casi siempre varían por configuración regional, porque el mismo chip es más ancho en alemán.
Los recortes se mantienen nítidos hasta aproximadamente 1.7x para elementos grandes. Los chips pequeños y el texto se convierten en leyendas, que son vectoriales y se mantienen nítidas a cualquier escala.
Archivo de configuración regional:
{
"captures": "es-ES",
"dir": "ltr",
"fonts": {"display": "fonts/NotoSansJP-Black.otf"},
"slides": {
"01-plan": {"verb": "Planificar", "desc": "Tu semana en minutos", "callouts": {"savings": "aprox. €7.81 ahorrados"}}
}
}
Solo se requiere slides. captures hace que una configuración regional reutilice las capturas de otra (es-AR leyendo es-ES). headline y statusBar anulan set.json. Archivo Black cubre solo latín; cirílico, griego, CJK, árabe y hebreo necesitan una anulación de fonts, RTL necesita "dir": "rtl".
8. Renderizado
$PY $S/render.py <proyecto> # todo
$PY $S/render.py <proyecto> --locale es-ES --device iphone-67 --slide 01-plan
$PY $S/render.py <proyecto> --compare # hojas de referencia vs nuevas
Los tamaños de titular se comparten en todas las diapositivas de una configuración regional y dispositivo, por lo que un conjunto se lee como uno solo. El renderizador se detiene cuando la relación de aspecto de una captura no coincide con la abertura de pantalla del dispositivo.
9. Revisión
Abre out/_sheets/<locale>-<device>.jpg y haz zoom en cada elemento emergente. Las líneas impresas como CHECK nombran el problema (titular en tamaño mínimo, superposición). Un titular en tamaño mínimo significa que el texto es demasiado largo: acórtalo, no bajes los límites. Muestra las hojas al usuario.
10. Más idiomas y dispositivos
Añadir un idioma:
$PY $S/add_locale.py <proyecto> es-ES init --from en-US # archivo de configuración regional, marcadores de posición byLocale, carpetas
$PY $S/capture.py <proyecto> run --locale es-ES # capturas
# traduce locales/es-ES.json: texto de tienda, verbos cortos, leyendas que coinciden con la captura
$PY $S/add_locale.py <proyecto> es-ES measure --from en-US # reencuentra rectángulos, revisa la imagen lado a lado
$PY $S/add_locale.py <proyecto> es-ES measure --from en-US --write
$PY $S/add_locale.py <proyecto> es-ES check # 0 errores antes de renderizar
$PY $S/render.py <proyecto> --locale es-ES
Una variante regional (es-AR, de-AT, fr-CA): init --share-captures, luego solo cambian los textos.
Dispositivos en $S/devices/devices.json:
| id | canvas | lee | espacio en tienda |
|---|---|---|---|
iphone-67 | 1290x2796 | teléfono (1206x2622) | ASC APP_IPHONE_67 |
android-phone | 1080x1920 | teléfono | capturas de pantalla de Play para teléfono |
ipad-13 | 2064x2752 | tableta (2064x2752) | ASC APP_IPAD_PRO_3GEN_129 |
android-tablet | 1792x2400 | tableta | tableta de 7" y 10" de Play |
iphone-duo | 2853x2007 | duo (2853x2007) | ASC iPhone Duo |
Los marcos son dibujos genéricos hechos con make_frame.py, libres para redistribuir. Un tamaño de captura sin un marco coincidente obtiene uno:
# capturas nativas de Android, 1080x2400, en sim-android/
$PY $S/make_frame.py --style android --screen 1080x2400 --device android-phone --captures android
# luego en set.json: "capture": {"sets": {"android": {"dir": "sim-android", "statusBarBand": [30, 105]}}}
Estilos: ios, android, tablet, landscape. --canvas WxH establece el tamaño de tienda para un nuevo id de dispositivo. Mide statusBarBand en una captura real: las filas de la barra de estado más algo de margen.