record-browser-gif

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Record Browser GIF

录制浏览器GIF

Produce a short, truthful UI demonstration as a local GIF, and — only when the task includes attaching it to a pull request — publish it through the assets-branch workflow at the end of this skill. Drive interaction with the repository's browser-control skill when available, otherwise the repository-declared Playwright dependency, and encode with the bundled encoder for repeatable timing, dimensions, and size.
生成一段简短、真实的UI演示本地GIF,并且仅当任务要求将其附加到拉取请求时,通过本技能末尾的资产分支工作流进行发布。若仓库提供浏览器控制技能,则使用该技能驱动交互;否则使用仓库声明的Playwright依赖,并通过捆绑的编码器进行编码,以保证时长、尺寸和大小的一致性。

Every GUI pull request includes a GIF

所有GUI相关拉取请求必须包含GIF

A pull request that changes product-user-visible GUI behavior MUST include a demonstration GIF recorded with this skill and embedded in the pull request body via the assets-branch workflow.
The recording itself is part of the evidence: use a real server booted from that pull request's branch tree, real credentials, and real data flow (real model rounds where the app is model-backed). Never substitute fixture queries, mock transports, synthetic event injection, or test-only hooks unless the user explicitly asked for a fixture recording. Next to the embed, state the exact demonstrated commit SHA, the tree and origin that served it, any mode flags or browser-state exceptions, and whether a real model round ran, so reviewers know exactly what the recording proves.
任何更改产品用户可见GUI行为的拉取请求,必须包含使用本技能录制的演示GIF,并通过资产分支工作流嵌入到拉取请求正文中。
录制本身属于证据的一部分:使用从该拉取请求分支树启动的真实服务器、真实凭据和真实数据流(若应用基于模型,则使用真实模型轮次)。除非用户明确要求使用固定数据录制,否则绝不能用固定查询、模拟传输、合成事件注入或仅测试用的钩子替代。在嵌入的GIF旁,需注明演示所用的精确提交SHA、提供服务的树和源、任何模式标志或浏览器状态异常,以及是否运行了真实模型轮次,以便评审者清楚了解录制所证明的内容。

Keep recording separate from publication

录制与发布分离

  • Recording produces frame images and one local
    .gif
    artifact only; it never mutates remote state.
  • Publication — pushing the GIF to an assets branch and embedding it in a pull request body — is the separate final step, performed only when the task includes attaching the GIF to a pull request. It never touches the pull request's own branch.
  • Preserve the requested recording conditions. A real-server or real-credential demo must not use fixture queries, mock transports, synthetic event injection, or test-only hooks. If credentials or the server are unavailable, report that limitation instead of substituting a fixture.
  • Never read or expose credential values. Use the application's normal configuration path and a benign demonstration prompt.
  • 录制仅生成帧图像和一个本地
    .gif
    文件,绝不会修改远程状态。
  • 发布——将GIF推送到资产分支并嵌入拉取请求正文——是独立的最终步骤,仅当任务要求将GIF附加到拉取请求时才执行。发布操作绝不会触及拉取请求自身的分支。
  • 保留要求的录制条件。真实服务器或真实凭据演示不得使用固定查询、模拟传输、合成事件注入或仅测试用的钩子。若凭据或服务器不可用,需报告该限制,而非用固定数据替代。
  • 绝不读取或暴露凭据值。使用应用的常规配置路径和无害的演示提示。

Stage the application

部署应用

A GIF for a specific pull request demonstrates that pull request's tree, so stage per pull request:
  1. Require a clean worktree, record its exact commit with
    git rev-parse HEAD
    , then build that recorded tree with the repository's build commands for the recorded tree. A GIF recorded against another commit's build misattributes the evidence.
  2. Boot one server per port from that tree with the application's fresh state roots, workspace, and session state. Give the browser a fresh isolated context or profile as well; if the browser workflow cannot create one, clear that origin's cookies and site storage before navigation so persisted client state cannot affect the evidence. Source credentials through the application's normal configuration path; never echo them.
  3. Treat one storyboard as one evidence run: every published frame comes from that server and those state roots, workspace, session, and data-flow scenario run. If capture automation fails, discard its frames and rerun from fresh roots; never splice frames from separate runs.
  4. When switching between pull requests, stop the old server by PID or an exact match on its command line. A broad
    pkill -f
    pattern can match and kill the shell that launched it — including your own.
