| 1 | # RFC-05:Project-centric 工作流(supersedes RFC-04 storyboard-first) |
| 2 | |
| 3 | > **Status**: Draft v0.1 · **Supersedes** [RFC-04](./2026-05-26-spec-04-storyboard-workflow.md) |
| 4 | > **Date**: 2026-05-27 |
| 5 | > **Scope**: 把"分镜/场景/时间线"的复杂模型整体移除,改成 HTML Anything-style 的"项目 + 单模板 + HTML 预览 + 导出 MP4"工作流 |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## 来源(用户原话) |
| 10 | |
| 11 | > 它应该是跟 HTML anything 这个项目,产品的界面的结构是有点相似的——左边有一个项目的创建栏,创建一个项目之后呢,它可以在这个项目里面去输入或者上传文字、图片、视频等等的素材,然后可以根据他的这些素材,在右边的这个效果栏里面,它首先是可以选择一个 hyperframes 的模板,然后选完模板之后,就可以根据它的输入和上传的内容去生成一个 HTML 的预览的效果,然后他先在 HTML 的预览效果里面,先看整体的视频效果是怎么样的,然后确认之后再可以是导出一个视频。 |
| 12 | > |
| 13 | > 你这里的分镜剪辑这些我觉得好像都是不用的,不用考虑剪辑,因为它本身就是 HTML 做的视频嘛,所以你只要给它能够预览到每个画面的效果就好了。 |
| 14 | |
| 15 | --- |
| 16 | |
| 17 | ## 关键认知更新 |
| 18 | |
| 19 | 之前 RFC-04 假设 html-video 要做"创作者级 storyboard 编辑器"——用 agent 把素材编排成多 scene 序列、加转场、人审、拼成 MP4。这是**借了电影制片的工作流**,但放在"HTML 模板内部已经全权决定动画序列"的语境下是**重复抽象**: |
| 20 | |
| 21 | - HTML 模板本身就是一段完整视频的可视化逻辑(GSAP timeline 已经定义了视觉序列) |
| 22 | - 用户的真实诉求:选一个我喜欢的视觉风格 + 把我的素材塞进去 + 看效果 + 导出 |
| 23 | - 多 scene 拼接 / 转场 / 时间线 = 二次剪辑工作流,对**用 HTML 做单段视频**这个场景是过度设计 |
| 24 | |
| 25 | → 简化为:**Project = 素材 + 单模板 + 单视频**。 |
| 26 | |
| 27 | --- |
| 28 | |
| 29 | ## 数据模型 |
| 30 | |
| 31 | ```ts |
| 32 | // 新增 |
| 33 | export interface Project { |
| 34 | id: string; // proj_xxxxxx |
| 35 | name: string; // 用户起的名字 |
| 36 | intent?: string; // 可选一句话描述 |
| 37 | assets: Asset[]; // 素材(沿用 RFC-04 的 Asset 类型) |
| 38 | templateId: string | null; // 当前选中的模板 id;未选则 null |
| 39 | variables: Record<string, unknown>; // 用户填的 / 表单填的变量(template inputs schema 兼容) |
| 40 | preferences: UserPreferences; // 沿用:aspect / duration / fps / commercial |
| 41 | status: ProjectStatus; // 'draft' | 'previewed' | 'rendered' |
| 42 | lastPreviewHtmlPath?: string; // 最近一次 renderToHtml 的输出 |
| 43 | lastOutputMp4Path?: string; // 最近一次 export 的 MP4 路径 |
| 44 | createdAt: string; |
| 45 | updatedAt: string; |
| 46 | } |
| 47 | |
| 48 | export type ProjectStatus = 'draft' | 'previewed' | 'rendered'; |
| 49 | ``` |
| 50 | |
| 51 | **删除**(不再使用): |
| 52 | - `Storyboard` |
| 53 | - `Scene` |
| 54 | - `StoryboardOrchestrator` |
| 55 | - `SceneSuggestion` |
| 56 | - `TransitionId` / `AudioTrack`(audio 在 v0.2 重新考虑放 project level) |
| 57 | - `planScenes()` 启发式 planner(用户主动选模板,不需要 agent 自动拼接) |
| 58 | |
| 59 | --- |
| 60 | |
| 61 | ## 文件存储 |
| 62 | |
| 63 | ``` |
| 64 | project-root/ |
| 65 | └── .html-video/ |
| 66 | ├── projects/ |
| 67 | │ └── <project_id>/ |
| 68 | │ ├── project.json # Project metadata |
| 69 | │ ├── assets/ # 内容寻址(沿用 RFC-04 的 AssetStore 行为) |
| 70 | │ │ ├── <sha1>.png |
| 71 | │ │ └── <sha1>.json |
| 72 | │ ├── preview.html # 最近一次 HTML 预览 |
| 73 | │ ├── preview-poster.svg |
| 74 | │ └── output.mp4 # 最终导出 |
| 75 | ``` |
| 76 | |
| 77 | 把之前的 `.html-video/bundles/` + `.html-video/storyboards/` 合并成 `.html-video/projects/`。每个 project 自包含。 |
| 78 | |
| 79 | --- |
| 80 | |
| 81 | ## 工作流(端到端) |
| 82 | |
| 83 | ``` |
| 84 | 1. 用户点 "+ 新建项目" → 输入 name → 创建空 Project (status=draft, templateId=null) |
| 85 | 2. 用户在素材区上传: |
| 86 | - 拖拽图片 / 视频文件 |
| 87 | - 粘贴文字 |
| 88 | - 粘贴 JSON 数据 |
| 89 | → AssetStore 内容寻址入库,加入 project.assets[] |
| 90 | 3. 用户在右侧选模板(下拉 / grid): |
| 91 | - 切换 templateId |
| 92 | - 系统读 template inputs schema → 自动渲染表单 |
| 93 | - 已有变量值若 key 名匹配则保留(比如 title 模板间通用),其余清空 |
| 94 | 4. 用户在表单里改值(实时持久化到 project.variables) |
| 95 | - 字段绑定素材(image_path 字段下拉选 project.assets 里的 image type 资产) |
| 96 | - 文本字段直接打字 |
| 97 | - 数据 array 字段允许从 data type 资产载入或手填 |
| 98 | 5. 实时 HTML 预览(右侧 iframe,每次 vars 变更就 reload) |
| 99 | - status 改 'previewed' 一次(首次预览成功) |
| 100 | 6. 用户点 "导出 MP4": |
| 101 | - status 改 'rendered' |
| 102 | - 调 EngineAdapter.render() → MP4 写到 project 目录 + 给用户下载 |
| 103 | ``` |
| 104 | |
| 105 | **关键约定**: |
| 106 | - **预览即效果** —— HTML iframe 里看到的就是最终视频效果,没有"分镜阶段 vs 完成阶段"的区分。MP4 export 只是把 HTML 录成视频。 |
| 107 | - **没有 approval gate** —— RFC-04 那个 `approved` 中间状态删除。预览满意直接 export。 |
| 108 | - **没有跨模板拼接** —— 想要更复杂的多段视频?建多个 project,最终用户自己用其他工具拼(v0.3 可能加 "compose projects" 功能,v0.1 不做)。 |
| 109 | |
| 110 | --- |
| 111 | |
| 112 | ## CLI(新命令集) |
| 113 | |
| 114 | ```bash |
| 115 | # 项目管理 |
| 116 | html-video project create --name "OD plugins demo" [--intent "..."] [--aspect 16:9] |
| 117 | html-video project list |
| 118 | html-video project show <projectId> |
| 119 | html-video project delete <projectId> |
| 120 | |
| 121 | # 素材管理(在某个 project 上下文中) |
| 122 | html-video project add-asset <projectId> --file <path> |
| 123 | html-video project add-asset <projectId> --inline-text "..." |
| 124 | html-video project add-asset <projectId> --inline-data-file <path> |
| 125 | html-video project remove-asset <projectId> --asset <assetId> |
| 126 | |
| 127 | # 模板与变量 |
| 128 | html-video project set-template <projectId> --template <templateId> |
| 129 | html-video project set-vars <projectId> --vars-file vars.json |
| 130 | html-video project set-var <projectId> --key <key> --value <json> |
| 131 | |
| 132 | # 渲染 |
| 133 | html-video project preview <projectId> # 启 HTTP server 给 iframe 看 |
| 134 | html-video project render <projectId> --output out.mp4 |
| 135 | |
| 136 | # 全 studio(最高频用法 = 一个命令打开 IDE-style 三栏 UI) |
| 137 | html-video studio [--port 3071] # 起 project studio,浏览器打开 |
| 138 | ``` |
| 139 | |
| 140 | **保留命令**(不变): |
| 141 | - `html-video doctor` |
| 142 | - `html-video list-engines` |
| 143 | - `html-video search-templates` |
| 144 | - `html-video inspect-template <id>` |
| 145 | |
| 146 | **删除命令**: |
| 147 | - `upload`(合并到 `project add-asset`) |
| 148 | - `sb-generate / sb-edit / sb-preview / sb-render`(整个 storyboard 子树) |
| 149 | |
| 150 | --- |
| 151 | |
| 152 | ## UI(项目 Studio,HTML Anything-style 三栏) |
| 153 | |
| 154 | ``` |
| 155 | ┌────────────┬──────────────────────────────────────────────────────────┐ |
| 156 | │ PROJECTS │ ASSETS │ TEMPLATE & PREVIEW │ |
| 157 | │ ───────── │ ─────────────────────────────── │ ───────────────────── │ |
| 158 | │ + New │ 📷 [image] od-logo.png │ ▼ [data-bar-chart] │ |
| 159 | │ │ 📝 [text] "Design that ..." │ │ |
| 160 | │ ▶ proj 1 │ 📊 [data] plugin-stats.json │ ┌──── form ──────┐ │ |
| 161 | │ proj 2 │ │ │ Title: │ │ |
| 162 | │ proj 3 │ [ + Drop or paste assets ] │ │ ▢ "OD Plugins" │ │ |
| 163 | │ │ │ │ │ │ |
| 164 | │ │ │ │ Subtitle: │ │ |
| 165 | │ │ │ │ ▢ "" │ │ |
| 166 | │ │ │ │ │ │ |
| 167 | │ │ │ │ Data: 4 rows │ │ |
| 168 | │ │ │ │ ▾ from asset: │ │ |
| 169 | │ │ │ │ [📊 plug...] │ │ |
| 170 | │ │ │ └────────────────┘ │ |
| 171 | │ │ │ │ |
| 172 | │ │ │ ┌─── iframe ────┐ │ |
| 173 | │ │ │ │ HTML preview │ │ |
| 174 | │ │ │ │ (live re- │ │ |
| 175 | │ │ │ │ render on │ │ |
| 176 | │ │ │ │ var change) │ │ |
| 177 | │ │ │ └────────────────┘ │ |
| 178 | │ │ │ │ |
| 179 | │ │ │ [📥 Export MP4] │ |
| 180 | └────────────┴──────────────────────────────────────────────────────────┘ |
| 181 | ``` |
| 182 | |
| 183 | **关键交互细节**: |
| 184 | |
| 185 | 1. **左侧 PROJECTS** |
| 186 | - 列表 + "New project" 按钮 |
| 187 | - 选中态高亮 |
| 188 | - 项目缩略图(最近一次 preview poster),无则用文字 placeholder |
| 189 | |
| 190 | 2. **中间 ASSETS** |
| 191 | - 拖拽 / 粘贴 / 点击上传按钮 |
| 192 | - 每个资产一行:icon + 文件名 + size + 删除按钮 |
| 193 | - 资产支持点击预览(图片小弹窗、文字 hover 显示前 200 字) |
| 194 | |
| 195 | 3. **右侧 TEMPLATE & PREVIEW** |
| 196 | - 顶部:模板选择下拉(带搜索 / 缩略图) |
| 197 | - 切模板时:弹一个 toast "保留了 N 个共名变量,新模板还需要补 M 个字段" |
| 198 | - 中部:**按 inputs schema 自动渲染表单**: |
| 199 | - `type: string` → `<input type="text">` |
| 200 | - `type: number` → `<input type="number" min/max>` |
| 201 | - `type: string + enum` → `<select>` |
| 202 | - `type: array of {label,value}` → 简易 table 编辑器(增/删行) |
| 203 | - `type: string + filename pattern image_path` → asset picker dropdown filtering project.assets[type=image] |
| 204 | - 默认值从 schema `default` 取 |
| 205 | - 必填字段标 `*` |
| 206 | - 底部:iframe 实时预览 |
| 207 | - "Export MP4" 按钮:disabled 直到 templateId 有 + 必填字段全填 |
| 208 | |
| 209 | 4. **变量持久化**:表单字段每次 blur 自动 save 到 project.json(debounce 300ms)。 |
| 210 | |
| 211 | --- |
| 212 | |
| 213 | ## 表单自动渲染规则(关键技术点) |
| 214 | |
| 215 | 变量编辑表单是从 template `inputs.schema` 自动生成的(运行时反射)。规则: |
| 216 | |
| 217 | | schema feature | UI | |
| 218 | |---|---| |
| 219 | | `type: string` | text input | |
| 220 | | `type: string` + `enum` | select dropdown | |
| 221 | | `type: string` + `format: date` | date picker | |
| 222 | | `type: string` + key contains `_path` | **asset picker** —— 只列 project.assets 里 type 匹配的(image_path → image 资产) | |
| 223 | | `type: string` + `maxLength` | textarea if > 100 else input | |
| 224 | | `type: number` | number input with `min/max` | |
| 225 | | `type: boolean` | toggle | |
| 226 | | `type: array` of primitives | tag-input | |
| 227 | | `type: array` of objects | inline table(每个 item 一行,按 item schema 展开) | |
| 228 | | `type: object` | nested fieldset | |
| 229 | | `default` | pre-filled | |
| 230 | | `required` | red asterisk | |
| 231 | | `description` | hover tooltip | |
| 232 | |
| 233 | asset picker 的过滤启发式(v0.1): |
| 234 | |
| 235 | - `image_path` / `logo_path` / `*_image` → image |
| 236 | - `audio_path` / `bgm_path` → audio |
| 237 | - `video_path` → video |
| 238 | - `data` / `*_data` → data |
| 239 | - 其他 → 不绑定,用户手填字符串 |
| 240 | |
| 241 | v0.2 加 schema custom annotation `x-asset-type`(更精确)。 |
| 242 | |
| 243 | --- |
| 244 | |
| 245 | ## EngineAdapter 接口影响 |
| 246 | |
| 247 | 无变化。`render()` 和 `renderToHtml()` 接口都保留(RFC-01 v0.2 增量): |
| 248 | |
| 249 | - 项目预览 = `EngineAdapter.renderToHtml({ template, variables, config }, ctx)` |
| 250 | - 项目导出 = `EngineAdapter.render({ template, variables, config }, ctx)` |
| 251 | |
| 252 | 简化点:input 不再有 `Scene` 概念,直接是 Project 的 `templateId + variables`。 |
| 253 | |
| 254 | --- |
| 255 | |
| 256 | ## TemplateRegistry / TemplateMetadata 影响 |
| 257 | |
| 258 | 无变化(RFC-02)。只是说明 `inputs.schema` 现在被 UI 自动渲染表单消费——之前 RFC-02 已经定义了 schema 是 JSON Schema 兼容,UI 渲染器只是它的 consumer 之一(agent 也是 consumer,跟 RFC-03 一致)。 |
| 259 | |
| 260 | 唯一新增:**强烈建议**作者在 schema 字段上加 `description`(现在用作 form tooltip)和 `default`(用作 form 初值)。已有 5 个 reference templates 都满足。 |
| 261 | |
| 262 | --- |
| 263 | |
| 264 | ## v0.1 MVP 范围(修订) |
| 265 | |
| 266 | ✅ 重构: |
| 267 | - 删 storyboard 整套 |
| 268 | - core 加 Project / ProjectStore / ProjectOrchestrator (render preview / export) |
| 269 | - cli 改 project 命令族 |
| 270 | - UI 改三栏 studio(form 自动渲染) |
| 271 | |
| 272 | ✅ 不变: |
| 273 | - EngineAdapter 抽象 + adapter-hyperframes(保留 stub render,v0.2 接真 HF) |
| 274 | - TemplateRegistry + 5 reference templates |
| 275 | - AssetStore 内容寻址 |
| 276 | - doctor / list-engines / search-templates / inspect-template |
| 277 | |
| 278 | ❌ 不进 MVP: |
| 279 | - 多项目拼接 |
| 280 | - agent 自动选模板(用户主动选) |
| 281 | - 富文本素材编辑 |
| 282 | - 模板预览 grid(v0.2 做缩略图 + tag filter) |
| 283 | - 协作 / 多人审稿 |
| 284 | |
| 285 | --- |
| 286 | |
| 287 | ## 给 Joey 的话 |
| 288 | |
| 289 | 承认我的设计偏了——把"创作者用 HTML 做单段视频"理解成了"剧情片导演用 storyboard 拼镜头"。新版回归 HTML Anything 的极简心智,**项目 = 素材 + 模板 + 视频,仅此而已**。 |
| 290 | |
| 291 | 下一次 commit 会做完整重构(删 storyboard 全树,加 project + 三栏 studio + 表单自动渲染),跑完 smoke 重启 preview server 给你看。 |
| 292 | |
| 293 | --- |
| 294 | |
| 295 | ## Open Questions(v0.2 待定) |
| 296 | |
| 297 | 1. **变量与素材绑定的精确度** —— `image_path` 启发式可能误判,是否引入 `x-asset-type` 扩展? |
| 298 | 2. **多素材同字段** —— 如果模板要 1 个 image_path,但用户有 N 张图,建议开 N 个项目?还是模板支持 multi-image 的 v2? |
| 299 | 3. **预览实时性** —— 字段 blur debounce 300ms 够吗?还是需要 explicit "Refresh preview" 按钮(避免大模板每键入一次都重 render)? |
| 300 | 4. **历史版本** —— 项目要不要支持 "undo / 历史快照"?v0.1 不做,更新即覆盖 |
| 301 | 5. **导出选项** —— 导出 MP4 时让用户选分辨率 / fps / 时长?还是按模板默认值? |
| 302 |