auto-save 2026-05-17 11:01 (~5)

This commit is contained in:
2026-05-17 11:01:54 +08:00
parent a30a9de26e
commit 31b8738dac
5 changed files with 89 additions and 77 deletions

View File

@@ -1,18 +1,5 @@
{ {
"entries": [ "entries": [
{
"files_changed": 1,
"message": "Codex 会话活跃 · 最近命令codex · 1 项未提交变更 · 最近提交auto-save 2026-05-14 16:42 (~1)",
"ts": "2026-05-14T08:46:14Z",
"type": "session-heartbeat"
},
{
"files_changed": 1,
"hash": "78861dd",
"message": "auto-save 2026-05-14 16:47 (~1)",
"ts": "2026-05-14T16:47:57+08:00",
"type": "commit"
},
{ {
"files_changed": 1, "files_changed": 1,
"message": "Codex 会话活跃 · 最近命令codex · 1 项未提交变更 · 最近提交auto-save 2026-05-14 16:47 (~1)", "message": "Codex 会话活跃 · 最近命令codex · 1 项未提交变更 · 最近提交auto-save 2026-05-14 16:47 (~1)",
@@ -3268,6 +3255,19 @@
"type": "session-heartbeat", "type": "session-heartbeat",
"message": "Codex 会话活跃 · 最近命令codex · 分支 main · 1 项未提交变更 · 最近提交docs: adopt work dashboard delivery rules", "message": "Codex 会话活跃 · 最近命令codex · 分支 main · 1 项未提交变更 · 最近提交docs: adopt work dashboard delivery rules",
"files_changed": 1 "files_changed": 1
},
{
"ts": "2026-05-17T10:56:31+08:00",
"type": "commit",
"message": "auto-save 2026-05-17 10:56 (+1, ~2)",
"hash": "a30a9de",
"files_changed": 3
},
{
"ts": "2026-05-17T02:58:22Z",
"type": "session-heartbeat",
"message": "Codex 会话活跃 · 最近命令codex · 分支 main · 2 项未提交变更 · 最近提交auto-save 2026-05-17 10:56 (+1, ~2)",
"files_changed": 2
} }
] ]
} }

View File

@@ -27,7 +27,7 @@
"type": "web_login" "type": "web_login"
} }
], ],
"description": "SKG AI 素材生产管线第二条思路验证TK 链接 → 拆轨 → 目标化关键帧 + ASR/翻译 → 接 SKG 产品信息改写口播 → MiniMax 配音 → nano-banana-pro/GPT Image 生图 → Seedance/Kling/Veo3 多模型生视频 → 合成带文案成品", "description": "SKG 信息流广告快速复刻工作台TK/本地视频 → 视频抽帧 → 音频解析生成文案 → 剧情规划与 SKG 产品融入 → Seedance/Kling/Veo3 生成候选片段 → 选择组合成片;主界面为左侧单任务工作表,右侧无限画布暂时清空保留。",
"kind": "app", "kind": "app",
"name": "SKG Marketing Studio / SKG 营销内容工作台", "name": "SKG Marketing Studio / SKG 营销内容工作台",
"ownership": "company", "ownership": "company",

View File

@@ -11,6 +11,7 @@
- 详见 `CLAUDE.md` 立项决策段 + `.memory/plan.md` 七步管线拆解 - 详见 `CLAUDE.md` 立项决策段 + `.memory/plan.md` 七步管线拆解
- 风格:`04-Dark-Gallery-Ambient`(路径:`~/Projects/research/20260305-网页风格库/04-Dark-Gallery-Ambient.md` - 风格:`04-Dark-Gallery-Ambient`(路径:`~/Projects/research/20260305-网页风格库/04-Dark-Gallery-Ambient.md`
- 第一冲刺:步骤 1-4下载 / 拆轨 / 关键帧 / ASR+翻译) - 第一冲刺:步骤 1-4下载 / 拆轨 / 关键帧 / ASR+翻译)
- 当前产品方向2026-05-17 确认):优先做信息流广告快速复刻产出,不再把主界面做成可视化流程节点;主界面为左侧单任务工作表(素材输入、抽帧、音频文案、剧情规划/产品融入、候选片段),右侧保留但暂时清空无限画布。
## 部署事实 ## 部署事实
- 平台VPS `76.13.31.179`Ubuntu 24.04 / Docker Compose / Coolify Traefik - 平台VPS `76.13.31.179`Ubuntu 24.04 / Docker Compose / Coolify Traefik

View File