针对特定拉取请求的GIF需演示该请求的分支树,因此需按拉取请求进行部署:
  1. 要求工作树干净,使用
    git rev-parse HEAD
    记录其精确提交,然后使用仓库的构建命令针对该记录的分支树进行构建。基于其他提交构建录制的GIF会导致证据归属错误。
  2. 从该分支树启动每个端口对应的服务器,使用应用的全新状态根、工作区和会话状态。同时为浏览器提供全新的隔离上下文或配置文件;若浏览器工作流无法创建,则在导航前清除该源的Cookie和站点存储,以避免持久化客户端状态影响证据。通过应用的常规配置路径获取凭据;绝不回显凭据。
  3. 将一个故事板视为一次证据运行:所有发布的帧均来自该服务器以及对应的状态根、工作区、会话和数据流场景运行。若捕获自动化失败,需丢弃其帧并从全新根重新运行;绝不能拼接来自不同运行的帧。
  4. 在切换拉取请求时,通过PID或命令行精确匹配停止旧服务器。宽泛的
    pkill -f
    模式可能匹配并杀死启动它的Shell——包括你自己的Shell。

Record the flow

录制流程

  1. Invoke the repository's browser-control skill when available, and follow its setup, interaction, and cleanup instructions. Use the user's existing browser state only when requested or required; state that exception in the provenance and do not claim fresh client state. If browser control is unavailable, use the repository-declared Playwright dependency in an isolated headless browser; do not install another driver or launch the user's browser. State that fallback in the provenance.
  2. Before recording, identify the exact origin, whether the app is built or in development, the transport, and any fixture or mock mode. Record only claims that the observed setup supports.
  3. When a production default opens a native operating-system surface that headless automation cannot drive, select an official browser-operable production backend through the application's normal configuration. State the override in the provenance; a fixture, mock transport, or test-only hook is not an acceptable substitute.
  4. Choose three to six states that tell one story, such as typed, running, settled, and detail. Prefer semantic state changes over continuous capture; omit loading churn that does not help the viewer.
  5. Keep one viewport and crop for every frame, and name frames lexically:
    00-initial.png
    ,
    01-typed.png
    , and so on.
  6. Store frames under the repository's gitignored browser-tool screenshots directory (for example
    .playwright-mcp/
    ), since browser-tool screenshots can only be written under the tool's allowed roots and relative filenames resolve against the repository root. Create the frame subdirectory first (
    mkdir -p <screenshots-dir>/gif-frames-<label>
    ); writing into a missing directory fails with ENOENT at capture time.
  7. Before each screenshot, wait for a concrete UI condition such as a unique label, enabled control, changed document title, or completed response. Require the locator to resolve exactly one element; for Playwright accessible-name locators, use
    exact: true
    when equality is intended because descendant text or a prompt echo can otherwise create a false match. Do not use a fixed delay as proof that the application reached the state.
  8. Make completion predicates match an exact-text element — for example, an element whose trimmed text equals the expected reply — never a substring check such as
    body.textContent.includes(...)
    , which the echo of the user's own prompt also satisfies.
  9. When the claim involves a tool call, rejection, or recovery, include a detail or trajectory frame that shows the tool identity, status or stable error code, and the downstream result. A chat-only outcome does not prove why the tool path behaved that way.
  10. Capture a transient state (spinner, running row) by driving a slow foreground operation — for example, a
    sleep 15
    bash command — and polling a concrete DOM marker (a
    data-*
    attribute) inside one browser-script call that also takes the screenshot. State polled across separate tool calls is lost, because the turn settles between calls.
  11. Engineer the prompt so the state you need actually occurs: instruct the model to wait in the foreground when it would otherwise background a slow command, and give it a settle sentinel such as "reply with the single word done" to anchor the completion predicate.
  12. Capture no secrets, personal data, unrelated tabs, or transient notifications. Stop any unnecessarily long real-server run after the demonstrated state is visible.
