@gitruck/cli 1.3.6 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/AGENT.md +23 -0
  2. package/CHANGELOG.md +32 -0
  3. package/README.en.md +1 -0
  4. package/README.md +1 -0
  5. package/assets/composite-kit/kit.css +2 -2
  6. package/assets/composite-kit/kit.js +2 -0
  7. package/assets/composite-kit/stage.html +14 -2
  8. package/assets/readme/wordmark.svg +6 -6
  9. package/contracts/README.md +16 -16
  10. package/contracts/gsap-emit-v1.md +163 -163
  11. package/contracts/handoff-contracts.json +10 -10
  12. package/dist/index.js +352 -313
  13. package/docs/development.en.md +47 -47
  14. package/docs/development.md +47 -47
  15. package/docs/reference.en.md +71 -13
  16. package/docs/reference.md +74 -15
  17. package/package.json +2 -2
  18. package/skills/gtrk-ai-drama/SKILL.md +10 -10
  19. package/skills/gtrk-ai-drama/references/sparse-key-scenes.md +38 -38
  20. package/skills/gtrk-ai-drama/scripts/validate-storyboard.mjs +119 -119
  21. package/skills/gtrk-composite/SKILL.md +28 -5
  22. package/skills/gtrk-cover/SKILL.md +3 -3
  23. package/skills/gtrk-cover/references/layout-recipes.md +337 -337
  24. package/skills/gtrk-cover/references/prompt-templates.md +119 -119
  25. package/skills/gtrk-food-recap/SKILL.md +2 -2
  26. package/skills/gtrk-live-slicing/SKILL.md +10 -4
  27. package/skills/gtrk-long2short/SKILL.md +3 -0
  28. package/skills/gtrk-matrix/SKILL.md +5 -0
  29. package/skills/gtrk-mg/SKILL.md +398 -391
  30. package/skills/gtrk-music-visualizer/SKILL.md +3 -3
  31. package/skills/gtrk-narration/SKILL.md +6 -2
  32. package/skills/gtrk-ref-reading/SKILL.md +145 -0
  33. package/skills/gtrk-ref-reading/references/close-reading-checklist.md +47 -0
  34. package/skills/gtrk-ref-reading/references/landing-templates.md +95 -0
  35. package/skills/gtrk-splitter/SKILL.md +298 -298
  36. package/skills/gtrk-splitter/references/example-visual-split.json +645 -645
  37. package/skills/gtrk-splitter/references/example-visual-split.md +439 -439
  38. package/skills/gtrk-splitter/references/field-schema.md +171 -171
  39. package/skills/gtrk-style-maker/SKILL.md +7 -0
  40. package/skills/gtrk-style-maker/references/ammo.md +16 -16
  41. package/skills/gtrk-style-maker/references/contracts-ref.md +9 -9
  42. package/skills/gtrk-style-maker/references/seeds/README.md +4 -4
  43. package/skills/gtrk-style-maker/references/seeds/seed-ecom-home-goods.md +37 -37
  44. package/skills/gtrk-style-maker/references/seeds/seed-psych-humanities.md +35 -35
  45. package/skills/gtrk-talking-head/SKILL.md +11 -9
  46. package/skills/gtrk-tools/SKILL.md +1 -1
  47. package/skills/gtrk-transcript/SKILL.md +4 -4
  48. package/skills/gtrk-transcript/agents/openai.yaml +4 -4
  49. package/skills/gtrk-travel-recap/SKILL.md +8 -0
  50. package/skills/gtrk-vlog-docu/SKILL.md +8 -0
  51. package/skills/gtrk-voiceover/SKILL.md +7 -2
package/AGENT.md CHANGED
@@ -312,6 +312,27 @@ gtrk transcript "D:/素材/采访视频.mp4" --json
312
312
  - 写回时只改 `## 总结` 到 `## 文字记录` 之间,保留后两段原样;建议 3–7 条要点,覆盖主题、关键论点/事实和结论,不补原文没有的信息。
313
313
  - `output` 就是最终 Markdown 的绝对路径;确认文件存在、三段标题齐全且待总结标记已消失后,才能回给用户。ASR 实时价格运行前从官网价格表查询,严禁引用记忆价格。
314
314
 
315
+ ### 2.3.1 参考片精读:`gtrk media`(看片取证 · 纯本地零计费)
316
+
317
+ 这一能力由 `gtrk-ref-reading` Skill 驱动。用户说「拆解这条视频 / 精读参考片 / 学一下这个博主的包装 / 把 XX 的风格蒸馏出来」时,按 Skill 的工序读片:
318
+ 先 `gtrk transcript <参考片> --json --out <精读目录>/transcript.md` 取字级时码(**这一步计费,先告知**),再用 `gtrk media` 按时码 / 台词看片(**纯本地、零计费、不联网**),
319
+ 结论落进精读目录的 `ANALYSIS.md`(整片模型)/ `TIMELINE.md`(时码细节)/ `PROGRESS.md`(接力)。
320
+
321
+ ```bash
322
+ gtrk media lines <精读目录>/transcript.json --find "买不动" # 台词出现在哪、几处
323
+ gtrk media tiles <参考片> --around "买不动" --occurrence 1 --every 0.5 --transcript <精读目录>/transcript.json -o <精读目录>/evidence/stamp
324
+ gtrk media tiles <参考片> --around "买不动" --occurrence 1 --every-frame --cols 6 --rows 4 --transcript <稿> -o <精读目录>/evidence/motion/stamp
325
+ gtrk media boundaries <参考片> --rate 4 --threshold 0.12 --transcript <稿> -o <精读目录>/evidence/boundaries.json
326
+ ```
327
+
328
+ **agent 纪律:**
329
+
330
+ - `-o` 必填,落精读目录(缺省 `<参考片同目录>/<参考片名>-精读/evidence/<描述名>`);已存在不覆盖,`--force` 只覆盖本命令族旧产物。只读本地文件,不下载链接。
331
+ - 网格是证据不是结论:每格下方标签 = 源片秒 · 时码(· 帧号)· 句号 / 正在读的字 / 所在整句。「此刻无字在读」不等于静音。判读结论由你写进 TIMELINE,并写上证据路径。
332
+ - `boundaries` 是**视觉变化候选**,不是镜头判定。
333
+ - 台词多处出现时命令会报错列出每处——用 `--occurrence` 拍板,别改用模糊的 `--start/--end` 绕过。
334
+ - 读完要建栏目 → 交 `/gtrk-style-maker`:精读出的系统只作「作品逆向」的证据,经用户认领才成栏目维度;参考片的帧与片段不进栏目 skill 的 assets。
335
+
315
336
  ---
316
337
 
317
338
  ## 2.4 元素级编辑:`gtrk patch`(改工程唯一入口)
@@ -420,6 +441,8 @@ gtrk composite bake --project <工程> [--only cp-w03] [--fps 30] [--workers 4]
420
441
  - **出片**:`gtrk composite bake` 在本机把合成烤成不透明视频(缓存命中即复用,纯本地零计费);`gtrk render` / `gtrk project export` 会自动先烤,成功的段以烘焙视频出画,整段落在合成窗口内的 MG / OV 不再预渲、不计费。
421
442
  - 浏览器优先用钉定版本 chrome-headless-shell,没有时退回本机 Chrome / Edge(更慢)。**MUST NOT 自作主张下载**:征得用户同意后 `gtrk deps install --browser`(约 110 MB)或 `bake --install-browser`。
422
443
  - 某颗烤失败(退出码 1,回执给原因)时,出片该段按粗剪出画——照实告诉用户。
444
+ - **舞台随工程画布**:`scaffold` 按 `.gtrk` 的 `video_size` 写场景舞台,竖屏、方形与横屏同口径;场景舞台与工程画布不一致时 `build` 逐颗报错(按新画布重写,`scaffold --force` 重起骨架)。`bake --abi` 的 `stage: "canvas"` 表示能烤任意舞台尺寸。
445
+ - **成片图纸里**:口播 / 解说 / 美食 / 配音 / 旅拍 / Vlog 的快速成片缺省带这一步(「精剪包装」,开工告知一句可关),直播切片与长剪短缺省不做、开工问一次——口径正本在《成片型图纸公约》§三⁵。
423
446
 
424
447
  ---
425
448
 
package/CHANGELOG.md CHANGED
@@ -11,6 +11,38 @@
11
11
 
12
12
  ## 未发布
13
13
 
