Communitygithub.com

mikey-relevanceai/record-demo

錄製一段模擬真人操作的 MP4 操作示範影片,可附加到 PR 中。支援透過無頭 Playwright 錄製網頁應用程式,以及透過真實螢幕或視窗擷取錄製 Electron 桌面應用程式。內含錄製作業守則;各平台的實作機制請參閱 `reference/` 目錄。

record-demo 是什麼?

這項技能「record-demo」讓 AI 代理程式(如 Claude Code、Codex 等)能夠錄製一段看起來像真人操作的操作示範影片。它特別強調模擬自然的使用行為——包含曲線滑鼠軌跡、逐字鍵入以及刻意停頓——而非機器人般快速的操作。此技能支援兩種主要執行環境:針對網頁應用程式,使用無頭 Playwright 搭配 DOM 游標;針對 Electron 桌面應用程式,則透過真實的顯示器或視窗擷取。核心守則包括:錄製前務必排練、使用真實控制項名稱進行探索(先傾印並截圖確認)、以角色或可見文字(而非 CSS 選擇器)定位控制項、避免使用固定計時器,以及徹底驗證最終影片的內容與功能。最後,它會利用 `gh` CLI 的 `--attach` 功能(需版本 2.99.0 以上)將影片上傳至 PR 留言或描述中。

相容平台✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/mikey-relevanceai/record-demo/tree/main/skills/record-demo

在你喜歡的 AI 中提問

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

預覽

來自技能 README

alt

說明文件

錄製示範

錄製一段看起來像是真人使用應用程式的操作示範——包含曲線滑鼠軌跡、逐字鍵入、刻意停頓——而非機器人瞬間穿梭於 UI 之間。

以下規則適用於所有平台上的每一次錄製。 它們被放在這裡而非各平台參考文件中,是因為每一條規則都曾被違反過而導致重錄,而且只放在某一種機制的規則,下一個人就不會讀到。

選擇執行平台

  • 網頁應用程式 - reference/web.md。使用 Playwright 搭配無頭 Chromium,並在頁面上繪製 DOM 游標。所有操作都不會出現在操作者的螢幕上,因此只要變更可在瀏覽器中看到,就優先採用此方式。
  • 桌面應用程式 - reference/desktop.md。當示範需要真實的 Electron 殼層時使用:例如原生選單、檔案對話框、系統匣、多視窗或任何渲染程序以外的功能。Linux 有打包好的執行器;macOS 則透過視窗 ID 錄製單一視窗,目前尚無執行器。

如果變更在兩者上都看得見,請錄製網頁應用程式。

輸入參數