Use the browser's own screenshot API. When it returns image bytes, save those bytes directly; the encoder detects image content independently of the filename extension.
  1. 若仓库提供浏览器控制技能,则调用该技能,并遵循其设置、交互和清理说明。仅在用户要求或必要时使用用户现有浏览器状态;需在来源信息中注明该例外情况,且不得声称使用了全新客户端状态。若浏览器控制不可用,则使用仓库声明的Playwright依赖在隔离的无头浏览器中运行;不得安装其他驱动或启动用户的浏览器。需在来源信息中注明该 fallback 方案。
  2. 录制前,确定精确的源、应用是已构建还是处于开发模式、传输方式,以及任何固定数据或模拟模式。仅录制观察到的设置所支持的内容。
  3. 当生产默认设置打开无头自动化无法驱动的原生操作系统界面时,通过应用的常规配置选择官方的可浏览器操作的生产后端。需在来源信息中注明该覆盖设置;固定数据、模拟传输或仅测试用的钩子不可作为可接受的替代方案。
  4. 选择3到6个能完整讲述一个流程的状态,例如输入中、运行中、已完成和详情。优先选择语义状态变化而非连续捕获;省略对查看者无帮助的加载波动。
  5. 所有帧使用相同的视口和裁剪,并按顺序命名:
    00-initial.png
    01-typed.png
    等。
  6. 将帧存储在仓库的git忽略浏览器工具截图目录下(例如
    .playwright-mcp/
    ),因为浏览器工具截图只能写入工具允许的根目录,且相对文件名会基于仓库根目录解析。需先创建帧子目录(
    mkdir -p <screenshots-dir>/gif-frames-<label>
    );写入不存在的目录会在捕获时因ENOENT错误失败。
  7. 每次截图前,等待具体的UI条件,例如唯一标签、已启用的控件、更改的文档标题或已完成的响应。要求定位器精确匹配一个元素;对于Playwright的可访问名称定位器,若需要精确匹配,需使用
    exact: true
    ,因为子文本或提示回显可能导致误匹配。不得使用固定延迟作为应用已达到该状态的证明。
  8. 完成谓词需匹配精确文本元素——例如,文本修剪后等于预期回复的元素——绝不能使用子字符串检查(如
    body.textContent.includes(...)
    ),因为用户自身提示的回显也会满足该条件。
  9. 当演示内容涉及工具调用、拒绝或恢复时,需包含一个细节或轨迹帧,展示工具标识、状态或稳定错误代码,以及下游结果。仅聊天界面的结果无法证明工具路径为何如此表现。
  10. 通过驱动一个缓慢的前台操作(例如
    sleep 15
    bash命令)并在同一个浏览器脚本调用中轮询具体的DOM标记(
    data-*
    属性)来捕获瞬态状态(加载 spinner、运行中的行),同时进行截图。跨不同工具调用轮询的状态会丢失,因为调用之间会完成状态结算。
  11. 设计提示以确保所需状态实际发生:指示模型在前台等待(若它原本会将慢命令后台运行),并给它一个结算标记(例如“回复单个单词done”)来锚定完成谓词。
  12. 不得捕获机密信息、个人数据、无关标签页或临时通知。在演示状态可见后,停止任何不必要的长时间真实服务器运行。
使用浏览器自身的截图API。当API返回图像字节时,直接保存这些字节;编码器会独立于文件扩展名检测图像内容。

Encode the GIF

编码GIF

Require
python3
,
ffmpeg
, and
ffprobe
. If either media binary is missing, report the dependency instead of installing software without authorization.
Export
GIF_SKILL_DIR
as this skill's absolute directory on its own line before the python command — an inline
GIF_SKILL_DIR=... python3 "$GIF_SKILL_DIR/..."
assignment fails, because the argument expands before the assignment takes effect:
sh
export GIF_SKILL_DIR=/absolute/path/to/this/skill
python3 "$GIF_SKILL_DIR/scripts/encode_gif.py" \
  /absolute/path/to/frames \
  /absolute/path/to/demo.gif \
  --durations 1.5,1.5,1.5,3.5 \
  --fps 10 \
  --max-width 1200 \
  --colors 128