14
+ ## 1.3.8(2026-10-18)
15
+
16
+ - 新增参考片精读:`gtrk media` 看片命令族(`probe` / `lines` / `frames` / `tile` / `tiles` / `boundaries` / `cut`,纯本地、不计费)。取帧网格下方标出源片秒、时码与正在念的字;`--around` 按台词定位;`boundaries` 给视觉变化候选(机械证据,不是镜头判定)。配套随包 skill `/gtrk-ref-reading`:按「改不改变讲者的位置、大小或可见区域」把参考片的每个视觉系统归到 MG 或合成风格;`/gtrk-style-maker` 的作品逆向可接视频参考。
17
+ - 竖屏 / 方形工程也能做合成:合成舞台随工程画布。`gtrk composite scaffold` 按工程 `.gtrk` 的 `video_size` 写场景 `stage`(缺席按 1920×1080);`build` 遇场景舞台与工程画布不一致逐颗报错,不缩放了事;机检、静帧与烘焙都按舞台尺寸出,烘焙产物尺寸与缓存身份随舞台;`--abi` 增 `stage:"canvas"`。客户端对竖屏合成的导出支持随下一版客户端发出。
18
+ - 成片图纸缺省带精剪包装(合成层):口播、解说、美食、配音、旅拍、Vlog 图纸的快速成片在字幕之后、出片之前做一轮合成(auto 选窗口不超过全片 25%,逐窗口复核、构建机检至多三轮,再铺轨烘焙),开工时告知一句;直播切片开工多问一句(缺省不做),长剪短跑前问一次。
19
+ - 修:竖屏 / 方形工程的 MG 颗粒经 `gtrk render` / `gtrk project export` 预渲后被压扁(横胖竖扁、旋转被拉平)。颗粒预渲原先一律按 1280×720 开舞台,云端按「舞台 / 颗粒声明尺寸」非等比挂载;现在舞台按颗粒声明的 `data-width` / `data-height` 等比缩到短边 720(竖屏 1080×1920 → 720×1280,方形 → 720×720),`project export` 登记的颗粒 MOV 尺寸随之对齐。横屏 16:9 颗粒的舞台与缓存键不变,存量缓存照常命中;竖屏 / 方形颗粒升级后首次渲染会重烤一次(旧缓存是压扁的,不复用)。客户端导出剪映 / PR 的颗粒预渲需客户端同步跟进(fix-particle-capture-follows-declared-aspect)。
20
+ - 修:`gtrk tool image_matting` 处理带 EXIF 方向标记的手机照片(如 Orientation=6),产物转了 90°。上传前先按显示方向摆正(原图不动,摆正件按原图指纹缓存在 `~/.gitruck/audio-cache/`);无方向标记或非 JPEG 照旧原文件直传;缺 ffmpeg 等摆正失败时提示后按原像素上传(fix-image-matting-exif-orientation)。
21
+ - `gtrk-mg` skill / 参考文档改准画幅口径:颗粒画幅须等于工程画布,竖屏可用;registry 中性块仍只有 1920×1080。
22
+ - 修:竖屏 / 方形工程取文字模板(`gtrk mg fetch --source text`)恒得 1920×1080 颗粒,铺进去被拉伸。`--canvas` 原先在这条路上只被收下、不生效;现在按目标画幅编:派单模式缺省取工程 `.gtrk` 的 `video_size`(显式 `--canvas` 与工程不符当场拒,找不到工程文件时按 1920×1080 编并提示),独立模式按 `--canvas`。`gtrk mg compile` 新增 `--canvas`,重置颗粒时缺省沿用颗粒自己的画幅,`--remote` 同样把画幅交给服务端。横屏工程与不给 `--canvas` 时产物不变(fix-text-template-canvas-not-compiled)。
23
+ - 修:画幅比例不对的 MG 颗粒(如竖屏工程里声明 1920×1080 的手写颗粒或旧颗粒)照样能铺进工程,渲染时被拉伸,全程不报错。`gtrk mg --project`(铺轨,含 `--lint-only`)与 `gtrk mg lint --dispatch / --project` 现在按工程 `.gtrk` 的 `video_size` 比对颗粒声明的比例:跨比例判 `1-canvas-match` 致命、不铺;同比例不同尺寸(如 4K 横屏工程里的 1920×1080 颗粒)照常放行,比例差 7% 以内视为同比例(DCI 4K 4096×2160 这类 17:9 工程里的 16:9 颗粒放行,接受 6.7% 横向拉伸)。工程缺 `.gtrk` 或 `video_size` 不合法时不比对并提示一句。裸 `gtrk mg lint <file>` 不变(fix-mg-lay-lint-reads-project-canvas)。
24
+ - 修:竖屏 / 方形工程派单取 registry 中性块(`gtrk mg fetch --pick <块> --slot <beat> --project <dir>`)照样下载并落一颗 1920×1080 颗粒,要到 lint / 铺轨才被拦。现在按工程 `.gtrk` 的 `video_size` 比例判(同上,容差 7%):比例不是 16:9 在下载前就拒、不落盘,报错指路 `--source text`(文字模板按工程画幅编)或栏目 skill。横屏 16:9 工程(含 4K)与 DCI 17:9 工程照取、产物不变;独立模式(`--as`)不读工程;找不到 `.gtrk` 或 `video_size` 不合法时不比对、照取并提示一句(fix-mg-fetch-registry-reads-project-canvas)。
25
+ - 修:`gtrk composite bake --progress jsonl` 的取消恰好落在工作页打开出片台时,要约 24 秒才报 `cancelled` 并退出(浏览器已被结束,打开页面却仍按每轮 8 秒等到期限);现在浏览器一退出即放弃,取消照常几秒内收尾。开烤前建工作目录那一段也有进度行了(原先这段可能超过 1 秒没有输出);装渲染组件期间关闭 stdin,装完即按取消收尾(原先会漏掉、照常烤完)(add-composite-bake)。
26
+ - 修:`gtrk matrix` 铺轨三处坑:
27
+ - `clip_id` 带来源层(`<beat>-<layer>-broll/bedfill/gapfill-…`),通用层与本地层同 beat 同序号不再撞名,`gtrk patch --clip` 可直接寻址;老工程重铺不受影响。
28
+ - 口播(fill 形态)工程里片段间 1–2 帧的缝、锚点前短于 1.2 秒的空档,在素材余量内并入相邻片段,不再闪黑;余量不够仍补黑,并逐段告警。
29
+ - `gtrk matrix --online` 收尾零候选时不再覆盖盘上已有的 plan(结果带 `plan_kept`)。
30
+ - 合成层在英文口播片上更好用:
31
+ - 场景锚点的关键词照句子原文写即可:匹配时关键词与句子都忽略空格、标点与大小写,英文按整词(`"cold brew"` 直接写,`tea` 不再先命中 `teas`);找不到时报错打印句子原文。
32
+ - `gtrk composite plan` 的「建议画面」识别英文的对照(unlike / but / instead of …)、列举(逗号列举,或转写不带标点时的一长串并列)、数字与分点;其他语种不做文字信号,审稿稿里写明。中文判定不变。
33
+ - `gtrk composite lay` 的回执改为当前出片口径:客户端(1.2.13 起)原生导出与剪映 / Pr 导出、`gtrk render`、`gtrk project export` 都先在本机烤合成,失败段按粗剪出画。
34
+ - `/gtrk-composite` 场景速查写明:工具箱只给舞台直接子元素定位,容器里的元素自己写 `position:absolute` 或加 `.abs`。
35
+ - `gtrk composite lint` 新增 `unpositioned-offset`:容器里的元素写了 `left` / `top` 偏移、却没写 `position:absolute` 也没加 `.abs`(偏移不生效、挤成一列)时报出来,点名元素与容器。只在 lint 时检查,颗粒与烘焙缓存不变、已烤的颗粒不用重烤。
36
+
37
+ ## 1.3.7(2026-10-18)
38
+
39
+ - 新增匿名使用统计:每条命令结束时记下命令名(注册名,不含任何参数)、成败、失败时的错误码或分类、耗时区间与调用方(终端或 agent 名称),用于了解命令热度、失败率与版本铺开情况。
40
+ - 不发参数、文件名、路径、内容、API Key、错误原文,也不带设备标识。
41
+ - 首次配置时(紧跟崩溃上报那一行)告知一次,默认开;`gtrk init --no-usage-stats`、`GITRUCK_USAGE_STATS=0` 或 `DO_NOT_TRACK=1` 可关,`gtrk doctor` 显示当前状态。
42
+ - 源码运行、单测与 `CI` 环境不发送。
43
+ - 只往 `~/.gitruck/usage-queue.jsonl` 追加一行,由后台进程发送,不拖慢命令;断网时留在本机最多 7 天,联网后补发。
44
+ - 隐私政策已同步更新(3.6「使用统计」);说明见 `docs/reference.md`「使用统计与关闭方式」。
45
+
14
46
  ## 1.3.6(2026-10-18)
15
47
 
