Files
ttstd-infinite-canvas/CONTEXT.md
爱笑的毛毛虫 7ec51732f7 feat(editor): AI 编辑助手悬浮面板、插件权限体系与时间线渲染管线(M4–M6.3) (#411)
* feat(editor): 编辑器实施 M0-M1 - 剪辑入口占位与插件 SDK v2 垂直切片

- detail.tsx 新增剪辑成片视图(editor tab,常驻),概览页插入剪辑步骤卡
- plugin-types.ts 新增 v2 类型(EditorSlotKind/EditorPluginPermission/PluginManifestV2),修复权限数组交叉归约 bug
- editor-slot-registry.ts 插槽注册表(优先级排序、幂等注册、订阅机制)
- plugin-registry.ts 注册器 v1/v2 分支校验,v1 路径零改动
- editor-shell.tsx 垂直切片插件:timeline-panel 插槽真实渲染,验证插件链路全通
- 测试:插槽注册表 5 用例 + 命令测试骨架,v1 插件回归 21 例零回归

* feat(editor): 命令协议黄金文件 - editor-commands 纯函数 reducer + 8 个黄金 op

* feat(editor): 快照撤销历史 - editor-history 有界 200 层 undo/redo

* feat(editor): 命令状态机 store - dispatch/手势预览/1.5s 防抖保存/串行保存链

* fix(editor): createEditorStore 返回 zustand hook 类型修复 typecheck

* feat(editor): 手势 echo 最小件 - 时间线面板拖拽移动/裁剪 + store 接线与本地持久化

* docs(editor): M2 完成状态记录

* feat(editor): M3.1 时间线面板选中联动 - store 选中状态 + 片段高亮/点击取消

* feat(editor): M3.2 预览监视器 - preview-renderer 插槽 + 真实媒体帧/播放头/时间码

* feat(editor): M3.3 片段检查器 - inspector 插槽 + 属性编辑入队可撤销

* feat(editor): M3.4 asset-ingest 素材库插件(addClip 命令入轨)

* feat(editor): M3.4 补充 - host 上下文(项目信息+资产)、editor.tsx 接线、asset-ingest 纯函数、死代码清理

* feat(editor): M3.5 subtitle-tools - SRT import/export + rebuild from node snapshot

* feat(editor): M3.6 transcription - deterministic mock ASR to subtitle track

* feat(editor): M3.7 export - render plan + ffmpeg.wasm fallback export

* feat(editor): M3.8 - render all 8 editor slots in three-pane layout + ai-assistant placeholder

* docs(editor): M3 完成状态 - 8 插件 + 全插槽接线,冒烟待确认

* feat(editor): 编辑器媒体与时间线打磨 - 资产来源分组筛选、时间线全宽布局、拖拽末片段修复、OSS 降级本地存储、Concat 风格控制条与检查器

* fix(plugins): 合并上游后修复插件分类筛选对 v2 清单的类型兼容

* feat(editor): 时间线工具态与轨道操作打磨 - 工具按钮选中仅图标变色、监视器 Concat 式控制条、removeTrack 黄金命令与轨道行头移除按钮、maskClosable 迁移

* fix(api): 资源上传超 MaxBytesReader 上限时报中文大小限制而非英文裸 400

* fix(editor): 监视器控制条 - 修复播放按钮 JSX 模板闭合花括号缺失导致的编译错误

* feat(api): 本地媒体导入分片化对齐 Concat - >50MB 视频不再失败 - 分片会话三接口(POST开始/PUT单片/complete合并)、前端超50MB自动切8MB分片并失败整传重试、默认配额放宽(日200MB→2GB、总2GB→20GB)、UploadResourceFile 豁免单文件限

* feat(editor): 轨道行头新增可见性/静音开关按钮 - 复用现有图标资源,按轨道类型显隐;命令层新增 setTrackFlag 黄金命令(flag 白名单 fail-closed)并补单测

* docs(progress): 记录轨道可见/静音开关改动与复测项

* docs(editor): M4 转写与渲染任务设计 - 复用 Task 表与状态机、独立创建 API、worker 领取类型过滤、whisper HTTP provider(OpenAI 兼容 /v1/audio)

* fix(api): OSS 上传失败自动降级本地存储时 body 已被 Transport 关闭 - newOSSRequest 用 NopCloser 包装请求体,服务端提前返回(签名 403)时不再关闭调用方持有的文件,降级分支 Seek 重读生效;补回归测试

* docs(progress): 记录媒体导入根治(分片+OSS 降级修复)复测项

* feat(api): 视频播放副本转码 H.265→H.264 + variant=playback 服务与启动回填 - 上传视频后异步探测 codec,H.265/AV1 走 moov 快速探测并转码 H.264 播放副本;GET /api/user-data/...?variant=playback 就绪时服务副本(独立 ETag :pb),未就绪回退原件并撤销后缀防 304 黑屏;storeResource 触发、启动幂等回填存量;前端播放失败自动轮询副本就绪后刷新

* fix(editor): 轨道至少保留一条 - 不可移除轨道点击 X 时居中浮现原因提示(2.2s 自动消失, aria-live);修正可见性 toggle 传参 (visible===false 语义) ;轨道行内移除按钮改为常驻(不可移除时点击给提示)

* feat(timeline): M4.1 字幕转写后台任务 - whisper.cpp 本地执行 - 新增 timeline_transcription 任务类型,worker 按类型分叉独立执行器(不走计费与模型渠道);输入 resourceId+language,读取用户资源后 ffmpeg 预处理 16k 单声道 wav,POST 本地 whisper.cpp /inference,结果写回 segments+srt+language;未配置 CANVAS_WHISPER_BASE_URL 等失败均落明确终态;feature 门控;进度分阶段;含解析/SRT/快速失败单元测试

* feat(media): 素材卡片时长与媒体面板打磨 - 导入媒体解析时长(前端 media-metadata + 后端 project_asset durationMs 透出)随卡片展示;媒体面板/监视器/时间线相关样式细节收尾

* docs(progress): 阶段 C 监视器播放复检清单 - H.265 播放副本/真实播放驱动/时长链路待浏览器确认

* feat(timeline): M4.1 转写任务创建 API - POST /api/timeline/transcriptions - service CreateTimelineTranscriptionTask:IsDraining 守护 + feature 门控 + 资源归属与可转写类型校验 + 排队计 active 限额(含任务级/项目级存储估算);local/whisper.cpp 标记,任务不经过模型路由与计费;createTaskWithinStorageQuota 复用与 ErrActiveTaskLimit 用户可读映射;测试覆盖排队成功(owner/type/provider 断言)、跨用户资源拒绝、非音视频拒绝

* feat(timeline): M4.3 字幕转写前端接线 - 真实任务替换 mock - 新增 services/api/timeline-tasks.ts(POST /timeline/transcriptions,复用 apiClient+request);EditorTranscription 改为 创建任务→waitForGenerationTask 轮询(25min)→结果 segments 映射 SrtEntry[]→rebuildSubtitleClips 写入字幕轨道;实时展示 stage/progress,失败/空识别/无字幕轨道分别提示;素材选择支持音频与视频(取 storageKey 中的 resource id 提交)

* fix(media): 播放副本状态收敛与轮询终态 - 无ffmpeg/远端存储/编码不可处理落 none 避免重复探测;前端 none 停轮询回原生播放并加 processing 上限

* feat(timeline): M4.2 时间线渲染任务 - POST /api/timeline/renders 创建(buildRenderPlan 校验含可渲染媒体、配额入队、local/ffmpeg、60min 超时);worker 顶部按类型分叉 processTimelineRender;ffmpeg 合成产物写资源存储并经 ResultJSON 记 resourceId;render 计划/结果测试 PASS

* fix(resource): 转码原子抢占与 OSS 请求体加固 - ClaimPlaybackTranscode 空/none→processing 单实例抢占避免重复转码,runPlaybackTranscode defer recover 落 failed;If-Range 与带 :pb 后缀的 serveETag 比较;无请求体的 OSS 请求不再包 NopCloser(nil)(防 Go 1.26 body 探测 nil 解引用)

* style(editor): 修正字幕回写面板缩进

* docs(timeline): M4 设计文档按实现回写 - 单一 worker 池 + processClaimedTask 顶部按类型分叉(决策 3/4),端点统一为复数 /api/timeline/transcriptions 与 /api/timeline/renders(注册于 RegisterTaskRoutes),ResultJSON/feature gate/ffmpeg env 同步;Runbook M4 表格与状态行更新;auth.go API 前缀白名单补 timeline

* fix(service): 修复上游 v1.2.5 公告配图删除保护自锁与测试夹具缺口

上游 #385 资源引用走查器对公告配图/草稿做了硬性引用检查,导致三个问题:
- UpdateAnnouncement 在更新公告前对旧配图走查,必然命中当前公告自身引用而自锁,
  公告替换配图功能实际不可用;DiscardAnnouncementImage 同理被草稿自身引用阻塞。
  修复:ensureResourceHasNoBusinessReferences 支持忽略指定引用(当前公告/当前草稿),
  其余引用仍按原语义拦截。
- admin DeleteAdminResources 将公告草稿引用视为阻塞,但删除事务会级联清理草稿与
  资源绑定,service 层永不进入事务,回滚审计注入测试无法到达预期路径。修复:service
  预检时过滤公告草稿引用,与 repository 级联语义对齐。
- project_settings_test.go 夹具未建 ResourceReferenceSnapshot 走查的 Task/Session/
  Message/TaskLog/Result/Announcement/AnnouncementImageDraft 表,报 no such table。
  修复:夹具 AutoMigrate 补齐。

后端全量 go test ./... 通过。

* feat(editor): 成片导出接入后端渲染任务(M4.2 前端接线)- timeline-tasks 增 createTimelineRenderTask;editor-export 主路径改 POST /timeline/renders 轮询取 resourceId 内嵌预览/下载,ffmpeg.wasm 降级保留

* feat(editor): M5 插件权限执行(fail-closed)- plugin-permission-check 新建(8 插槽最小权限表/判定/断言);SlotStack 渲染前过滤缺权限插槽改诊断条;editor-shell manifest 补 timeline.command/export.run;测试 10 用例含 editor-shell 真实回归

* feat(editor): M6.1 AI 命令 schema + M6.2 时间线摘要 - ai-command-schema(parse/dry-run 校验/12-op 提示词契约/版本化+golden);timeline-summary(确定性中文摘要/折叠上限);各 20/10 用例全绿

* docs(progress): M6 质检清单 - M6.1 schema/M6.2 摘要标记完成(含测试计数),M6.3 待模型源确认;清理 M5.1 重复条目

* feat(editor): AI 命令 schema 支持只读问答 - commands 空数组合法化(无法执行/纯提问时 reasoning 直接作答),新增 AI_EDITING_MAX_COMMANDS=8 上限防失控编辑,提示词同步

* feat(editor): AI 助手重构为右上悬浮对话面板(M6.3)+ 素材入库原子化收尾

- editor-ai-assistant.tsx: 占位面板替换为 OpenAI Canvas 风格右上悬浮对话面板;
  面板内文本模型选择器(effective.textModels,选择即持久化并覆写请求模型),
  消息流 user/assistant/error/preview 四类气泡 + 思考中占位,空态引导示例 chips
  一键发送,底部输入条;顶部工具栏常驻 Sparkles 入口(aria-pressed 切换)。
  修复上一版重复/截断函数声明(appendToEntry 双定义、settleAnswer 三声明等)。
  宿主链路:buildAiEditingSystemPrompt(summarizeTimeline(project)) 喂上下文 →
  parseAiCommandPlan → validateAiCommandBatch dry-run(任一非法整批拒绝)→
  ≤3 条 dispatch 直执行 / >3 条预览确认卡片;宿主拒绝时 failTurn 回填 FAILED_MARK 可重试。
- editor-asset-ingest/editor-preview-monitor/detail/editor.tsx: 配套接线与打磨
  (编辑器顶栏 AI 入口、素材导入面板、预览监视、项目页顶栏整理)。
- backend: LinkProjectAsset 事务内原子创建资产 + 项目链接(OnConflict 幂等),
  资产首次入库不再预落库,失败整体回滚,消除"有资产无链接"孤儿记录。
- docs: pending-test.mdx / runbook M6.3 状态更新(tsc + 测试 + 构建通过,
  浏览器冒烟待真实文本渠道)。
2026-09-04 14:03:33 +08:00

82 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 影策 · 编辑器上下文
面向 AI 影视与短剧创作工作台中的"时间线智能编辑器"子域:时间线模型、编辑命令、预览与导出、自动字幕、插件体系的统一语言。术语主要受 Concat(WolfCut)架构启发,插件部分参考 open-vetta「万物皆可插件」理念,适配 Web + Go 技术栈,详见 `docs/adr/`。
## 语言
**时间线 (Timeline)**:
项目的时序编排视图,由轨道与片段组成;`TimelineProject`(v2)是编辑真相源的数据形状。
_Avoid_: 时间轴、剪辑线
**轨道 (Track)**:
同一类型片段的有序序列(video / audio / subtitle / text / image),有 `order` 与 `locked`。
_Avoid_: 层(Layer 特指画布节点叠放,不用于时间线)
**片段 (Clip)**:
时间线上对素材的一段引用:`startMs` + `durationMs` 定位在时间线,`sourceStartMs` / `sourceDurationMs` 定位在源素材内。
_Avoid_: 素材(Media 指源头资产,Clip 是其时间线引用)
**直连媒体 (directMedia)**:
仅时间线作用域的媒体引用(素材库/项目资产/本地上传直接入轨),存在时预览/导出优先从该字段解析,不回画布查节点。
_Avoid_: 内联媒体、画布素材
**编辑命令 (EditCommand)**:
唯一允许修改时间线的可序列化指令 `{ op, payload }`,经单队列顺序应用;UI 不直接改写模型。
_Avoid_: 操作、mutate、setState(指组件内部状态)
**手势回显 (Echo)**:
拖拽/裁剪等手势期间在本地预演的位置与效果,手势结束才提交一个编辑命令——撤销的粒度是手势而非每一像素。
_Avoid_: 预览态、假数据
**预览近似 (Preview Approximation)**:
交互期间用浏览器原生能力(`<video>` 裁剪缩放、CSS 滤镜、canvas 合成)渲染的近似画面,不是导出像素。
_Avoid_: 预览帧、真实预览
**渲染真相 (Render Truth)**:
导出产物的真实合成画面,由单一滤镜图计划驱动;预览近似与它参数同源、允许画质级差。
_Avoid_: 最终预览、监看画面
**滤镜图 (Filter Graph / Render Plan)**:
`buildTimelineRenderPlan` 生成的 ffmpeg 参数计划(trim → gap → concat → subtitle → burn),是导出滤镜串的唯一来源;新增效果禁止在 UI 手写第二套滤镜串。
_Avoid_: 滤镜链、ffmpeg 参数(指手工拼的字符串)
**转写字幕 (Transcription)**:
后端服务任务:把音频/视频片段经 ASR 转成字幕片段写回字幕轨道;前端只提交任务并展示进度。
_Avoid_: 自动字幕(指 UI 按钮名,模型层术语为转写)
**预设插件 (Preset Plugin)**:
随应用分发、默认启用、不可卸载的插件:编辑器全部 UI 与能力(时间线面板、预览、检查器、转写、导出、字幕工具)均为预设插件;与用户插件同 SDK 同清单,仅分发与权限语义不同。
_Avoid_: 内置功能、硬编码面板(内置 = builtin 源码目录语义,指非插件化的历史形态)
**UI 插槽 (UI Slot)**:
宿主暴露的注册式挂载点(`registerTimelinePanel` / `registerPreviewRenderer` / `registerExportRenderer` / `registerTranscriptionProvider` / `registerSubtitleTool` / `registerShortcutScope`),内置与第三方插件平等占位;插件不得绕过插槽直接挂载 DOM。
_Avoid_: 面板数组、硬编码容器
**插件 SDK (Plugin SDK / yingce.plugin/v2)**:
`definePlugin` + 清单 + 贡献类型 + 权限声明的统一形态;v2 在 v1 基础上新增 UI 插槽、预设分发与权限执行校验。
_Avoid_: 插件协议(指 v1 旧称,v2 起称 SDK/清单)
**能力提供者 (Capability Provider)**:
插件以 provider 身份注册的能力(如转写提供者、导出渲染器、预览渲染器),宿主在调用点校验权限后执行;未声明即不可见不可用。
_Avoid_: 能力函数、功能开关
**语音滤镜 (Voice Filter)**:
音频效果能力(音量/淡入淡出/均衡/回声/降噪/变速变调):预览近似走 Web Audio API,导出真相走 ffmpeg 音频滤镜,两者同源同参数模型(ADR-0006)。
_Avoid_: 声音特效、音频美化
**模板 (Template)**:
项目快照(章节 + 画布 + 时间线 + 资产引用清单,不含媒体二进制),支持存为模板 / 从模板新建;服务端模板表 + 缩略图(ADR-0006)。
**AI 编辑命令 (AI Edit Command)**:
LLM 输出的受约束命令序列 JSON `{ commands, reasoning }`,op 来自命令白名单、payload 按 per-op schema 校验(fail-closed,单条非法整批拒绝);与手势命令同队列、同撤销栈(ADR-0007)。
_Avoid_: AI 操作、自动剪辑
**时间线摘要 (Timeline Summary)**:
时间线结构化精简版(轨道/片段/时长/关键属性,不含媒体二进制),作为 AI 对话上下文输入;纯函数生成、可测试(ADR-0007)。
_Avoid_: 时间线导出、prompt 原文
**AI 助手插件 (editor-ai-assistant)**:
对话式剪辑预设插件:AI 只读问答(timeline.read)+ 命令执行(timeline.command)+ 聚合转写/字幕高亮/素材入轨入口;复用画布 agent 聊天 UI 交互模式,不共享会话上下文(ADR-0007)。
_Avoid_: 画布 Agent(指 canvas 会话桥接,与编辑器 AI 面板是两套会话)
_Avoid_: 预设项目、项目副本