One duration applies to every frame; otherwise provide one comma-separated positive duration per frame, holding the final settled state longest. The encoder rejects fewer than two frames, mismatched dimensions or durations, invalid limits, accidental overwrite, unexpected duration, and output above
--max-bytes
.
For a large artifact, reduce
--max-width
first, then
--colors
or
--fps
; retain readable text and the final state long enough to inspect. Use
--force
only after resolving the exact output path.
需要
python3
ffmpeg
ffprobe
。若缺少任一媒体二进制文件,需报告依赖情况,而非未经授权安装软件。
在执行python命令前,单独一行导出
GIF_SKILL_DIR
为本技能的绝对目录——内联的
GIF_SKILL_DIR=... python3 "$GIF_SKILL_DIR/..."
赋值会失败,因为参数会在赋值生效前展开:
sh
export GIF_SKILL_DIR=/absolute/path/to/this/skill
python3 "$GIF_SKILL_DIR/scripts/encode_gif.py" \
  /absolute/path/to/frames \
  /absolute/path/to/demo.gif \
  --durations 1.5,1.5,1.5,3.5 \
  --fps 10 \
  --max-width 1200 \
  --colors 128
若仅提供一个时长,则该时长适用于所有帧;否则为每个帧提供一个逗号分隔的正时长,最终稳定状态的时长应最长。编码器会拒绝少于两帧、尺寸或时长不匹配、限制无效、意外覆盖、时长异常以及输出超过
--max-bytes
的情况。
对于较大的文件,首先减小
--max-width
,然后调整
--colors
--fps
;需保留可读文本,并确保最终状态的展示时长足够检查。仅在确定输出路径后使用
--force

Verify the artifact

验证产物

  1. Read the encoder's JSON summary and confirm the output path, source and encoded frame counts, dimensions, duration, and byte size.
  2. Visually read the encoded GIF itself, not only the source frames. Confirm that the transition is legible, the last state is held long enough, and no sensitive content appears. If the viewer renders only the first frame, decode representative frames from the encoded GIF with
    ffmpeg
    and inspect those; the pre-encode screenshots do not prove the encoded order, palette, or final hold.
  3. Run
    git status --short
    and confirm frames and the artifact landed only under ignored paths.
  4. Return the absolute GIF path, render it when the client supports local media, and state whether the recording used a real server, fixture, or another transport. When the task does not include attaching the GIF to a pull request, stop here.
  1. 读取编码器的JSON摘要,确认输出路径、源帧和编码帧数量、尺寸、时长和字节大小。
  2. 直观检查编码后的GIF本身,而非仅源帧。确认过渡清晰、最后一个状态展示时长足够,且无敏感内容。若查看者仅渲染第一帧,需使用
    ffmpeg
    从编码后的GIF中解码代表性帧并检查;编码前的截图无法证明编码顺序、调色板或最终展示时长。
  3. 运行
    git status --short
    ,确认帧和产物仅位于忽略路径下。
  4. 返回GIF的绝对路径,若客户端支持本地媒体则渲染它,并说明录制使用的是真实服务器、固定数据还是其他传输方式。若任务不要求将GIF附加到拉取请求,在此步骤停止。

Publish to an assets branch

发布到资产分支

