Synthetic UI Demos
沒有任何畫面是螢幕錄製的。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/是畫面套件。
何時使用此方法 vs. Playwright
| Synthetic(此技能) | 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 Product Review', 128)} />
</Stage>
Stage在固定大小的應用程式畫布周圍繪製瀏覽器邊框,套用鏡頭,並將游標置於 zIndex 100 之上。縮放 1 倍時,視窗為畫面寬度的fit(0.8625)。關鍵影格重複一個值以保持其狀態。Cursor和cursorAt可獨立用於非瀏覽器場景;near(cursor, target)驅動懸停狀態。- 產品特定的畫面套件(主題、圖示、佈局)保留在專案中。它們不屬於 lib 素材。
方法
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顯示對話框)。匯出點擊影格,以便父層可以放置點擊音效。 - 從旁白中查找影格,不要手動設定。
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-50 畫素。在每個點擊影格及其前幾個影格處渲染靜止影像,將它們拼貼,並檢查游標尖端是否位於目標上:
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 ...,然後執行 qc.py --render out/video.mp4 --fix 以達到 -14 LUFS。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)。