합성 UI 데모
어떤 것도 화면 녹화되지 않습니다. Claude Code가 제품 저장소를 읽고, 장면이 보여주는 화면만 정적 React로 재구성하며, 스크립트된 커서와 카메라가 Remotion에서 흐름을 수행합니다. 모든 클릭은 프레임 번호이므로, 장면이 동일하게 재렌더링되며, 시드 계정이나 실시간 환경이 필요 없고, 제품이 변경될 때 코드를 수정하여 업데이트됩니다.
참조 빌드 (이 스킬과 함께 읽으세요; 둘 다 제품 저장소 없이 빌드됩니다):
examples/ds-free-schedule-demo: 26초 흐름(통화 예약)에 내레이터 타일이 포함된 가장 간단한 완전한 예제입니다.src/Demo.tsx는 모든 커서 키프레임, 클릭 및 카메라 이동이 포함된 단일 타임라인입니다.examples/ds-release-649: 80초 릴리스 노트 동영상으로, 세 가지 앱에 걸쳐 여덟 개의 장면이 있습니다.templates/ds-release-notes는 재사용 가능한 템플릿입니다(릴리스 650으로 제공됨).src/Release.tsx가 장면들을 순서화하고,src/scenes/는 각 릴리스 항목당 하나의 파일을 가지며,src/room/과src/ac/는 화면 키트입니다.
이 방법을 Playwright와 비교하여 사용하는 경우
| 합성 (이 스킬) | Playwright 레코딩 (/record-demo) |
|---|---|
| 앱 소스 코드를 읽을 수 있음 | 실행 중인 앱만 있음 |
| 릴리스마다 다시 작성해야 하는 릴리스 노트 및 기능 둘러보기 | 실제 동작이나 실제 데이터의 일회성 캡처 |
| 흐름에 데이터가 필요함(전체 통화, 다른 사용자, 관리자 보기) | 실제 제품에 쉽게 접근 가능하고 현재 상태로도 충분함 |
| 카메라 이동, 정확한 클릭 타이밍, 음성 변경 후 재렌더링 | 재구축 비용이 많이 드는 길고 복잡한 상호작용 |
빌딩 블록 (lib/components)
import { Stage, typed, cursorAt, near } from '../../../../lib/components';
<Stage
url="app.example.com/settings" // 주소 표시줄; 호스트는 경로보다 어둡게
camera={CAMERA} // [{ f, z, x, y }] 캔버스 픽셀 단위, 이징 처리, 창에 클램프됨
path={PATH} // [{ f, x, y }] 캔버스 픽셀 단위 커서 키프레임
clicks={CLICKS} // 커서를 누르고 물결 효과를 내는 프레임
aiNote // "표시된 사람들은 AI 생성입니다" (또는 직접 텍스트 전달)
canvasWidth={1440} canvasHeight={810} // 기본값
>
<SettingsScreen field={typed(frame, 'Q4 제품 리뷰', 128)} />
</Stage>
Stage는 고정 크기의 앱 캔버스 주위에 브라우저 크롬을 그리고, 카메라를 적용하며, 커서를 zIndex 100의 맨 위에 배치합니다. 줌 1에서 창은 프레임 너비의fit(0.8625)입니다. 키프레임은 값을 반복하여 유지합니다.Cursor와cursorAt은 브라우저가 아닌 장면에서 독립적으로 작동합니다.near(cursor, target)은 호버 상태를 구동합니다.- 제품별 화면 키트(테마, 아이콘, 레이아웃)는 프로젝트에 남습니다. 이들은 라이브러리 자료가 아닙니다.
방법
1. 서브 에이전트를 사용하여 앱 저장소에서 시각적 사양 가져오기
앱 또는 영역당 하나의 서브 에이전트를 병렬로 보내고, 각각 파일 덤프 대신 간결한 사양을 반환하도록 합니다:
- 테마: 팔레트, 글꼴 스택, 반경, 간격, 그림자(테마 파일 또는 CSS 변수에서).
- 문자열: 장면이 보여주는 모든 레이블, 메뉴 항목, 대화상자 및 토스트에 대한 정확한 영어 로케일 문자열 (
public/locales/en/*.json,src/i18n/locales/en/*). UI 텍스트를 의역하지 마십시오. - 아이콘: 앱이 아이콘 폰트를 사용하는 경우, icomoon/fontello
selection.json에 각 글리프의 SVG 경로가 있습니다. 필요한 것을 작은 JSON으로 추출하여<svg><path d=...>로 렌더링합니다. 그렇지 않으면 SVG 컴포넌트를 복사합니다. - 레이아웃 및 동작: 표시된 화면의 컴포넌트 트리, 도구 모음 순서, 기본값, 각 컨트롤의 기능.
원격에서 기본 브랜치를 읽습니다 (git show origin/main:path, 또는 git fetch 후 origin/<default> 읽기). 로컬 체크아웃은 종종 오래되어 지난 분기의 UI를 제공합니다.
릴리스 노트의 경우, 각 항목을 처음 제공하는 커밋 및 컴포넌트까지 추적합니다. 이를 통해 표시할 정확한 문자열과 기본값을 얻을 수 있습니다.
2. 장면이 보여주는 것만 고정 캔버스에 재구축
- 모든 요소를 고정 캔버스(1440x810이 좋은 기본값)에 절대 위치로 배치합니다. 중요한 직사각형을 상수로 내보냅니다 (
SAVE_BTN = { x, y, w, h }). 이렇게 하면 클릭 대상이 추측이 아닌 산술로 결정됩니다. - 카메라가 보는 것만 구축합니다. 절대 보이지 않는 패널은 단색으로 처리할 수 있습니다.
- 정적 React로 유지합니다: 상태 없음, 효과 없음. 모든 시각적 변경은
frame의 함수입니다. - 키트를 자체 폴더(
src/ui/,src/room/)에 넣고, 상수용theme.ts를 포함시킵니다.
3. 음성 우선, 그런 다음 타임라인을 프레임으로 배치
- 장면당 한 줄씩 작성하고, 문장 단위로 나눕니다 (구절당 하나의
.txt파일). 먼저 오디오를 생성합니다 (voiceover.py --scene-dir ...). 세 번의 테이크와tools/pick_takes.py는 단일 테이크에--max-wpm을 사용하는 것보다 낫습니다: 클론은 참조 클립의 속도와 평탄함을 복사하므로, 라인이 급하거나 단조롭게 나오면tools/voice_prosody.py expand로 클립을 수정하고 출력을 늘리는 대신 사용합니다 (CLAUDE.md의 "Clone Prosody" 참조). 측정된 지속 시간을 읽고,tools/captions.py로 단어 타이밍을 가져옵니다. - 각 장면을 해당 라인에 비트를 더한 크기로 조정하고, 각 라인의 시작과 길이를 나열하는 헤더 주석을 유지합니다. 해당 주석이 진실의 원천입니다 (CLAUDE.md의 "Audio-Anchored Timelines" 참조).
- 모든 것은 장면 파일 상단의 프레임 상수입니다: 커서 키프레임,
CLICKS, 카메라 키프레임, UI 상태 전환 (frame >= C.save는 대화상자를 표시). 부모가 클릭 SFX를 배치할 수 있도록 클릭 프레임을 내보냅니다. - 프레임을 수동으로 설정하지 말고 내레이션에서 찾으십시오.
voice()(lib/components/cues.ts; ds-release-notes 템플릿에 복사본 있음)는 장면의 구절을.captions.json및 시작 프레임과 함께 가져오고,cue('save')는 단어가 시작되는 프레임을 반환합니다. 모든 동작을 설명하는 단어에 오프셋과 함께 고정하고, 모든 커서 및 카메라 키프레임을 해당 앵커에 고정합니다:구절을 재생성하거나 재타이밍하면 클릭, 타이핑, 커서 및 카메라가 따라옵니다. 더 이상 말하지 않는 단어의 큐는 해당 단어와 함께 번들을 실패시킵니다. 옵션:import line06a from '../../public/audio/scenes/06a_link.captions.json'; export const LINK_VOICE = voice([{ file: '06a_link', at: 10, captions: line06a }, ...]); const { cue } = LINK_VOICE; const C = { field: cue('before', { offset: -3 }), save: cue('save') }; const PATH = [{ f: C.field - 6, ...field }, { f: C.save - 24, ...saveBtn }];offset,edge: 'end',after(반복된 단어의 경우 프레임),nth,in(하나의 구절). 부모는LINK_VOICE.lines에서 오디오를 배치하므로, 구절의 시작 프레임은 한 번만 작성됩니다.templates/ds-release-notes/src/scenes/FreeCustomLink.tsx가 실제 예제입니다.
4. 밀집된 목록 라인을 일시 중지 지점에서 분할
여러 항목을 나열하는 라인("일정 관리, 연락처, 녹음 및 대화 기록")은 한 번의 샷으로 너무 빠릅니다. 오디오를 일시 중지 지점에서 구절 파일(07a, 07b, ...)로 분할하고, 각 구절에 자체 샷을 제공하며, 각 구절을 자체 비트에 배치합니다. ds-release-649의 moreVo 필드가 이를 수행합니다.
5. 전체 렌더링 전에 연락처 시트에서 클릭 대상 측정
추정된 좌표는 일반적으로 카메라가 줌되면 20-50px 정도 벗어납니다. 모든 클릭 프레임과 그 몇 프레임 전의 스틸을 렌더링하고, 타일로 배열한 후 커서 끝이 대상에 있는지 확인합니다:
i=1; for f in 46 78 122 212; do npx remotion still MyComp out/stills/s$i.png --frame=$f; i=$((i+1)); done
ffmpeg -start_number 1 -i out/stills/s%d.png -vf "scale=640:-1,tile=2x2" -frames:v 1 out/contact.png
상수를 수정하고, 스틸을 다시 렌더링한 후에야 동영상을 렌더링합니다.
6. 화면에 AI 생성된 사람에 레이블 지정
실제 사람이 아닌 사람(타일의 FLUX.2 초상화, 아바타, 내레이터)은 보이는 레이블을 받습니다: Stage의 aiNote 또는 내레이터 타일의 "AI" 배지. 이름, 연락처 및 통화 데이터도 가상이므로, 명백히 허구로 유지합니다.
7. 렌더링 후 정규화
렌더링 전에 tools/qc.py --project ..., 그런 다음 -14 LUFS를 위해 qc.py --render out/video.mp4 --fix. qc.py --project ... --layout은 동영상 자체의 텍스트(자막, 카드, AI 노트)의 크기와 대비를 확인하고, 제품 UI의 텍스트를 별도로 나열합니다. Stage는 창에 data-qc="product"를 표시합니다. 그 외부에 구축한 전화기 또는 태블릿 목업에도 동일한 속성을 넣지 않으면 UI 텍스트가 동영상의 텍스트로 간주됩니다.
주의사항
- 단색 SVG 로고가 검은색으로 렌더링됩니다.
logo_mono.svg는 일반적으로 검은색 채우기를 가지고 있습니다. 어두운 배경에서는filter: brightness(0) invert(1)을 사용하십시오. - 오래된 로컬 브랜치. 로컬 체크아웃이 어떤 브랜치에 있든 상관없이
origin/<default>를 읽으십시오. rsync --exclude src는node_modules/*/src도 제외합니다. 프로젝트나 키트를 복사할 때는 선행 슬래시로 제외를 전송 루트에 고정하십시오:--exclude /src.- 카메라가 클램프됩니다. 캔버스 가장자리 근처의 카메라 중심은 창 가장자리가 프레임을 넘어 표시되지 않도록 안쪽으로 당겨집니다. 줌이 예상치 못한 곳에 도달하면 그 이유입니다. 더 안쪽 지점을 목표로 하십시오.
- 메뉴와 대화상자는 캔버스 내부에 있어야 합니다. 캔버스는 자식을 잘라냅니다. 실제 앱처럼 메뉴가 안쪽으로 열리도록 위치를 지정하십시오.
- 호버 상태는 커서 경로에서 비롯됩니다. 두 번째 프레임 상수 세트 대신
near(cursorAt(frame, PATH), target)을 사용하면 키프레임을 이동할 때 호버도 함께 이동합니다. - 변경된 음성 라인은 그 이후의 모든 것을 이동시킵니다. 큐가 있는 장면에서는 클릭, 커서 및 카메라가 자체적으로 따라오지만 장면 길이와 이후 구절의
at는 여전히 확인이 필요합니다. 수동 설정 장면에서는 모든 프레임을 다시 측정하십시오. 어느 쪽이든 헤더 주석과 장면 길이를 업데이트한 다음 클릭 스틸을 다시 확인하십시오. 립싱크 내레이터가 조립된 음성 트랙에 의해 구동되는 경우, 그것도 재생성하십시오. - 키트는 데모보다 오래 지속됩니다. 재구축된 화면 키트는 다른 동영상을 호스팅할 수 있습니다:
ds-release-notes방은 타일에 애니메이션 얼굴이 있고 새로운 화면 작업이 없는 패러디 단편의 브랜드 에디션 무대가 되었습니다. 키트를 새 프로젝트에 복사하고 논리적 캔버스를 프레임에 맞게 조정하십시오 (.claude/skills/parody-short/formats.md).