Perform this step only when the task includes attaching the GIF to a pull request.
Never commit a GIF to the pull request's own branch or any branch that merges into a long-lived branch: binary media committed there bloats the repository history for every future clone. GIFs live on a dedicated orphan assets branch — a branch with no parent commit and nothing but media — and one assets branch serves a whole pull request series (named
<series>-assets
; list existing ones with
git ls-remote --heads origin '*assets*'
).
Before either workflow below pushes, verify that the assets branch contains media only and that the staged GIF's checksum matches the verified local artifact.
For an existing assets branch, work in a shallow single-branch scratch clone so the publication cannot touch your working tree:
sh
git clone --branch <assets-branch> --single-branch --depth 1 <repo-url> /tmp/assets-checkout
cp /absolute/path/to/demo.gif /tmp/assets-checkout/<name>.gif
cd /tmp/assets-checkout
git add <name>.gif
git commit -m "assets: <what it shows> gif (#<pr>)"
git push origin <assets-branch>
For a new series, make a fresh shallow scratch clone (
git clone --depth 1 <repo-url> /tmp/assets-checkout
), create the orphan branch with
git switch --orphan <assets-branch>
, then add the GIF, commit, and push the same way.
After pushing, use authenticated GitHub API or raw requests to confirm the remote path, byte size, checksum,
200
response, and
image/gif
content type. An anonymous
404
does not disprove a private-repository asset; authenticate the verification instead. This proves the repository-member review path, not public availability.
Immediately before editing the pull-request body, re-read its live head and compare it with the commit recorded next to the GIF. Stop and re-record when it moved. After the edit, re-read the live head and require it to remain at that recorded commit. Separately, render the body through GitHub's Markdown API and confirm that the expected
<img>
is present.
Embed the GIF in the pull request body with the raw blob URL; the
?raw=true
suffix is required, because the plain blob URL renders GitHub's file page instead of the image:
markdown
![<alt text>](https://github.com/<owner>/<repo>/blob/<assets-branch>/<name>.gif?raw=true)
Never delete or rewrite an assets branch, and never force-push it: merged pull request bodies reference its URLs forever. Append new commits only.
仅当任务要求将GIF附加到拉取请求时执行此步骤。
绝不要将GIF提交到拉取请求自身的分支或任何合并到长期分支的分支:提交到这些分支的二进制媒体会膨胀仓库历史,影响未来所有克隆操作。GIF应存储在专用的孤立资产分支——无父提交且仅包含媒体的分支——一个资产分支可服务整个拉取请求系列(命名为
<series>-assets
;使用
git ls-remote --heads origin '*assets*'
列出现有分支)。
在以下任一工作流推送前,验证资产分支仅包含媒体,且待提交GIF的校验和与已验证的本地产物匹配。
对于现有资产分支,在浅克隆的单分支临时目录中操作,以避免发布操作影响你的工作树:
sh
git clone --branch <assets-branch> --single-branch --depth 1 <repo-url> /tmp/assets-checkout
cp /absolute/path/to/demo.gif /tmp/assets-checkout/<name>.gif
cd /tmp/assets-checkout
git add <name>.gif
git commit -m "assets: <what it shows> gif (#<pr>)"
git push origin <assets-branch>
对于新系列,创建一个全新的浅克隆临时目录(
git clone --depth 1 <repo-url> /tmp/assets-checkout
),使用
git switch --orphan <assets-branch>
创建孤立分支,然后按相同方式添加GIF、提交并推送。
推送后,使用已认证的GitHub API或原始请求确认远程路径、字节大小、校验和、
200
响应以及
image/gif
内容类型。匿名请求返回的
404
无法证明私有仓库资产不存在;需通过认证进行验证。这证明的是仓库成员的评审路径,而非公开可用性。
在编辑拉取请求正文前,立即读取其当前头部,并与GIF旁记录的提交进行比较。若头部已变更,停止操作并重新录制。编辑完成后,再次读取当前头部,要求其保持在记录的提交。另外,通过GitHub的Markdown API渲染正文,确认预期的
<img>
标签存在。
使用原始Blob URL将GIF嵌入拉取请求正文;必须添加
?raw=true
后缀,因为普通Blob URL会渲染GitHub的文件页面而非图像:
markdown
![<alt text>](https://github.com/<owner>/<repo>/blob/<assets-branch>/<name>.gif?raw=true)
绝不要删除或重写资产分支,也不要强制推送:已合并的拉取请求正文会永久引用其URL。仅允许追加新提交。