在第一次正式錄製前先確認這些參數。優先從專案中讀取(README、package.json 中的 scripts、Electron 主程序);找不到的項目就詢問使用者,不要自行猜測。

  • 網頁版: 執行中應用程式的 URL,以 PR_RECORDER_BASE_URL 傳遞給錄製器(例如 http://localhost:3000)。應用程式必須已經從包含變更的檢出目錄中執行(包含未提交的編輯內容)——重新檢出同一個分支不會包含這些變更,而且 git 拒絕檢出同一個分支兩次。應用程式也必須處於示範所需的狀態。如果流程需要已登入的使用者或已植入的資料,那是專案本身設定的責任,而非錄製器的責任——但請在開始擷取前確認,否則你得到的錄製內容會是登入畫面。
  • 桌面版:
    • DEMO_APP_DIR - 應用程式啟動指令執行的目錄。
    • DEMO_WINDOW_TITLE - 主視窗的完整標題,用於尋找 X11 視窗及渲染頁面。
    • DEMO_LAUNCH_CMD - 可選。一個 shell 指令,用於啟動應用程式並啟用 {port} 的遠端除錯。預設值 pnpm dev --remoteDebuggingPort {port} --noSandbox 適用於 electron-vite。

技能存放位置

本文檔中的 <skill-dir> 代表載入此 SKILL.md 的目錄。在 Claude Code 中為 ${CLAUDE_SKILL_DIR},適用於專案、個人及外掛安裝。在其他環境中,請使用技能載入的路徑——對於 Codex,可能是倉儲中的 .agents/skills/record-demo 目錄或 ~/.agents/skills/record-demo。請解析一次後使用絕對路徑;這些腳本從不假設技能的安裝位置。

在正式錄製前先排練

在錄製要發佈的版本之前,先把整個流程從頭到尾跑一次。示範是「發現」出來的,而非「寫」出來的:控制項的名稱可能與預期不符,按鈕的功能可能與文字敘述不同,特定平台的載入時間可能比你想像的長。在鏡頭前才發現這些問題,就等於讓觀眾看著你發現它們。

排練的成本很低,重錄的成本也很低。送出一個帶有錯誤轉折的影片,或是要求審查者忽略中間那八秒,成本就高了。

先探查真實的控制項名稱

絕對不要根據你尚未確認存在且功能與名稱相符的控制項來編寫腳本。 先載入應用程式一次,傾印可存取名稱,並擷取螢幕截圖:

const names = await page
  .getByRole("button")
  .evaluateAll((els) =>
    els.map((e) => (e.getAttribute("aria-label") || e.textContent || "").trim()).filter(Boolean),
  );
console.log(JSON.stringify(names));
await page.screenshot({ path: "/tmp/probe.png" });

然後查看那張螢幕截圖。傾印告訴你名稱是否存在;只有圖片才能告訴你控制項實際上是什麼。以下四種情況會害到你:

  • 名稱與你想的不同。 某個切換器的可存取名稱是目前選取項目的名稱,而非「選取專案」。
  • 名稱聽起來不是那個意思。 側邊欄中的一個「檔案」按鈕實際上是區段切換器,點擊它會收合該區段而非導航到任何地方。這曾經被送進正式錄製中。
  • 名稱是另一個名稱的一部分。 getByLabel("第一則訊息") 同時匹配到附近一個標籤包含「第一則訊息」的選項,導致輸入從未到達目標欄位,卻也沒有任何失敗訊息。請加上 exact: true,或者同時比對角色與名稱。
  • 看似文字但實際上不是。 某個選擇器的標題在螢幕截圖上看起來是輸入框的佔位文字,導致 getByText 永遠無法匹配。請在編寫腳本之前先確認定位器能夠解析。

當多個元素共享同一個名稱時——例如某個項目同時出現在側邊欄、頁面標題和麵包屑中——請使用 .first() 來限定範圍。

以真人操作的方式驅動

透過可見的角色或文字來尋找控制項(getByRole、getByText、getByLabel、getByPlaceholder)。絕對不要使用 CSS 選擇器、僅供測試用的屬性,也不要為了讓錄製工具能找到而特別在應用程式中加入掛鉤。示範腳本是臨時性的且與 PR 相關;請將它們保留在倉儲之外。

絕對不要為了示範而修改應用程式、技能或正式程式碼。 如果示範需要應用程式有所不同,那表示示範本身就是錯的。

絕對不要等待時鐘

使用 waitForTimeout(9000) 來「等待編輯器載入」是可看但無聊的示範中最大的單一成因。它永遠支付最差情況的等待時間,而且每一秒都留在檔案裡。請等待你真正在等待的東西——例如第一個要互動的控制項——這樣示範會在應用程式準備好時立刻繼續,並在失敗時大聲報錯(包含定位器名稱),而不是繼續操作一個半載入的頁面。

固定的停頓只為了控制節奏:在操作之後加入一個節拍,讓觀眾能理解發生了什麼變化。單位是秒,而不是十秒,而且絕對不能取代真正的等待。

有寫入檔案不代表成功

錄製器在腳本拋出異常時會寫出部分檔案,而某些擷取工具則會在結束時寫入任何狀態。因此,檔案存在本身不代表什麼。在你相信一個錄製內容之前:

  • 檢查結束狀態碼和 stderr,如果有錯誤也請閱讀——它通常會指出哪個定位器沒有匹配到任何元素。
  • 檢查是否有舊的擷取程序仍然佔用輸出路徑。 從先前嘗試中留下的擷取程序仍然擁有該檔案名稱,並且會默默地錄製出錯誤的內容。
  • 檢查畫面幀。 提取連絡表、最後一幀以及任何你不確定的過渡畫面,並仔細檢視。

驗證的是示範內容,而非腳本

一個乾淨的結束狀態只代表腳本執行完畢,不代表功能正常。請同時確認:

  • 視覺上 - 變更在畫面幀中顯示出來,而且是請求中所要求的狀態。
  • 功能上 - 持久化狀態確實發生了變化,透過資料庫或 API 讀取確認,而非從 UI 推論。

當變更涉及佈局時——例如兩個面板並排、面板停靠在邊緣——請透過邊界框來斷言。元素存在並不代表它們位在正確的位置。

讓最終狀態停留大約三秒鐘,讓觀眾能夠看清楚,然後在交出檔案前使用 ffprobe 確認輸出檔案的編解碼器、尺寸和時長。

保留證據

在成功產出最終版本之前,請保留失敗的日誌、部分錄製檔和螢幕截圖。只有在確定有了可以發佈的內容後,才刪除可拋棄的中間狀態。被丟棄的中間檔案,往往是了解錄製為何失敗的唯一線索。

不要干擾正在錄製的機器

本機錄製是在使用者實際使用的電腦上進行的。

  • 為應用程式提供一個獨立的一次性使用者設定檔,絕對不要觸碰操作者的真實設定檔。
  • 在第一次啟動之前就植入示範所需的資料,否則首次執行畫面會擋在他們面前。
  • 掌握你所啟動的每一個行程,並在完成後停止它們——尤其是擷取程序,它們在腳本返回後仍會持續執行。
  • 將長時間執行的行程作為你能掌握的背景任務來啟動,而不是在前景包裝器中使用尾隨的 &:當包裝器結束時,它會帶著伺服器一起終止。
  • 以行程預期的方式來停止它。一個被 SIGKILL 殺死的開發伺服器會跳過它的清理陷阱,並留下一個被改寫的環境檔案和一個過時的埠號檔案。請對你啟動的行程發送訊號,讓它自行清理。絕對不要使用模式比對來殺死行程(例如 pkill -f next):在共用機器上,這會同時終止其他使用者的伺服器。
  • 檢查示範中的應用程式是連接到哪個後端。某個開發啟動器預設連接到正式環境的 API,除非特別指定,因此示範中的點擊可能會寫入真實資料。請在第一次正式錄製前,將其指向本地或開發環境的後端。
  • 預先檢查示範所依賴的任何外部項目——例如必須已登入的 CLI、必須正常運作的服務——在佔用顯示器或開始擷取之前就完成檢查,這樣一個遺漏的相依性會在一秒內失敗,而不是在錄製中途。絕對不要為了證明某個工具已啟動而執行具有副作用(例如發送訊息)的操作。
  • 如果錄製需要使用他們的螢幕、視窗管理器或輸入裝置,請提出並詢問,而不是直接佔用。

將檔案附加到 PR

從 v2.99.0 開始,gh 可以自行上傳影片:

gh pr comment <number> --body-file ./note.md --attach ./demo.mp4

--attach 也適用於 gh pr create、gh pr edit 以及 gh issue 的對應指令。它可以重複使用,每次指令最多 50 個檔案,並會上傳至 user-attachments CDN,讓 GitHub 直接在頁面上呈現影片。

有四件事值得注意:

  • 替代文字僅適用於圖片。 './shot.png#錯誤狀態' 可以為圖片設定替代文字;同樣的語法用在影片上會被拒絕,並顯示 cannot set alt text on video。請直接以純路徑附加影片。
  • 如果內文已經參照了本地路徑,該路徑會被改寫。 在你想放置影片的位置寫入 ![alt](./demo.mp4),gh 會自動替換成上傳後的 URL;否則附件會被附加到內文結尾。
  • 使用 gh pr edit --attach 但沒有指定內文旗標時,會保留現有內文 並附加內容,因此可以在事後將示範加入 PR 描述中。
  • 在使用前先確認版本。 在低於 2.99.0 的 gh 中沒有 --attach 參數,且無法透過 API 以其他方式存取 CDN——此時就只能退而提供檔案路徑,讓使用者自行拖進 PR 中。

相關技能