Skip to content

feat(storyboard): SB-6 序列预览——按叙事链把整条分镜从头播一遍 - #222

Merged
KerroKapple merged 1 commit into
mainfrom
feat/sb-6-sequence-preview
Aug 7, 2026
Merged

feat(storyboard): SB-6 序列预览——按叙事链把整条分镜从头播一遍#222
KerroKapple merged 1 commit into
mainfrom
feat/sb-6-sequence-preview

Conversation

@KerroKapple

Copy link
Copy Markdown
Owner

M4 E1,接 #219(SB-5)/ #220(artifacts util)/ #221(SB-3)。分镜线第一个「打开就能看见」的成果:顶栏点「序列预览」,按 narrative 链顺序把每镜的最新产物依次放出来。没生成的镜显备注占位且照样计时 —— 预览的总时长就是成片的预期时长。

分层

models/sequence_shot.dart              播放清单条目(一镜放什么、放多久)
util/sequence_builder.dart             画布节点+边 → 播放清单(纯函数,全单测)
widgets/sequence_preview_dialog.dart   只负责"放"与"推进"

排序复用 SB-5 的 narrative_order,产物查找复用 EX-1′ 首建的 node_artifacts —— 本卡只消费,不再造第二套(卡面裁决)。

关键设计:折叠

「用本镜备注生成图像」建出来的真实拓扑是:

shot ──narrative──> image config ──sourceNodeId──> image result

config 节点也在 narrative 链上。 照单把每个链上节点都当一镜,一条 3 镜的分镜会播出 6 条(shot1 占位、cfg1 出图、shot2 占位、cfg2 出图……)。

这不是边角情况 —— _generateImageFromNotes 是当前把图接进分镜链的唯一入口,不折叠等于这张卡不可用。

规则:节点把产物借给前面的 shot 后不再单独成镜;已被借走的产物不会被第二个 shot 再借(多个 shot 指向同一 config 的分叉/汇合场景)。第二条是我写完实现跑测试才发现漏的 —— 原先只挡住了 config 不单独成镜,没挡住同一张图被两镜共用。

推进两套

镜的类型 怎么推进
图片 / 无产物占位 定时器,停留 durationMs(缺省 3s)
视频 真实播放进度(position ≥ duration),外加兜底定时器

视频那条兜底不是冗余:播放器打不开文件、进度流不动、时长拿不到时,没有它就永远卡死在这一镜。余量 +1.5s,正常播放先于它触发。有两条用例分别钉「position 推进先于兜底」和「拿不到 duration 时兜底接管」。

media_kit 生命周期(卡面点名泄漏高发)

  • 整个对话框只 create 一个 handle,换镜靠 open() 换源
  • handle 只在 initState 建、只在 dispose 放,中途任何路径都不再 create
  • 纯图片序列完全不 create —— 不该为一堆静态图唤起 media_kit
  • _epoch 计数器防迟到回调:换镜时自增,异步回调(open 完成、position 事件)拿它比对,不会推进一个早已翻过去的镜

三条都有用例钉死,包括「拆掉 widget 后 handle.dispose 被调用一次」。

入口

顶栏「序列预览」,画布有 narrative 边才可用。可用性只 watch 边不 watch 节点 —— 拖动节点不重建顶栏,且"一堆互不相连的节点"本来就不构成序列。有用例钉死只有 data / generation_source 边时仍禁用。

验证

顺带被仓库的硬编码视觉值闸门抓到一处 InkSpacing.xs / 2 —— 改用整档而不是放松守卫。

文档

features/storyboard/README.md(含折叠的来龙去脉与 media_kit 约束)、BOARD M3 分镜行 + 近期落地表、docs/CLAUDE.md 结构快照(新增目录同 commit 更新,顺带补注 canvas/export 的新 util)。

说明

未跑两路对抗评审 —— 本会话指令是不主动派 subagent。这张卡是 L 级、含 media_kit 生命周期与定时器竞态,是这一轮四张卡里最该过一次 /code-review 的。另外真实播放(媒体解码、换源时序)测试覆盖不到,需要手动回归。

🤖 Generated with Claude Code

M4 E1。分镜线第一个「打开就能看见」的成果:顶栏点「序列预览」,按 narrative
链顺序把每镜的最新产物依次放出来,没生成的镜显备注占位且照样计时——预览的
总时长就是成片的预期时长。

新 features/storyboard 三层:
- models/sequence_shot.dart  播放清单条目(一镜放什么、放多久)
- util/sequence_builder.dart 画布节点+边 → 播放清单(纯函数,全单测)
- widgets/sequence_preview_dialog.dart 只负责「放」与「推进」

排序复用 SB-5 的 narrative_order、产物查找复用 EX-1' 首建的 node_artifacts
——本卡只消费,不再造第二套(卡面裁决)。

**关键设计:折叠。** 「用本镜备注生成图像」建出来的真实拓扑是
shot ──narrative──> image config ──sourceNodeId──> image result,
也就是 config 节点也在 narrative 链上。照单把每个链上节点都当一镜,一条
3 镜的分镜会播出 6 条(占位/出图交替)。而那是当前把图接进分镜链的**唯一**
入口——不折叠等于这张卡不可用。规则:节点把产物借给前面的 shot 后不再单独
成镜;已被借走的产物不会被第二个 shot 再借(多 shot 指向同一 config 时)。

**推进两套。** 图片/占位走定时器(缺省 3s);视频以真实播放进度推进
(position ≥ duration),**外加兜底定时器**——播放器打不开文件、进度流不动、
时长拿不到时,没有它就永远卡死在这一镜(余量 +1.5s,正常播放先于它触发)。

**media_kit 生命周期**(卡面点名泄漏高发):全对话框只 create 一个 handle、
换镜靠 open 换源、只在 initState 建只在 dispose 放、**纯图片序列完全不
create**(不该为一堆静态图唤起 media_kit);_epoch 计数器防迟到回调推进一个
早已翻过去的镜。

入口=顶栏「序列预览」,画布有 narrative 边才可用(可用性只 watch 边,拖动
节点不重建顶栏)。

ARB +11 键(en/zh 同 commit,gen-l10n 已跑并提交 generated/)。
测试 +30:builder 14 例(折叠/借用只发生一次/时长优先级/取最新产物/跳过
规则)、dialog 12 例(handle 只建一个且必被 dispose、纯图不 create、按时长
推进、暂停真停、末镜不越界、position 推进先于兜底、拿不到 duration 时兜底
接管)、顶栏 4 例(门控只认 narrative 边)。
顺带被仓库硬编码视觉值闸门抓到一处 `InkSpacing.xs / 2`——改用整档而不是
放松守卫。

analyze 0 issue;全量非 golden +1878 / 0 失败 / 66 skip(原 1848)。
文档:features/storyboard/README.md、BOARD M3 分镜行 + 近期落地表、
docs/CLAUDE.md 结构快照(新增目录同 commit 更新,含 canvas/export util 补注)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@KerroKapple
KerroKapple merged commit bdfaf97 into main Aug 7, 2026
5 checks passed
@KerroKapple
KerroKapple deleted the feat/sb-6-sequence-preview branch August 7, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant