Communitygithub.com

录制演示视频 (Record a Demo)

录制一段像真人操作一样自然的界面演示视频(MP4),用于附加到 Pull Request 中。支持基于 Playwright 的无头 Web 应用录制,以及通过真实屏幕或窗口捕获的 Electron 桌面应用录制。

录制演示视频 (Record a Demo) 是什么?

该技能提供一个完整的框架,用于录制高质量的、逼真的用户界面演示视频。其核心目标不是生成自动化测试,而是创建可供人类审查的、自然流畅的操作演示,包括鼠标曲线移动、逐字键入和适当的停顿,以模拟真实用户行为。技能文档明确规定了适用于所有录制平台的通用规则(如必须探针真实控件名称、禁止依赖定时等待、必须验证演示效果而非脚本执行成功),并指导如何根据项目类型(Web 应用或 Electron 桌面应用)选择正确的录制机制。对于 Web 应用,它使用 Playwright 在无头 Chromium 中录制,并在页面上绘制 DOM 光标,从而不干扰操作员屏幕。对于桌面应用,它记录真实的 Electron 窗口(包括原生菜单、文件对话框等),支持 Linux 和 macOS。此外,本技能还提供了细致的操作指南,包括录制前必须确认的输入参数(如应用URL、窗口标题)、如何通过排练验证流程、如何避免常见陷阱(如错误匹配控件名称、使用固定时间等待而非等待实际元素出现)、以及如何通过 `gh` 命令行工具将生成的 MP4 文件附加到 GitHub 的 Pull Request 或 Issue 上。它强调必须保留失败日志和部分录制产物,直至获得可交付的最终成品,以确保过程的可靠性和可追溯性。

兼容平台✓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

文档

录制演示视频

录制一段看起来像真实用户在操作应用的演示视频——包括曲线鼠标轨迹、逐字键入和刻意停顿,而不是机器在界面间快速跳转。

以下规则适用于每次录制,无论使用哪种平台。 它们被放在这里而非各个平台参考文档中,是因为每条规则都曾因被忽略而花费了一次重录,而且如果规则只放在一种录制机制旁边,下一个使用者可能就不会读到。

选择录制平台

  • Web 应用 - 参见 reference/web.md。使用 Playwright 驱动无头 Chromium,并在页面内绘制 DOM 光标。不会影响操作员的显示屏,因此只要变更能在浏览器中可见,优先选择此方式。
  • 桌面应用 - 参见 reference/desktop.md。当演示需要真实的 Electron 原生功能时(如原生菜单、文件对话框、系统托盘、多窗口或渲染进程之外的功能)使用。Linux 有内置的运行器;macOS 通过窗口 ID 录制单个窗口,目前尚无运行器。

如果变更在 Web 和桌面应用上均可见,则优先录制 Web 应用。

输入参数

在首次正式录制前确定这些参数。应尽可能从项目中读取(如项目的 README、package.json 中的脚本、Electron 主进程);对于无法找到的参数,直接询问用户,而非自行猜测。

  • Web 应用: 运行中应用的 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("First message") 可能同时匹配了一个附近选项的标签,其内容也包含“first message”,导致输入从未到达目标字段,且脚本未报错。请使用 exact: true,或同时匹配角色和名称。
  • 某些看起来像文本,但实际不是。 截图中读取的某个选择器标题实际上是输入框的占位符,因此 getByText 永远无法匹配到它。在针对某个定位器编写脚本前,务必确认其能正确解析。

当多个元素共享同一名称时(例如某个条目同时出现在侧边栏、页面标题和面包屑导航中),请使用 .first() 限定范围。

像真人一样操作

通过可见的角色或文本来查找控件(getByRole、getByText、getByLabel、getByPlaceholder)。绝对不要使用 CSS 选择器、仅用于测试的属性,或者为了录制而向应用添加钩子。演示脚本是一次性的,且每个 PR 单独使用;请将它们保留在仓库之外。

