示範影片
從實際運行的應用程式錄製的腳本化導覽。觀看者可以看到產品在執行真實工作;慢速部分(模型回覆、任務)會被快轉,每個節拍都有一個簡短的說明文字提示注意事項。每次使用相同的格式,以便片段可以合併成一部影片。
統一格式
嚴格遵守以下規定,使新片段能與舊片段匹配:
- 影片: 1440×900,30fps,H.264(
libx264 -preset slow -crf 18),yuv420p,+faststart,無音訊。 - 標題卡與結尾卡: 全白畫面,置中:可選的小寫灰色品牌行(在
kit.start中設定brand;預設無),46px 粗體標題,20px 灰色副標題。停留約 2.8 秒,然後淡出。片段以標題卡開場;最後一個片段以一行結尾要點卡結束(例如「想法 → 評估 → 線上測試 → 上線」/「中間無需部署應用程式」)。 - 說明文字: 深色膠囊樣式(
rgba(17,17,17,.88)),白色 17px 半粗體 Lato,底部置中。每個說明只傳達一個概念,約 60 個字元或更少,句子大小寫,無句號,使用現在式,從觀看者的角度撰寫(「結果以工件形式在對話旁開啟」)。當節拍變化時更換說明文字;每個說明停留約 2.5–4 秒。 - 點擊: 每次點擊前,目標位置會出現紫色波紋(
rgba(122,71,255,…))。 - 打字: 可見,逐字輸入(約每字 25ms;長文字為 14ms)。
- 等待: 任何慢速操作都在
ff()內部執行,該操作會在大約 2 秒內播放完畢。其他所有操作均為即時。 - 架構場景: 固定 1440×900 的白色場景頁面(標題 → 之前 → 問題高亮 → 之後 → 指標計數 → 功能列表),Lato 字體,墨水色
#111,紫色強調色#6f43d6;「之前」標籤為灰色,「之後」標籤為紫色。assets/architecture-example.html是參考範例:重複使用其 CSS 和結構,並更改內容。
影片存放位置:示範儲存庫
每部影片在團隊的示範儲存庫(預設 ~/src/demos,或 $DEMOS_REPO)中都有一個資料夾,存放其錄製腳本和 demo.json 清單。產品儲存庫中不應包含示範程式碼。錄製的內容永遠不會被提交;清單中的 output 欄位指定 MP4 的輸出位置。
~/src/demos/
package.json playwright-core + ffmpeg-static(執行一次 npm install)
<video>/
demo.json 清單
architecture.html 「場景」片段(可選)
record-<part>.js 「導覽」片段,使用 demo-kit 撰寫
.build/ 每個片段的 MP4 和影格(已加入 gitignore)
清單(demo.json):
{
"name": "<video-name>",
"title": "一行:影片展示的內容",
"description": "目標觀眾及影片要傳達的重點",
"owner": "email",
"repos": ["所記錄功能所在的儲存庫及分支"],
"needs": [{ "what": "service", "check": "啟動時回傳 2xx/3xx 的 URL", "start": "如何啟動該服務" }],
"reset": "如何重置狀態以取得乾淨的錄製,以及錄製後留下的內容",
"segments": [
{ "name": "architecture", "kind": "scenes", "page": "architecture.html", "timeline": "0:3000,1:4500" },
{ "name": "walkthrough", "kind": "walkthrough", "script": "record-walkthrough.js" },
{ "name": "intro", "kind": "video", "file": "~/path/to/existing.mp4" }
],
"output": "~/Downloads/<name>.mp4"
}
此技能的 examples/sift/ 目錄是參考資料夾:包含一個雙片段影片(一個場景頁面和一個真實導覽),以及同目錄中的一個小型範例應用程式。
如果示範儲存庫尚未複製,請建立它(git init、上述的 package.json,以及涵蓋 node_modules/、.build/、*.mp4、*.jpg、*.png、*-frames/ 和 *-timeline.json 的 .gitignore),並告知使用者目前還沒有遠端儲存庫。
工具(位於 scripts/,相對於此技能)
| 腳本 | 功能 |
|---|---|
find-demos.js | node find-demos.js [root…] [--json]:列出示範儲存庫中的每部影片,包括片段、儲存庫、輸出位置、最後錄製和提交的時間。 |
record.js | node record.js <video-dir> [--only <segment>]:檢查 needs,錄製並編碼每個片段到 .build/ 中,然後合併成 output(舊影片會保留為 *.previous.mp4)。從示範儲存庫根目錄執行。 |
demo-kit.js | 導覽腳本的函式庫:kit.start() 提供 page、title/untitle、caption、tap、type、ff、scroll/scrollTop、ripple、navigate、startCast、finish。影格會串流到磁碟。 |
encode.js | node encode.js <frames-dir> <timeline.json> <out.mp4>:建置 MP4,壓縮 ff() 視窗。 |
record-scenes.js | node record-scenes.js <page.html> <out.mp4> "0:3000,1:4500,…":錄製一個定義了 window.step(n) 的場景頁面。 |
concat.js | node concat.js <out.mp4> <a.mp4> <b.mp4> …:合併片段,重新編碼為統一格式。 |
review.js | node review.js <video.mp4> <out-dir> [stills] [at=s,s,…]:產生 2×2 的靜態影像聯絡表以供回顧。 |
assets/example-walkthrough.js 是一個完整的範例(而 examples/sift/record-walkthrough.js 是真實範例)。在撰寫新的導覽腳本之前,請先閱讀它;它展示了結構,包括在影片中切換應用程式、快轉任務以及結束卡。
工作流程
0. 找出已存在的內容
首先執行 find-demos.js。如果使用者想要重新錄製或擴展現有的影片,且該影片已有資料夾,則在該資料夾中工作:使用 record.js <folder> 重新錄製(或在 UI 變更後使用 --only <segment>),或在清單中新增一個片段。不要為同一部影片建立第二個資料夾。只有當影片是全新的時,才建立一個新資料夾,並以其名稱命名(kebab-case)。
1. 先撰寫故事板
在撰寫任何程式碼之前,先將故事板寫成編號的清單,每個章節包含其節拍:畫面上發生什麼事以及說明文字。事先決定:
- 哪些必須是真實的,哪些可以模擬。 優先使用真實內容(真實模型呼叫、真實搜尋)。如果資料必須模擬(例如數週的即時流量),請在畫面上標明,無論是在說明文字中還是在應用程式本身(如果可以標記)。觀看者會以表面價值看待示範,因此將模擬結果呈現為真實是具有誤導性的。
- 哪些不能點擊。 任何會寫入共享環境的操作(儲存看板、發送電子郵件、購買)。顯示按鈕;但不要按下它。
- 長度: 每個片段約 60–90 秒。一部 3 分鐘的合併影片是可接受的;每個片段應傳達一個重點。
如果使用者的目標在對話中很明確,請在工作時將故事板分享在更新中。只有在影片的重點不明確時才提問。
2. 設定影片的資料夾
在 <demos repo>/<video>/ 中建立 demo.json(為影片觸及的每個服務在 needs 中填入 check URL,並寫下 reset)。如果 node_modules 遺失,請在示範儲存庫根目錄執行 npm install。工具組和腳本會從工作目錄解析 playwright-core 和 ffmpeg-static,因此請從儲存庫根目錄執行所有命令。必須安裝 Chrome(channel: 'chrome';無需下載 Playwright 瀏覽器)。
3. 預先檢查應用程式
- 確認影片觸及的每個服務都已啟動(使用 curl 檢查每個 URL),並且本機 HTTPS 正常運作(工具組會忽略自簽憑證)。
- 重置狀態,確保錄製從乾淨的狀態開始(全新的資料庫、清除的 localStorage),並讓腳本在狀態不乾淨時立即拋出錯誤。一個在 3 分鐘後失敗的錄製會浪費真實的模型呼叫和時間。
- 計時影片會輪詢的慢速端點。一個 4 秒的結果查詢會在鏡頭前顯示為載入中的骨架畫面;請修正查詢或等待查詢完成。
- 使用一個簡短的 Playwright 腳本截取每個關鍵狀態的螢幕截圖並查看:佈局、文字換行、損壞的圖片、在 1440×900 解析度下哪些內容在折疊線以上。這比錄製成本更低。
4. 撰寫錄製腳本
複製 assets/example-walkthrough.js 的結構:
const kit = require(process.env.DEMO_KIT || `${process.env.HOME}/.claude/skills/demo-video/scripts/demo-kit`);
const demo = await kit.start(); // record.js 提供影格和時間軸路徑
await demo.page.goto(URL, { waitUntil: 'networkidle' });
await demo.title('功能名稱', '它能讓使用者做的事');
await demo.startCast(); // 在標題覆蓋頁面後開始
await demo.sleep(2800); await demo.untitle(); await demo.sleep(900);
await demo.caption('…'); await demo.tap(locator); await demo.type(locator, 'text');
await demo.ff(() => page.waitForFunction(() => /* 回覆完成 */ true, null, { timeout: 180000 }));
await demo.finish();
- 使用穩定的選擇器(
aria-label、id、data-*)並對照實際的 DOM 進行檢查。 - 在
ff()內部等待真實的完成條件,而不是固定的 sleep,並設定寬鬆的超時時間。 - 當說明文字提到某個內容時,先將其滾動到畫面上(對於內容在元素內部滾動的應用程式,使用
demo.scroll(px, 'main'))。 demo.navigate(url)會切換頁面或應用程式;它會在新頁面上重新啟動螢幕擷取。- 對於架構片段,請根據範例建立場景頁面,並使用
record-scenes.js錄製。此外,為其提供方向鍵和空白鍵導航(範例已有此功能),以便同一頁面在現場簡報時也能使用。
5. 錄製
從示範儲存庫根目錄,在前景執行整部影片(或一個片段),設定較長的超時時間,並記錄輸出:
set -o pipefail; node ~/.claude/skills/demo-video/scripts/record.js <video> [--only <segment>] 2>&1 | tee <video>/.build/record.log
不要 pipe 到 tail:它會隱藏錄製器的結束代碼,導致失敗的錄製看起來像成功。如果必須在背景執行,請等待實際的 PID(while kill -0 $PID; do sleep 5; done),而不是信任「完成」通知,因為該通知可能在程序結束前送達。
6. 審查
record.js 已經編碼並合併了所有內容;它會列印審查命令:
node ~/.claude/skills/demo-video/scripts/review.js <output.mp4> <video>/.build/review 16
查看單一片段時,請審查 <video>/.build/<segment>.mp4。
在宣布影片完成之前,請閱讀每個聯絡表。檢查:
- 每個說明文字是否與該時刻螢幕上的內容相符;
- 說明文字所指的內容是否可見,而不是在折疊線以下;
- 沒有損壞的圖片或替代文字、卡住的骨架畫面、錯誤提示、換行或截斷的標題、或偵錯 UI;
- 快轉的等待看起來像是進度的快速模糊,而不是凍結的畫面;
- 標題卡和結尾卡是否存在。
修正腳本或應用程式(例如,為無法載入的圖片設定預留位置、使用更快的查詢),重置狀態,然後重新錄製。預計需要兩到三次錄製。
7. 交付
- 審查連接點:在每個片段邊界使用
review.js <output> <dir> 0 at=<t-1>,<t+1>。 record.js會將先前的版本保留為*.previous.mp4。切勿覆寫不屬於此資料夾本身output的影片:若要新增到其他人的影片,請將其作為新資料夾的video片段加入。- 告知使用者影片資料夾已準備好提交到示範儲存庫。只有在他們要求時才提交。
- 向使用者報告:檔案路徑和持續時間、每個片段顯示的內容、哪些是真實的、哪些是模擬的,以及錄製留下的任何狀態(正在執行的服務、建立的記錄、仍在開啟的項目)以及如何復原。
之前遇到的陷阱
- 記憶體: 當有動畫時,CDP 螢幕擷取每秒最多發送 60 個影格。將影格緩衝在記憶體中以進行多分鐘的錄製,會導致錄製器被終止且沒有輸出。工具組會將影格串流到磁碟,並在
ff()期間減少影格數量;請保持這種方式。 - 時鐘:
ff()視窗和影格時間戳記都使用 wall-clock 秒,因此請在一個ff()呼叫內計時整個等待時間(包括輪詢)。 - 阻塞性工作(例如
execFileSync用於資料腳本)在ff()內部是可以的:影格會在其後繼續,且視窗無論如何都會被壓縮。 - 連接埠: 在為示範啟動服務之前,檢查連接埠是否可用;不要終止你未啟動的行程。
- 殘留狀態: 一個以發佈或啟用某些功能結束的示範會將其保持開啟。事後將其關閉或告知使用者。