qwen-code cua-driver 的 cursor-gallery 全解析:cua.default 光标渲染器的交互预览与文档素材确定性导出
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
本指南围绕 cursor-gallery 的 README 展开,深入讲解 qwen-code 中 Cua Driver(计算机使用驱动)为生产级cua.default光标渲染器搭建的维护者预览工具:包括它的定位、serve与export-docs两条入口命令、页面上覆盖的 12 种动作与 15 种徽标上下文,以及从 Rust 原生渲染器到 WebM 预览、再到文档 GIF 的完整生成管线。读完本文,你将掌握如何在本地启动该画廊、理解动作/投放/目标三层语义契约,并能按需重新导出公开文档素材。
一、它是什么:cua.default 渲染器的维护者预览台
cursor-gallery位于 packages/cua-driver/tools/cursor-gallery,是 Cua Driver 为生产渲染器cua.default准备的维护者预览台(maintainer preview)。它不是一个独立的业务功能,而是用于视觉审查、回归对照与文档素材生产的内部工具。
几个关键设计约束(源自 README):
- 预览即真相:画廊里展示的每一帧动画,都是由生产环境同一套 Rust 渲染器(
cursor-overlaycrate)导出的"精确帧",而非前端重新模拟的效果,因此看到的效果就是运行时叠加层实际呈现的效果。 - 生成媒体不入库:所有预览媒体(WebM / 帧图)都是运行时构建的产物,由
cursor-overlay生成并**有意不提交(intentionally not committed)**到仓库。因此克隆仓库后需要先执行素材导出命令才能看到完整动画。 - 单一事实源:文档 GIF 与画廊页面共用同一批渲染帧,保证文档插图和交互预览在视觉上严格一致。
目录中的静态骨架文件有:index.html(页面结构)、app.js(交互逻辑)、styles.css(视觉样式)、capture-gallery.mjs(无头浏览器捕获脚本),以及入口脚本 cursor-gallery.sh。
二、两条入口命令:serve 与 export-docs
在仓库根目录执行(README 给出的官方用法):
./packages/cua-driver/scripts/cursor-gallery.sh serve ./packages/cua-driver/scripts/cursor-gallery.sh export-docs脚本还支持第三个子命令assets,即"只生成素材不启动服务"。完整用法为cursor-gallery.sh {assets|serve|export-docs}。
serve:本地交互预览
- 不自动打开浏览器,启动后访问
http://127.0.0.1:3001即可(端口可用环境变量CURSOR_GALLERY_PORT覆盖)。 - 行为流程(见 cursor-gallery.sh):先调用
export_assets生成全部帧与 WebM,再exec python3 -m http.server "$PORT" --bind 127.0.0.1 --directory "$GALLERY_DIR"以画廊目录为根启动静态服务。由于是exec替换进程,Ctrl+C 即可整体退出。
export-docs:再生成公开文档 GIF
README 明确列出其前置依赖:Chrome、支持 WebSocket 的 Node.js、Python 3、ffmpeg。对照脚本源码(cursor-gallery.sh),export_docs实际还会require curl与cargo(export_assets需要 Rust 工具链编译示例程序)。它从同一批渲染帧出发,通过无头 Chrome 截图并编码为 GIF,从而"确定性"地再生成公开文档素材。
三、页面内容纵览:交互配置器 + 15 个徽标状态 + 12 个动作动画
README 对页面内容有一句话概括:画廊以一个覆盖全部 12 种动作、可选background/foreground投放、可选ax/pixel/browser/desktop目标的生产级光标交互配置器开场,随后展示15 个徽标上下文状态与12 个隔离的主题自有动作动画。对照 index.html 可拆分为三大区块:
- Build the final cursor(运行时预览区):通过 Action / Delivery / Target 三个下拉框自由组合,实时预览"最终光标"。页面右侧同步展示**运行时解剖(runtime anatomy)**的四个组成部分:
- ① Pointer and action:围绕指针播放的动作动画;
- ② Session:会话身份标签(预览中固定为
Research); - ③ Delivery:实心投放徽标(Filled chip);
- ④ Target:描边目标徽标(Outlined chip)。
- Every delivery and target combination(徽标上下文区):3 种投放 × 5 种目标 =15 个徽标状态,按运行时徽标顺序排列。
- Action animations(动作动画区):12 个由主题持有的动作图层被隔离出来单独审查。
页面顶部提供动画控制条:Pause/Play、Replay(全部回卷到 0 帧重放)、Speed(0.5×/1×/1.5×/2×)与背景切换(Dark/Mixed/Light/Brand 四档,见 app.js),便于在不同底色下检查光标对比度。页脚特别注明:预览尺度为放大显示,生产光标实际尺寸为 42 点——这与渲染器常量DISPLAY_SIZE: f32 = 42.0(theme.rs)一致。
四、语义契约:12 种动作、3 种投放、5 种目标
预览页的选项并非随意定义,而是对应 cua-driver 的传输无关语义契约(定义于 cua-driver-contract/src/cursor.rs)。契约文件开头的注释强调:这些语义由工具实现作为"尽力而为的视觉遥测"发出,从不影响授权、调度、输入投递或工具结果。
12 种动作(CursorAction)
app.js 中维护了与契约一致的完整清单:
| 动作 id | 显示名 | 描述 | playback(cursor.rs) | 时长 |
|---|---|---|---|---|
idle | Idle | 动作之间等待 | Resting | 4.0s |
observe | Observe | 读取屏幕或界面 | Loop | 1.6s |
click | Click | 点击或选中元素 | OneShot | 0.67s |
drag | Drag | 拖拽元素或选区 | Held | 1.6s |
scroll | Scroll | 滚动内容 | Loop | 1.6s |
text | Text | 键入或填充文本 | Held | 1.6s |
key | Key | 按键或快捷键 | OneShot | 1.6s |
navigate | Navigate | 移动、导航或切换标签 | OneShot | 1.6s |
app | App | 管理应用或窗口 | OneShot | 1.6s |
transfer | Transfer | 上传、下载、复制或移动文件 | Loop | 1.6s |
record | Record | 录制或重放轨迹 | Loop | 1.6s |
system | System | 管理会话、权限或配置 | OneShot | 1.6s |
四种 playback 类型(cursor.rs):Resting(静止待机)、OneShot(播完即止,到时自动回到 Idle)、Held(按住期间持续)、Loop(循环播放)。在 theme.rs 的状态机中,Held/Loop 动作结束时还会进入 0.4s 的 grace period(ending_secs),保证短工具调用结束后视觉提示至少停留一帧。
投放(Delivery)与目标(Target)
- 投放:
None/background(后台) /foreground(前台),对应契约中的 CursorDelivery; - 目标:
None/ax/pixel/browser/desktop,对应契约中的 CursorTarget。
两者在徽标中的视觉语义固定为:投放 = 实心(filled)徽标,目标 = 描边(outlined)徽标(app.js)。组合标签规则为:两者皆无 →Session only;仅其一 →X only;两者皆有 →X + Y。README 特别强调:投放与目标字形只出现在徽标内部的权威运行时位置——即它们由宿主拥有的会话徽标绘制,属于叠加层而非主题产物(详见第七节)。
会话颜色:每会话一个稳定填充色
预览中会话标签Research并非写死的装饰。运行时光标默认使用 Cua 蓝#5EC0E8(DEFAULT_CURSOR_FILL,见 theme.rs);命名会话则通过session_fill_rgba稳定哈希进 9 色调色板(theme.rs),使并发运行的多光标在视觉上彼此可区分,且不信任 Agent 传入的样式参数。哈希策略对数字/单字母后缀有特判(分别取模映射),其余走 FNV 风格散列。
五、帧级素材生成:export_gallery_frames 示例程序
预览所用的全部 WebM 素材,来自 cursor-overlay 的示例程序 export_gallery_frames.rs。export_assets通过以下命令调用它(cursor-gallery.sh):
cargo run --quiet \ --manifest-path packages/cua-driver/rust/Cargo.toml \ -p cursor-overlay \ --example export_gallery_frames \ --features theme-authoring \ -- target/cursor-gallery/renderer-frames需要显式开启theme-authoringfeature,输出目录为仓库根的target/cursor-gallery/renderer-frames。
该示例的帧导出参数(export_gallery_frames.rs):
| 参数 | 值 | 说明 |
|---|---|---|
SIZE | 256 | 每帧像素尺寸(预览放大用) |
FPS | 30 | 帧率 |
DURATION_SECS | 4 | 每段动画时长,即每状态 120 帧 |
PREVIEW_BACKING_SCALE | 1.5 | 渲染背衬缩放 |
RUNTIME_SESSION_LABEL | "Research" | 预览会话标签 |
导出状态分为两组:
- actions/:12 个纯动作状态,
delivery=None、target=None、session_label=None,即"隔离的主题自有动作动画"; - previews/:12 × 3 × 5 =180 个完整组合,均带
Research会话标签,slug 形如observe--background--browser。
每帧通过RenderStateCore组装:cursor_id = "gallery-session"、idle_hide_ms = 0、光标置于画布中心、朝向 45°(FRAC_PI_4),依次应用SetSessionLabel与BeginAction { action, delivery, target }命令后调用render_frame输出 PNG(export_gallery_frames.rs)。导出使用thread::scope并行分块,工作线程数取min(available_parallelism, 12)。
示例程序自带两个单元测试(export_gallery_frames.rs):一个校验运行时预览组合(Observe + Background + Browser + Research),另一个断言预览清单恰好覆盖每种运行时组合一次(12 * 3 * 5个 slug 互不重复,且包含observe--background--browser、idle--none--none、system--foreground--desktop)。这也从测试层面印证了 README 中"覆盖全部组合"的承诺。
六、从帧到 WebM:ffmpeg 编码与页面装配
export_assets随后对actions与previews两组帧目录逐一执行 ffmpeg 编码(cursor-gallery.sh):
ffmpeg -y -loglevel error -framerate 30 \ -i "$state_dir/%04d.png" \ -c:v libvpx-vp9 -pix_fmt yuva420p -auto-alt-ref 0 \ -crf 28 -b:v 0 -row-mt 1 \ "$GENERATED_DIR/$group/$state.webm"要点:VP9 编码、yuva420p保留透明通道(光标叠加层需要 alpha)、auto-alt-ref 0与-b:v 0配合 CRF 28 保证质量优先且码率不失控,row-mt 1开启行级多线程。产物写入 tools/cursor-gallery/generated 下的actions/与previews/。
页面侧由 app.js 动态渲染:contexts = deliveries.flatMap(...)生成 15 个徽标卡片,actions生成 12 个动作卡片;运行时预览则按action--delivery--target拼接 WebM 路径并热切换(app.js)。卡片底色按 light/dark/blue 三色调轮转,并在顶部背景切换按钮下支持 Dark/Mixed/Light/Brand 四态。
七、文档 GIF 导出:headless Chrome + CDP 确定性截图
export-docs的完整链路(cursor-gallery.sh):
- 复用
export_assets生成帧与 WebM; - 解析 Chrome 路径(按
CURSOR_GALLERY_CHROME→ macOS 默认路径 →google-chrome→chromium顺序探测); - 用随机空闲端口启动
python3 -m http.server(以仓库根为目录)与无头 Chrome(--headless=new --disable-gpu --autoplay-policy=no-user-gesture-required --window-size=1600,2200 --remote-debugging-port=<port>); - 通过 CDP 端口执行 capture-gallery.mjs;
- 用 ffmpeg 将截图序列编码为 GIF,写入脚本定义的文档目录
docs/public/img/cua-driver/cursor-themes/action-animations.gif。
capture-gallery.mjs 的关键实现:
- 通过 WebSocket 直连 Chrome DevTools Protocol(依赖 Node 内置 WebSocket,README 中"支持 WebSocket 的 Node.js"即指此,需以
node --experimental-websocket运行); Emulation.setDeviceMetricsOverride将视口设为 1600×2200(与 Chrome--window-size对应),导航后等待所有.cursor-video元素readyState >= 2;- 把视频元素替换为
<img>,按FRAME_URL_ROOT/<group>/<state>/NNNN.png逐帧加载渲染器原始帧,从 30fps 源帧中每隔一帧取一张(sourceFrame = frame * 2)降采样为 15fps; - 依据区块标题与网格的
getBoundingClientRect()计算截图裁剪区(含 30px 边距与底部留白),每帧Page.captureScreenshot输出%04d.png; - 环境变量:
CDP_ENDPOINT(默认http://127.0.0.1:9229)、PAGE_URL(默认画廊地址)、FRAME_URL_ROOT(默认渲染帧目录)。
GIF 编码采用双遍调色板(cursor-gallery.sh):palettegen=stats_mode=diff生成 1080px 宽 Lanczos 缩放的调色板,再以paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle输出。由于帧源、参数与视口全部固定,每次export-docs都能得到视觉一致的文档素材,这正是 README 所称"deterministically"的含义。
八、主题与运行时边界:cua.default 从哪里来
画廊预览的cua.default并非像素贴图,而是一套有界矢量主题。其权威来源与编译产物说明见 cursor-overlay/assets/README.md:
cua.default.lottie是规范源档案(dotLottie 格式);cua.default.cua-theme是由源档案编译出的有界运行时产物;- 特权叠加层只嵌入并解码
.cua-theme,运行时绝不解析 dotLottie ZIP 或 Lottie JSON; - 产物包含有界矢量几何、画笔、变换与采样动画帧,而非固定分辨率像素图集;Skia 以实时显示背衬尺度光栅化这些指令。
重新生成两者的命令:
python3 crates/cursor-overlay/assets/build_default_theme.py cargo run -p cursor-theme-cli -- build \ crates/cursor-overlay/assets/cua.default.lottie \ --output crates/cursor-overlay/assets/cua.default.cua-theme cargo run -p cursor-theme-cli -- inspect \ crates/cursor-overlay/assets/cua.default.cua-theme配色边界(与画廊语义呼应):源主题以 Cua 蓝为调色板键、白色为描边;只有内嵌默认主题在运行时被重着色为稳定的会话填充色,已安装的自定义主题保留作者配色。共享浮动动画施加于选中动作之上,而投放/目标上下文由宿主拥有的会话徽标绘制、不属于主题产物——这解释了为什么画廊中"徽标上下文"与"动作动画"被分为两个独立区块。
运行时配置入口可参考 lib.rs 的 CursorConfig:--cursor-theme <id>、--cursor-reduced-motion auto|on|off、--no-overlay、--glide-ms、--dwell-ms、--idle-hide-ms等参数会在各平台后端初始化叠加窗口时生效。
九、环境变量与调试速查
| 环境变量 | 默认值 | 作用 |
|---|---|---|
CURSOR_GALLERY_PORT | 3001 | serve的 HTTP 端口 |
CURSOR_GALLERY_CHROME | 自动探测 | Chrome/Chromium 可执行文件路径 |
CURSOR_GALLERY_EXPORT_PORT | 随机空闲端口 | export-docs的静态文件服务端口 |
CURSOR_GALLERY_EXPORT_CDP_PORT | 随机空闲端口 | Chrome 远程调试端口 |
CDP_ENDPOINT | http://127.0.0.1:9229 | 捕获脚本连接的无头 Chrome 端点 |
PAGE_URL | http://127.0.0.1:3001/ | 捕获脚本导航的页面地址 |
FRAME_URL_ROOT | http://127.0.0.1:3001/target/cursor-gallery/renderer-frames | 渲染帧静态根 |
常见问题排查要点:
- 页面只有静态骨架没有动画:
generated/目录未生成,先运行serve(其内部会自动执行export_assets)或单独运行assets; - 报 Chrome not found:设置
CURSOR_GALLERY_CHROME指向 Chrome 可执行文件; node提示 WebSocket 未定义:Node 版本过旧,需要支持--experimental-websocket的版本;- 端口被占用:
serve用CURSOR_GALLERY_PORT覆盖;export-docs的 HTTP/CDP 端口会自动挑选空闲端口,一般无需干预; - 素材未提交属预期行为:
generated/、target/cursor-gallery/均为构建产物,不应出现在版本控制中。
整个画廊的价值在于"预览即生产":无论是手工交互审查、回归比对,还是文档 GIF 的再生成,所有素材都来自同一套 Rust 渲染器与同一批帧,杜绝了前端模拟与真实渲染之间的视觉漂移。
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考