당신은 스크린샷 세트를 생성된 이미지가 아닌 데이터로 구축합니다. 각 타일은 레이어로 구성된 HTML Canvas입니다: 배경, 헤드라인, 실제 캡처를 담은 기기 프레임, 그리고 3D로 화면 밖으로 돌출된 브레이크아웃(캡처의 일부 또는 벡터 설명)으로 이루어집니다. 모든 문자열은 locales/<locale>.json에 저장되므로, 새 언어는 JSON 파일 하나와 캡처만 추가하면 되며, 렌더링은 결정론적입니다: 동일한 입력은 동일한 픽셀을 보장합니다.
$S는 이 SKILL.md 파일이 위치한 폴더, $PY는 $S/.venv/bin/python입니다. 가상 환경이 없으면 한 번 생성하세요:
python3 -m venv $S/.venv && $S/.venv/bin/pip install -r $S/requirements.txt && $S/.venv/bin/playwright install chromium
파이프라인:
1 장점 -> 2 슬라이드 -> 3 capture.json -> 4 캡처 (시뮬레이터) -> 5 구성
-> 6 배경 -> 7 set.json + 로케일 -> 8 렌더링 -> 9 검토 -> (더 많은 언어, 기기)
저장소의 $S/../../examples/demo/는 완전한 프로젝트 예제입니다 (가상 앱, 2개 언어, 5개 기기). 직접 작성하기 전에 set.json을 읽어보세요.
프로젝트 구조
<project>/
capture.json 앱, 캡처 기기, 캡처할 화면
set.json 모든 로케일이 공유하는 구성
locales/<locale>.json 모든 문자열: 동사, 설명, 설명 텍스트
sim/<locale>/<slide>.png 폰 캡처 (캡처 세트당 하나의 폴더)
sim-tablet/ sim-duo/ sim-android/
fonts/ assets/ 선택적 폰트, 배경 이미지, 소품
out/<locale>/<device>/ 스토어 파일만
out/_sheets/ 로케일 및 기기별 연락처 시트
out/_captures/ 캡처 후 로케일별 연락처 시트
앱 저장소 루트에 store-screenshots/를 권장합니다. out/과 .cache/를 .gitignore에 추가하세요.
1. 장점
코드베이스를 읽으세요: 네비게이션 (탭, 라우트, 화면), 온보딩 문구, 페이월 문구, README, 기존 스토어 등록 정보를 확인합니다. 사용자가 앱을 설치하게 할 만한 5~8가지 항목을 순위별로 나열하세요. 슬라이드당 하나의 장점입니다.
각 슬라이드 헤드라인은 대문자로 된 하나의 짧은 동사와 최대 두 줄의 설명으로 구성됩니다: PLAN / YOUR WEEK IN MINUTES, SAVE / ON EVERY SHOP. 동사는 장점을 나타내며 기능을 나타내지 않습니다. 각 장점을 증명하는 화면과 함께 목록을 보여주고 승인을 받으세요.
2. 슬라이드
ID는 NN-name 형식이며, 숫자는 스토어에서의 위치입니다. 슬라이드당 화면을 선택하고 필요한 상태(로그인, 데이터 존재, 시트 열림 등)를 기록하세요. 이미지가 많은 화면은 기울이고, 데이터가 많은 화면은 수직으로 유지하세요.
3. capture.json
$S/templates/capture.json에서 시작하세요.
| 키 | 의미 |
|---|---|
app.ios | bundleId, 그리고 app (시뮬레이터 .app 빌드 경로) 또는 copyFrom (앱이 설치된 시뮬레이터 UDID) |
app.android | package, 선택적 activity, 선택적 apk 설치 경로 |
devices.<key> | platform ios 또는 android, dir (캡처 폴더), iOS: name (전용 시뮬레이터, setup으로 생성) 및 type (xcrun simctl list devicetypes), 선택적 runtime; Android: avd (emulator -list-avds) |
| 기기 추가 설정 | appearance (light), locale (아래 모드 재정의), displaySize (2853x2007은 iPhone Duo 내부 디스플레이 선택), rotate (도, 캡처가 옆으로 나올 때) |
locale | 플랫폼별. iOS reboot (기본값, 가장 안정적), relaunch (재부팅 없음, 폴더블 자세 유지) 또는 launchArgs. Android app (기본값, Android 13+에서 앱별 언어, 앱의 localeConfig 필요) 또는 system (루트 권한이 있는 에뮬레이터 이미지 필요) |
launch | 실행 후 wait 초, 선택적 url (앱 대신 열 URL, Expo dev client URL), 선택적 steps |
screens | {id, steps, ready, after, devices, copyFrom} 목록, 각 화면에 병합되는 byDevice / byLocale 블록 포함 |
단계는 순서대로 실행되며 각각 대기합니다 (wait, 기본 1~2초):
| 단계 | 효과 |
|---|---|
{"open": "myapp://route"} | 딥링크. 탭보다 권장: 모든 기기와 플랫폼에서 작동 |
{"tap": {"label": "Save"}} | 해당 접근성 레이블이 있는 요소 탭 (또는 {"id": ...}), 정확히 일치 후 부분 문자열 일치 |
{"tap": {"at": [0.5, 0.9]}} | 화면 분율 위치 탭 |
{"tap": {...}, "until": {"label": "Details"}} | 해당 요소가 나타날 때까지 최대 3회 탭 반복; 아직 안정화되지 않은 화면의 탭에 사용 |
{"waitFor": {"label": "Done", "timeout": 20}} | 로드되거나 애니메이션 중인 화면에서 탭 전에 요소가 화면에 나타날 때까지 대기 |
{"type": "pasta"}, {"key": "enter"}, {"key": "back"} | 텍스트 및 키 입력 |
{"swipe": [0.5, 0.8, 0.5, 0.3]} | 스와이프, 분율 좌표 |
{"launch": true} | 앱 재시작 |
ready: {"region": [x, y, w, h], "minStd": 20, "tries": 5, "delay": 3}는 해당 영역이 단색(스켈레톤 행, 스피너, 로딩 중인 이미지)일 때 재촬영합니다. iPhone 캡처는 시뮬레이터가 Dynamic Island를 화면에 구울 때 자동으로 재촬영됩니다.
copyFrom: "01-home"는 해당 기기의 다른 캡처를 재사용합니다 (태블릿이 분할 보기에서 두 화면을 표시하는 경우). devices: ["phone"]는 특정 화면을 일부 기기로 제한합니다.
capture.py PROJECT describe --device phone는 레이블과 프레임이 있는 탭 가능한 요소를 출력합니다.
4. 캡처
$PY $S/capture.py <project> doctor # 도구, 기기, 앱 ID 확인
$PY $S/capture.py <project> setup # 전용 시뮬레이터 생성/부팅, AVD 부팅, 앱 설치
$PY $S/capture.py <project> run --locale en-US # 모든 기기와 화면 캡처
$PY $S/capture.py <project> run --locale en-US --device phone --screen 03-search
- 캡처 기기는 전용입니다: iOS 시뮬레이터는 이름으로 생성되고 (
Capture iPhone...), Android는 capture.json에 지정된 AVD를 사용합니다. capture.json을 사용자가 작업 중인 시뮬레이터로 지정하지 말고, 기기를 부팅하거나 언어를 변경하기 전에 물어보세요. setup후, 앱은 일반적으로 데모 상태가 필요합니다: 데모 계정 로그인, 데이터 시드, 첫 실행 대화상자(비밀번호 저장, 알림, 리뷰 요청) 해제. 기기당 한 번 수행하면 상태가 언어 변경 후에도 유지됩니다. 사용자에게 데모 자격 증명을 요청하고 capture.json에 절대 직접 기록하지 마세요.- 디버그 오버레이(개발 메뉴 버튼, 토스트)를 표시하는 개발 빌드는 캡처 세션 동안 이를 숨겨야 합니다. 로컬에서 패치하고 커밋하지 마세요.
out/_captures/<locale>.jpg를 타일별로 검토하세요: 다른 언어 문자열, 다이얼로그, 토스트, 스피너, 빈 상태가 없어야 합니다. 데이터 또는 단계를 수정하고 해당 화면을 다시 실행하세요. 렌더러는 캡처를 그대로 표시합니다; 실수를 덮어쓰지 않습니다.- iPhone Duo: 시뮬레이터 또는 DeviceHub에서 먼저 시뮬레이터를 펼치고 가로 모드로 설정하세요; simctl은 접거나 회전할 수 없습니다. 딥링크 사용(HID 탭이 내부 디스플레이에 도달하지 못할 수 있음),
"locale": "relaunch"및"displaySize": "2853x2007"를 사용하세요.
크로스플랫폼 앱(React Native, Flutter)은 Android 캡처를 건너뛸 수 있습니다: Android 타일은 iPhone 및 iPad 캡처를 재사용하며, 이것이 기본 기기의 동작입니다. 네이티브 Android 앱은 자체 화면을 sim-android/에 캡처합니다(10절 참조).
5. 구성 (렌더링 전 사용자 승인 필요)
각 슬라이드를 ASCII로 스케치하세요: 헤드라인, 기기 위치 및 기울기, 어떤 요소가 돌출되고 어디에 배치될지. 승인을 받은 후 set.json을 작성하세요.
+------------------------------+
| WATER | 동사, 한 줄
| EVERY PLANT RIGHT | 설명, 최대 2줄
| ON TIME |
| .--------------. |
| [* 3 plants today] | 설명, 돌출, 왼쪽으로 나옴
| | Today ... | | 기기 기울기 y 15, x 4
+------------------------------+
6. 배경
세트당 하나의 배경, 모든 로케일과 기기가 공유합니다. CSS ("background": {"css": "radial-gradient(...), #1f4a37"}, 데모에서 사용) 또는 이미지 ({"image": "assets/bg.png"}, background-size cover) 중 하나입니다. 이미지 생성기는 부드러운 판에 잘 맞습니다; 브랜드 색상에 부드러운 조명이 있는 9:16 시즌리스 스튜디오 배경을 프롬프트하고 명시적으로: 물체 없음, 텍스트 없음, 기기 없음, 도형 없음. 두 개를 생성하고 사용자가 선택합니다. 소품(스티커, 제품 사진)은 알파 채널이 있는 PNG로 슬라이드당 배치됩니다.
7. set.json 및 로케일
모든 사각형은 눈으로 측정하지 말고 캡처 픽셀 단위로 measure.py로 측정하세요:
$PY $S/measure.py sim/en-US/05-fill.png --at 700,790 # 버튼, 칩, 알약을 플러드 필
$PY $S/measure.py sim/en-US/01-plan.png --ink 40,415,700,75 # 평평한 영역의 텍스트 범위
$PY $S/measure.py sim/en-US/02-cook.png --grid 440,1510,340,110 # 임시 디렉토리의 확대 그리드 이미지
| 키 | 의미 |
|---|---|
locales, devices | 전체 렌더링이 다루는 대상; device id는 $S/devices/devices.json에서 가져옴 |
background | {"image": ...} 또는 {"css": ...}, 선택적 position |
fonts | {"family": "fonts/File.otf"}, 경로는 프로젝트 기준; 기본 display는 번들된 Archivo Black |
headline | font, descFont, verbColor, descColor, shadow, verbTracking, descTracking, descWordSpacing |
capture | platform, statusBarBand (캡처 행 블렌드 제거, iOS 기본 [66, 128]), sets ({"tablet": {"dir": "sim-tablet", "statusBarBand": [12, 56]}}), devices ({"android-phone": "android"}로 기기를 다른 캡처 세트로 지정) |
style | gloss, deviceShadow, thicknessRatio (측면 벽 깊이), sideLayers |
statusBar | 렌더러가 그리는 상태 표시줄의 time, color |
layout | device id별로, devices.json 레이아웃 재정의 (deviceTop, deviceWidth, verbMax ...) |
baseline | 라이브 세트의 {device: {locale: folder}}, --compare용 |
slides | 아래 목록 |
슬라이드: id, sim (캡처 파일, <id>.png가 아닌 경우), tilt {x, y, z} 도, offset {x, y} 캔버스 분율, layout, breakouts, props (src, x, y, w 캔버스 분율, rotate, shadow, z: front 또는 back).
브레이크아웃:
| 키 | 의미 |
|---|---|
type | crop: 캡처 자체 픽셀; callout: 로케일 텍스트가 있는 벡터 알약 |
rect 또는 anchor | [x, y, w, h] 캡처 픽셀 단위, 가져오는 요소의 위치 |
at 또는 dx/dy | 배치 위치 (at = 캡처 픽셀 단위 새 왼쪽 위 모서리) 또는 오프셋 |
scale, lift | 크기 및 z 리프트 (픽셀, 기본 80) |
vacate, vacatePad | 원래 위치를 주변 색상으로 채움 (복사가 아닌 이동) |
radius, shadow, origin | 모서리 반경 (캡처 픽셀), 그림자 강도, 변환 원점 |
text, icon, style | 설명: 로케일의 callouts 키, tag 또는 sparkles, 그리고 font, size, bg, fg, padX, padY, gap, radius, weight, tracking, iconScale, iconColor |
slides 내의 모든 값은 기기 및 로케일별로 다를 수 있습니다:
{"byDevice": {"phone": ..., "tablet": ..., "default": ...}} (키: device id, 캡처 세트, default) 및 {"byLocale": {"de-DE": ..., "default": ...}}, 서로 중첩 가능. 사각형은 거의 항상 로케일별로 다릅니다. 동일한 칩이라도 독일어에서는 더 넓기 때문입니다.
크롭은 큰 요소의 경우 약 1.7배까지 선명도를 유지합니다. 작은 칩과 텍스트는 설명(벡터)이 되어 모든 배율에서 선명도를 유지합니다.
로케일 파일:
{
"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"}}
}
}
slides만 필수입니다. captures는 로케일이 다른 로케일의 캡처를 재사용하게 합니다 (en-GB가 en-US를 읽음). headline과 statusBar는 set.json을 재정의합니다. Archivo Black은 라틴 문자만 지원합니다; 키릴 문자, 그리스 문자, CJK, 아랍어, 히브리어는 fonts 재정의가 필요하고, RTL은 "dir": "rtl"이 필요합니다.
8. 렌더링
$PY $S/render.py <project> # 전체 렌더링
$PY $S/render.py <project> --locale de-DE --device iphone-67 --slide 01-plan
$PY $S/render.py <project> --compare # 기준 vs 새 시트 비교
헤드라인 크기는 로케일과 기기의 모든 슬라이드에서 공유되므로 세트가 하나로 읽힙니다. 캡처의 가로세로 비율이 기기의 화면 개구부와 일치하지 않으면 렌더러가 중단됩니다.
9. 검토
out/_sheets/<locale>-<device>.jpg를 열고 모든 브레이크아웃을 확대하세요. CHECK로 출력된 줄은 문제를 식별합니다 (헤드라인이 최소 크기, 겹침). 헤드라인이 최소 크기라는 것은 카피가 너무 긴 것입니다: 제한을 낮추지 말고 카피를 줄이세요. 시트를 사용자에게 보여주세요.
10. 더 많은 언어 및 기기
언어 추가:
$PY $S/add_locale.py <project> es-ES init --from en-US # 로케일 파일, byLocale 플레이스홀더, 폴더
$PY $S/capture.py <project> run --locale es-ES # 캡처
# locales/es-ES.json 번역: 스토어 카피, 짧은 동사, 캡처와 일치하는 설명
$PY $S/add_locale.py <project> es-ES measure --from en-US # 사각형 재탐색, 나란히 이미지 검토
$PY $S/add_locale.py <project> es-ES measure --from en-US --write
$PY $S/add_locale.py <project> es-ES check # 렌더링 전 오류 0개 확인
$PY $S/render.py <project> --locale es-ES
지역 변형 (en-GB, de-AT, fr-CA): init --share-captures, 텍스트만 변경됩니다.
$S/devices/devices.json의 기기:
| id | 캔버스 | 읽기 | 스토어 슬롯 |
|---|---|---|---|
iphone-67 | 1290x2796 | 폰 (1206x2622) | ASC APP_IPHONE_67 |
android-phone | 1080x1920 | 폰 | Play 폰 스크린샷 |
ipad-13 | 2064x2752 | 태블릿 (2064x2752) | ASC APP_IPAD_PRO_3GEN_129 |
android-tablet | 1792x2400 | 태블릿 | Play 7" 및 10" 태블릿 |
iphone-duo | 2853x2007 | 듀오 (2853x2007) | ASC iPhone Duo |
프레임은 make_frame.py로 만든 일반 도면이며 무료로 재배포할 수 있습니다. 일치하는 프레임이 없는 캡처 크기는 새로 생성됩니다:
# 네이티브 Android 캡처, 1080x2400, sim-android/에 저장
$PY $S/make_frame.py --style android --screen 1080x2400 --device android-phone --captures android
# 그런 다음 set.json에서: "capture": {"sets": {"android": {"dir": "sim-android", "statusBarBand": [30, 105]}}}
스타일: ios, android, tablet, landscape. --canvas WxH는 새 device id의 스토어 크기를 설정합니다. 실제 캡처에서 statusBarBand를 측정하세요: 상태 표시줄의 행 + 약간의 여유.