录制与渲染¶
TermCap 将终端媒体处理拆成三层:PTY 录制、终端状态重建、媒体导出。每一层都有明确的数据契约,便于单独验收和复用。
-
Capture
PTY 输出 → asciicast v2
.cast -
Render
CAST events →
pyte.Screen→ SVG 帧 -
Export
SVG 时间轴 → Chrome 确定性采样 → GIF
PTY → CAST¶
termcap record 使用 pty.fork() 启动子进程,设置终端 geometry,并将输出事件写成 asciicast v2:
录制器会:
- 使用增量 UTF-8 decoder,避免多字节字符跨 chunk 时损坏;
- 保留 ANSI 控制序列、颜色和光标移动;
- stdin EOF 后停止轮询 stdin,但继续读取 PTY;
- 子进程退出后 drain PTY 到 EOF/EIO,避免丢失最后一段输出;
- 子进程 exit code 通过 recorder 返回给
termcap exec,因此包装录制不会隐藏命令失败。
termcap exec 复用同一层录制能力,但入口从“录制文件优先”改成“执行命令优先”:
这会按顺序完成:运行原命令、写出 .cast、按需渲染 SVG/GIF、返回原命令 exit code。多步骤和间隔由外层脚本表达,例如 bash scripts/workflow.sh;TermCap 负责忠实记录 PTY,不额外声明进程隔离。
执行前默认环境可由 ChatEnv 管理:TERMCAP_EXEC_ENV_SCRIPTS 是需要 source 的脚本列表(POSIX 下用 : 分隔)。如果没有显式配置,TermCap 还会尝试 ChatArch 约定入口 $CHATARCH_HOME/config/termcap/exec-env.sh 与 exec-env.d/*.sh。
CAST → SVG¶
渲染器把 events 输入 pyte.Stream,在每个有效时间点保存终端 screen buffer。SVG 中的稳定 DOM 契约包括:
动画使用 @keyframes roll 和 steps(1,end),因此终端帧之间不会出现平滑滚动产生的半帧。
时间控制¶
--min-duration:合并过密输出,避免无意义微帧;--max-duration:限制长时间 idle;--loop-delay:最后一帧到下一次循环之间的停留时间。
CAST → GIF¶
TermCap 先在临时目录生成 SVG,再使用同一 SVG→GIF 后端导出。中间 SVG 不写入当前仓库。
SVG → GIF¶
对 TermCap/termtosvg 风格 SVG,转换器会:
- 读取
--animation-duration; - 提取
translateY(...)离散关键帧百分比; - 在每个关键帧边界之后确定性冻结动画;
- 等待
document.fonts.ready和两个 animation frame; - 动态增大 Chrome outer window,直到
innerWidth/innerHeight能容纳#terminal; - 截图后校验像素尺寸与 SVG 期望尺寸一致;
- 按 GIF 的 10ms 精度量化每帧时长,并补偿累计误差。
普通 SVG 没有可识别的离散关键帧时,才回退到 --fps 均匀采样。
避免裁切和空白¶
底部或右侧被裁掉¶
Selenium 的 set_window_size() 设置 outer window,并不等于网页 viewport。TermCap 会读取 window.innerWidth/innerHeight 并动态补足差值;如果仍然小于目标 SVG,转换会失败而不是静默写出残缺 GIF。
终端内部空白较多¶
这是录制 geometry 的一部分,不是浏览器截图边框。短命令演示应使用更紧凑的尺寸:
TermCap 不默认按最后一帧内容自动裁行,因为早期帧或 vim、htop 等全屏程序可能使用完整终端区域。
黑帧或字体错位¶
转换器不会仅依赖固定 sleep;它会等待字体和双 RAF。若浏览器截图仍小于目标尺寸,会立即报错。
验收建议¶
至少覆盖:
- 短命令与交互式 shell;
- ANSI 颜色和光标移动;
- 中文/宽字符;
- 回车原地更新;
- CAST→SVG、CAST→GIF、SVG→GIF;
- GIF 尺寸等于 SVG viewBox,帧总时长接近 SVG 时间轴。