绝不要为了演示而修改应用、本技能或生产代码。 如果演示需要应用以不同方式运行,那说明演示本身是错的。

永远不要依赖时钟等待

使用 waitForTimeout(9000) 来“等待编辑器加载”是导致录制的演示视频虽然可看但非常乏味的首要原因。它总是按最坏情况等待,且每一秒的等待时间都会最终写入文件。请等待你实际等待的东西——即你将要与之交互的第一个控件——这样演示才能在应用准备就绪时立即继续,并在失败时清晰地报出定位器的名称,而不是驱动一个尚未加载完成的页面。

固定的暂停仅用于控制节奏:在某个操作后短暂停顿,让观众消化刚发生的变更。按秒计,而非数十秒,并且绝不能作为等待的真正替代品。

存在文件并不等于验证成功

录制器在脚本抛出异常时会刷新部分文件,某些捕获工具在退出时也会写入当前状态。因此,文件存在本身毫无意义。在相信一次录制成功之前:

  • 检查退出状态和 stderr,并读取错误信息——错误通常会指明哪个定位器未匹配到任何元素。
  • 检查是否有旧的捕获进程仍占用输出路径。 先前尝试中遗留的捕获进程可能仍持有输出文件名,并静默地生成错误内容的录制。
  • 检查帧内容。 提取一张缩略图集、最后一帧以及任何你怀疑的过渡帧,并仔细查看。

验证演示效果,而非脚本执行结果

脚本干净退出只说明脚本运行了,不等于功能正确。请确认以下两点:

  • 视觉效果 - 帧中的画面确实显示了变更,且处于请求中指定的状态。
  • 功能效果 - 持久化状态确实发生了变更,这应通过数据库或 API 读取,而非仅从 UI 推断。

如果描述的是布局方面的变更(例如两个面板并排显示、某个面板停靠到边缘),则应根据元素的边界框进行断言。元素存在并不能说明它们的位置。

将最终状态保持大约三秒钟,以便观众确认看到结果,然后在交付前使用 ffprobe 验证输出文件的编码格式、尺寸和时长。

保留证据

在取得可交付的成功录制之前,请保留失败的日志、部分录制的产物和截图。仅在确保拥有值得交付的成品后,再删除临时的状态文件。丢弃的中间产物往往是理解录制为何出错的唯一记录。

不要干扰正在录制的机器

本地录制运行在用户实际使用的计算机上,且可能在他们正在使用时进行。

  • 为应用创建独立的可丢弃配置文件,绝不要触碰操作员真实的配置文件。
  • 在首次启动之前播种演示所需的所有数据,否则首次运行时的设置界面将出现在用户面前。
  • 管理好你启动的每一个进程,并在完成后停止它们——尤其是捕获进程,它们会在脚本返回后继续运行。
  • 将长时间运行的进程作为后台任务启动并持有其句柄,而不是在前台包装器中使用后缀的 & 符号:当包装器退出时,服务器也会随之终止。
  • 以进程期望的方式停止它。使用 SIGKILL 杀死开发服务器会导致其跳过清理陷阱,留下被重写的环境文件和过时的端口文件。请向你启动的进程发送信号,让其自行清理。绝不要使用模式匹配的 kill 命令(如 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#The error state' 可以为图片设置替代文本;相同的语法用于视频会被拒绝,并返回 cannot set alt text on video。请直接使用裸路径附加视频。
  • 如果正文中已引用本地路径,该路径会被自动替换。 在你希望视频出现的地方写入 ![alt](./demo.mp4),gh 会在上传后自动替换为 CDN URL;否则附件会被追加到正文末尾。
  • gh pr edit --attach 如果不指定 body 参数,会保留现有正文 并在其后追加内容,因此可以在 PR 创建后为描述添加演示视频。
  • 在使用前检查版本。 在低于 2.99.0 版本的 gh 中,没有 --attach 参数,也无法通过 API 以其他方式访问 CDN——此时只能将文件路径告知用户,让他们自行拖拽到 PR 中。

相关技能