16
48
  - `gtrk composite bake` 认合成槽位的用户覆盖 `crop` / `box` / `slip_ms`:校验与输入快照和服务端参考实现同口径,出片台按「颗粒几何 → box → crop → 适配 / 构图 → 形状圆角」绘制,ref 槽取帧加 `slip_ms`;`--abi` 新增 `overrides` 能力位。**客户端 1.2.13 起会写这些覆盖**,旧版 CLI 遇到带覆盖的合成按粗剪出片并提示升级。
package/README.en.md CHANGED
@@ -194,6 +194,7 @@ gtrk ai-drama lay --project ./my-video --desk-project <project-id>
194
194
  | Matting, denoising, vocal separation, upscaling, interpolation, image motion or video dubbing | [Toolbox](https://hocassian.feishu.cn/wiki/Uh19wAphailtSqkt6jsclW0Rnie) · `gtrk tool list` |
195
195
  | Covers in multiple aspect ratios | [Cover workflow](https://hocassian.feishu.cn/wiki/AD5bwUAHris75vkWl4DcsvPJntb) |
196
196
  | A spectrum video for a song | [Music visualizer Skill](skills/gtrk-music-visualizer/SKILL.md) |
197
+ | Break down a reference video's packaging and pacing | [Reference-reading Skill](skills/gtrk-ref-reading/SKILL.md) · `gtrk media` |
197
198
  | A visual style for your show | [Style-maker Skill](skills/gtrk-style-maker/SKILL.md) |
198
199
  | Third-party animation, typography or creative Skills | [Skill map](https://hocassian.feishu.cn/wiki/SxRtwJ0G7inTbQkwckFcZmClnrd) |
199
200
 
package/README.md CHANGED
@@ -194,6 +194,7 @@ gtrk ai-drama lay --project ./my-video --desk-project <project-id>
194
194
  | 抠像、降噪、人声分离、超分、补帧、图片运镜、视频译制 | [单点工具箱](https://hocassian.feishu.cn/wiki/Uh19wAphailtSqkt6jsclW0Rnie) · `gtrk tool list` |
195
195
  | 做一套多尺寸封面 | [封面工作流](https://hocassian.feishu.cn/wiki/AD5bwUAHris75vkWl4DcsvPJntb) |
196
196
  | 把一首歌做成频谱视频 | [音乐可视化 Skill](skills/gtrk-music-visualizer/SKILL.md) |
197
+ | 拆解一条参考视频的包装与节奏 | [参考片精读 Skill](skills/gtrk-ref-reading/SKILL.md) · `gtrk media` |
197
198
  | 给自己的栏目建立视觉风格 | [风格体系 Skill](skills/gtrk-style-maker/SKILL.md) |
198
199
  | 接入第三方动画、排版或创作 Skills | [Skill 地图](https://hocassian.feishu.cn/wiki/SxRtwJ0G7inTbQkwckFcZmClnrd) |
199
200
 
@@ -4,7 +4,7 @@
4
4
  * --accent 强调 / --accent2 / --accent3 辅助色 / --alert 警示 / --font 正文字体 / --font-serif 标题字体。 */
5
5
  html, body { margin: 0; height: 100%; overflow: hidden; background: transparent; }
6
6
  #stage {
7
- position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; transform-origin: 0 0; overflow: hidden;
7
+ position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; transform-origin: 0 0; overflow: hidden; /* 实际尺寸由 kit.js 按舞台写回 */
8
8
  --bg: #F4F4F2; --bg2: #E6E6E2; --ink: #1D1D1F; --muted: #6B6B70; --line: #CFCFCB;
9
9
  --accent: #2F6FDE; --accent2: #2E8B57; --accent3: #4A5568; --alert: #D14343;
10
10
  --font: "Noto Sans SC", "Source Han Sans CN", "Microsoft YaHei", "PingFang SC", sans-serif;
@@ -14,7 +14,7 @@ html, body { margin: 0; height: 100%; overflow: hidden; background: transparent;
14
14
  #stage > *, #stage .abs { position: absolute; box-sizing: border-box; }
15
15
  [data-slot] { visibility: hidden !important; }
16
16
  .serif { font-family: var(--font-serif); }
17
- .bed { left: 0; top: 0; width: 1920px; height: 1080px; background: var(--bg); }
17
+ .bed { left: 0; top: 0; width: 100%; height: 100%; background: var(--bg); }
18
18
  .bed.dark { background: radial-gradient(ellipse at 30% 35%, #2A2A2E 0%, #141416 70%); }
19
19
  /* 槽位装饰:阴影在下(back),描边在上(front)。描边只给最上层的画面——下层画面的 front 描边会压到上层画面上 */
20
20
  .shadow { border-radius: 18px; box-shadow: 0 18px 50px rgba(0, 0, 0, .22), 0 3px 10px rgba(0, 0, 0, .12); background: #000; }
@@ -16,6 +16,8 @@
16
16
  const META = JSON.parse(document.querySelector("script[data-gtrk-composite]").textContent);
17
17
  const W = META.stage.width, H = META.stage.height;
18
18
  const stage = document.getElementById("stage");
19
+ // 舞台随工程画布(竖屏 / 方形同口径):舞台根按元数据块定尺寸,满幅元素(.bed 等)按百分比随它
20
+ stage.style.width = W + "px"; stage.style.height = H + "px";
19
21
  let FPS = (META.resolved && META.resolved.video_rate) || 60;
20
22
  let ANCHORS = (META.resolved && META.resolved.anchors_f) || {};
21
23
  let LAYER = "all";
@@ -29,10 +29,21 @@
29
29
  * 每个槽位一个 <video>(同源取帧不共用解码,见客户端 R15);图片素材用 <img>。
30
30
  *
31
31
  * 用法:stageInit({ fps, entries, materials, images?, segs?, tracks }) → renderAt(时间线秒)。全部经 file:// 读本地文件。
32
+ * 舞台随工程画布(adjust-composite-stage-follows-canvas):每颗 `entries[i].stage = [宽, 高]`(缺省 1920×1080),
33
+ * 挂载时页面、两层 iframe 与槽位画布都改成这个尺寸;调用方同时把浏览器视口设成它(截图即这个尺寸)。
32
34
  * `window.SETTLE`:画完后等多少毫秒(缺省 60,lint 静帧用);烘焙置 -1(截帧由 beginFrame / 截图本身推进合成器)。 */
33
35
  let FPS = 60, entries = [], matUrl = new Map(), imageIds = new Set(), segsOf = new Map(), byClip = new Map();
34
36
  const back = document.getElementById("back"), front = document.getElementById("front");
35
37
  const canvas = document.getElementById("slots"), ctx = canvas.getContext("2d");
38
+ let SW = 1920, SH = 1080;
39
+ /** 页面、两层图形 iframe(颗粒按 iframe 宽度缩放舞台)与槽位画布改成舞台尺寸。 */
40
+ function sizeStage(stage) {
41
+ const [w, h] = Array.isArray(stage) ? stage : [1920, 1080];
42
+ if (w === SW && h === SH) return;
43
+ SW = w; SH = h;
44
+ for (const el of [document.documentElement, document.body, back, canvas, front]) { el.style.width = w + "px"; el.style.height = h + "px"; }
45
+ canvas.width = w; canvas.height = h;
46
+ }
36
47
 
37
48
  window.stageInit = (data) => {
38
49
  FPS = data.fps; entries = data.entries;
@@ -66,6 +77,7 @@ const waitFor = (key, ms = 15000) => new Promise((res, rej) => {
66
77
  const load = (frame, src) => new Promise((res) => { frame.onload = () => res(); frame.src = src; });
67
78
  async function mount(entry) {
68
79
  if (current && current.entry.id === entry.id) return current.ready;
80
+ sizeStage(entry.stage);
69
81
  const ready = (async () => {
70
82
  await Promise.all([load(back, entry.url), load(front, entry.url)]);
71
83
  for (const [frame, layer] of [[back, "back"], [front, "front"]]) {
@@ -170,7 +182,7 @@ function applySlotOverrides(g, b) {
170
182
  let out = g;
171
183
  if (b.box) {
172
184
  const s = b.box.scale_pm / 1000, w = g.w * s, h = g.h * s;
173
- const cx = g.x + g.w / 2 + (b.box.dx_pm / 1000) * 1920, cy = g.y + g.h / 2 + (b.box.dy_pm / 1000) * 1080;
185
+ const cx = g.x + g.w / 2 + (b.box.dx_pm / 1000) * SW, cy = g.y + g.h / 2 + (b.box.dy_pm / 1000) * SH;
174
186
  out = { ...out, x: cx - w / 2, y: cy - h / 2, w, h, radius: (g.radius || 0) * s };
175
187
  }
176
188
  if (b.crop) out = { ...out, crop: [b.crop.t_pm / 1000, b.crop.r_pm / 1000, b.crop.b_pm / 1000, b.crop.l_pm / 1000] };
@@ -260,7 +272,7 @@ window.renderAt = async (T) => {
260
272
  return { g, b, v, k: ver.k, tag: at.tag };
261
273
  });
262
274
  const ready = (await Promise.all(jobs)).filter(Boolean);
263
- ctx.clearRect(0, 0, 1920, 1080);
275
+ ctx.clearRect(0, 0, SW, SH);
264
276
  for (const { g: g0, b, v, k, tag, img } of ready) {
265
277
  const g = applySlotOverrides(g0, b);
266
278
  // 中间文件底部的帧号条不进画面:源高度按去掉条带算
@@ -1,6 +1,6 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="560" height="170" viewBox="0 0 560 170" role="img" aria-labelledby="title">
2
- <title id="title">gtrk CLI</title>
3
- <text x="84" y="128" fill="#f43b48" font-family="Segoe UI, Helvetica, Arial, sans-serif" font-size="150" font-weight="750" letter-spacing="-9">gtrk</text>
4
- <path d="M403 63v61" stroke="#f43b48" stroke-opacity=".3" stroke-width="2"/>
5
- <text x="425" y="106" fill="#888888" font-family="Consolas, monospace" font-size="28" letter-spacing="3">CLI</text>
6
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="560" height="170" viewBox="0 0 560 170" role="img" aria-labelledby="title">
2
+ <title id="title">gtrk CLI</title>
3
+ <text x="84" y="128" fill="#f43b48" font-family="Segoe UI, Helvetica, Arial, sans-serif" font-size="150" font-weight="750" letter-spacing="-9">gtrk</text>
4
+ <path d="M403 63v61" stroke="#f43b48" stroke-opacity=".3" stroke-width="2"/>
5
+ <text x="425" y="106" fill="#888888" font-family="Consolas, monospace" font-size="28" letter-spacing="3">CLI</text>
6
+ </svg>
@@ -1,16 +1,16 @@
1
- # 框架契约库(handoff contracts)
2
-
3
- 同合云管线的 handoff 契约**正本**,随 `@gitruck/cli` npm 包分发。创作 skill 以「契约名 + 版本」引用(如 `gsap-emit v1`);`handoff-contracts.json` 是「handoff 类型 → 契约」映射表,消费方查表、不硬编码。
4
-
5
- ## 收录双轴边界(handoff-contract-library spec,扩项须走 change 过审)
6
-
7
- - **轴一(管线性,审美零预设)**:契约只约束**机器可判定的管线消费属性**(封装/注册/确定性/依赖可达/产物格式)。**不得**规定颜色、字体、构图、红点数量等任何视觉取值或栏目词表词条;凡涉视觉取值处一律参数化/中性占位。「全片最多一个红点」这类审美铁律属栏目 skill 侧,**不入库**。
8
- - **轴二(脱敏)**:只收**自有管线**的合规契约——渲染引擎合规、`.gtrk` 契约、派单格式(封闭列举)。外部供应商/外部平台适配内容(AI 视频平台差异、文生图平台参数等)**永不收录**,那属于用户自己的 skill。
9
-
10
- ## 现有契约
11
-
12
- | 契约 | 版本 | 适用 handoff | 文档 |
13
- |---|---|---|---|
14
- | gsap-emit(HTML 动画颗粒逐帧 seek 合规) | v1 | MG(去品牌化前 RRV_MG,消费方查表前归一) | [gsap-emit-v1.md](gsap-emit-v1.md) |
15
-
16
- 维护者证据附录:[`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)。该文件不属于 agent 执行时的必读契约。
1
+ # 框架契约库(handoff contracts)
2
+
3
+ 同合云管线的 handoff 契约**正本**,随 `@gitruck/cli` npm 包分发。创作 skill 以「契约名 + 版本」引用(如 `gsap-emit v1`);`handoff-contracts.json` 是「handoff 类型 → 契约」映射表,消费方查表、不硬编码。
4
+
5
+ ## 收录双轴边界(handoff-contract-library spec,扩项须走 change 过审)
6
+
7
+ - **轴一(管线性,审美零预设)**:契约只约束**机器可判定的管线消费属性**(封装/注册/确定性/依赖可达/产物格式)。**不得**规定颜色、字体、构图、红点数量等任何视觉取值或栏目词表词条;凡涉视觉取值处一律参数化/中性占位。「全片最多一个红点」这类审美铁律属栏目 skill 侧,**不入库**。
8
+ - **轴二(脱敏)**:只收**自有管线**的合规契约——渲染引擎合规、`.gtrk` 契约、派单格式(封闭列举)。外部供应商/外部平台适配内容(AI 视频平台差异、文生图平台参数等)**永不收录**,那属于用户自己的 skill。
9
+
10
+ ## 现有契约
11
+
12
+ | 契约 | 版本 | 适用 handoff | 文档 |
13
+ |---|---|---|---|
14
+ | gsap-emit(HTML 动画颗粒逐帧 seek 合规) | v1 | MG(去品牌化前 RRV_MG,消费方查表前归一) | [gsap-emit-v1.md](gsap-emit-v1.md) |
15
+
16
+ 维护者证据附录:[`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)。该文件不属于 agent 执行时的必读契约。
@@ -1,163 +1,163 @@
1
- # GSAP-emit 契约 v1 · HTML 动画颗粒的逐帧 seek 渲染合规
2
-
3
- > **契约版本**:gsap-emit v1。产 HTML 动画颗粒、经同合云 `html_animate_render` 逐帧 seek 合成的 skill/工具,其产物 MUST 满足本契约。
4
- > **边界**:本契约只约束机器可判定的管线消费属性(封装/注册/确定性/自包含/体积上限/依赖可达/禁 `var()`/字体名命中注册表)。颜色、字体取值、构图和节奏由调用方栏目规则决定。
5
-
6
- ## 原理(为什么不能用 CSS animation)
7
-
8
- 渲染引擎逐帧渲染时,靠调用每个子合成在 `window.__timelines` 注册的 GSAP 时间线的 `.seek(t)` 把画面定格到第 t 秒。GSAP `paused` 时间线 = 可被外部 seek 的虚拟时钟 → 逐帧正确;纯 CSS `animation-delay` 动画不在 `window.__timelines` 里,引擎 seek 不到 → 画面冻结。
9
-
10
- ## 回调与 seek 语义
11
-
12
- > 颗粒可以用时间线回调(`onUpdate` 等)驱动画面;保证定帧时回调可达是渲染引擎侧的义务,不是颗粒作者的义务。本节是该主题的唯一口径来源。
13
-
14
- - **引擎侧(MUST)**:定帧时必须保证 GSAP 时间线回调可达,并定位到包含 repeat / yoyo 的完整时间轴。优先使用 `seek(t, false)` 或 `totalTime(t, false)`;`time(t)` / `progress(p)` 只适用于无 repeat 的根时间线,不能视为完整时间轴定位的通用等价替代。不能使用默认会抑制回调的裸 `seek(t)`。
15
- - **颗粒侧(MUST NOT)**:颗粒可以用 `onUpdate` / `onStart` / `onComplete` / `onRepeat` 写 DOM 或属性;不得运行时覆写 `tl.seek`,也不得替换 `window.__timelines[…]`。新颗粒不得添加 seek 垫片;已有垫片可在重渲复验后择期清理。
16
- - **幂等回调**:逐帧 scrub 会反复触发回调,回调必须从当前补间状态重算画面,不得累加计数、追加数组或执行一次性 DOM 插入。
17
- - **lint 哨兵**:`x-callback-driven`、`x-engine-api-override`、`x-raf-interval` 均为非致命提醒;它们用于提示引擎接口风险,不要求作者绕过契约自行添加垫片。
18
-
19
- 实验和版本证据见 [`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)「回调与 seek 语义」节。
20
-
21
- ## 八条铁律(违反任一条 → 整片渲染失败 / 颗粒冻结 / 全黑 / 坑位内突兀消失 / 画面被切成横带)
22
-
23
- 1. **`<template>` 包裹根元素**:根元素必须位于 `<template>` 内,并带 `data-composition-id`、`data-width`、`data-height`。声明尺寸 MUST 为正整数且等于目标工程画幅;跨比例会拉伸,文字 IR 可在编译期按目标画幅缩放。
24
- 2. **GSAP `paused` 时间线 + 注册**:使用 `gsap.timeline({paused:true})`,并把时间线注册到 `window.__timelines["<id>"]`;`<id>` 必须等于根的 `data-composition-id`。
25
- 3. **确定性**:禁用 `Math.random`、`Date.now` 和无参 `new Date()`。需要随机感时使用固定种子、解析式或递归生成。
26
- 4. **自包含 + 实心底下沉子层 + 透明度显式声明**:颗粒不得依赖外部文件(脚本可由渲染管线 vendor 或可达 CDN 提供)。
27
- - 实心底 MUST 写在根下第一个全幅子层,MUST NOT 写在根元素 `style` 上。
28
- - 根元素 MUST 保持零视觉;透明 overlay MUST 在根显式写 `background:transparent`。
29
- - `gtrk mg lint` 按根与首个全幅子层的 `background` 推导 `opaque`;`4-bg-explicit` 与 `4-bg-on-root` 为非致命提示。
30
- - HTML 总长 MUST ≤ 2,000,000 个 JS UTF-16 码元,SHOULD ≤ 500,000;位图按实际显示尺寸裁剪缩放后再内嵌,不透明图优先 JPEG/WebP,需要 alpha 才用 PNG,超限则把大图放素材轨。
31
- 5. **脚本用渲染机可达的 CDN(编译期内联)**:使用 `lib.baomitu.com` GSAP 或渲染管线 vendor;不得依赖运行时相对路径。
32
- 6. **颜色/字体用字面值**:禁 CSS `var()` 自定义变量;SVG 属性同样适用。具名字体 MUST 命中渲染服务端注册表,并以 `sans-serif` / `serif` 通用族收尾;栏目主题在生成期替换字面值。
33
- 7. **占满坑位 + 终态驻留**:时间线总长 MUST ≥ 落轨 clip 的实际坑位时长(不是 `duration_hint`)。主叙事结束后定格或有限循环至坑位末尾;禁止 `repeat:-1`、整体渐隐到空或清空画面。循环次数按坑位算死:`repeat = ceil((坑位时长 − 循环起点) / 单圈时长) − 1`。
34
- 8. **重复图元合并**:同色、同 `stroke` / `fill`、整组同步驱动或静态的网格、排线、刻度、点阵 MUST 合并成单个元素;逐元素动画(stagger/逐条画入/逐个变色)可保留元素身份。该规则是作者侧规避,真实触发轴由渲染侧决定,命中批次 MUST 做真渲染抽帧验收。
35
-
36
- > **⚠️ 铁律编号的唯一定义在本文件。** 栏目 skill / 作坊引用时直接写「gsap-emit v1 铁律 N」,不得另立同号条款;栏目自身规则请使用独立编号空间。
37
-
38
- > 铁律 4 和铁律 8 的实验依据见 [`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)。正文只保留当前可执行契约。
39
-
40
- ## 渲染成本:真卷积滤镜
41
-
42
- > 本节约束逐帧成本,不改变八条正确性铁律;命中成本项时画面仍可能正确,lint 提示保持非致命、不拦铺轨。
43
-
44
- ### 射程
45
-
46
- | 进射程(真卷积) | 射程外 |
47
- |---|---|
48
- | CSS `filter` / `-webkit-filter` / `backdrop-filter` 中的非零 `blur` 或带模糊半径的 `drop-shadow` | `blur(0)`、无模糊半径的 `drop-shadow`、`filter:none`、任何 `box-shadow` |
49
- | SVG `feGaussianBlur` / `feDropShadow`(含 `filter:url(#id)` 引用) | `opacity`、`transform`、`mix-blend-mode`、`border-radius` |
50
-
51
- `box-shadow` 整族不报警;铁律 4②管根元素绘制属性的位置,本节管卷积成本,两者互不冲突。
52
-
53
- ### 条文
54
-
55
- - 真卷积每帧代价随**覆盖面积 × 模糊半径 × 帧数**增长。
56
- - MUST NOT 用时间线补间驱动 `filter` 或 SVG `stdDeviation`;需要同一叙事动作时改用 `opacity` / `transform`,或做静态两态切换。
57
- - 带静态滤镜的元素 MUST NOT 再用补间驱动 `transform`;优先拆分滤镜层与运动层,其次缩小覆盖面积,最后才考虑预烘。
58
- - `gsap.set(el, {filter: …})` 只设一次属于静态形态;`opacity` 补间不在本节射程内。
59
- - 整幅静态滤镜 SHOULD 缩小覆盖面积。预烘不是默认解法,确需预烘时 MUST 使用 RGBA PNG,并先做同颗对照;MUST NOT 用 JPEG 代替带 alpha 的颗粒素材。
60
- - 判断权在作者,契约只规定成本机制与改法,不规定视觉取值。
61
-
62
- ### 维护边界
63
-
64
- 成本项保持非致命、不拦铺轨;实际收益取决于颗粒形态,不能把单批实验数字当成通用提速承诺。调整严重度时另开 change,并先复扫现网生产语料。实验和版本证据见 [`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)。
65
-
66
- ## Alpha 交付口径
67
-
68
- > 颗粒内一律使用直通 alpha;剪映 qtrle 交付由渲染管线负责预乘。颗粒与 CLI MUST NOT 再做第二次预乘。
69
-
70
- 1. **颗粒侧(MUST)**:颜色使用 `rgba(r,g,b,a)`、`#RRGGBBAA`、`opacity` 或渐变透明值;不得为迁就 NLE 自行把半透明色改暗或回避半透明。
71
- 2. **交付面**:客户端预览、本地 webm 和云端 overlay 合成使用直通 alpha;剪映 qtrle 由管线预乘。
72
- 3. **验收(MUST)**:含软光晕、渐变透明、带 blur 的 `text-shadow` 或 `opacity<1` 图层时,剪映验收必须使用本颗粒或同类半透明颗粒,不能用实心颗粒代验。
73
- 4. **哨兵**:`gtrk mg lint` 的 `x-soft-alpha` 为非致命提示,不拦铺轨、不要求作者改写。
74
-
75
- ## 模板颗粒(ir 态)的改法
76
-
77
- > **一句话**:由 IR 编译出来的颗粒,改内容 **MUST 改 IR 再重编译**,**MUST NOT 直接改 HTML**。
78
- > 词表、不变量、编译确定性与三态判定的正本在 infra capability `text-ir-profile`
79
- > ,本节**只作导读、不复制规则**。
80
-
81
- 同合云的文字特效模板是由一份 **IR 文字子集**确定性编译出来的普通 gsap-emit v1 颗粒——
82
- 对本契约而言它和别的颗粒没有任何区别,`beat_track` 也不为它新增任何键。
83
-
84
- 区别只在**它自带出身证明**:编译产物在 `<template>` 内以
85
- `<script type="application/json" data-gtrk-ir>` 内嵌完整 IR,并在首行注释写
86
- `gtrk-ir-sha256` 与 `gtrk-html-sha256` 两个哈希。判定只看文件内容,不依赖文件名或目录。
87
-
88
- | 三态 | 含义 | 还能云端改写吗 |
89
- |---|---|---|
90
- | `ir` | 内嵌 IR 在,且声明的 html 哈希与实测一致 | 能 |
91
- | `detached` | 内嵌 IR 在但哈希对不上 ⇒ HTML 被直接改过 | 不能;可按内嵌 IR 重编回模板 |
92
- | `html` | 无内嵌 IR(绝大多数颗粒) | 不能(本来也不需要) |
93
-
94
- **手改 HTML 是单向操作**:改一个字节就从 `ir` 掉到 `detached`,`gtrk mg edit` 与客户端
95
- 属性面板都会拒绝它。颗粒本身照常能渲能铺,所以 `gtrk mg lint` 只给**非致命**的
96
- `x-ir-detached` 提醒,不拦。
97
-
98
- 两条合法改法:
99
- - **改字 / 换色 / 改字号描边阴影 / 改时长**:改 IR 后 `gtrk mg compile <ir.json>`(L0,0 积分)。
100
- - **换效果**:`gtrk mg edit <particle.html> --say "<一句话>"`(经云端模型链,按候选计费)。
101
-
102
- **内嵌的 JSON 块不参与渲染**,也不在本契约任何检查的射程内(它既没有 `src` 也不执行)。
103
-
104
- ## 颗粒骨架(中性模板)
105
-
106
- ```html
107
- <template id="p">
108
- <!-- 铁律4:根 MUST 零视觉。这里的 background 恒为 transparent(显式声明,不是省略),实心底一律下沉到下面的 .bgfill 子层 -->
109
- <div data-composition-id="<id>" data-width="1920" data-height="1080"
110
- style="position:absolute;inset:0;background:transparent;overflow:hidden;font-family:'<你的字体·须命中服务端 font_manifest.json>',sans-serif;">
111
- <!-- 铁律4①:实心底 MUST 是根下第一个全幅子层。满屏颗粒填你栏目的底色;透明叠加(overlay)颗粒把整行删掉 -->
112
- <div class="bgfill" style="position:absolute;inset:0;background:<你的底色>;z-index:0;"></div>
113
- <style> [data-composition-id="<id>"] .xxx{ … } </style> <!-- 样式用属性选择器作用域,防跨颗粒污染 -->
114
- <svg viewBox="0 0 1920 1080" preserveAspectRatio="xMidYMid meet" style="position:absolute;inset:0;width:100%;height:100%;">…</svg>
115
- <script src="https://lib.baomitu.com/gsap/3.13.0/gsap.min.js"></script>
116
- <script>(function(){
117
- var ROOT='[data-composition-id="<id>"]';
118
- /* 1) 确定性构建静态结构 */
119
- /* 2) gsap.set 初始态 */
120
- /* 3) var tl = gsap.timeline({paused:true}); … 编排 … */
121
- window.__timelines = window.__timelines || {};
122
- window.__timelines["<id>"] = tl;
123
- })();</script>
124
- </div>
125
- </template>
126
- ```
127
-
128
- > **骨架里 `.bgfill` 那一行的三个要点**:① 它 MUST 是**根下第一个**子元素(后续前景层自然压在它之上,不必给前景排 z-index);
129
- > ② 它 MUST **全幅**(`position:absolute;inset:0`,或等价的 `top/left/width/height` 铺满)——不铺满就不是「实心底」;
130
- > ③ 类名 `bgfill` 只是**惯例**,其底色写在**行内 style** 上(不靠 `<style>` 里的类规则),故不受「样式作用域」那条专属坑影响;
131
- > 换名字不违约,但 `gtrk mg lint` 认的是「根下首个全幅子层的 `background`」这个**结构**,不认类名。
132
-
133
- 缓动可用 CustomEase 精确还原你栏目自己的 cubic-bezier(缺插件时给近似回退)——bezier 数值属于栏目审美,本契约不规定。
134
-
135
- ## 确定性配方(替代 random)
136
-
137
- - 递归结构:固定角度、比例、深度参数 → 完全确定。
138
- - "噪声感":`Math.sin(i*0.18 + j*0.2)` 类解析式伪随机。
139
- - 打散:用 index 派生(如 `i*137.5°` 黄金角),不要 `Math.random()`。
140
-
141
- ## 验证(交付前必做)
142
-
143
- 墙钟截图类工具驱动不了 paused 时间线(只看到 t=0),**不能**用来验收。必须真渲染引擎 seek 验证:
144
- 1. 颗粒放进最小 composition(root `index.html` 用 `data-composition-src` 引它);
145
- 2. 走渲染管线(html_animate_render)渲染;
146
- 3. 抽不同时间点的帧**比对应当不同**(相同=冻结=铁律没守住)。客观自检:根有 `<template>`、`window.__timelines["<id>"]` 已注册且 id 匹配、无 random/Date、tl 总长 ≥ 颗粒时长、HTML ≤ 2,000,000 字符(铁律 4;`gtrk mg lint` 的 `4-html-size`)。
147
- > ⚠️ 不要用「本地等价 seek 脚本 / Node 无头模拟」替代真引擎渲染——它不经真编译+挂载,无法覆盖 var()、CDN 内联和 StaticGuard 等真引擎行为。
148
- >
149
- > ⚠️ **客户端预览对「底色 / 透明度」类问题结构性失明,MUST NOT 用作这类验收的判据**。
150
- > 客户端预览会按 clip 登记的 `opaque` 位**给颗粒根盒强行打底**(`!important`,恒胜过颗粒自身的设计期底色)——
151
- > 于是无论根 `background` 有没有被丢弃、有没有实心底子层,**预览看到的都是「按登记值应该长的样子」**。
152
- > 铁律 4 的证据锚正是靠**真渲染出片抽帧**取得的,不是靠预览。预览截图只作对照留档,不入判据。
153
-
154
- ## 专属坑
155
-
156
- - **样式作用域**:颗粒与其他颗粒/根同处一个文档,全局类会撞——用 `[data-composition-id="<id>"] .xxx` 属性选择器作用域。
157
- - **`<template>` 内的 `<script>` 默认不执行**——引擎把 template 内容克隆进文档后才执行;本地直接开浏览器不会跑,必须经引擎/player。
158
- - **transform-origin(SVG)**:缩放 `<g>` 用 GSAP `svgOrigin:"x y"`(SVG 用户坐标),别用 CSS transform-origin。
159
- - **时间线总长 ≥ 坑位时长**:颗粒 tl 总时长 ≥ 落轨 clip 时长(坑位包络),否则 seek 越界(已升格为铁律 7,含终态驻留要求)。
160
- - **整组同步驱动的重复图元必须合并**:网格、排线、刻度、点阵用单元素表达,SVG 以一条 `<path>` 的多子路径合成;逐元素动画批次豁免。详见 gsap-emit v1 铁律 8。
161
- - **真卷积滤镜别用补间驱动(也别让带滤镜的元素被 `transform` 补间驱动)、整幅静态滤镜先缩面积**:`blur()` / `drop-shadow()` / `feGaussianBlur` 按「面积 × 半径 × 帧数」增加每帧成本。预烘是次选、必须 RGBA PNG,先做同颗对照再用;`box-shadow` 整族不在射程内。详见本文件「渲染成本:真卷积滤镜」一节。
162
- - **别用 `requestAnimationFrame`/`setInterval` 驱动画面**——不被 seek,等于冻结。所有视觉变化必须挂在 tl 上。(与「回调与 seek 语义」一节同源:任何**不经 tl** 的自有时钟都不被定帧驱动;而挂在 tl 上的回调是否被触发,则由该节的引擎侧条款保证。`gtrk mg lint` 对本条给**非致命**项 `x-raf-interval`——静态正则分不清「驱动画面」与其它用途,故只提醒不拦。)
163
- - **自包含 ≠ 无限内嵌**:原图整张 base64 塞进颗粒会撞客户端 2,000,000 字符硬限。位图先按实际显示尺寸裁剪缩放再内嵌,大图走图片素材轨。详见铁律 4;`gtrk mg lint` 报 `4-html-size`(致命)/`4-html-size-heavy`(非致命)。
1
+ # GSAP-emit 契约 v1 · HTML 动画颗粒的逐帧 seek 渲染合规
2
+
3
+ > **契约版本**:gsap-emit v1。产 HTML 动画颗粒、经同合云 `html_animate_render` 逐帧 seek 合成的 skill/工具,其产物 MUST 满足本契约。
4
+ > **边界**:本契约只约束机器可判定的管线消费属性(封装/注册/确定性/自包含/体积上限/依赖可达/禁 `var()`/字体名命中注册表)。颜色、字体取值、构图和节奏由调用方栏目规则决定。
5
+
6
+ ## 原理(为什么不能用 CSS animation)
7
+
8
+ 渲染引擎逐帧渲染时,靠调用每个子合成在 `window.__timelines` 注册的 GSAP 时间线的 `.seek(t)` 把画面定格到第 t 秒。GSAP `paused` 时间线 = 可被外部 seek 的虚拟时钟 → 逐帧正确;纯 CSS `animation-delay` 动画不在 `window.__timelines` 里,引擎 seek 不到 → 画面冻结。
9
+
10
+ ## 回调与 seek 语义
11
+
12
+ > 颗粒可以用时间线回调(`onUpdate` 等)驱动画面;保证定帧时回调可达是渲染引擎侧的义务,不是颗粒作者的义务。本节是该主题的唯一口径来源。
13
+
14
+ - **引擎侧(MUST)**:定帧时必须保证 GSAP 时间线回调可达,并定位到包含 repeat / yoyo 的完整时间轴。优先使用 `seek(t, false)` 或 `totalTime(t, false)`;`time(t)` / `progress(p)` 只适用于无 repeat 的根时间线,不能视为完整时间轴定位的通用等价替代。不能使用默认会抑制回调的裸 `seek(t)`。
15
+ - **颗粒侧(MUST NOT)**:颗粒可以用 `onUpdate` / `onStart` / `onComplete` / `onRepeat` 写 DOM 或属性;不得运行时覆写 `tl.seek`,也不得替换 `window.__timelines[…]`。新颗粒不得添加 seek 垫片;已有垫片可在重渲复验后择期清理。
16
+ - **幂等回调**:逐帧 scrub 会反复触发回调,回调必须从当前补间状态重算画面,不得累加计数、追加数组或执行一次性 DOM 插入。
17
+ - **lint 哨兵**:`x-callback-driven`、`x-engine-api-override`、`x-raf-interval` 均为非致命提醒;它们用于提示引擎接口风险,不要求作者绕过契约自行添加垫片。
18
+
19
+ 实验和版本证据见 [`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)「回调与 seek 语义」节。
20
+
21
+ ## 八条铁律(违反任一条 → 整片渲染失败 / 颗粒冻结 / 全黑 / 坑位内突兀消失 / 画面被切成横带)
22
+
23
+ 1. **`<template>` 包裹根元素**:根元素必须位于 `<template>` 内,并带 `data-composition-id`、`data-width`、`data-height`。声明尺寸 MUST 为正整数且等于目标工程画幅;跨比例会拉伸,文字 IR 可在编译期按目标画幅缩放。
24
+ 2. **GSAP `paused` 时间线 + 注册**:使用 `gsap.timeline({paused:true})`,并把时间线注册到 `window.__timelines["<id>"]`;`<id>` 必须等于根的 `data-composition-id`。
25
+ 3. **确定性**:禁用 `Math.random`、`Date.now` 和无参 `new Date()`。需要随机感时使用固定种子、解析式或递归生成。
26
+ 4. **自包含 + 实心底下沉子层 + 透明度显式声明**:颗粒不得依赖外部文件(脚本可由渲染管线 vendor 或可达 CDN 提供)。
27
+ - 实心底 MUST 写在根下第一个全幅子层,MUST NOT 写在根元素 `style` 上。
28
+ - 根元素 MUST 保持零视觉;透明 overlay MUST 在根显式写 `background:transparent`。
29
+ - `gtrk mg lint` 按根与首个全幅子层的 `background` 推导 `opaque`;`4-bg-explicit` 与 `4-bg-on-root` 为非致命提示。
30
+ - HTML 总长 MUST ≤ 2,000,000 个 JS UTF-16 码元,SHOULD ≤ 500,000;位图按实际显示尺寸裁剪缩放后再内嵌,不透明图优先 JPEG/WebP,需要 alpha 才用 PNG,超限则把大图放素材轨。
31
+ 5. **脚本用渲染机可达的 CDN(编译期内联)**:使用 `lib.baomitu.com` GSAP 或渲染管线 vendor;不得依赖运行时相对路径。
32
+ 6. **颜色/字体用字面值**:禁 CSS `var()` 自定义变量;SVG 属性同样适用。具名字体 MUST 命中渲染服务端注册表,并以 `sans-serif` / `serif` 通用族收尾;栏目主题在生成期替换字面值。
33
+ 7. **占满坑位 + 终态驻留**:时间线总长 MUST ≥ 落轨 clip 的实际坑位时长(不是 `duration_hint`)。主叙事结束后定格或有限循环至坑位末尾;禁止 `repeat:-1`、整体渐隐到空或清空画面。循环次数按坑位算死:`repeat = ceil((坑位时长 − 循环起点) / 单圈时长) − 1`。
34
+ 8. **重复图元合并**:同色、同 `stroke` / `fill`、整组同步驱动或静态的网格、排线、刻度、点阵 MUST 合并成单个元素;逐元素动画(stagger/逐条画入/逐个变色)可保留元素身份。该规则是作者侧规避,真实触发轴由渲染侧决定,命中批次 MUST 做真渲染抽帧验收。
35
+
36
+ > **⚠️ 铁律编号的唯一定义在本文件。** 栏目 skill / 作坊引用时直接写「gsap-emit v1 铁律 N」,不得另立同号条款;栏目自身规则请使用独立编号空间。
37
+
38
+ > 铁律 4 和铁律 8 的实验依据见 [`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)。正文只保留当前可执行契约。
39
+
40
+ ## 渲染成本:真卷积滤镜
41
+
42
+ > 本节约束逐帧成本,不改变八条正确性铁律;命中成本项时画面仍可能正确,lint 提示保持非致命、不拦铺轨。
43
+
44
+ ### 射程
45
+
46
+ | 进射程(真卷积) | 射程外 |
47
+ |---|---|
48
+ | CSS `filter` / `-webkit-filter` / `backdrop-filter` 中的非零 `blur` 或带模糊半径的 `drop-shadow` | `blur(0)`、无模糊半径的 `drop-shadow`、`filter:none`、任何 `box-shadow` |
49
+ | SVG `feGaussianBlur` / `feDropShadow`(含 `filter:url(#id)` 引用) | `opacity`、`transform`、`mix-blend-mode`、`border-radius` |
50
+
51
+ `box-shadow` 整族不报警;铁律 4②管根元素绘制属性的位置,本节管卷积成本,两者互不冲突。
52
+
53
+ ### 条文
54
+
55
+ - 真卷积每帧代价随**覆盖面积 × 模糊半径 × 帧数**增长。
56
+ - MUST NOT 用时间线补间驱动 `filter` 或 SVG `stdDeviation`;需要同一叙事动作时改用 `opacity` / `transform`,或做静态两态切换。
57
+ - 带静态滤镜的元素 MUST NOT 再用补间驱动 `transform`;优先拆分滤镜层与运动层,其次缩小覆盖面积,最后才考虑预烘。
58
+ - `gsap.set(el, {filter: …})` 只设一次属于静态形态;`opacity` 补间不在本节射程内。
59
+ - 整幅静态滤镜 SHOULD 缩小覆盖面积。预烘不是默认解法,确需预烘时 MUST 使用 RGBA PNG,并先做同颗对照;MUST NOT 用 JPEG 代替带 alpha 的颗粒素材。
60
+ - 判断权在作者,契约只规定成本机制与改法,不规定视觉取值。
61
+
62
+ ### 维护边界
63
+
64
+ 成本项保持非致命、不拦铺轨;实际收益取决于颗粒形态,不能把单批实验数字当成通用提速承诺。调整严重度时另开 change,并先复扫现网生产语料。实验和版本证据见 [`gsap-emit-v1-evidence.md`](gsap-emit-v1-evidence.md)。
65
+
66
+ ## Alpha 交付口径
67
+
68
+ > 颗粒内一律使用直通 alpha;剪映 qtrle 交付由渲染管线负责预乘。颗粒与 CLI MUST NOT 再做第二次预乘。
69
+
70
+ 1. **颗粒侧(MUST)**:颜色使用 `rgba(r,g,b,a)`、`#RRGGBBAA`、`opacity` 或渐变透明值;不得为迁就 NLE 自行把半透明色改暗或回避半透明。
71
+ 2. **交付面**:客户端预览、本地 webm 和云端 overlay 合成使用直通 alpha;剪映 qtrle 由管线预乘。
72
+ 3. **验收(MUST)**:含软光晕、渐变透明、带 blur 的 `text-shadow` 或 `opacity<1` 图层时,剪映验收必须使用本颗粒或同类半透明颗粒,不能用实心颗粒代验。
73
+ 4. **哨兵**:`gtrk mg lint` 的 `x-soft-alpha` 为非致命提示,不拦铺轨、不要求作者改写。
74
+
75
+ ## 模板颗粒(ir 态)的改法
76
+
77
+ > **一句话**:由 IR 编译出来的颗粒,改内容 **MUST 改 IR 再重编译**,**MUST NOT 直接改 HTML**。
78
+ > 词表、不变量、编译确定性与三态判定的正本在 infra capability `text-ir-profile`
79
+ > ,本节**只作导读、不复制规则**。
80
+
81
+ 同合云的文字特效模板是由一份 **IR 文字子集**确定性编译出来的普通 gsap-emit v1 颗粒——
82
+ 对本契约而言它和别的颗粒没有任何区别,`beat_track` 也不为它新增任何键。
83
+
84
+ 区别只在**它自带出身证明**:编译产物在 `<template>` 内以
85
+ `<script type="application/json" data-gtrk-ir>` 内嵌完整 IR,并在首行注释写
86
+ `gtrk-ir-sha256` 与 `gtrk-html-sha256` 两个哈希。判定只看文件内容,不依赖文件名或目录。
87
+
88
+ | 三态 | 含义 | 还能云端改写吗 |
89
+ |---|---|---|
90
+ | `ir` | 内嵌 IR 在,且声明的 html 哈希与实测一致 | 能 |
91
+ | `detached` | 内嵌 IR 在但哈希对不上 ⇒ HTML 被直接改过 | 不能;可按内嵌 IR 重编回模板 |
92
+ | `html` | 无内嵌 IR(绝大多数颗粒) | 不能(本来也不需要) |
93
+
94
+ **手改 HTML 是单向操作**:改一个字节就从 `ir` 掉到 `detached`,`gtrk mg edit` 与客户端
95
+ 属性面板都会拒绝它。颗粒本身照常能渲能铺,所以 `gtrk mg lint` 只给**非致命**的
96
+ `x-ir-detached` 提醒,不拦。
97
+
98
+ 两条合法改法:
99
+ - **改字 / 换色 / 改字号描边阴影 / 改时长**:改 IR 后 `gtrk mg compile <ir.json>`(L0,0 积分)。
100
+ - **换效果**:`gtrk mg edit <particle.html> --say "<一句话>"`(经云端模型链,按候选计费)。
101
+
102
+ **内嵌的 JSON 块不参与渲染**,也不在本契约任何检查的射程内(它既没有 `src` 也不执行)。
103
+
104
+ ## 颗粒骨架(中性模板)
105
+
106
+ ```html
107
+ <template id="p">
108
+ <!-- 铁律4:根 MUST 零视觉。这里的 background 恒为 transparent(显式声明,不是省略),实心底一律下沉到下面的 .bgfill 子层 -->
109
+ <div data-composition-id="<id>" data-width="1920" data-height="1080"
110
+ style="position:absolute;inset:0;background:transparent;overflow:hidden;font-family:'<你的字体·须命中服务端 font_manifest.json>',sans-serif;">
111
+ <!-- 铁律4①:实心底 MUST 是根下第一个全幅子层。满屏颗粒填你栏目的底色;透明叠加(overlay)颗粒把整行删掉 -->
112
+ <div class="bgfill" style="position:absolute;inset:0;background:<你的底色>;z-index:0;"></div>
113
+ <style> [data-composition-id="<id>"] .xxx{ … } </style> <!-- 样式用属性选择器作用域,防跨颗粒污染 -->
114
+ <svg viewBox="0 0 1920 1080" preserveAspectRatio="xMidYMid meet" style="position:absolute;inset:0;width:100%;height:100%;">…</svg>
115
+ <script src="https://lib.baomitu.com/gsap/3.13.0/gsap.min.js"></script>
116
+ <script>(function(){
117
+ var ROOT='[data-composition-id="<id>"]';
118
+ /* 1) 确定性构建静态结构 */
119
+ /* 2) gsap.set 初始态 */
120
+ /* 3) var tl = gsap.timeline({paused:true}); … 编排 … */
121
+ window.__timelines = window.__timelines || {};
122
+ window.__timelines["<id>"] = tl;
123
+ })();</script>
124
+ </div>
125
+ </template>
126
+ ```
127
+
128
+ > **骨架里 `.bgfill` 那一行的三个要点**:① 它 MUST 是**根下第一个**子元素(后续前景层自然压在它之上,不必给前景排 z-index);
129
+ > ② 它 MUST **全幅**(`position:absolute;inset:0`,或等价的 `top/left/width/height` 铺满)——不铺满就不是「实心底」;
130
+ > ③ 类名 `bgfill` 只是**惯例**,其底色写在**行内 style** 上(不靠 `<style>` 里的类规则),故不受「样式作用域」那条专属坑影响;
131
+ > 换名字不违约,但 `gtrk mg lint` 认的是「根下首个全幅子层的 `background`」这个**结构**,不认类名。
132
+
133
+ 缓动可用 CustomEase 精确还原你栏目自己的 cubic-bezier(缺插件时给近似回退)——bezier 数值属于栏目审美,本契约不规定。
134
+
135
+ ## 确定性配方(替代 random)
136
+
137
+ - 递归结构:固定角度、比例、深度参数 → 完全确定。
138
+ - "噪声感":`Math.sin(i*0.18 + j*0.2)` 类解析式伪随机。
139
+ - 打散:用 index 派生(如 `i*137.5°` 黄金角),不要 `Math.random()`。
140
+
141
+ ## 验证(交付前必做)
142
+
143
+ 墙钟截图类工具驱动不了 paused 时间线(只看到 t=0),**不能**用来验收。必须真渲染引擎 seek 验证:
144
+ 1. 颗粒放进最小 composition(root `index.html` 用 `data-composition-src` 引它);
145
+ 2. 走渲染管线(html_animate_render)渲染;
146
+ 3. 抽不同时间点的帧**比对应当不同**(相同=冻结=铁律没守住)。客观自检:根有 `<template>`、`window.__timelines["<id>"]` 已注册且 id 匹配、无 random/Date、tl 总长 ≥ 颗粒时长、HTML ≤ 2,000,000 字符(铁律 4;`gtrk mg lint` 的 `4-html-size`)。
147
+ > ⚠️ 不要用「本地等价 seek 脚本 / Node 无头模拟」替代真引擎渲染——它不经真编译+挂载,无法覆盖 var()、CDN 内联和 StaticGuard 等真引擎行为。
148
+ >
149
+ > ⚠️ **客户端预览对「底色 / 透明度」类问题结构性失明,MUST NOT 用作这类验收的判据**。
150
+ > 客户端预览会按 clip 登记的 `opaque` 位**给颗粒根盒强行打底**(`!important`,恒胜过颗粒自身的设计期底色)——
151
+ > 于是无论根 `background` 有没有被丢弃、有没有实心底子层,**预览看到的都是「按登记值应该长的样子」**。
152
+ > 铁律 4 的证据锚正是靠**真渲染出片抽帧**取得的,不是靠预览。预览截图只作对照留档,不入判据。
153
+
154
+ ## 专属坑
155
+
156
+ - **样式作用域**:颗粒与其他颗粒/根同处一个文档,全局类会撞——用 `[data-composition-id="<id>"] .xxx` 属性选择器作用域。
157
+ - **`<template>` 内的 `<script>` 默认不执行**——引擎把 template 内容克隆进文档后才执行;本地直接开浏览器不会跑,必须经引擎/player。
158
+ - **transform-origin(SVG)**:缩放 `<g>` 用 GSAP `svgOrigin:"x y"`(SVG 用户坐标),别用 CSS transform-origin。
159
+ - **时间线总长 ≥ 坑位时长**:颗粒 tl 总时长 ≥ 落轨 clip 时长(坑位包络),否则 seek 越界(已升格为铁律 7,含终态驻留要求)。
160
+ - **整组同步驱动的重复图元必须合并**:网格、排线、刻度、点阵用单元素表达,SVG 以一条 `<path>` 的多子路径合成;逐元素动画批次豁免。详见 gsap-emit v1 铁律 8。
161
+ - **真卷积滤镜别用补间驱动(也别让带滤镜的元素被 `transform` 补间驱动)、整幅静态滤镜先缩面积**:`blur()` / `drop-shadow()` / `feGaussianBlur` 按「面积 × 半径 × 帧数」增加每帧成本。预烘是次选、必须 RGBA PNG,先做同颗对照再用;`box-shadow` 整族不在射程内。详见本文件「渲染成本:真卷积滤镜」一节。
162
+ - **别用 `requestAnimationFrame`/`setInterval` 驱动画面**——不被 seek,等于冻结。所有视觉变化必须挂在 tl 上。(与「回调与 seek 语义」一节同源:任何**不经 tl** 的自有时钟都不被定帧驱动;而挂在 tl 上的回调是否被触发,则由该节的引擎侧条款保证。`gtrk mg lint` 对本条给**非致命**项 `x-raf-interval`——静态正则分不清「驱动画面」与其它用途,故只提醒不拦。)
163
+ - **自包含 ≠ 无限内嵌**:原图整张 base64 塞进颗粒会撞客户端 2,000,000 字符硬限。位图先按实际显示尺寸裁剪缩放再内嵌,大图走图片素材轨。详见铁律 4;`gtrk mg lint` 报 `4-html-size`(致命)/`4-html-size-heavy`(非致命)。
@@ -1,10 +1,10 @@
1
- {
2
- "version": 1,
3
- "comment": "handoff 类型 → 契约映射表(handoff-contract-library spec)。消费方(含 gtrk-style-maker)查此表,不得硬编码映射;消费方查表前先将遗留品牌值(如 RRV_MG)归一为中性名(MG)。null = 暂无契约(产线可用,无合规绑定要求)。注册集当前 = 现行四车道(P2 注册表化前的过渡事实)。",
4
- "mappings": {
5
- "A_ROLL": null,
6
- "MG": { "contract": "gsap-emit", "version": "v1", "doc": "gsap-emit-v1.md" },
7
- "AI_DRAMA": null,
8
- "FILM_BROLL": null
9
- }
10
- }
1
+ {
2
+ "version": 1,
3
+ "comment": "handoff 类型 → 契约映射表(handoff-contract-library spec)。消费方(含 gtrk-style-maker)查此表,不得硬编码映射;消费方查表前先将遗留品牌值(如 RRV_MG)归一为中性名(MG)。null = 暂无契约(产线可用,无合规绑定要求)。注册集当前 = 现行四车道(P2 注册表化前的过渡事实)。",
4
+ "mappings": {
5
+ "A_ROLL": null,
6
+ "MG": { "contract": "gsap-emit", "version": "v1", "doc": "gsap-emit-v1.md" },
7
+ "AI_DRAMA": null,
8
+ "FILM_BROLL": null
9
+ }
10
+ }