agentshot: 스크린샷과 데모 비디오
도구는 /path/to/agentshot에 있습니다. 다음과 같이 실행합니다:
node /path/to/agentshot/bin/agentshot.mjs <명령어> ...
아래에서 agentshot은 해당 명령어를 의미합니다. 모든 명령어는 JSON 한 줄("ok": true|false)을 출력합니다. 긴 렌더링 작업은 stderr에 진행 상황을 출력합니다.
명령어 목록은 agentshot help를 실행하세요.
워크플로우
- 먼저 제품의 규칙을 읽습니다:
agentshot style <product>는style.json(기본 스타일)과guidelines.md(사용자가 선호하는 사항)를 출력합니다. 이를 따르세요. 제품이 새 것이라면agentshot style <product> --init을 실행합니다. - 캡처 (다음 중 하나를 선택):
- 웹 앱 또는 Expo 앱 →
"url"이 포함된 시나리오 JSON을 작성하고agentshot capture scenario.json을 실행합니다. - Mac에서 iOS 앱 →
"simulator"가 포함된 시나리오(iOS 시뮬레이터의 실제 네이티브 앱, "iOS 시뮬레이터" 참조); 빠른 정지 이미지 →agentshot sim snap --name <capture> --clean-status-bar. - 데스크톱 프로그램 →
"window"가 포함된 시나리오: 녹화 중에 실제 마우스/키보드를 사용하여 조작합니다("데스크톱 앱" 참조). 정지 이미지만 필요하면 →agentshot snap --window "<title>" --name <capture>. 사용자가 직접 조작하는 경우 →agentshot record start/stop. - 사용자가 이미 파일을 가지고 있는 경우(iPhone 스크린샷/화면 녹화) →
agentshot import <file> --name <capture>.
- 웹 앱 또는 Expo 앱 →
- 검사:
agentshot inspect <capture>는 쇼트, 요소 박스(화면 포인트), 클릭, 지속 시간 및 비디오의 경우moves(포인터 이동),changes(페이지 전환: 큰 시각적 점프),quiet(움직임 없음: 컷 또는 속도 향상) 및 시나리오timeline을 나열합니다. 이 숫자를 바탕으로 컷과 확대/축소를 계획한 다음,agentshot frames <capture> --from a --to b(타임스탬프가 있는 연속 이미지 시트; 출력된 PNG를 읽으세요)로 확인합니다. 스타일을 지정하기 전에 쇼트 PNG를 읽기 도구로 살펴보세요. - 레시피 JSON을 작성하고
agentshot render recipe.json --note "<변경 내용>"을 실행합니다. - 직접 결과를 확인합니다: PNG를 읽습니다. MP4의 경우
agentshot frames <product>/<asset>(전체 비디오) 및agentshot frames <product>/<asset> --from 3 --to 4 --every 0.033 --crop x,y,w,h(컷 또는 클릭 주변을 프레임별로 확인)를 실행합니다. 수정하고 다시 렌더링합니다(새 버전이 추가됨). 사용자가 직접 레시피를 스크러빙하거나 조정하려는 경우, 백그라운드에서agentshot preview recipe.json을 실행합니다: 로컬 페이지에 장면과 스크러버를 열고 레시피(또는 제품의style.json)가 편집될 때 실시간으로 업데이트됩니다.url이 포함된 JSON 한 줄을 출력하고 계속 서빙합니다. - 출력 경로를 보고합니다(
results[].output).--copy-to <dir>는 이를 사용자 프로젝트에 복사합니다. - 학습: 사용자가 수정하거나 칭찬할 때, 제품의
guidelines.md에 날짜가 포함된 줄을 추가합니다(기본 스타일인 경우style.json기본값도 업데이트). 다음에 읽으세요.
시나리오와 레시피는 사용자 프로젝트(예: docs/shots/)에 보관하여 UI 변경 후 다시 실행할 수 있도록 하세요.
시나리오 (웹 앱 캡처)
아래 JSON 예제의 주석은 설명일 뿐입니다. 실제 파일은 일반 JSON이어야 합니다.
{
"name": "ellie-today-drag", // 캡처 이름, 고유해야 함; 제품 이름으로 접두사 사용
"url": "http://localhost:8081", // 또는 파일 경로
"device": "iphone", // iphone | iphone-max | ipad | desktop | laptop
"safeArea": true, // Expo/RN 웹: Chrome이 iPhone safe-area 여백을 보고하면 앱이
// 기기에서처럼 레이아웃됨 ("pad" = 여백을 무시하는 앱에 패딩 강제 적용)
"css": "[class*='navigationMenuRoot'] { display: none }", // 웹 전용 크롬 숨기기(Expo Router 탭은 웹에서 상단 메뉴로 렌더링됨)
"waitFor": "today-list", // 시작 전에 이 요소를 기다림
"track": { "row": "text=Plan a birthday" }, // 기록할 추가 명명된 요소 박스
"steps": [
{ "shot": "main" }, // 전체 해상도 정지 이미지 (스크린샷용)
{ "wait": 0.6 },
{ "tap": "add-button" }, // 대상 클릭/탭 (먼저 그곳으로 이동)
{ "type": "Buy milk", "into": "title-input" },
{ "press": "Enter" },
{ "drag": "task-birthday", "to": "slot-11am", "duration": 1.4 },
{ "scroll": 400, "duration": 0.8 },
{ "move": [200, 300], "duration": 0.6 },
{ "goto": "/settings" },
{ "waitFor": "settings-title" },
{ "shot": "after" }
]
}
대상: 단순 이름은 data-shot="..." 또는 data-testid="..."(React Native testID는 웹에서
data-testid가 됨, 가리키려는 요소에 testID를 추가)와 일치하거나, 모든 Playwright 셀렉터(text=Save,
role=button[name="Save"], #id, .class) 또는 CSS px 단위의 [x, y]와 일치합니다.
data-shot/data-testid가 있는 모든 요소는 해당 박스와 함께 track 항목에 기록됩니다.
타이밍: 기본적으로("timing": "frame") 페이지 시계가 일시 중지되고 한 번에 정확히 한 프레임씩
진행됩니다(60fps). 각 프레임에 대해 전체 해상도 스크린샷이 제공되므로 앱 애니메이션이 모든 해상도에서
부드럽게 유지됩니다. 캡처는 시나리오보다 몇 배 더 오래 걸립니다. 제어된 클록에서 앱이 중단되는 경우에만
"timing": "real"을 사용하세요(단계가 시간 초과됨). agentshot capture는 클릭 시간(clicks)을
보고하여 cuts/zoom을 계획하고 콘솔 오류/경고(console)도 보고합니다. 개발자 오버레이(Expo LogBox
토스트, Next.js 표시기)는 숨겨집니다. "devOverlays": true로 유지할 수 있습니다.
여러 개의 하드 컷보다 하나의 연속된 cuts 세그먼트를 선호하세요. 세그먼트 간 점프는 어색해 보입니다.
testID가 없는 앱에서 대상 찾기: Playwright로 페이지를 열고 버튼/입력 필드를 나열한 다음(role,
aria-label, 텍스트) text=..., role=button[name=/^My daughter/] 또는 [aria-label='Your prayer']를
사용하고, track을 통해 리프트/루페하려는 박스 이름을 지정하세요. 고정 하단 영역이 있는 필드에 입력해도
작동합니다(필드가 포커스를 받음).
Expo: 먼저 웹 빌드를 시작하세요(npx expo start --web, 일반적으로 http://localhost:8081). 네이티브 전용
모듈은 웹에서 작동하지 않을 수 있습니다. 화면이 깨지면 사용자에게 iPhone 스크린샷/녹화를 요청하고 import하세요.
데스크톱 앱 ("window"가 있는 시나리오)
agentshot windows는 창(제목, 프로세스, 크기)을 나열합니다. 창은 제목으로 일치시킨 다음 프로세스 이름으로
일치시키며, 최소화된 경우 복원되고 전면으로 가져옵니다. 좌표는 창 픽셀입니다. 먼저 정지 이미지를 살펴보고
(agentshot snap --window "X" --name x-probe, PNG 읽기) track에서 사용할 박스 이름을 지정하세요.
{
"name": "notes-tour",
"window": "Notes", // 제목 일부 또는 프로세스 이름
"track": { "nav-recent": [20, 200, 190, 40], "nav-search": [20, 96, 190, 40], "content": [232, 40, 1030, 650], "profile": [16, 718, 150, 62] },
"cursor": [1000, 330], // 포인터 시작 위치
"steps": [
{ "shot": "home" }, // 정지 이미지 = 이 순간의 기록된 프레임
{ "wait": 1 },
{ "click": "nav-recent", "duration": 0.9 }, // 그곳으로 부드럽게 이동(완만함, 약간의 호), 즉시 클릭
{ "waitStill": 0.6, "region": "content" }, // 화면이 변경될 때까지 기다린 다음, 안정될 때까지 기다림
{ "shot": "recent" },
{ "wheel": 4, "at": [760, 460] }, // 아래로 4 노치 스크롤 (음수 = 위로)
{ "click": "nav-search" }, { "press": "ctrl+a" },
{ "type": "Weekly plan", "cps": 12 }, // 모든 텍스트, 비라틴 문자 포함
{ "press": "Enter" }, // 키: enter esc tab space backspace delete arrows home end f1..f12, ctrl/alt/shift/win+x
{ "drag": [100, 300], "to": [400, 300], "duration": 1.2 },
{ "click": [640, 400], "button": "right" }, { "click": [640, 400], "double": true },
{ "note": "beat 2" } // 타임라인의 마커
]
}
-
결과에는
timeline(각 단계의 시작/종료 시간(비디오 초),at= 클릭 순간)과shots(비디오에서 잘라낸 정지 이미지)이 있습니다. 레시피는track이름을 대상으로 사용합니다("profile"에 대한"blur"등). -
waitStill은 먼저(최대changeTimeout, 3초) 영역이 이전과 달라질 때까지 기다린 다음(change, 평균 픽셀 차이, 기본값 6;"change": false로 설정하면 안정될 때까지만 기다림), 주어진 초 동안 아무것도 변경되지 않을 때까지 기다립니다(threshold0.6). 애니메이션 부분(플레이어, 라이브 배경)이 없는region을 사용하세요. -
도착 직후 클릭: 컨트롤 위에 커서를 약 0.5초 동안 놓으면 Windows 도구 설명이 비디오에 깜박입니다.
-
이전 텍스트를 유지하는 필드:
type전에press ctrl+a를 누르세요. -
드라이버는 녹화 전에 시작되며(몇 초 필요) 실제 커서를 움직입니다. 그동안 컴퓨터를 사용하지 마세요. 데스크톱 캡처는 1x이므로 루페가 부드럽지 않게 보입니다.
lift를 선호하세요. -
녹화 중 언제든지 정지 이미지:
agentshot snap <capture> --at 12.4 --shot name. -
작은 변경 사항(팝업 닫힘)이 전체 창에 대해
change에 도달하지 못할 수 있습니다. 주변에region을 지정하세요. -
단축키:
mod는 Windows에서 Ctrl, macOS에서 Cmd입니다("press": "mod+a"). 따라서 하나의 시나리오가 두 OS에서 실행됩니다.
수동 녹화(사용자가 직접 조작하고 커서는 렌더러에서 기록 및 그림):
agentshot record --name app-filter --window "Figma" --duration 12 # 12초 녹화 후 정지
agentshot record start --name app-filter --window "Figma" # 또는 start...
agentshot record stop # ...하고 나중에 정지
macOS
Windows와 동일한 명령어와 시나리오가 적용됩니다. recorder/mac.swift는 첫 번째 사용 시 컴파일됩니다(Xcode
명령줄 도구와 macOS 14+ 필요). 먼저 agentshot doctor를 실행하세요. 에이전트를 실행하는 앱(터미널, iTerm,
Claude, VS Code)은 시스템 설정 > 개인정보 보호 및 보안에서 화면 녹화 및 손쉬운 사용 권한이 필요합니다
(agentshot doctor --request가 프롬프트를 엽니다. 권한 부여 후 해당 앱을 다시 시작하세요). 화면 녹화 권한이
없으면 창 제목이 앱 이름으로만 표시되고 녹화가 실패합니다. 손쉬운 사용 권한이 없으면 드라이버가 클릭하거나
입력할 수 없습니다.
- 좌표(
track, 단계, 레시피 대상)는 포인트입니다. 정지 이미지와 비디오는 디스플레이의 픽셀 스케일로 기록됩니다(Retina에서 2,inspect가scale을 표시). 따라서 리프트와 확대/축소가 선명하게 유지됩니다. - ScreenCaptureKit은 창 자체를 기록합니다. 부분적으로 가려질 수 있지만 최소화되면 안 됩니다.
node scripts/selftest-mac.mjs는 TextEdit(시뮬레이터가 부팅된 경우 시뮬레이터 포함)에서 모든 것을 확인하고 실패한 항목을 출력합니다. 동기화 확인:agentshot record ... --show-cursor는 실제 커서를 비디오에 기록합니다. 이를 기록된 포인터(agentshot framesvsinspect)와 비교하여 일반 녹화처럼 확인합니다.
iOS 시뮬레이터 (macOS)
웹 빌드 대신 네이티브 앱: 실제 글꼴, 네이티브 모듈, iOS 애니메이션. 시뮬레이터를 부팅하고(open -a Simulator),
그 안에서 앱을 실행한 다음(npx expo run:ios 또는 개발 빌드):
agentshot sim devices # 부팅된 시뮬레이터 목록
agentshot sim snap --name app-home --clean-status-bar # 무손실 정지 이미지, 9:41 / 배터리 완충
agentshot sim record start --name app-flow # 다른 사람이 조작...
agentshot sim record stop
시나리오는 idb로 구동됩니다(brew install facebook/fb/idb-companion, pipx install fb-idb). 좌표는
디바이스 포인트입니다(sim snap을 읽고 픽셀을 scale로 나누세요):
{
"name": "app-onboarding",
"simulator": "booted", // 또는 UDID 또는 디바이스 이름의 일부
"statusBar": "clean", // 캡처 중에는 9:41, 캡처 후에는 원래대로
"appearance": "light", // 또는 "dark"
"openUrl": "exp://127.0.0.1:8081/--/today", // 녹화 전 딥 링크 (또는 "launch": "<번들 id>")
"track": { "add": [330, 760, 48, 48], "title": [24, 120, 340, 44] },
"steps": [
{ "shot": "today" },
{ "tap": "add" },
{ "waitStill": 0.5 },
{ "type": "Buy milk" }, // 포커스된 필드에 입력
{ "press": "enter" }, // enter esc backspace tab space arrows home lock
{ "swipe": [200, 600], "to": [200, 250], "duration": 0.4 },
{ "openUrl": "exp://127.0.0.1:8081/--/settings" },
{ "waitStill": 0.6 },
{ "shot": "settings" }
]
}
정지 이미지는 무손실 시뮬레이터 스크린샷입니다. 탭과 스와이프는 터치 포인터와 클릭이 되므로, 레시피는
브라우저 캡처와 마찬가지로 "pointer": "touch"와 "device": "iphone-black"을 사용합니다. idb가 없으면
wait/waitStill/agentshot/openUrl/launch 단계만 작동합니다.
레시피 (스타일 지정 및 렌더링)
{
"name": "today-drag", // 라이브러리의 에셋 이름
"product": "ellie", // 라이브러리 폴더; style.json 기본값 적용
"capture": "ellie-today-drag",
"shot": "main", // PNG 정지 이미지용 설정; MP4 비디오의 경우 생략
"size": "16:9", // 16:9 16:10 4:3 3:2 1:1 4:5 9:16, 또는 너비/높이
"background": "lavender", // lavender peach sage sky rose mint neutral midnight ink white,
// 모든 CSS 색상/그라데이션, 또는 public/ 아래의 이미지 (backgrounds/x.jpg)
"backgroundBlur": 0.02, // 사진 배경용, 짧은 쪽의 비율
"device": "iphone-black", // iphone-black iphone-natural iphone-white browser window none
"url": "app.ellie.so/today", // 브라우저 주소 표시줄 텍스트; "title": "window"의 창 제목
"padding": 0.08, "tilt": 12, // 디바이스 여백; 3D 기울기 (도)
"position": [0.69, 0.5], // 캔버스에서 디바이스 중심, 옆에 제목을 위한 공간 확보
"accent": "#6d5ce8", // 단계, 윤곽선, 레이블, 클릭 물결의 색상
"font": "Gilda Display", "labelFont": "Inter", // 제목 / 리프트 레이블: Inter Manrope Poppins Nunito Fraunces Lora
// "Playfair Display" "DM Serif Display" "Gilda Display" "Cormorant Garamond" Caveat
"lift": { "target": "task-row", "label": "Drag it onto your day", "start": 3.9, "pad": 10, "dim": 0.1 },
"loupe": { "target": "label-chip", "anchor": [0.2, 0.5], "zoom": 2.3, "size": 0.12, "offset": [-0.15, 0.12] },
"annotations": [
{ "type": "arrow", "from": [517, 346], "to": [554, 484] },
{ "type": "step", "at": [0.6, 0.12], "label": "1", "content": "Tap +" },
{ "type": "cut", "at": 4.3 },
{ "type": "blur", "target": "profile", "radius": 0.04 },
{ "type": "pointer", "visible": true, "color": "#ff4500", "size": 18 },
{ "type": "zoom", "start": 0, "end": 4.3, "scale": 1.1, "anchor": [0.5, 0.5] },
{ "type": "speed", "range": [4.3, 7.5], "rate": 1.5 },
{ "type": "title", "text": "Drag tasks", "position": [0.08, 0.08], "color": "#1a1a2e", "size": 24 },
{ "type": "subtitle", "text": "Reordering made easy" }
]
}
agentshot fields recipe.json은 알려진 모든 필드와 설명을 출력합니다.
렌더러 동작
- 장면은 1~2초의 기본
opening(로고/제품 이름 표시,"opening": false로 비활성화)과buffer로 구성됩니다. 컷at은 이 순서를 유지합니다. - 컷(
"type": "cut")이 없으면 전체 캡처가 하나의 세그먼트로 렌더링됩니다. - 뷰: 기본적으로 1.0 = 전체 캔버스에 맞춤.
"view": "fill"은 캡처 콘텐츠가 비율을 유지하며 프레임을 채우도록 확대/축소합니다."view": [0.1, 0.1, 0.8, 0.8]은 재사용 가능한 자르기를 위해 캔버스의 직사각형 영역을 정의합니다. - step 및 arrow가
"animate": true이면 나타나고 페이드인/아웃되며,"frame": 10동안 유지됩니다. - 블러는 대상 상자를 흐리게 합니다. 포인터 애니메이션은 포인터가 이미 그 위에 있지 않는 한 블러 뒤에 숨겨집니다(블러된 영역을 가리키는 화살표가 보이도록).
- 루페는 대상의 확대된 원형 뷰를 보여주며 커서 바로 다음에 움직입니다.
- 리프트는 대상 위에 떠 있는 라벨이 있는 확대된 패널입니다.
start가 없으면 컷 바로 후입니다. - 속도는
rate배속으로 재생됩니다. - 첫 번째 컷 전의 오프닝은
"opening": false로 비활성화하거나"opening": { "text": "...", "position": [0.5, 0.5] }으로 사용자 정의할 수 있습니다. "view": [x, y, w, h]는 캔버스 좌표에서의 뷰포트입니다. 콘텐츠가 이 직사각형을 채우도록 확대/축소됩니다."view": "fill"은 콘텐츠가 닿는 첫 번째 가장자리로 확대됩니다.- 캡처의 속도 조정(
speed), 컷(cut), 타이틀(title)은 현재 세그먼트에만 영향을 미칩니다. - 자르기:
"crop": { "x": 0.1, "y": 0.08, "w": 0.8, "h": 0.7 }은 장면이 렌더링되기 전에 적용됩니다(확대/축소 전)."padding": 0.02는 콘텐츠 주변에 작은 여백을 추가합니다. - 서체는 무료 Google Fonts 목록에서 선택합니다. 오프닝 텍스트의 마지막 줄은 작게 표시됩니다.
- 스크롤은
target에 대해 예를 들어 0.5초의 페이드 전환을 추가합니다. 하나의 영역에서 스크롤하는 여러 장면(scrolls에 나열)은 같은 대상으로 간주되어 하나의 페이드 전환만 생성됩니다.
컬러 배경
lavender peach sage sky rose mint neutral midnight ink white
#ceb5f3 #ffceae #b3e0c6 #b2d6f0 #f3b5c6 #a3e0d0 #e8e4e0 #1e1e2a #2a2a3a #ffffff
배경의 예는 https://agentshot.site/backgrounds에서 확인할 수 있습니다.
자르기 마커 (crops)
레시피는 capture로부터 crops 배열을 참조할 수 있습니다: 각 자르기는 "name"을 가지며, 레시피의
키 경로에서 "capture": "base-capture"``, "crop": "crop-name"으로 사용됩니다.
{
"captures": [
{ "name": "base", "url": "http://app", "device": "desktop", ... },
]
}
이는 캡처 정의를 재사용하고 자르기별로 다른 스타일을 적용할 수 있게 합니다.
스타일 (style.json)
agentshot style <product>(또는 --init으로 새 제품 생성)의 출력입니다.
{
"background": "white",
"device": "browser",
"accent": "#000000",
"font": "Inter",
"shot": null,
"size": "16:9",
"padding": 0.04,
"tilt": 8
}
레시피는 style.json의 키를 재정의합니다. 새 기본값이 마음에 들면 agentshot style <product> --set <key> <value>.
내보내기
agentshot export <product> --dir path/to/project --formats png,mp4 --copy-to docs/shots
agentshot list <product>는 모든 에셋과 버전을 나열합니다. agentshot rename <product>/<asset> <new-name>.
렌더링 문제 해결
- 비디오가 더듬거리거나 품질이 낮음: 더 높은
fps(기본 30, 60으로 시도)를 설정하거나"quality": "high"를 설정하세요. - 컬러가 일치하지 않음: HDR이 꺼져 있는지 확인하세요. macOS에서 '하이 다이나믹 레인지' 설정을 조정하세요.
기여
보고된 문제와 기능 요청은 GitHub 저장소를 참조하세요.
라이선스
MIT