mcpField
S
synthetic-ui-demo

synthetic-ui-demo

github.com/digitalsamba创意与媒体2.4k62 次安装今天更新
代码仓库

关于 synthetic-ui-demo

Synthetic UI Demos 是一套用于制作产品演示视频的技能,面向能读取产品源代码并需要可重复演示的场景。它用静态 React 重建镜头中的界面,并以 Remotion 按帧控制光标、镜头和点击,无需录制屏幕或运行产品。

主要功能

  1. 01从产品源代码重建静态界面
  2. 02用 Remotion 按帧编排演示
  3. 03控制光标、点击和镜头移动
  4. 04按场景组织语音与时间线
  5. 05通过静帧联系表校准点击位置

适用场景

  1. 01制作每次发布都要更新的功能演示
  2. 02呈现无法实际访问的数据或界面
  3. 03为演示精确安排点击和镜头
  4. 04制作跨多个应用的发布说明视频

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/digitalsamba/claude-code-video-toolkit --skill synthetic-ui-demo

安装后在 Claude Code 中输入/synthetic-ui-demo确认已加载。

详细介绍

合成 UI 演示

不会录制屏幕。Claude Code 会读取产品代码仓库,只用静态 React 重建场景中展示的屏幕,并在 Remotion 中通过脚本控制的光标和摄像机演绎整个流程。每次点击都对应一个帧编号,因此场景每次渲染的结果都完全一致,不需要预置账号或实时环境;产品变更时,只需编辑代码即可更新。

参考构建(请结合本技能阅读;两者都不依赖产品代码仓库即可构建):

  • examples/ds-free-schedule-demo:一个 26 秒的流程(安排通话),带有旁白图块。最简单的完整示例。其 src/Demo.tsx 是一条包含所有光标关键帧、点击和摄像机移动的时间线。
  • examples/ds-release-649:一段 80 秒的版本说明视频,涵盖三个应用中的八个场景。src/Release.tsx 用于排列场景,src/scenes/ 中每个版本更新项各有一个文件,src/room/ 和 src/ac/ 则是屏幕组件套件。

何时使用此方法,而不是 Playwright

合成演示(本技能)Playwright 录制(/record-demo)
可以读取应用源码只能使用正在运行的应用
每次发布都要重新制作的版本说明和功能导览一次性的真实行为或真实数据录制
流程需要你没有的数据(完整通话、其他用户、管理员视图)实际操作很容易完成,而且现有外观就符合要求
摄像机移动、精确的点击时机、语音变更后的重新渲染重建成本很高的长流程复杂交互

构建模块(lib/components)

import { Stage, typed, cursorAt, near } from '../../../../lib/components';

<Stage
  url="app.example.com/settings"             // address bar; host darker than path
  camera={CAMERA}                             // [{ f, z, x, y }] in canvas px, eased, clamped to the window
  path={PATH}                                 // [{ f, x, y }] cursor keyframes in canvas px
  clicks={CLICKS}                             // frames that squash the cursor and throw a ripple
  aiNote                                      // "People shown are AI-generated" (or pass your own text)
  canvasWidth={1440} canvasHeight={810}       // the defaults
>
  <SettingsScreen field={typed(frame, 'Q4 Product Review', 128)} />
</Stage>
  • Stage 会在固定尺寸的应用画布周围绘制浏览器边框,应用摄像机效果,并将光标置于最上层,zIndex 为 100。缩放为 1 时,窗口宽度占画面宽度的 fit(0.8625)。关键帧重复某个值即可保持该值。
  • Cursor 和 cursorAt 可独立用于非浏览器场景;near(cursor, target) 用于驱动悬停状态。
  • 产品专属的屏幕组件套件(主题、图标、布局)应保留在项目中,不属于库的内容。

方法

1. 使用子 Agent 从应用代码仓库提取视觉规格

每个应用或区域并行分配一个子 Agent,让每个子 Agent 返回精简规格,而不是大段文件内容:

  • **主题:**调色板、字体栈、圆角、间距、阴影(从主题文件或 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. 先录制语音,再按帧安排时间线
  • 每个场景写一句台词,并先生成各场景的音频(voiceover.py --scene-dir ... --json,添加 --max-wpm 165)。读取测得的时长。
  • 每个场景的时长设为台词长度再加一拍,并保留一条标题注释,列出每句台词的开始时间和时长。该 注释是唯一可信来源(参见 CLAUDE.md 中的“音频锚定时间线”)。
  • 场景文件顶部的所有内容都设为帧常量:光标关键帧、CLICKS、摄像机关键帧、UI 状态切换(frame >= C.save 时显示对话框)。导出点击帧,让父级可以放置点击音效。
  • 每次点击都要与提到该对象的词同步。
4. 在密集列举台词的停顿处分句

一条列出多个事项的台词(“日程安排、联系人、录音和转录文本”)太快,无法用单个镜头呈现。 在停顿处分割音频,生成短语文件(07a、07b 等),为每个短语安排自己的镜头,并将每个 短语放在各自的节拍上。ds-release-649 的 moreVo 字段就是这样处理的。

5. 完整渲染前,先用联系表测量点击目标

摄像机放大后,猜测的坐标通常会偏差 20-50px。在每个点击帧及其前几帧渲染静帧,将它们拼成图块,并检查光标尖端是否落在目标上:

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。

注意事项

  • 单色 SVG 标志渲染为黑色。 logo_mono.svg 通常使用黑色填充;在深色背景上使用 filter: brightness(0) invert(1)。
  • 本地分支可能过时。 读取 origin/<default>,而不是本地检出所在的分支。
  • rsync --exclude src 也会排除 node_modules/*/src。 复制项目或工具包时,在传输根目录前加斜杠来限定排除路径:--exclude /src。
  • 摄像机位置会被限制。 如果摄像机中心靠近画布边缘,就会被往内拉,避免窗口边缘出现在画面外。如果缩放位置出乎意料,原因就在这里;应瞄准更靠内的位置。
  • 菜单和对话框必须放在画布内。 画布会裁剪其子元素。菜单的位置应让它向内展开,与真实应用的行为一致。
  • 悬停状态由光标路径决定。 使用 near(cursorAt(frame, PATH), target),而不是另一组帧常量,这样移动关键帧时,悬停状态也会随之移动。
  • 更改语音台词会让后续所有内容移位。 重新测量,更新标题注释和场景时长,然后重新检查点击静帧。如果口型同步的旁白由合成后的语音轨驱动,也要重新生成。

评论

支持 Markdown