Communitygithub.com

lyonsv/demo-video-skill

錄製精美的腳本化示範影片,以統一的格式輸出為 1440×900 MP4,包含白色標題卡、膠囊說明文字、點擊波紋及快轉等待效果。

demo-video-skill 是什麼?

這是一個專為開發者設計的 AI 代理技能,用於自動錄製網頁應用程式的腳本化示範影片。它透過 Playwright 驅動無頭 Chrome,並使用 ffmpeg 編碼,產出符合統一格式的 MP4 影片(1440×900、30fps、H.264)。功能包括:白色標題卡與結尾卡、底部膠囊說明文字、紫色點擊波紋、按鍵輸入動畫,以及將慢速操作(如模型回應、後台任務)快轉至約 2 秒內。支援多平台(Claude Code、Cursor 等),並遵循一套嚴謹的工作流程:先撰寫故事板、設定示範專案資料夾、編寫錄製腳本(使用 demo-kit 函式庫),最後進行錄製與審查。此技能特別適合製作產品導覽、架構動畫、功能展示等影片,並可將多個片段合併為單一影片。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lyonsv/demo-video-skill/tree/HEAD

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

示範影片

從實際運行的應用程式錄製的腳本化導覽。觀看者可以看到產品在執行真實工作;慢速部分(模型回覆、任務)會被快轉,每個節拍都有一個簡短的說明文字提示注意事項。每次使用相同的格式,以便片段可以合併成一部影片。

統一格式

嚴格遵守以下規定,使新片段能與舊片段匹配:

  • 影片: 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.jsnode find-demos.js [root…] [--json]:列出示範儲存庫中的每部影片,包括片段、儲存庫、輸出位置、最後錄製和提交的時間。
record.jsnode 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.jsnode encode.js <frames-dir> <timeline.json> <out.mp4>:建置 MP4,壓縮 ff() 視窗。
record-scenes.jsnode record-scenes.js <page.html> <out.mp4> "0:3000,1:4500,…":錄製一個定義了 window.step(n) 的場景頁面。
concat.jsnode concat.js <out.mp4> <a.mp4> <b.mp4> …:合併片段,重新編碼為統一格式。
review.jsnode 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() 內部是可以的:影格會在其後繼續,且視窗無論如何都會被壓縮。
  • 連接埠: 在為示範啟動服務之前,檢查連接埠是否可用;不要終止你未啟動的行程。
  • 殘留狀態: 一個以發佈或啟用某些功能結束的示範會將其保持開啟。事後將其關閉或告知使用者。

相關技能