@@ -485,7 +485,7 @@
<h2>这个页面是产品协作地图,不是应用功能页。</h2> <h2>这个页面是产品协作地图,不是应用功能页。</h2>
<p> <p>
它把“你看到的界面、你想改的功能、实际要动的源码、可能影响的数据和接口”放在同一个地方。 它把“你看到的界面、你想改的功能、实际要动的源码、可能影响的数据和接口”放在同一个地方。
后续描述需求时,可以直接说“改源码地图里的某个区域 / 某个节点职责 / 某个接口行为”,这样改动范围会更准,也更容易追踪每次变更带来的影响。 后续描述需求时,可以直接说“改左侧看板里的某个工作段 / 右侧画布要承载什么 / 某个接口行为”,这样改动范围会更准,也更容易追踪每次变更带来的影响。
</p> </p>
<div class="meta-grid"> <div class="meta-grid">
<div class="meta"><b>项目路径</b><span>/Users/kangwan/Projects/business/20260512-20260512-skg-tk-二创验证</span></div> <div class="meta"><b>项目路径</b><span>/Users/kangwan/Projects/business/20260512-20260512-skg-tk-二创验证</span></div>
@@ -500,7 +500,7 @@
<div class="grid-3"> <div class="grid-3">
<div class="card"> <div class="card">
<h3>1. 先说你在改哪个产品区</h3> <h3>1. 先说你在改哪个产品区</h3>
<p>例如“镜头拆解 / 元素提取面板”、“元素改造 Storyboard 节点”、“分镜头编排下拉区 4 图槽”。不要只说“这里乱”,要指向页面里的功能区。</p> <p>例如“左侧看板的视频抽帧区”、“剧情规划 / 产品融入行”、“候选片段选择区”、“右侧空白画布”。不要只说“这里乱”,要指向页面里的功能区。</p>
</div> </div>
<div class="card"> <div class="card">
<h3>2. 再说这个区应该承担什么职责</h3> <h3>2. 再说这个区应该承担什么职责</h3>
@@ -569,17 +569,14 @@
<section id="pipeline" data-search> <section id="pipeline" data-search>
<h2>业务管线</h2> <h2>业务管线</h2>
<p>当前产品不是“复制别人的视频”,而是拆解参考视频,提取可借鉴的镜头元素,再改造成 SKG 产品语境的视频素材</p> <p>当前产品方向收敛为“信息流广告快速复刻工作台”:不再把主界面设计成可视化节点流程,而是把单个视频任务放进左侧工作表,按抽帧、音频文案、剧情规划、产品融入、候选片段选择推进。右侧保留无限画布能力,但当前版本先清空,等待后续定义要展示的内容</p>
<div class="pipeline"> <div class="pipeline">
<div class="step"><div class="num">1</div><h3>输入</h3><p>TK 链接或本地上传,后端下载/保存源视频</p></div> <div class="step"><div class="num">1</div><h3>素材输入</h3><p>TK / 信息流视频链接或本地上传,创建单个当前任务</p></div>
<div class="step"><div class="num">2</div><h3>镜头拆解</h3><p>拆轨、抽关键帧、手动加帧,形成参考分镜池。当前主题默认直接抽 12 帧,并使用“透明骨架人”抽帧目标:抽帧阶段只走本机算力扫描、评分、去重和时间覆盖;透明骨架人的语义判断放到后续审核/识别,不在抽帧阶段逐帧调用 Vision</p></div> <div class="step"><div class="num">2</div><h3>视频抽帧</h3><p>按目标、精度和数量抽关键帧,形成可勾选的分镜池;手动加帧和深度素材处理能力仍在底层保留</p></div>
<div class="step"><div class="num">3</div><h3>清洗水印</h3><p>对关键帧做全图或区域清洗,清洗版先进入待审核状态;确认后可单张替换,也可一键替换全部待应用清洗版</p></div> <div class="step"><div class="num">3</div><h3>音频文案</h3><p>从原视频提取音频并生成口播文案,作为后续剧情规划和产品植入的节奏依据</p></div>
<div class="step"><div class="num">4</div><h3>主体识别</h3><p>识别场景和主体候选,只是候选,不应锁死</p></div> <div class="step"><div class="num">4</div><h3>剧情规划</h3><p>对选中的关键帧逐行填写剧情、产品融入、镜头动作和时长</p></div>
<div class="step"><div class="num">5</div><h3>素材准备</h3><p>清洗关键帧,把多张关键帧作为同一主体的参考,先重绘六张标准站立主体资产图,再按关键帧生成多个去主体、相似或换风格场景图</p></div> <div class="step"><div class="num">5</div><h3>生成片段</h3><p>按每行分镜调用 Seedance / Kling / Veo 生成候选视频片段,并回写到当前任务</p></div>
<div class="step"><div class="num">6</div><h3>分镜改造</h3><p>把内置透明骨架人角色、场景/镜头描述和固定 SKG 产品放入分镜结构;产品融合使用纵向 6 行镜头工作表,只选角色、微调描述词和秒数</p></div> <div class="step"><div class="num">6</div><h3>选择组合</h3><p>在候选片段区勾选适合组合成最终广告的片段;最终合成 mp4 当前仍未实现</p></div>
<div class="step"><div class="num">7</div><h3>生成视频</h3><p>普通分镜可调用 Seedance / Kling / Veo 3产品融合自动传入所选角色 7 张参考图和固定 4 张 SKG 产品图,用 Seedance 按秒数生成视频,结果回写到对应行。</p></div>
<div class="step"><div class="num">8</div><h3>声音文案</h3><p>音频轨独立处理:提取原音频并按实际秒数生成 SKG 英文产品介绍 voice-overASR/翻译只作为改前对照和节奏参考;配置 MiniMax 后从男声、女声、成熟声池随机生成自然英文配音 mp3。底部音频条播放原音频时指针会按时间走过字幕节点。</p></div>
<div class="step"><div class="num">9</div><h3>合成成品</h3><p>片段、字幕、配音、转场合成最终 mp4。当前未实现。</p></div>
</div> </div>
</section> </section>
@@ -591,14 +588,15 @@
<table> <table>
<tbody> <tbody>
<tr><td><code>web/next.config.mjs</code></td><td>Next.js 构建配置:静态导出、图片不走优化、禁用开发环境左下角 Next Dev Indicator避免本地登录页截图出现额外字母标识。</td></tr> <tr><td><code>web/next.config.mjs</code></td><td>Next.js 构建配置:静态导出、图片不走优化、禁用开发环境左下角 Next Dev Indicator避免本地登录页截图出现额外字母标识。</td></tr>
<tr><td><code>web/app/page.tsx</code></td><td>产品工作台主状态jobs、activeJobId、按 job 隔离的 selectedFrames/详情面板状态、clipboard、ReactFlow 节点和边;负责打开/找回画布工作面板</td></tr> <tr><td><code>web/app/page.tsx</code></td><td>产品工作台主状态jobs、activeJobId、按 job 隔离的 selectedFrames/音频条/生成任务状态;主渲染改为左侧广告复刻看板 + 右侧空白 ReactFlow 无限画布</td></tr>
<tr><td><code>web/components/ad-recreation-board.tsx</code></td><td>左侧信息流广告复刻工作表:素材输入、视频抽帧、音频文案、关键帧选择、剧情规划/产品融入、候选片段选择。</td></tr>
<tr><td><code>web/app/login/page.tsx</code></td><td>生产登录页:访问账号/访问密钥表单、保持登录、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加一个组合登录框,桌面端左侧是动态角色,右侧是图标化登录表单;面板左上角展示官网 SKG 字标和中文“营销内容工作台”系统标识。</td></tr> <tr><td><code>web/app/login/page.tsx</code></td><td>生产登录页:访问账号/访问密钥表单、保持登录、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加一个组合登录框,桌面端左侧是动态角色,右侧是图标化登录表单;面板左上角展示官网 SKG 字标和中文“营销内容工作台”系统标识。</td></tr>
<tr><td><code>web/app/login/layout.tsx</code></td><td>登录路由专属 layout覆盖全站默认网页标题和描述为空避免 <code>/login</code> 继承工作台 metadata 后在页面源码里继续出现登录界面文字以外的文案。</td></tr> <tr><td><code>web/app/login/layout.tsx</code></td><td>登录路由专属 layout覆盖全站默认网页标题和描述为空避免 <code>/login</code> 继承工作台 metadata 后在页面源码里继续出现登录界面文字以外的文案。</td></tr>
<tr><td><code>web/components/login/oasis-canvas.tsx</code></td><td>登录页全屏动态视觉层:用 iframe 直接承载下载包 <code>web/public/oasis-source/index.html</code> 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标同时用原生事件和 <code>postMessage</code> 转发给 iframe避免登录面板或输入框遮挡时草地失去鼠标响应。</td></tr> <tr><td><code>web/components/login/oasis-canvas.tsx</code></td><td>登录页全屏动态视觉层:用 iframe 直接承载下载包 <code>web/public/oasis-source/index.html</code> 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标同时用原生事件和 <code>postMessage</code> 转发给 iframe避免登录面板或输入框遮挡时草地失去鼠标响应。</td></tr>
<tr><td><code>web/public/oasis-source/index.html</code></td><td>从下载包 <code>remix-3d-website-the-digital-o</code> 复制来的原始视觉源码。嵌入登录页时会隐藏 demo 站自己的导航、文字和设置面板保留原多段滚动背景变化、WebGPU 草场、景深、风动和鼠标交互源码;末端阶段保留,只禁用原 footer 出现时把 canvas 上移的逻辑,避免底部露黑边。</td></tr> <tr><td><code>web/public/oasis-source/index.html</code></td><td>从下载包 <code>remix-3d-website-the-digital-o</code> 复制来的原始视觉源码。嵌入登录页时会隐藏 demo 站自己的导航、文字和设置面板保留原多段滚动背景变化、WebGPU 草场、景深、风动和鼠标交互源码;末端阶段保留,只禁用原 footer 出现时把 canvas 上移的逻辑,避免底部露黑边。</td></tr>
<tr><td><code>web/public/skg-logo-black.svg</code></td><td>从官网 <code>https://cn.skg.com/logo-black.svg</code> 获取的 SKG 官方黑色 SVG 字标;登录页通过 CSS 反相成白色玻璃标识使用。</td></tr> <tr><td><code>web/public/skg-logo-black.svg</code></td><td>从官网 <code>https://cn.skg.com/logo-black.svg</code> 获取的 SKG 官方黑色 SVG 字标;登录页通过 CSS 反相成白色玻璃标识使用。</td></tr>
<tr><td><code>web/components/login/animated-login-characters.tsx</code></td><td>登录页四个几何动态角色组件:当前嵌入登录框顶部,去掉独立网格背景,保留鼠标眼神跟随、输入、显示密码、错误和成功状态反馈。</td></tr> <tr><td><code>web/components/login/animated-login-characters.tsx</code></td><td>登录页四个几何动态角色组件:当前嵌入登录框顶部,去掉独立网格背景,保留鼠标眼神跟随、输入、显示密码、错误和成功状态反馈。</td></tr>
<tr><td><code>web/components/nodes/index.tsx</code></td><td>DAG 节点定义Input、VisualLab、Audio、Compose以及画布工作面板 KeyframePanel / VideoFramePanel旧 Keyframe/Storyboard/VideoGen 组件保留但不再挂主画布。</td></tr> <tr><td><code>web/components/nodes/index.tsx</code></td><td>DAG 节点和深度素材面板定义仍保留,当前主界面不再把这些节点挂到画布</td></tr>
<tr><td><code>web/components/audio-strip.tsx</code></td><td>底部吸附音频条:可拖拽调整高度;播放原音频时移动指针,逐个高亮英文/中文字幕节点和对应波形,并在右侧固定显示按原音频时长生成的 SKG 英文产品口播和 MiniMax 随机英文配音。</td></tr> <tr><td><code>web/components/audio-strip.tsx</code></td><td>底部吸附音频条:可拖拽调整高度;播放原音频时移动指针,逐个高亮英文/中文字幕节点和对应波形,并在右侧固定显示按原音频时长生成的 SKG 英文产品口播和 MiniMax 随机英文配音。</td></tr>
<tr><td><code>web/components/lightbox.tsx</code></td><td>关键帧素材准备面板:清洗、统一主体候选、参考帧网格、六张主体重绘图、每帧去主体场景图、纵向 6 行产品融合镜头工作表和审核。</td></tr> <tr><td><code>web/components/lightbox.tsx</code></td><td>关键帧素材准备面板:清洗、统一主体候选、参考帧网格、六张主体重绘图、每帧去主体场景图、纵向 6 行产品融合镜头工作表和审核。</td></tr>
<tr><td><code>web/components/product-library-picker.tsx</code></td><td>SKG 内置白底产品图库选择器:搜索、品类筛选、预览尺寸,并把库内图片复制为当前 job 的 <code>asset</code></td></tr> <tr><td><code>web/components/product-library-picker.tsx</code></td><td>SKG 内置白底产品图库选择器:搜索、品类筛选、预览尺寸,并把库内图片复制为当前 job 的 <code>asset</code></td></tr>
@@ -626,12 +624,10 @@
</div> </div>
<pre>前端主链路: <pre>前端主链路:
web/app/page.tsx web/app/page.tsx
-> ReactFlow 节点web/components/nodes/index.tsx -> 左侧复刻看板web/components/ad-recreation-board.tsx
-> 主画布Input → VisualLab / Audio → Compose -> 右侧 ReactFlow 无限画布:当前 nodes=[] / edges=[],先保留空白画布能力
-> 底部音频条web/components/audio-strip.tsx原音频播放 / 指针 / 英文 / 中文 / 波形 / 英文改写稿) -> 底部音频条web/components/audio-strip.tsx原音频播放 / 指针 / 英文 / 中文 / 波形 / 英文改写稿)
-> 画布内视频抽帧面板InputNode 单击视频缩略图打开 videoFramePanel -> 旧节点/深度素材面板web/components/nodes/index.tsx、web/components/lightbox.tsx、web/components/storyboard-workbench.tsx底层保留当前不作为主入口
-> 画布内镜头拆解面板VisualLabNode 打开 keyframePanel内嵌 web/components/lightbox.tsx
-> 分镜工作台web/components/storyboard-workbench.tsx底层保留
-> API 契约web/lib/api.ts -> API 契约web/lib/api.ts
后端主链路: 后端主链路:
@@ -645,22 +641,22 @@ api/main.py
<h2>界面区域到源码</h2> <h2>界面区域到源码</h2>
<div class="flow"> <div class="flow">
<div class="flow-row"> <div class="flow-row">
<div><strong>你看到的区域</strong><span>Input 节点和视频缩略图</span></div> <div><strong>你看到的区域</strong><span>左侧信息流广告复刻看板</span></div>
<div><strong>主要源码</strong><span><code>InputNode</code> in <code>web/components/nodes/index.tsx</code>;状态处理<code>page.tsx</code></span></div> <div><strong>主要源码</strong><span><code>AdRecreationBoard</code> in <code>web/components/ad-recreation-board.tsx</code>;状态、轮询和接口回写仍<code>web/app/page.tsx</code></span></div>
<div><strong>适合怎么描述</strong><span>输入/下载/视频就绪这个节点应该如何提示用户下一步”。</span></div> <div><strong>适合怎么描述</strong><span>左侧看板里的素材输入、抽帧、音频文案、分镜规划、候选片段选择要如何调整”。</span></div>
</div> </div>
<div class="flow-row"> <div class="flow-row">
<div><strong>你看到的区域</strong><span>画面工作台 · Visual Lab</span></div> <div><strong>你看到的区域</strong><span>右侧无限画布</span></div>
<div><strong>主要源码</strong><span><code>VisualLabNode</code> in <code>web/components/nodes/index.tsx</code>;它现在是素材准备看板,汇总关键帧、主体资产包、场景图和视频任务</span></div> <div><strong>主要源码</strong><span><code>ReactFlow</code> in <code>web/app/page.tsx</code>;当前传入空 <code>nodes=[]</code> / <code>edges=[]</code>,只保留缩放和平移画布能力</span></div>
<div><strong>适合怎么描述</strong><span>画面工作台的素材准备进度、分组缩略图、关键帧审核入口和后续分镜入口应该如何组织”。</span></div> <div><strong>适合怎么描述</strong><span>右侧空白画布后续应该展示哪些工作对象、是否需要拖拽、预览或对比”。</span></div>
</div> </div>
<div class="flow-row"> <div class="flow-row">
<div><strong>你看到的区域</strong><span>关键帧素材审核面板</span></div> <div><strong>你看到的区域</strong><span>旧深度素材面板(当前不作为主路径)</span></div>
<div><strong>主要源码</strong><span><code>FrameLightbox</code>;按“原图/清洗、主体资产、首尾帧、产品融合、审核”五个页签组织;左侧只放主图/框选画布,但主体资产页左侧改为全部已清洗/已选参考帧网格,首尾帧页左侧显示全部关键帧并可勾选人物/机位参考。主体识别页会显示透明骨架人目标和 Vision 验收分数。清洗页右侧支持一键清洗未处理帧、单张替换清洗版和一键替换全部待应用清洗版;批量替换顺序调用 <code>applyCleanedFrame</code>,不新增后端接口。产品融合页左侧是纵向 6 行镜头工作表:顶部选择 5 个内置透明骨架人角色之一,并常驻显示桌面四张真实 SKG 产品角度图;每行只显示已预填场景/产品使用/享受描述、秒数、生成按钮和可横向追加的视频历史结果,产品图和结果视频都支持鼠标停留放大预览。描述词内置 36 条镜头语言模板,按“建立出场、产品入画、佩戴贴合、使用感受、生活延展、收尾记忆”排列,并且会按角色自动改写场景气质、使用动作和享受状态。每行还内置角色参考图调度:例如正面/半身用于出场,侧面/背部特写用于佩戴贴合,半身/背部特写用于收尾产品记忆点。点击“换一组”只刷新 6 行描述词。四张桌面 SKG 产品图是真实产品真源,所选角色 7 张参考图是人物身份参考,生成时分别通过 <code>copyProductLibraryAsset</code><code>copyCharacterLibraryAssets</code> 自动写入当前 job视频 prompt 要求产品作为外置刚性实物合成到后颈外侧,禁止穿模、融进透明身体或重绘产品。不再暴露产品角度槽、产品融合辅助栏、产品图库选择器或首尾帧槽。主体资产页只确认一个统一主体,后端按参考重绘六张纯背景、占满画面的标准站立透明骨架人资产图;首尾帧页保留给旧流程/单独生图,不再是产品融合必填步骤。相关接口包括 <code>cleanupFrame</code><code>applyCleanedFrame</code><code>addElement</code><code>generateSubjectAssets</code><code>generateSceneAsset</code><code>copyProductLibraryAsset</code><code>copyCharacterLibraryAssets</code></span></div> <div><strong>主要源码</strong><span><code>FrameLightbox</code>;按“原图/清洗、主体资产、首尾帧、产品融合、审核”五个页签组织;左侧只放主图/框选画布,但主体资产页左侧改为全部已清洗/已选参考帧网格,首尾帧页左侧显示全部关键帧并可勾选人物/机位参考。主体识别页会显示透明骨架人目标和 Vision 验收分数。清洗页右侧支持一键清洗未处理帧、单张替换清洗版和一键替换全部待应用清洗版;批量替换顺序调用 <code>applyCleanedFrame</code>,不新增后端接口。产品融合页左侧是纵向 6 行镜头工作表:顶部选择 5 个内置透明骨架人角色之一,并常驻显示桌面四张真实 SKG 产品角度图;每行只显示已预填场景/产品使用/享受描述、秒数、生成按钮和可横向追加的视频历史结果,产品图和结果视频都支持鼠标停留放大预览。描述词内置 36 条镜头语言模板,按“建立出场、产品入画、佩戴贴合、使用感受、生活延展、收尾记忆”排列,并且会按角色自动改写场景气质、使用动作和享受状态。每行还内置角色参考图调度:例如正面/半身用于出场,侧面/背部特写用于佩戴贴合,半身/背部特写用于收尾产品记忆点。点击“换一组”只刷新 6 行描述词。四张桌面 SKG 产品图是真实产品真源,所选角色 7 张参考图是人物身份参考,生成时分别通过 <code>copyProductLibraryAsset</code><code>copyCharacterLibraryAssets</code> 自动写入当前 job视频 prompt 要求产品作为外置刚性实物合成到后颈外侧,禁止穿模、融进透明身体或重绘产品。不再暴露产品角度槽、产品融合辅助栏、产品图库选择器或首尾帧槽。主体资产页只确认一个统一主体,后端按参考重绘六张纯背景、占满画面的标准站立透明骨架人资产图;首尾帧页保留给旧流程/单独生图,不再是产品融合必填步骤。相关接口包括 <code>cleanupFrame</code><code>applyCleanedFrame</code><code>addElement</code><code>generateSubjectAssets</code><code>generateSceneAsset</code><code>copyProductLibraryAsset</code><code>copyCharacterLibraryAssets</code></span></div>
<div><strong>适合怎么描述</strong><span>“这一组关键帧如何共同生成一个统一主体包;某张关键帧的水印、去主体场景图、产品融合镜头组和质量风险应该如何审核”。</span></div> <div><strong>适合怎么描述</strong><span>“这一组关键帧如何共同生成一个统一主体包;某张关键帧的水印、去主体场景图、产品融合镜头组和质量风险应该如何审核”。</span></div>
</div> </div>
<div class="flow-row"> <div class="flow-row">
<div><strong>你看到的区域</strong><span>顶部分镜头编排下拉面板</span></div> <div><strong>你看到的区域</strong><span>旧分镜明细区(底层保留)</span></div>
<div><strong>主要源码</strong><span><code>StoryboardWorkbench</code>;保存到 <code>frame.storyboard</code>;接口 <code>PUT /storyboard</code>。SKG 产品参考区同时支持上传、剪贴板和内置白底产品库。</span></div> <div><strong>主要源码</strong><span><code>StoryboardWorkbench</code>;保存到 <code>frame.storyboard</code>;接口 <code>PUT /storyboard</code>。SKG 产品参考区同时支持上传、剪贴板和内置白底产品库。</span></div>
<div><strong>适合怎么描述</strong><span>“每个分镜需要哪些图片槽、哪些改造说明,哪些 SKG 产品图要作为视频生成参考”。</span></div> <div><strong>适合怎么描述</strong><span>“每个分镜需要哪些图片槽、哪些改造说明,哪些 SKG 产品图要作为视频生成参考”。</span></div>
</div> </div>
@@ -851,34 +847,34 @@ SubjectAsset {
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td><span class="tag blue">输入 Input</span></td> <td><span class="tag blue">左侧复刻看板</span></td>
<td>创建/上传任务,显示视频就绪;每个视频缩略图上方都有绑定自己的自动抽帧快捷工具条,默认只露出目标和抽帧按钮,张数/自动精度收进设置;也可在视频抽帧侧边面板内自动抽帧。多个视频抽帧可先后入队,切换 active 视频不会清空其他视频已选帧或关闭它们的异步生成回写</td> <td>承载当前单任务主路径:导入视频、抽帧、解析音频、选择关键帧、填写剧情/产品融入、提交单段视频生成、勾选候选片段。它是当前唯一主工作入口</td>
<td>不要自动一路跑到 ASR 或生图;用户需要控制解析节奏</td> <td>不要再拆成多个画布节点;不要把右侧空白画布的职责提前塞回看板</td>
<td><code>page.tsx</code><code>InputNode</code><code>VideoFramePanelNode</code><code>api/main.py</code></td> <td><code>web/components/ad-recreation-board.tsx</code><code>web/app/page.tsx</code></td>
</tr> </tr>
<tr> <tr>
<td><span class="tag violet">画面工作台 Visual Lab</span></td> <td><span class="tag violet">右侧无限画布</span></td>
<td>作为素材准备看板:显示准备进度、质量风险、关键帧 / 统一主体包 / 场景图 / 分镜视频四个入口;上方缩略图按关键帧、主体包、场景图、视频任务分组。点击关键帧进入素材审核面板,点击资产图复制到分镜编排</td> <td>当前只保留 ReactFlow 的无限画布能力,主渲染传空节点和空边。后续如果需要展示素材对比、片段组合或时间线,再明确画布对象后实现</td>
<td>不要在主卡片里堆复杂表单;主卡片只做状态总览和入口</td> <td>当前不要恢复 Input / Visual Lab / Audio / Compose 这类流程节点</td>
<td><code>VisualLabNode</code><code>FrameLightbox</code><code>generateSceneAsset</code><code>generateSubjectAssets</code>、视频任务接口</td> <td><code>web/app/page.tsx</code></td>
</tr> </tr>
<tr> <tr>
<td><span class="tag violet">分镜工作台</span></td> <td><span class="tag violet">旧节点 / 深度素材面板</span></td>
<td>每个分镜填 4 图槽和改造 brief为后续生成首帧/视频片段做准备</td> <td><code>InputNode</code><code>VisualLabNode</code><code>AudioNode</code><code>ComposeNode</code><code>FrameLightbox</code> 等底层能力暂保留,避免本次大改同时破坏抽帧、音频、素材处理和历史 job 数据</td>
<td>当前不负责真实调用视频模型</td> <td>不要作为当前用户主路径展示;后续迁移时按看板工作流重新整理</td>
<td><code>StoryboardWorkbench</code><code>updateStoryboard</code></td> <td><code>web/components/nodes/index.tsx</code><code>web/components/lightbox.tsx</code><code>web/components/storyboard-workbench.tsx</code></td>
</tr> </tr>
<tr> <tr>
<td><span class="tag gray">Audio / ASR / Rewrite</span></td> <td><span class="tag gray">音频条</span></td>
<td>独立声音文案轨:从 <code>source.mp4</code> 直接提取 <code>audio.wav</code>,按原音频时长生成 SKG 产品语境英文 voice-overASR/翻译保留为改前对照和节奏参考。MiniMax T2A 配置后从男声、女声、成熟声池随机生成自然英文配音 mp3。不再等待抽帧完成用户在主画布 <code>AudioNode</code> 点击卡片或“提取音频 / 重新提取音频”即可打开底部音频条并启动;即使视觉抽帧正在进行,也通过 <code>audio_script.status</code> 并行管理音频忙碌态。<code>AudioNode</code> 用“改前 · 原音频 / 改后 · SKG Product VO”摘要展示底部 <code>AudioStrip</code> 吸附屏幕底端,可拖拽调整高度,按时间段展示英文、中文翻译和波形;原音频播放时指针同步穿过字幕节点,右侧显示英文产品口播和 MiniMax 英文配音</td> <td>左侧看板触发音频解析,底部 <code>AudioStrip</code> 仍负责原音频播放、字幕/口播文本、波形和配音预览</td>
<td>不要阻断视觉素材管线。</td> <td>不要阻断视觉素材管线。</td>
<td><code>AudioNode</code><code>AudioStrip</code><code>ASRNode</code><code>TranslateNode</code><code>RewriteNode</code><code>pipeline_transcribe</code><code>AudioScript</code></td> <td><code>web/components/audio-strip.tsx</code><code>pipeline_transcribe</code><code>AudioScript</code></td>
</tr> </tr>
<tr> <tr>
<td><span class="tag green">Video / Compose</span></td> <td><span class="tag green">候选片段</span></td>
<td>视频任务状态展示在 Visual LabCompose 承载最终合成。</td> <td>生成视频结果直接在左侧看板的候选片段区展示和勾选,后续再接组合成</td>
<td>不要把 Compose 提前变成视频生成控制台</td> <td>不要把 Compose 提前变成最终剪辑台;最终合成仍是占位</td>
<td><code>VisualLabNode</code><code>/storyboard/video</code><code>generated_videos</code><code>ComposeNode</code></td> <td><code>/storyboard/video</code><code>generated_videos</code><code>AdRecreationBoard</code></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -907,7 +903,7 @@ SubjectAsset {
<li>ASR优先走当前 OpenAI-compatible 音频转写入口;如果该网关没有 <code>/audio/transcriptions</code>,自动 fallback 到 <code>ASR_FALLBACK_MODEL</code>(默认 <code>gemini-2.5-flash</code>)的多模态音频识别。</li> <li>ASR优先走当前 OpenAI-compatible 音频转写入口;如果该网关没有 <code>/audio/transcriptions</code>,自动 fallback 到 <code>ASR_FALLBACK_MODEL</code>(默认 <code>gemini-2.5-flash</code>)的多模态音频识别。</li>
<li>MiniMax当前接入的是官方 T2A 英文配音能力,不是 ASR默认随机音色池是 <code>English_magnetic_voiced_man</code><code>English_Upbeat_Woman</code><code>English_MaturePartner</code>。API Key 只能放本地环境变量,不能写入仓库。</li> <li>MiniMax当前接入的是官方 T2A 英文配音能力,不是 ASR默认随机音色池是 <code>English_magnetic_voiced_man</code><code>English_Upbeat_Woman</code><code>English_MaturePartner</code>。API Key 只能放本地环境变量,不能写入仓库。</li>
<li>Audio Product Brief默认是通用 SKG 放松产品卖点,后续可改成跟已选产品库条目联动。</li> <li>Audio Product Brief默认是通用 SKG 放松产品卖点,后续可改成跟已选产品库条目联动。</li>
<li>Video Gen模型层按业务保留 Seedance / Kling / Veo/Voe 选择;后端已支持 Poe、火山方舟和 SKG 豆包视频网关。Seedance 可通过 <code>VIDEO_API_BASE_URL=https://ai.skg.com/doubao</code> 走 content JSON 异步任务,提交后写入 Video Gen 节点并轮询到完成。</li> <li>Video Gen模型层按业务保留 Seedance / Kling / Veo/Voe 选择;后端已支持 Poe、火山方舟和 SKG 豆包视频网关。Seedance 可通过 <code>VIDEO_API_BASE_URL=https://ai.skg.com/doubao</code> 走 content JSON 异步任务,提交后写入候选片段并轮询到完成。</li>
<li>Compose还没做本地 ffmpeg 字幕/TTS 合成。</li> <li>Compose还没做本地 ffmpeg 字幕/TTS 合成。</li>
</ul> </ul>
</div> </div>
@@ -921,24 +917,24 @@ SubjectAsset {
<h2>需求描述模板</h2> <h2>需求描述模板</h2>
<div class="todo"> <div class="todo">
<div class="todo-item"> <div class="todo-item">
<h3>关键帧素材准备</h3> <h3>左侧看板</h3>
<p>“我在关键帧素材准备面板里,主体候选应该怎么编辑/删除;主体资产包怎么生成;场景图怎么基于主体去除、换风格、审核、复制到分镜。”</p> <p>“我在左侧复刻看板的抽帧 / 音频文案 / 分镜规划 / 候选片段区,这里应该怎么展示、编辑、保存和触发下一步。”</p>
</div> </div>
<div class="todo-item"> <div class="todo-item">
<h3> Storyboard 节点</h3> <h3>右侧画布</h3>
<p>我在 DAG 的元素改造节点它上方缩略图展示什么、hover 预览什么、点击后是否进入工作台、是否自动选中对应分镜。”</p> <p>右侧无限画布现在是空的,我希望它展示素材对比 / 片段组合 / 时间线 / 预览墙中的哪一种,以及这些对象是否需要拖拽和缩放。”</p>
</div> </div>
<div class="todo-item"> <div class="todo-item">
<h3>改分镜工作台</h3> <h3>改分镜字段</h3>
<p>我在顶部分镜头编排下拉面板,每个分镜需要哪些槽位、字段如何命名、保存后如何传给后续生成视频。”</p> <p>“每个分镜需要哪些文本字段、图片参考、秒数、模型选择和自动保存规则,保存后如何传给生成视频。”</p>
</div> </div>
<div class="todo-item"> <div class="todo-item">
<h3>改数据/接口</h3> <h3>改数据/接口</h3>
<p>“这个动作需要持久化到 state.json字段加在 Job/KeyFrame/KeyElement/StoryboardScene 哪一层,刷新后要恢复。”</p> <p>“这个动作需要持久化到 state.json字段加在 Job/KeyFrame/KeyElement/StoryboardScene 哪一层,刷新后要恢复。”</p>
</div> </div>
<div class="todo-item"> <div class="todo-item">
<h3>节点语义</h3> <h3>工作区语义</h3>
<p>“这个节点的业务职责要改,不只是 UI 文案;请同步更新节点标题、subtitle、说明、可点击行为、状态推导和本源码解析页。”</p> <p>“这个工作区的业务职责要改,不只是 UI 文案;请同步更新标题、说明、可点击行为、状态推导和本源码解析页。”</p>
</div> </div>
</div> </div>
</section> </section>
@@ -947,6 +943,18 @@ SubjectAsset {
<h2>变更记录</h2> <h2>变更记录</h2>
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p> <p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
<div class="changelog"> <div class="changelog">
<article class="change">
<header>
<h3>2026-05-17 · 主界面改为左侧信息流广告复刻看板</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>此前主界面把输入、画面、音频、合成组织成 ReactFlow 可视化节点,但当前目标已确认是“快速产出信息流广告复刻片段”,节点式展示增加理解成本。</p>
<p><strong>改动:</strong>新增 <code>web/components/ad-recreation-board.tsx</code>,把素材输入、视频抽帧、音频文案、关键帧选择、剧情规划/产品融入和候选片段选择合并到左侧工作表。<code>web/app/page.tsx</code> 主渲染改为左侧看板 + 右侧空白 ReactFlow 无限画布,画布节点和边暂不展示。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/ad-recreation-board.tsx</code><code>RULES.md</code><code>.project.json</code><code>docs/source-analysis.html</code>。后续需求应优先描述“左侧看板里的哪一段工作表”和“右侧空白画布未来要承载什么”。</p>
</div>
</article>
<article class="change"> <article class="change">
<header> <header>
<h3>2026-05-16 · 登录页 Logo 静态资源公开访问</h3> <h3>2026-05-16 · 登录页 Logo 静态资源公开访问</h3>
@@ -2354,7 +2362,7 @@ SubjectAsset {
<tr><th>改动类型</th><th>必须更新本页哪里</th><th>原因</th></tr> <tr><th>改动类型</th><th>必须更新本页哪里</th><th>原因</th></tr>
</thead> </thead>
<tbody> <tbody>
<tr><td>节点文案 / 节点功能</td><td>业务管线、节点职责边界、界面区域到源码、变更记录</td><td>避免用户按旧节点理解描述需求。</td></tr> <tr><td>看板文案 / 工作区功能</td><td>业务管线、节点职责边界、界面区域到源码、变更记录</td><td>避免用户按旧节点理解描述需求。</td></tr>
<tr><td>新增 / 修改接口</td><td>接口地图、数据模型、变更记录</td><td>避免前后端契约不清。</td></tr> <tr><td>新增 / 修改接口</td><td>接口地图、数据模型、变更记录</td><td>避免前后端契约不清。</td></tr>
<tr><td>新增数据字段</td><td>数据模型、源码结构地图、变更记录</td><td>刷新恢复和 state.json 依赖字段一致。</td></tr> <tr><td>新增数据字段</td><td>数据模型、源码结构地图、变更记录</td><td>刷新恢复和 state.json 依赖字段一致。</td></tr>
<tr><td>改交互路径</td><td>界面区域到源码、需求描述模板、变更记录</td><td>用户描述“点击哪里”时必须和真实路径一致。</td></tr> <tr><td>改交互路径</td><td>界面区域到源码、需求描述模板、变更记录</td><td>用户描述“点击哪里”时必须和真实路径一致。</td></tr>

View File

@@ -24,6 +24,9 @@ import {
} from "@/lib/api" } from "@/lib/api"
import { TRANSPARENT_HUMAN_NEGATIVE_PROMPT, TRANSPARENT_HUMAN_VIDEO_PROMPT } from "@/lib/workflow-target" import { TRANSPARENT_HUMAN_NEGATIVE_PROMPT, TRANSPARENT_HUMAN_VIDEO_PROMPT } from "@/lib/workflow-target"
type FlowNodeData = NodeData & Record<string, unknown>
type StudioFlowNode = Node<FlowNodeData>
const NODE_TYPES = { const NODE_TYPES = {
input: InputNode, input: InputNode,
visual: VisualLabNode, visual: VisualLabNode,
@@ -767,7 +770,7 @@ export default function Home() {
// 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag // 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag
const savedSizes = useMemo(() => loadNodeSizes(), []) const savedSizes = useMemo(() => loadNodeSizes(), [])
const [nodes, setNodes] = useNodesState<Node>( const [nodes, setNodes] = useNodesState<StudioFlowNode>(
LAYOUT.map((n) => { LAYOUT.map((n) => {
const s = savedSizes[n.id] ?? {} const s = savedSizes[n.id] ?? {}
const w = s.w ?? n.w const w = s.w ?? n.w
@@ -777,7 +780,7 @@ export default function Home() {
id: n.id, id: n.id,
type: n.type, type: n.type,
position: { x: n.x, y: n.y }, position: { x: n.x, y: n.y },
data: nodeData, data: nodeData as FlowNodeData,
draggable: !isPinned, draggable: !isPinned,
width: w, width: w,
...(typeof h === "number" ? { height: h } : {}), ...(typeof h === "number" ? { height: h } : {}),
@@ -876,7 +879,7 @@ export default function Home() {
// Job 数据变化时只更新节点 data 不动 position // Job 数据变化时只更新节点 data 不动 position
useEffect(() => { useEffect(() => {
setNodes((prev) => prev.map((n) => ({ ...n, data: nodeData }))) setNodes((prev) => prev.map((n) => ({ ...n, data: nodeData as FlowNodeData })))
}, [nodeData, setNodes]) }, [nodeData, setNodes])
// 关键帧详情面板是独立 ReactFlow 节点:可拖动、跟随画布缩放。 // 关键帧详情面板是独立 ReactFlow 节点:可拖动、跟随画布缩放。
@@ -900,7 +903,7 @@ export default function Home() {
return prev.map((n) => n.id === KEYFRAME_PANEL_ID return prev.map((n) => n.id === KEYFRAME_PANEL_ID
? { ? {
...n, ...n,
data: nodeData, data: nodeData as FlowNodeData,
draggable: !framePanelPinned, draggable: !framePanelPinned,
dragHandle: framePanelPinned ? undefined : ".keyframe-panel-drag", dragHandle: framePanelPinned ? undefined : ".keyframe-panel-drag",
} }
@@ -914,7 +917,7 @@ export default function Home() {
id: KEYFRAME_PANEL_ID, id: KEYFRAME_PANEL_ID,
type: "keyframePanel", type: "keyframePanel",
position: defaultPosition, position: defaultPosition,
data: nodeData, data: nodeData as FlowNodeData,
draggable: !framePanelPinned, draggable: !framePanelPinned,
dragHandle: framePanelPinned ? undefined : ".keyframe-panel-drag", dragHandle: framePanelPinned ? undefined : ".keyframe-panel-drag",
selectable: true, selectable: true,
@@ -953,7 +956,7 @@ export default function Home() {
return prev.map((n) => n.id === VIDEO_FRAME_PANEL_ID return prev.map((n) => n.id === VIDEO_FRAME_PANEL_ID
? { ? {
...n, ...n,
data: nodeData, data: nodeData as FlowNodeData,
draggable: videoPanelDock === "canvas", draggable: videoPanelDock === "canvas",
dragHandle: videoPanelDock === "canvas" ? ".video-frame-panel-drag" : undefined, dragHandle: videoPanelDock === "canvas" ? ".video-frame-panel-drag" : undefined,
} }
@@ -967,7 +970,7 @@ export default function Home() {
id: VIDEO_FRAME_PANEL_ID, id: VIDEO_FRAME_PANEL_ID,
type: "videoFramePanel", type: "videoFramePanel",
position: defaultPosition, position: defaultPosition,
data: nodeData, data: nodeData as FlowNodeData,
draggable: videoPanelDock === "canvas", draggable: videoPanelDock === "canvas",
dragHandle: videoPanelDock === "canvas" ? ".video-frame-panel-drag" : undefined, dragHandle: videoPanelDock === "canvas" ? ".video-frame-panel-drag" : undefined,
selectable: true, selectable: true,