Files
20260512-skg-tk/docs/source-analysis.html

4643 lines
479 KiB
HTML
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.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SKG 营销内容生产平台 · 源码解析与协作地图</title>
<style>
:root {
--bg: #f8fafc;
--panel: #ffffff;
--panel-soft: #f1f5f9;
--ink: #0f172a;
--muted: #64748b;
--line: #dbe3ed;
--blue: #2563eb;
--violet: #7c3aed;
--orange: #ea580c;
--green: #059669;
--rose: #e11d48;
--code: #111827;
--shadow: 0 18px 50px rgba(15, 23, 42, 0.08);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
color: var(--ink);
background:
linear-gradient(180deg, rgba(37, 99, 235, 0.06), transparent 340px),
var(--bg);
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.6;
}
a { color: inherit; }
code, pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
.shell {
display: grid;
grid-template-columns: 280px minmax(0, 1fr);
min-height: 100vh;
}
aside {
position: sticky;
top: 0;
height: 100vh;
overflow: auto;
padding: 24px 18px;
border-right: 1px solid var(--line);
background: rgba(255, 255, 255, 0.82);
backdrop-filter: blur(16px);
}
main {
width: min(1180px, calc(100vw - 320px));
padding: 34px 40px 72px;
}
.brand {
margin-bottom: 18px;
padding-bottom: 18px;
border-bottom: 1px solid var(--line);
}
.brand h1 {
margin: 0 0 8px;
font-size: 18px;
line-height: 1.25;
letter-spacing: 0;
}
.brand p {
margin: 0;
color: var(--muted);
font-size: 13px;
}
nav a {
display: block;
padding: 8px 10px;
margin: 2px 0;
border-radius: 8px;
color: #334155;
text-decoration: none;
font-size: 13px;
}
nav a:hover,
nav a:focus {
color: var(--blue);
background: #eff6ff;
outline: none;
}
.toolbar {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
margin-top: 18px;
}
.search {
width: 100%;
min-height: 42px;
padding: 0 14px;
border: 1px solid var(--line);
border-radius: 10px;
background: #fff;
color: var(--ink);
font-size: 14px;
outline: none;
}
.search:focus {
border-color: rgba(37, 99, 235, 0.55);
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.12);
}
.button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 42px;
padding: 0 14px;
border: 1px solid var(--line);
border-radius: 10px;
background: #fff;
color: #334155;
font-size: 13px;
cursor: pointer;
}
.button:hover { border-color: #94a3b8; color: var(--ink); }
.hero {
padding: 28px;
border: 1px solid var(--line);
border-radius: 18px;
background: var(--panel);
box-shadow: var(--shadow);
}
.hero .eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
color: var(--blue);
font-size: 12px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.hero h2 {
margin: 0 0 12px;
max-width: 980px;
font-size: clamp(30px, 5vw, 54px);
line-height: 1.04;
letter-spacing: 0;
}
.hero p {
max-width: 880px;
margin: 0;
color: #475569;
font-size: 16px;
}
.meta-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-top: 18px;
}
.meta {
padding: 13px;
border: 1px solid var(--line);
border-radius: 12px;
background: #f8fafc;
}
.meta b {
display: block;
margin-bottom: 4px;
font-size: 12px;
color: #334155;
}
.meta span {
color: var(--muted);
font-size: 12px;
word-break: break-word;
}
section {
margin-top: 28px;
padding: 24px;
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255, 255, 255, 0.86);
box-shadow: 0 10px 35px rgba(15, 23, 42, 0.045);
}
section h2 {
margin: 0 0 12px;
font-size: 24px;
letter-spacing: 0;
}
section > p {
margin-top: 0;
color: #475569;
}
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.card {
padding: 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
}
.card h3 {
margin: 0 0 8px;
font-size: 16px;
}
.card p,
.card li {
color: #475569;
font-size: 14px;
}
.card p:last-child { margin-bottom: 0; }
.tag {
display: inline-flex;
align-items: center;
min-height: 24px;
padding: 0 8px;
border-radius: 999px;
background: #e2e8f0;
color: #334155;
font-size: 12px;
font-weight: 650;
}
.tag.blue { background: #dbeafe; color: #1d4ed8; }
.tag.violet { background: #ede9fe; color: #6d28d9; }
.tag.orange { background: #ffedd5; color: #c2410c; }
.tag.green { background: #d1fae5; color: #047857; }
.tag.rose { background: #ffe4e6; color: #be123c; }
.tag.gray { background: #e2e8f0; color: #475569; }
.pipeline {
display: grid;
grid-template-columns: repeat(8, minmax(120px, 1fr));
gap: 10px;
overflow-x: auto;
padding-bottom: 4px;
}
.step {
min-width: 138px;
padding: 14px;
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
}
.step .num {
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
border-radius: 8px;
background: #eff6ff;
color: var(--blue);
font-weight: 800;
font-size: 12px;
}
.step h3 {
margin: 0 0 6px;
font-size: 14px;
}
.step p {
margin: 0;
color: #64748b;
font-size: 12px;
line-height: 1.45;
}
table {
width: 100%;
border-collapse: collapse;
overflow: hidden;
border: 1px solid var(--line);
border-radius: 12px;
background: #fff;
}
th,
td {
padding: 11px 12px;
border-bottom: 1px solid var(--line);
text-align: left;
vertical-align: top;
font-size: 13px;
}
th {
background: #f8fafc;
color: #334155;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
tr:last-child td { border-bottom: 0; }
td code {
color: #0f172a;
background: #f1f5f9;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 2px 5px;
font-size: 12px;
}
pre {
margin: 12px 0 0;
padding: 14px;
overflow: auto;
border-radius: 12px;
background: var(--code);
color: #e5e7eb;
font-size: 12px;
line-height: 1.55;
}
.callout {
padding: 14px 16px;
border-radius: 14px;
border: 1px solid #bfdbfe;
background: #eff6ff;
color: #1e3a8a;
}
.callout.warn {
border-color: #fed7aa;
background: #fff7ed;
color: #9a3412;
}
.callout.good {
border-color: #bbf7d0;
background: #f0fdf4;
color: #166534;
}
.callout p { margin: 0; }
.flow {
display: grid;
gap: 10px;
}
.flow-row {
display: grid;
grid-template-columns: 170px minmax(0, 1fr) minmax(0, 1fr);
gap: 10px;
align-items: stretch;
}
.flow-row > div {
padding: 12px;
border: 1px solid var(--line);
border-radius: 12px;
background: #fff;
font-size: 13px;
}
.flow-row strong { display: block; margin-bottom: 4px; }
.flow-row span { color: var(--muted); }
.todo {
display: grid;
gap: 10px;
counter-reset: todo;
}
.todo-item {
position: relative;
padding: 14px 14px 14px 46px;
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
}
.todo-item::before {
counter-increment: todo;
content: counter(todo);
position: absolute;
left: 14px;
top: 14px;
width: 22px;
height: 22px;
display: grid;
place-items: center;
border-radius: 7px;
background: #f1f5f9;
color: #475569;
font-size: 12px;
font-weight: 800;
}
.todo-item h3 { margin: 0 0 4px; font-size: 14px; }
.todo-item p { margin: 0; color: var(--muted); font-size: 13px; }
.changelog {
display: grid;
gap: 12px;
}
.change {
border: 1px solid var(--line);
border-radius: 14px;
background: #fff;
overflow: hidden;
}
.change header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 12px 14px;
border-bottom: 1px solid var(--line);
background: #f8fafc;
}
.change h3 { margin: 0; font-size: 14px; }
.change .body { padding: 14px; }
.change .body p { margin: 0 0 8px; color: #475569; font-size: 13px; }
.change .body p:last-child { margin-bottom: 0; }
.hidden-by-search { display: none !important; }
@media (max-width: 980px) {
.shell { display: block; }
aside {
position: static;
height: auto;
border-right: 0;
border-bottom: 1px solid var(--line);
}
main {
width: 100%;
padding: 24px 18px 56px;
}
.meta-grid,
.grid-2,
.grid-3,
.grid-4,
.flow-row {
grid-template-columns: 1fr;
}
}
@media print {
aside, .toolbar { display: none; }
.shell { display: block; }
main { width: 100%; padding: 0; }
section, .hero { box-shadow: none; break-inside: avoid; }
body { background: #fff; }
}
</style>
</head>
<body>
<div class="shell">
<aside>
<div class="brand">
<h1>SKG 营销内容生产平台</h1>
<p>源码解析与协作地图。用于把产品需求准确映射到代码位置。</p>
</div>
<nav aria-label="页面目录">
<a href="#purpose">为什么有这页</a>
<a href="#how-to-use">怎么用它描述需求</a>
<a href="#runtime">运行与入口</a>
<a href="#pipeline">业务管线</a>
<a href="#source-map">源码结构地图</a>
<a href="#ui-map">界面区域到源码</a>
<a href="#data-model">数据模型</a>
<a href="#api-map">接口地图</a>
<a href="#node-contract">节点职责边界</a>
<a href="#current-state">当前已通与阻塞</a>
<a href="#request-language">需求描述模板</a>
<a href="#change-log">变更记录</a>
<a href="#update-rule">更新规则</a>
</nav>
<div class="toolbar">
<input id="search" class="search" type="search" placeholder="搜索:节点 / 文件 / 接口 / 功能" />
<button class="button" type="button" onclick="window.print()">打印</button>
</div>
</aside>
<main>
<div class="hero" id="purpose" data-search>
<div class="eyebrow">Source Analysis · 2026-05-13</div>
<h2>这个页面是产品协作地图,不是应用功能页。</h2>
<p>
它把“你看到的界面、你想改的功能、实际要动的源码、可能影响的数据和接口”放在同一个地方。
后续描述需求时,可以直接说“改素材输入列 / 音频解析结果 / 分镜工作台 / 候选视频预览下载 / 某个接口行为”,这样改动范围会更准,也更容易追踪每次变更带来的影响。
</p>
<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>Next.js 16 · 端口 4290 · web/app/page.tsx</span></div>
<div class="meta"><b>后端</b><span>FastAPI · 端口 4291 · api/main.py</span></div>
<div class="meta"><b>本文档位置</b><span>docs/source-analysis.html · 独立文件,不接入应用</span></div>
</div>
</div>
<section id="how-to-use" data-search>
<h2>怎么用它描述需求</h2>
<div class="grid-3">
<div class="card">
<h3>1. 先说你在改哪个产品区</h3>
<p>例如“素材输入列”、“音频解析结果”、“逐句时间轴 / 讲话人分析 / 背景音分析”、“信息流复刻分镜工作台”。不要只说“这里乱”,要指向页面里的功能区。</p>
</div>
<div class="card">
<h3>2. 再说这个区应该承担什么职责</h3>
<p>例如“Vision 只给候选元素,用户必须能编辑、删除、重新提取”,这会直接落到 <code>FrameLightbox</code> 和元素接口。</p>
</div>
<div class="card">
<h3>3. 最后说不希望发生什么</h3>
<p>例如“点击元素不要跳页面”、“不要直接进入编排打断思路”、“不要把参考视频复刻成一样的东西”。这能约束交互和文案。</p>
</div>
</div>
<div class="callout good" style="margin-top:14px">
<p>建议表达格式:我要改「功能区」;当前问题是「行为」;正确职责是「业务目的」;不要影响「已有流程」。</p>
</div>
</section>
<section id="runtime" data-search>
<h2>运行与入口</h2>
<table>
<thead>
<tr><th>项目</th><th>命令 / 入口</th><th>说明</th></tr>
</thead>
<tbody>
<tr>
<td>本地后台启动</td>
<td><code>./scripts/start-dev-background.sh</code></td>
<td>不弹出 macOS Terminal 窗口;自动检查 4290 / 4291缺哪个启动哪个日志写入 <code>.logs/</code>PID 写入 <code>.pids/</code></td>
</tr>
<tr>
<td>本地后台停止</td>
<td><code>./scripts/stop-dev-background.sh</code></td>
<td><code>.pids/</code> 里的 PID 停止后台前端 / 后端进程。</td>
</tr>
<tr>
<td>生产站点</td>
<td><code>https://marketing.skg.com</code></td>
<td>公司域名已解析到 VPS <code>76.13.31.179</code>。线上由既有 Coolify / Traefik 负责 HTTPS 入口,项目 <code>web</code> 容器用 Nginx 承载静态前端;登录后根路径直接进入 Vue / Vite 个人生成画布,<code>/canvas/</code> 只作为旧链接兼容跳转到根路径。<code>/login/</code><code>/_next/</code><code>/assets/</code><code>/skg-logo-black.svg</code><code>/oasis-source/</code> 为公开登录页资源,未登录访问工作台跳转 <code>/login/</code>。登录页只允许飞书免登录,回调为 <code>/api/auth/feishu/callback</code>;生产通过 <code>PASSWORD_AUTH_ENABLED=false</code> 停用账号密码入口。<code>/api/</code> 通过 <code>auth_request</code> 校验 FastAPI 会话 Cookie 后再反代,后端按 Cookie 里的用户身份隔离 <code>Job</code><code>AgentRun</code> 数据。</td>
</tr>
<tr>
<td>画布构建</td>
<td><code>cd web && npm run build</code></td>
<td>受同一登录保护的 Vue / Vite 画布应用。构建时先执行 <code>pnpm build:canvas</code> 生成 <code>web/canvas-app/dist</code>,再执行 Next 静态导出,最后由 <code>web/scripts/sync-canvas-root.mjs</code> 把画布产物覆盖到 <code>web/out</code> 根目录Nginx 登录校验后的 <code>/</code> fallback 到画布 <code>index.html</code><code>/canvas/</code> 只做 308 兼容跳转。画布项目以服务端 Postgres 为主持久化,浏览器 <code>localStorage</code> 只作为缓存和首次导入来源;生成出来的图片 / 视频资产通过本项目 <code>/api</code> 写入当前登录用户自己的后端 job。</td>
</tr>
<tr>
<td>生产部署</td>
<td><code>./scripts/deploy-prod-safe.sh</code></td>
<td>服务器目录为 <code>/opt/skg-marketing-studio</code>;后端任务文件挂载到 <code>./data/jobs</code>,全局资源中心挂载到 <code>./data/asset_library</code><code>./data/prompt_library</code><code>./data/_trash</code>Postgres 数据目录为 <code>./data/postgres</code>,真实 Key 和数据库密码只放服务器 <code>deploy/.env.production</code>。生产部署唯一入口是 <code>deploy-prod-safe.sh</code>:先备份服务器 env、案例、资源库和 secrets如 Postgres 容器存在则额外导出 <code>pg_dump</code>,再用 protect/exclude 保护 <code>data/</code><code>jobs/</code><code>secrets/</code><code>deploy/.env.production</code> 后同步代码,最后 Docker 重建并跑 <code>verify-prod-docker.sh</code>。禁止再用裸 <code>rsync --delete</code> 手动同步。</td>
</tr>
<tr>
<td>前端开发服务</td>
<td><code>cd web && npm run dev</code></td>
<td>Next.js App Router主页面是 <code>web/app/page.tsx</code>,默认端口 4290。</td>
</tr>
<tr>
<td>后端开发服务</td>
<td><code>cd api && source .venv/bin/activate && uvicorn main:app --host 127.0.0.1 --port 4291</code></td>
<td>FastAPI所有任务状态、视频、关键帧、清洗、元素、分镜保存都在 <code>api/main.py</code>。长下载 / 抽帧 / 音频处理期间不要带 <code>--reload</code>,否则 reload 会等待后台任务结束并让新请求卡住。</td>
</tr>
<tr>
<td>测试页面</td>
<td><code>http://localhost:4290/?job=c6767f3a166b</code></td>
<td>URL 里可放多个 job id<code>?job=id1,id2,id3</code>,前端会恢复多个任务并激活最后一个。</td>
</tr>
<tr>
<td>源码解析页</td>
<td><code>open docs/source-analysis.html</code></td>
<td>独立静态 HTML不被 Next 构建、不影响产品工作台。</td>
</tr>
</tbody>
</table>
</section>
<section id="pipeline" data-search>
<h2>业务管线</h2>
<div class="callout warn" style="margin-bottom:14px">
<p><strong>2026-05-24 完整重设计:</strong>默认首页已从“TK 信息流复刻 / 三字段分镜管线”推倒,改为面向公司约 6 名成员同时使用的 <strong>SKG 营销内容多人创作平台</strong>。主路径是文生图、图生图、文生视频、图生视频和营销图文方案生成;每个登录用户只看到自己的任务和详情页结果。旧 TK 复刻工作台与 Agent Cut 一键出片保留为高级入口,不再作为默认工作台。</p>
<p><strong>2026-05-25 即梦 generate 式简化:</strong>默认首页进一步压缩为窄导航栏、会话侧栏和中央 prompt composer不再把四入口、参考图、我的任务和结果区平铺成三栏。图片 / 视频 / 图文模式、自动设置和参考上传都收进 composer 底部的小按钮;参考图是输入框左侧倾斜上传卡;结果只用右下角浮层提示,完整沉淀交给详情页。</p>
<p><strong>2026-05-25 三模式版:</strong>默认首页再收敛为一个中央对话框,首页和画布底部输入框只让用户选文生图、文生视频、图生视频,然后手写提示词生成。图生视频只显示“上传图片”,不再把首帧 / 首尾帧这类模型实现概念作为主入口;营销图文不再作为首页默认入口。后端 <code>/health</code> 返回可选图片 / 视频模型、图片尺寸、视频画幅和真实可用视频时长,首页按返回值显示模型和规格选择;当前 Doubao / Seedance 生产链路单条最长 15 秒,不向用户暴露 30 秒按钮。</p>
<p><strong>2026-05-25 根域名画布版:</strong><code>https://marketing.skg.com</code> 登录后直接进入个人生成画布,不再先进入 React 单对话框首页再点画布;<code>/canvas/</code> 只保留为旧链接兼容跳转。后续优先少改成熟画布结构,只在必要时改模式文案、生成接入和结果/队列显示。</p>
<p><strong>2026-05-25 上游能力恢复版:</strong>用户明确要求“API 没关系,其他恢复,别削弱”。因此根域名画布恢复 <code>chatfire-AI/huobao-canvas</code> 的成熟节点和工作流结构推荐词、AI 润色、自动执行、工作流模板、首帧/尾帧/参考图节点、图片/视频/LLM 配置、多角度分镜、故事板、绘本和批量下载都保留;只继续替换品牌、路由和 API 接入。生成请求仍走 SKG 后端 <code>/api</code> 与登录 Cookie员工不需要个人 API Key。</p>
<p><strong>2026-05-25 媒体模型接入收口:</strong>图片和视频模型选择只暴露当前后端真实可用项:图片为 <code>auto</code><code>gpt-image-2</code><code>gemini-3-pro-image-preview</code>;视频当前只接通 <code>Seedance 2.0 Fast</code>(真实模型 <code>doubao-seedance-2-0-fast-260128</code>)。旧上游的 Nano Banana、Seedream、Kling、Veo 或浏览器本地自定义媒体模型不能进入生成下拉,避免同事选到实际不可用的模型。</p>
<p><strong>2026-05-26 公司沉淀版:</strong>画布项目从浏览器本地存储升级为服务端 Postgres 持久化;<code>localStorage</code> 只作为离线缓存和首次导入来源。后端同时建立用户、任务、资源索引和审计表,保留原有 <code>state.json</code> 文件作为任务详情真源,避免一次迁移动到大文件资产结构。</p>
<p><strong>2026-05-26 AI 润色中性化:</strong>画布 <code>AI 润色</code> 不再复用 SKG 广告文案接口 <code>/creative/copy</code>。后端新增 <code>POST /prompt/polish</code>,前端 <code>useChat</code>、根画布输入框、文本节点和自动执行意图分析改走中性提示词/通用生成接口:只优化用户已经给出的主体、风格、镜头和细节,不主动添加 SKG、按摩产品、TikTok 广告话术或用户没有提到的品牌。当前润色链路会先清理上一次润色遗留的模板尾巴,再判断人物/无人/物体/场景/动物/未知主体;原文明确有人时才声明虚构 AI 角色,原文明确无人时才保留无人物约束,原文没写人时不主动造人但也不追加“必须无人物”的模板尾巴;当输入或参考图已经有人物时,按 AI 生成的虚拟角色继续描述,而不是把人物参考图判定为不可用。</p>
<p><strong>2026-05-26 我的工作流云端版:</strong>工作流面板从只有公共模板扩展为“公共工作流 / 我的工作流”两类。当前画布可以保存成当前登录用户自己的云端工作流模板,后续在同一账号的其他电脑或浏览器打开后可插回画布;保存时只沉淀节点结构、连线、配置和提示词,主动清掉已生成图片、视频、任务进度、错误和运行态字段,避免把一次性生成结果误当模板复用。</p>
<p><strong>2026-05-26 生图配置恢复版:</strong>按用户要求撤回后续“低/中/高画质、自定义尺寸、Gemini 官方 1K/2K/4K 尺寸、取消自动模型”的实验改动,恢复最初简单配置:图片模型为 <code>auto</code><code>gpt-image-2</code><code>gemini-3-pro-image-preview</code>,尺寸只保留 <code>auto</code><code>1024x1536</code><code>1024x1024</code><code>1536x1024</code>,画质回到单一标准项;<code>auto</code> 仍按后端既有策略优先 GPT Image 2必要时由熔断/兜底走 Gemini。</p>
</div>
<p>当前默认业务管线是“个人隔离任务 → 根域名进入个人画布 → 画布项目同步到服务端 Postgres → 用提示词、推荐词、AI 润色、公共工作流或我的工作流创建节点 → 画布自动执行或手动连接图片/视频/文本节点 → 生成结果沉淀在当前个人画布 → 可把当前节点结构保存为我的工作流 → 需要时进入详情页继续编辑”。画布不再被削成三模式入口;首帧、尾帧、参考图、图生视频、多角度分镜、故事板和绘本等上游概念按节点能力保留。底层生成仍由 <code>web/canvas-app/src/hooks/useApi.js</code> 适配到本项目 <code>/creative/jobs/image</code><code>/jobs/{id}/frames/{idx}/generate</code><code>/jobs/{id}/frames/{idx}/storyboard/video</code>AI 润色和通用 LLM 文本生成走 <code>/prompt/polish</code> 并保持中性专业:不主动套入 SKG不主动补产品、平台、广告语境或人物只扩写用户明确写出的主体、动作、场景、镜头、光线和质量细节视频提交若带参考图会在最终提示词中条件声明“参考图里若有人物应按 AI 生成的虚拟角色处理”,避免把 AI 人像素材误当成真实肖像。生成资产按当前登录用户写入个人 job。图片尺寸只显示 <code>auto</code><code>1024x1536</code><code>1024x1024</code><code>1536x1024</code>;视频画幅只显示 <code>720x1280</code><code>1280x720</code><code>1024x1024</code><code>960x1280</code>;视频时长只显示 <code>5/8/10/12/15</code> 秒。多人互不影响依赖后端 <code>owner_id</code>、画布项目 owner、我的工作流 owner 和飞书 / 备用登录会话隔离。旧 React 单对话框首页、信息流复刻链路仍保留在源码里作为回滚/高级能力,但不作为生产默认入口。</p>
<div class="pipeline">
<div class="step"><div class="num">01</div><h3>个人任务</h3><p><code>GET /jobs</code> 按当前登录用户过滤;旧无 owner 任务只对备用账号可见。</p></div>
<div class="step"><div class="num">02</div><h3>进入画布</h3><p>用户直接在根域名个人画布里操作;项目列表优先读取服务端 <code>/canvas-projects</code>,本地旧项目会首次导入。</p></div>
<div class="step"><div class="num">03</div><h3>组织节点</h3><p>可通过底部 prompt、AI 润色、自动执行、手动添加节点、公共工作流或我的工作流创建文本、图片、视频、LLM、配置和参考图节点。</p></div>
<div class="step"><div class="num">04</div><h3>参考素材</h3><p>首帧、尾帧、参考图和图片节点按上游节点语义保留;提交到后端时由 <code>useApi.js</code> 转成 <code>first_image</code><code>last_image</code> 或图片编辑参考。</p></div>
<div class="step"><div class="num">05</div><h3>工作流执行</h3><p>自动执行会根据提示词创建文生图、图转视频、故事板、多角度分镜或绘本等节点组;手动模式下用户可自行连接节点。</p></div>
<div class="step"><div class="num">06</div><h3>生成图片 / 视频</h3><p><code>generateImage</code><code>generateStoryboardVideo</code> 继续走 SKG 后端 <code>/api</code>;视频提交后先写入 <code>queued</code> 占位,再由后端队列按并发上限启动。</p></div>
<div class="step"><div class="num">07</div><h3>结果沉淀</h3><p>生成图、视频 URL、任务状态和下载入口回填到画布节点画布项目和个人工作流结构保存到 Postgres完整任务结果仍可进入 <code>/detail/?job=</code> 查看。</p></div>
<div class="step"><div class="num">08</div><h3>详情页</h3><p><code>/detail/?job=&lt;id&gt;</code> 展示参考图、全量生成图、视频候选、提示词和营销图文,并支持继续生成。</p></div>
<div class="step"><div class="num">09</div><h3>高级复刻</h3><p><code>AdRecreationBoard</code><code>/agent/</code> 作为高级入口保留,不再是默认路径。</p></div>
</div>
</section>
<section id="source-map" data-search>
<h2>源码结构地图</h2>
<div class="grid-2">
<div class="card">
<h3>前端核心</h3>
<table>
<tbody>
<tr><td><code>web/next.config.mjs</code></td><td>Next.js 构建配置:静态导出、图片不走优化、禁用开发环境左下角 Next Dev Indicator并移除 Next 16 已不支持的 <code>eslint</code> 顶层配置,避免本地 dev 出现配置 Issue 提示。</td></tr>
<tr><td><code>web/app/globals.css</code></td><td>全局主题变量、登录页视觉样式、信息流工作台玻璃拟态 token、ReactFlow 样式引用,以及本地开发态 <code>nextjs-portal</code> 遮挡隐藏规则。工作台在 <code>skg-board-theme</code> 内按 Figma 本地 MCP 参考改成黑灰玻璃系统:深灰背景、<code>#383838</code> 胶囊侧栏、<code>rgba(255,255,255,.1)</code> 玻璃面、<code>backdrop-filter: blur(5px)</code><code>20px</code> 圆角、<code>10px 10px 10px rgba(0,0,0,.3)</code> 阴影和绿黄状态色;新增 <code>skg-board-shell</code><code>skg-board-rail</code><code>skg-glass-card</code><code>skg-glass-card--flat</code><code>skg-status-orb</code> 等样式。侧栏改为跟随视口拉满工作台可用高度的悬停胶囊,桌面最小 600px展开时在同一侧栏内承载素材输入抽屉。明暗主题已分开维护 shell、panel、glass、stat、action 和音频波形 token暗色压低灰雾和面板底色明亮模式改为暖白工作台避免指标卡、按钮和波形继续残留黑底/白线;顶部指标卡增加紫、黄绿、琥珀、青绿、绿色光斑变量,接近原版多色玻璃卡效果。主/次按钮、指标卡和空状态继续走统一类,避免各板块散写不同玻璃效果。</td></tr>
<tr><td><code>web/app/page.tsx</code></td><td>旧 React 单对话框生成台源码仍保留,便于以后回滚或抽能力;当前生产根域名已经由 <code>web/canvas-app/</code> 画布产物覆盖,不再把这个 React 首页作为默认首屏。该页面里的模式也已收敛为文生图、文生视频、图生视频;图生视频只显示“上传图片”,不把“首帧/首尾帧”作为用户入口。旧 TK 复刻工作台组件仍保留在 <code>web/components/ad-recreation-board.tsx</code>,但不再作为默认首页渲染。</td></tr>
<tr><td><code>web/canvas-app/</code></td><td>SKG 内部画布应用:从 <code>chatfire-AI/huobao-canvas</code> 交互逻辑改造而来。当前策略是“保留成熟画布能力,替换品牌/路由/API”Vue Flow 节点画布、项目列表、推荐词、AI 润色、自动执行、公共工作流、我的工作流、首帧/尾帧/参考图节点、图片/视频/LLM 配置节点、模型配置和批量下载都保留;可见品牌收敛为 SKG logo不展示上游注册链接或外部品牌。生产路径固定为根域名 <code>/</code>,内部路由用 <code>/p/:id?</code>;项目列表、画布 JSON 和个人工作流模板优先同步到服务端 Postgres浏览器本地存储只是缓存/导入来源;来源说明保存在 <code>THIRD_PARTY_NOTICES.md</code>,不展示给终端用户。</td></tr>
<tr><td><code>web/canvas-app/src/stores/projects.js</code></td><td>画布项目 Pinia store启动时先读本地 <code>localStorage["ai-canvas-projects"]</code> 作为缓存,再调用 <code>GET /canvas-projects</code> 拉服务端项目;如果发现本地旧项目,会调用 <code>POST /canvas-projects/import</code> 导入到当前登录用户。新建、重命名、画布节点变更、复制和删除会同步到 <code>/canvas-projects</code>,本地缓存只用于快速打开和网络异常兜底。</td></tr>
<tr><td><code>web/canvas-app/src/stores/workflows.js</code></td><td>我的工作流 store调用 <code>GET/POST/DELETE /canvas-workflows</code> 读取、保存和删除当前登录用户自己的云端工作流模板。保存前会清理节点里的 <code>base64</code>、生成 URL、任务进度、错误、视频结果和 LLM 输出等运行态字段,只保留可复用的节点结构、连线、配置和提示词。</td></tr>
<tr><td><code>web/canvas-app/src/views/Canvas.vue</code></td><td>画布主交互:恢复上游底部 prompt composer、<code>AI 润色</code><code>自动执行</code>、推荐词、节点菜单、工作流面板、API/模型设置入口和批量下载入口。自动执行会调用 <code>useWorkflowOrchestrator</code> 分析提示词,创建文生图、图转视频、故事板、多角度分镜或绘本节点组;手动模式只创建文本节点,用户自行连接节点。工作流面板支持公共模板和我的工作流:公共模板走本地 <code>createNodes()</code>,我的工作流从云端 <code>workflow_data</code> 插回当前画布,并重新生成节点 ID、按视口中心重排、按映射重连边。底部推荐词来自共享短词池4 个一组单行展示,刷新按钮在 30 组内轮换,不改变输入面板高度。</td></tr>
<tr><td><code>web/canvas-app/src/config/suggestions.js</code></td><td>首页和画布共用的推荐词配置:维护 <code>QUICK_SUGGESTION_GROUPS</code>,当前为 30 组 / 120 个短词,每组 4 个,控制刷新按钮的轮换范围;词条保持短小,避免推荐栏换行或顶起 composer。</td></tr>
<tr><td><code>web/canvas-app/src/config/models.js</code></td><td>画布媒体模型和规格的前端白名单:图片只内置 <code>auto</code><code>gpt-image-2</code><code>gemini-3-pro-image-preview</code>,尺寸只内置 <code>auto</code><code>1024x1536</code><code>1024x1024</code><code>1536x1024</code>;视频只内置 <code>seedance</code> / <code>Seedance 2.0 Fast</code>,画幅和时长对齐后端 <code>/health</code> 能力边界。<code>useModelConfig.js</code> 和 Pinia 模型 store 会忽略浏览器本地自定义图片/视频模型,防止旧缓存把不可用模型带回生成下拉。</td></tr>
<tr><td><code>web/canvas-app/src/hooks/useApi.js</code></td><td>画布到本项目后端的适配层:不再读取浏览器 API Key而是使用当前登录会话 Cookie 调用 <code>/api</code>。文生图 / 图生图先创建轻量 creative job再调用 <code>/frames/0/generate</code>;文生视频 / 图生视频调用 <code>/storyboard/video</code> 并轮询 <code>/jobs/{id}</code>,完成后把图片或 mp4 URL 写回画布节点。<code>useChat</code> 已从 SKG 广告文案接口切到 <code>/prompt/polish</code>AI 润色显式使用 image/video prompt 模式LLM 节点使用通用 chat 模式,避免自动注入用户没有提到的 SKG、产品、平台或营销语境后端会清理旧润色模板尾巴、判断人物/无人/物体/场景意图,并在输出后检查“有人却禁止人物、无人却新增人物、未写 SKG 却出现 SKG”等冲突。图生视频实际提交到后端后后端会对参考图追加 AI 虚拟角色条件说明,不要求前端判断图片里是否有人脸。</td></tr>
<tr><td><code>web/scripts/sync-canvas-root.mjs</code></td><td>构建桥接脚本:在 <code>next build</code> 静态导出完成后,把 Vite 画布产物 <code>web/canvas-app/dist</code> 覆盖到 <code>web/out</code> 根目录,使 <code>https://marketing.skg.com</code> 登录后直接进入画布;旧 <code>web/scripts/sync-canvas-dist.mjs</code> 保留但不再由生产构建调用。</td></tr>
<tr><td><code>web/app/detail/page.tsx</code></td><td>任务详情页:静态导出路由 <code>/detail/?job=&lt;id&gt;</code>,通过 query 读取 job id调用 <code>getJob</code> 恢复同一任务。页面展示参考图、全部生成图、视频候选、营销图文方案和历史提示词,可继续调用 <code>generateImage</code><code>generateStoryboardVideo</code><code>generateCreativeCopy</code>,并支持删除图片/视频。该页继续依赖后端 owner 过滤,用户不能通过切换 URL 读取别人的任务。</td></tr>
<tr><td><code>web/app/agent/page.tsx</code></td><td>新增一键出片终端页:只保留 TikTok 链接、产品图上传、实时 <code>Agent Terminal</code> 和最终成片播放器;通过 <code>POST /agent-runs</code> 创建受限后台状态机任务,通过 <code>GET /agent-runs/{id}</code> 轮询日志、进度、审片图和最终 mp4。该页不替代旧工作台深度编辑能力只承接“用户只看成品”的快速出片主路径。</td></tr>
<tr><td><code>web/components/ad-recreation-board.tsx</code></td><td>信息流广告复刻工作表:外壳按 Figma “Dashboard Glassmorphism”参考整体改为黑灰玻璃工作台<code>WorkbenchRail</code> 默认收起为拉满工作台可用高度的 65px 胶囊工具条,只保留真实动作入口:素材任务、资源库和主题切换;鼠标移入或键盘聚焦侧栏时,<code>skg-board-rail</code> 切换 <code>is-open</code> 并从左侧展开 320px 素材输入抽屉,点击素材任务按钮可固定展开。顶部从登录页式 brand strip 改为轻量生产控制条,左侧显示 <code>未来健康 · 营销内容工作台</code>、主标题 <code>营销内容工作台</code> 和副标题 <code>信息流广告复刻生产</code>,右侧保留素材/当前/视频/文案段/背景音指标,并用紫、黄绿、琥珀、青绿、绿色光斑卡片增强原版玻璃拟态的颜色层次。主内容只保留源视频拆解工作区,素材输入的数据流、接口、模型调用和状态推导不变。工作台外层已取消 <code>1800x1000</code> 固定基准画布、<code>ResizeObserver</code> 档位计算和 CSS <code>zoom</code> 整页缩放,改为正常流式桌面容器:<code>min-height: 100vh</code><code>width: 100%</code><code>max-width: 1920px</code>,并保留 <code>min-width: 1280px</code> 作为最低操作宽度;核心列宽不再被整体缩放,文字、图标和边线由浏览器原生字号渲染,避免小数缩放导致发虚。<code>buildWorkflowSteps</code> 仍统一生成 01-09 流程顺序、状态和判定依据,<code>WorkflowStepBadge</code> / <code>PipelineLane</code> / 分镜列标题也继续共用同一套编号;但完整 <code>WorkflowOrderBar</code>、右侧素材/视频/音频/文案/参考帧需求 chips、文案依据下拉和“音频文案、抽帧参考、主体重构、产品素材池”四个状态条不再默认渲染在工作区顶部。侧边素材输入面板只负责链接/上传和任务切换,不再重复放横版原视频预览;主画布源视频工作区直接进入核心操作。讲话人、节奏和背景音分析仍写入 <code>AudioScript</code>,但不再作为“音频解析结果”卡片默认渲染;源视频工作区撤销右上“布局调节”临时面板,不再读取或写入 <code>localStorage["skg-source-workspace-layout:v1"]</code>;当前固定为左侧原视频列 380px、9:16 视频高 500px、逐句时间轴最大高 360px、参考帧池 140px、主体空态 78px转换层不再固定拉长按内容自然高度显示内容过多时最多到 560px 后在自身区域内滚动;上方是按 9:16 显示的竖版原视频播放器,播放器内覆盖“当前点抽帧”,按当前播放秒数手动补参考帧,播放器下方是逐句时间轴,英文和中文都最多显示两行;右侧上方是无标题的波形与切点参考框,下方主体链路改为上方参考帧池 + 转换层、下方主体元素结果栏。音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点,并通过 <code>skg-audio-waveform</code> 读取明暗主题变量,避免明亮模式继续使用黑底/白色波形;顶部把低/中/高密度按钮和当前播放秒数、总时长、鼠标指针停点秒数直接放在波形上方。视频播放时通过 <code>requestAnimationFrame</code> 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。音频波形下方同框渲染无标题的 <code>TimelineFilmstrip</code> 临时画面胶片,前端按低/中/高密度从源视频 canvas 截取预览缩略图,并按 <code>frame.time / duration</code> 的百分比定位到和波形同一条时间轴上波形与胶片之间不显示分隔横线胶片轨道贴近波形缩略图轻微上下错落并倾斜重叠排列hover 时用同一张胶片卡在原位置生成固定顶层克隆,约 4.8 倍放大并自动限制在视口内,避免被工作区、滚动容器或相邻面板遮挡;单击胶片只跳转视频时间点,不写入任务数据,双击胶片或拖进参考帧池时才调用手动抽帧并正式加入 <code>job.frames</code>,已加入的胶片显示“已添加”;胶片预览按 job、视频、密度和时长缓存未切换低/中/高时返回页面不重新扫视频。参考帧池的主入口是“自动抽帧 12 张”,一键按动作峰值目标重新抽取 12 张源视频参考帧,优先抓手势、表情变化、节奏点和镜头变化;缩略图按竖版完整比例显示不裁切,点选状态直接叠在参考帧池缩略图上,鼠标停留会通过固定浮层放大展示完整帧。转换层改为轻量对话式生图确认区并拿到主操作宽度:左侧参考帧可点 <code>+</code> 或直接拖入转换层,本地图片拖入会通过 <code>uploadReferenceFrame</code> 保存为参考帧;转换层上方是参考输入区,下方不再显示当前要求摘要、保留元素副本或对话记录计数,只保留带张数控件的“发送消息”输入 composer模型确认类回复不再逐条展示生成英文 prompt 后发送区主按钮直接切换为“确认生成 N 张”,点击后才调用主体套图生成。主体元素结果栏在转换层下方,空态只占紧凑提示;有结果时按每次生成的套图文件夹显示,左侧横向展示当前套图,右侧切换套图包,保留单张重生和删除;缩略图上提供“重新生成这一张”和“删除这一张”,单张重生会用 <code>replace_views=true</code> 替换同一视角。前端对卡通重构传 <code>subject_style=cartoon_subject</code>,其他方向传 <code>subject_style=source_actor</code>;形象锁定或自主描述空文本可走 <code>reconstruction_mode=same</code>,其他参考创新走 <code>similar</code> 并把参考帧作为 <code>/images/edits</code> 的 image refs 一起提交。主体生成完成后会形成 <code>subject_consensus_brief</code>。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后按“套在脖子上的 U 形肩颈按摩仪”进行同一产品批量识别,左/右按佩戴者身体左右、上/下按佩戴方向,额外标注内外侧、开口方向、局部结构点、背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。脚本区在分镜行上方提供“作者想法”和“整片改写”,每行新口播文案可直接编辑并可单段 AI 改写。每条音频分镜默认是左侧三字段、右侧横向视频候选轨;高级区仍保留首尾帧 prompt、产品出现方式和旧 6 字段。<code>ModelTrace</code> 会在音频解析、产品识别/补图、主体重构视图包、脚本改写等入口旁直接展示模型名;生图入口会显示 <code>gpt-image-2 / gemini-3-pro-image-preview</code> 链路和短时熔断规则,点击后用固定浮层展示模型链路、输入输出和回退逻辑。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。</td></tr>
<tr><td><code>SourceSubjectPipeline</code></td><td>源视频工作区主体管线主路径:上方是竖向 <strong>参考帧池</strong> 和宽幅 <strong>转换层</strong>,下方是 <strong>主体元素</strong> 结果栏。参考帧池固定 140px转换层不再固定高度按内容自然显示并以 560px 为最大高度,超出后在自身区域内滚动;主体空态固定为 78px 紧凑提示。参考帧池保留自动 12 张、胶片拖入正式成帧、点击勾选和删除;参考帧缩略图保持小尺寸固定宽度、<code>aspect-[9/16]</code><code>object-contain</code> 显示hover 预览通过 <code>MediaAssetTile</code> 的左侧紧凑浮层显示,并新增 <code>+</code> 操作把参考帧送入转换层。转换层是轻量对话式生图确认区:顶部选择 GPT 套件或 Gemini 套件,参考输入区支持左侧 <code>+</code>、拖拽参考帧、胶片拖入和本地图片拖拽上传(上传图会写入 <code>job.frames</code>),下方固定为带张数控件的“发送消息”输入 composer不再重复显示当前要求摘要、保留元素副本或收起记录计数模型确认类回复不再逐条显示复刻、参考创意换人物、卡通风格和人物占比等常用意图也不再显示为独立快捷 chip识别结果里的特征 chip 是“保留元素”选择,点亮表示随下一条消息提交给 <code>subject-agent/message</code>,再次点击取消,清空按钮一次性取消全部,单次点击不再直接请求模型;<code>subject-agent/message</code> 返回英文 <code>generation_prompt_en</code> 后不再自动弹窗,标题右侧显示“提示词就绪”,底部主按钮从“发送消息”切换为“确认生成 N 张”;用户继续输入会更新需求,点击确认生成才调用 <code>generateSubjectAssets</code>。后端会为每次主体套图注入同一份 pack bible参考创新模式锁定同一个全新主体和同一套服装源形象锁定模式锁定参考帧里的可见主体、体态、发型、服装和配色后处理会裁出白底主体并允许放大到画布高度上限约 96%,实测典型主体有效高度约 90%,避免模型生成“小人 + 大白边”。主体元素结果栏按每次生成的 <code>pack_id</code> 组织成“套图文件夹”:左侧横向展开当前选中套图,右侧显示可滚动的套图包列表;同一方向可保留多套,生成中按 pack 显示 <code>2/6</code> 这类进度,单张完成就替换对应占位卡;空态只显示紧凑提示,不再占右侧整列。缩略图复用 <code>MediaAssetTile</code>,支持 hover 放大、单张重生和删除。旧下方 <code>SourceReferenceBuildPanel</code> 不再主路径渲染。</td></tr>
<tr><td><code>AudioStoryboardPlanPanel</code> 三字段候选生成</td><td>当前分镜主路径:每行是左右双栏,左侧默认显示 <code>skg_copy_*</code><code>scene_one_line_*</code><code>action_one_line_*</code> 三组中英字段,右侧直接显示视频候选横向轨。用户改中文镜像后,字段失焦会通过 <code>refineStoryboard</code> 优化对应英文主值,失败时退回 <code>translateText</code>;英文仍是后续 prompt 主值。<code>quickPlanStoryboard</code> 把三字段和主体 brief 展开为完整 <code>StoryboardScene</code><code>generateStoryboardVideo</code><code>count</code> 可由单行数字控件选择,候选提交后立刻写入 <code>generated_videos</code> 并在完成时自动回填 mp4候选新生成后持续向右追加不再用 4-grid 撑高每行。视频候选卡的普通点击用于打开视频预览,下载通过卡片右上角按钮显式触发,不再把点击候选卡解释成选择视频或保存视频。整片生成同样可选择每行数量,并以 <code>concurrency=1</code> 按行排队提交。产品素材池、批量控制、每行主体区和高级区都可折叠,高级抽屉仍展示旧 6 字段、首尾帧 prompt 和首尾帧资产槽,但客户默认不用先处理首尾帧。</td></tr>
<tr><td><code>web/components/resource-library/library-drawer.tsx</code></td><td>全局资源中心浮窗:由工作台顶部“资源库”按钮打开,叠加在工作台上方但不阻塞主界面;尺寸、位置和当前 Tab 写入 <code>localStorage["skg-resource-library-drawer"]</code>。提示词 Tab 固定 5 列场景描述、视频描述、主体描述、SKG 文案、产品角度),每列先显示 use_count 排名前 5 的“常用”再按月份倒序分组提示词节点常驻复制按钮hover 可选英文/中文/双语复制,并调用 use 接口。素材 Tab 固定 4 列(主体、产品、场景、视频),节点不可拖动,按月份倒序硬编码排列;“应用到当前 job”只调用后端复制接口得到普通 <code>ImageRef(kind="asset")</code> 后再写入产品素材池或复制 ID。浮窗顶部最近 24 小时横条混合显示提示词和素材;新建提示词、上传素材、删除前查引用、详情侧栏都在该组件内完成。</td></tr>
<tr><td><code>AdRecreationBoard</code> 主题切换</td><td>左侧中段 65px 胶囊工具条上方图标组里有 <code>Sun</code> / <code>Moon</code> 图标按钮,切换 <code>skg-board-theme--light</code> 类名,并把选择写入 <code>localStorage["skg-board-theme"]</code>。暗色仍是默认模式;明亮模式只改变工作台外观,不改变任务、素材、分镜、模型调用或接口数据。</td></tr>
<tr><td><code>SourceReferenceBuildPanel</code></td><td>旧的“相似主体 / 主体模板”大面板代码仍保留在文件里,方便以后恢复模板库复用、入库命名和自定义视图选择;但当前源视频工作区主路径已经由 <code>SourceSubjectPipeline</code> 承接,不再在页面下方渲染这块,避免和“参考帧池 → 转换层 → 主体元素”重复。</td></tr>
<tr><td><code>web/components/media-asset-tile.tsx</code></td><td>项目内媒体素材缩略图基底组件:图片、视频、抽帧、产品图、相似主体图、首尾帧和视频候选默认从这里获得统一交互。组件负责缩略图显示、顶层固定浮层 hover 放大、删除按钮、下载/重新生成等操作按钮、忙碌遮罩、图片/视频共用预览,以及按场景启用原生视频播放 controls避免每个新板块重复手写不同的媒体交互。hover 预览支持 <code>previewPlacement</code><code>previewMaxWidth</code>,视频候选可让操作按钮常显,保证下载入口不是隐藏语义;参考帧池用左侧紧凑预览避免遮住转换层;画面胶片是例外:为了保持胶片原位浏览,不使用额外弹出预览,只让胶片缩略图自己在轨道内放大。</td></tr>
<tr><td><code>web/app/login/page.tsx</code></td><td>生产登录页:先读取 <code>/api/auth/config</code>,飞书 OAuth 配好时显示“飞书免登录”主按钮;只有后端返回 <code>password_enabled=true</code> 时才展示账号密码表单。当前生产 <code>PASSWORD_AUTH_ENABLED=false</code>,所以普通浏览器也只显示飞书入口;如果 <code>feishu_enabled=true</code> 且浏览器 UA 命中飞书 / Lark 客户端,会在登录页自动跳转 <code>/api/auth/feishu/start</code>,让飞书客户端内打开应用时不需要再点一次按钮;登录成功后由后端设置 HttpOnly 会话 Cookie。当前只在原版 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/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/skg-logo-black.svg</code></td><td>从官网 <code>https://cn.skg.com/logo-black.svg</code> 获取的 SKG 官方黑色 SVG 字标;登录页通过 CSS 反相成白色玻璃标识使用,工作台顶部 brand strip 通过米白 logo chip 直接复用。</td></tr>
<tr><td><code>web/components/login/animated-login-characters.tsx</code></td><td>登录页四个几何动态角色组件:当前嵌入登录框顶部,去掉独立网格背景,保留鼠标眼神跟随、输入、显示密码、错误和成功状态反馈;工作台低密度空状态通过 <code>AnimatedLoginCharacters</code> 复用同一品牌角色,不在高密度分镜区堆叠角色。</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>旧底部吸附音频条组件:当前主界面不再渲染,音频文案、翻译、讲话人、节奏和背景音统一在右侧复刻工作表里查看。</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/storyboard-bar.tsx</code></td><td>顶部分镜编排条:展示选入编排的关键帧,并作为唯一分镜导航。</td></tr>
<tr><td><code>web/components/storyboard-workbench.tsx</code></td><td>顶部分镜编排条下方的明细区4 图槽、改造目标、时长、自动保存。</td></tr>
<tr><td><code>web/lib/api.ts</code></td><td>前端类型和 API client是前后端数据契约镜像<code>RuntimeHealth</code> / <code>RuntimeModels</code> 读取 <code>GET /health</code>,把 ASR、翻译、视觉、图像、视频等模型名作为前端模型标注的真源并同步承接 <code>RuntimeSizeOption</code>、图片尺寸、视频画幅、视频时长和最大单条秒数。<code>GeneratedVideo</code> 额外承接 <code>queue_position</code><code>queue_size</code><code>queue_message</code>,用于首页和后续个人画布显示视频生成队列。默认首页主要使用 <code>createCreativeImageJob</code><code>uploadReferenceFrame</code><code>generateImage</code><code>generateStoryboardVideo</code><code>generateImage</code> 请求体现在可传 <code>size</code><code>generateCreativeCopy</code> 仍保留给详情页和后续高级能力。资源库相关类型和 CRUD/use/copy-to-job 函数继续保留给旧工作台和后续资源中心。</td></tr>
</tbody>
</table>
</div>
<div class="card">
<h3>后端核心</h3>
<table>
<tbody>
<tr><td><code>api/main.py</code></td><td>FastAPI 单文件后端登录会话、状态模型、任务恢复、下载、抽帧、Vision、清洗、元素、分镜、原音频转写/翻译、声音与背景音分析、后续口播改写/TTS、文件返回同时承载全局 <code>prompt_library</code><code>asset_library</code> 的磁盘索引、CRUD、删除保护和复制到 job API。启动时会初始化 Postgres schema、扫描现有 <code>state.json</code> / 资源库并写入索引;<code>/canvas-projects</code> 系列接口把画布项目按当前登录用户持久化,<code>/canvas-workflows</code> 系列接口把我的工作流按当前登录用户持久化为可复用模板。轻量创作入口 <code>POST /creative/jobs/image</code> 把上传图片或空白底图写成一个只有 0 号关键帧的 <code>Job</code>,让首页直接复用生图/生视频接口;该接口兼容无 body / JSON 空对象 / 正常 multipart 上传,避免无首帧文生图或文生视频时空 multipart 被 FastAPI 在业务前置解析阶段拒绝;<code>POST /prompt/polish</code> 用于中性 AI 润色和通用 LLM 文本生成,只保留用户明确给出的主体、品牌、产品、地点、风格和意图,不默认加入 SKG、按摩产品、平台或短视频广告话术。润色链路会先用 <code>_strip_previous_polish_boilerplate</code> 去掉旧模板尾巴,再用 <code>_classify_prompt_intent</code> 判断人物、无人、物体、场景、动物或未知主体,最后用 <code>_repair_polished_prompt</code> 修掉有人/无人矛盾、未写人却新增人物、未写 SKG 却出现 SKG 等冲突;<code>_append_reference_image_person_guard</code> 会在视频任务最终入队前给参考图请求追加条件提示,声明参考图里若有人物则按 AI 生成的虚拟角色处理;<code>/health</code> 返回 <code>database</code><code>image_options</code><code>image_size_options</code><code>video_options</code><code>video_size_options</code><code>video_duration_options</code><code>video_max_duration_seconds</code><code>/frames/{idx}/generate</code><code>model</code> 字段用于图片模型偏好,<code>size</code> 字段用于图片输出尺寸;<code>/storyboard/video</code> 继续使用 <code>model</code> 字段选择视频别名,并先校验画幅与时长能力边界,然后把 <code>GeneratedVideo</code> 写成 <code>queued</code> 占位并进入进程内视频队列。队列默认 <code>VIDEO_QUEUE_MAX_CONCURRENT=2</code><code>VIDEO_QUEUE_MAX_CONCURRENT_PER_USER=1</code>,同一用户连续提交不会占满全局并发;排队任务会回写 <code>queue_position</code><code>queue_size</code><code>queue_message</code>。旧 <code>AgentRun</code> 一键出片状态机、TK 复刻接口和 <code>POST /creative/copy</code> 作为明确的 SKG 营销文案接口继续保留。</td></tr>
<tr><td><code>api/db.py</code></td><td>Postgres 适配层:在 <code>DATABASE_URL</code> 存在且 <code>psycopg</code> 可用时启用;负责建表、健康检查、用户 upsert、审计日志、画布项目 CRUD、我的工作流 CRUD以及把 <code>Job</code><code>AgentRun</code>、提示词库和素材库写入索引表。数据库不可用时本地开发会降级为 disabled生产 <code>verify-prod-docker.sh</code> 会要求 <code>database.connected=true</code></td></tr>
<tr><td><code>video_model_options()</code></td><td>视频模型能力出口:如果 <code>seedance</code><code>kling</code><code>veo3</code><code>veo</code> 等业务别名实际都映射到同一个真实模型,会按真实模型去重,只给前端返回一个可用选项;当前生产真实模型为 <code>doubao-seedance-2-0-fast-260128</code>,前端显示为 <code>Seedance 2.0 Fast</code>。后续只有在服务器真的配置了不同可用视频模型时,才应把新的模型重新暴露给画布。</td></tr>
<tr><td><code>api/product_library/skg-products</code></td><td>内置 SKG 白底产品图库:<code>manifest.json</code> 记录从桌面产品图筛出的 gallery 白底图和桌面 4 张产品角度图,<code>images/</code> 存 45 张参考图。</td></tr>
<tr><td><code>api/character_library/skg-characters</code></td><td>内置相似主体形象库:从桌面 5 套策划形象导入,<code>manifest.json</code> 记录运动阳光男、都市型男、优雅白领女、运动辣妹、绅士大叔,每套含 7 张透明骨架参考图和一段 <code>prompt_brief</code>。相似主体生成时优先使用文字 brief 作为创意方向,避免把内置图作为强参考图复制。</td></tr>
<tr><td><code>asset_library/</code></td><td>全局素材库目录,和 <code>jobs/</code> 平级,不写入任何 job state。四类目录为 <code>subjects</code><code>products</code><code>scenes</code><code>videos</code>;每个素材自带 <code>manifest.json</code> 和图片/视频文件,<code>index.json</code> 只是启动扫描重建出来的缓存。库素材选用到 job 时必须复制文件到 <code>jobs/&lt;jobId&gt;/assets</code><code>storyboard-videos</code>,禁止直接保存 library 引用。</td></tr>
<tr><td><code>prompt_library/</code></td><td>全局提示词库目录,和 <code>jobs/</code> 平级;<code>items/&lt;id&gt;.json</code> 是 source of truth<code>index.json</code> 是缓存。提示词只存文本和中英镜像,不涉及 <code>ImageRef</code></td></tr>
<tr><td><code>jobs/&lt;jobId&gt;/state.json</code></td><td>运行时状态文件,不在源码列表里,但刷新恢复依赖它。</td></tr>
<tr><td><code>jobs/&lt;jobId&gt;/audio.wav</code></td><td>拆轨得到的原始音频,当前只作为后端分析和后续必要预览的只读文件来源;主界面不再默认渲染底部音频条。</td></tr>
<tr><td><code>jobs/&lt;jobId&gt;/frames</code></td><td>关键帧 jpg。注意 frame.index 是稳定 ID不等于数组下标。</td></tr>
<tr><td><code>jobs/&lt;jobId&gt;/cleaned</code></td><td>清洗后待应用图片。</td></tr>
<tr><td><code>jobs/&lt;jobId&gt;/elements</code></td><td>元素提取图,多版本命名:<code>idx_elementId_cutoutId.jpg</code></td></tr>
<tr><td><code>jobs/&lt;jobId&gt;/gen</code></td><td>关键帧生图结果。</td></tr>
</tbody>
</table>
</div>
</div>
<pre>当前前端主链路:
web/canvas-app/(生产根域名 /
-> 画布底部对话框:文生图 / 文生视频 / 图生视频
-> 模型选项GET /health → image_options / video_options
-> 创建轻量任务POST /creative/jobs/image → 生成轻量 Job图生视频时把上传图片作为视频参考图
-> 生图generateImage(job.id, 0, { prompt, mode: text, model }) → jobs/&lt;jobId&gt;/gen
-> 生视频generateStoryboardVideo(job.id, 0, { prompt, model, first_image?, duration }) → jobs/&lt;jobId&gt;/storyboard_videos
-> 当前结果:图片 / 视频节点自动排列到画布
-> 画布项目web/canvas-app/src/stores/projects.js → GET/POST/PUT/DELETE /canvas-projects → Postgres canvas_projects
-> 我的工作流web/canvas-app/src/stores/workflows.js → GET/POST/PUT/DELETE /canvas-workflows → Postgres canvas_workflows
-> 任务详情页web/app/detail/page.tsx?job=&lt;id&gt; → getJob → 展示参考图、生成图、视频、提示词、图文方案 → 可继续生成 / 删除 / 复制
旧版 TK 复刻链路(最后版本保留):
web/app/page.tsx
-> 信息流广告复刻工作表web/components/ad-recreation-board.tsx
-> 开始分析:创建/激活 job → 下载完成后并行触发视频视觉路 analyzeJob 与音频文案路 triggerTranscribe
-> 后台流程判定01 素材输入 → 02 源视频下载 → 03 音频文案 → 04 抽帧参考 → 05 主体重构 → 06 产品素材池 → 07 分镜文案 → 08 三字段规划 → 09 视频候选;每步从 buildWorkflowSteps 取判定依据和状态,但默认不渲染完整状态条
-> 左侧侧边素材输入面板 + 右侧源视频工作区(竖版 9:16 原视频播放器放大并内置当前点抽帧,逐句时间轴移到原版视频下方,英文/中文最多两行显示;右侧上方连续响度波形显示当前/总时长/指针停点,波形下方是可调低/中/高密度的临时画面胶片,单击仅跳转、双击或拖入参考帧池才正式选帧,并复用同密度胶片缓存;右侧下方主体链路为上方参考帧池 + 转换层、下方主体元素结果栏:参考帧池竖排,转换层负责参考图分析/对话/提示词确认,主体元素横向展示生成结果;旧相似主体 / 主体模板区不再主路径渲染;讲话人/节奏/背景音分析写入数据但不默认显示成卡片)
-> 信息流复刻分镜工作台06 同一产品素材池不限量上传 → 自动识别视角 / 背景 / 用途 / 风险 → 人工检查备注 → 07 逐句时间轴 / 原内容 / 新口播文案 → 08 紧凑三字段(文案、场景一句话、人物+产品+动作;可折叠)→ quick-plan 自动展开高级字段 → 单条生成 4 个视频候选 / 横向候选轨追加生成 / 点击预览 / 显式下载 → 09 整片一键后台批量提交
-> 快速出片终端web/app/agent/page.tsx → POST /agent-runs → 轮询 GET /agent-runs/{id} → 播放 /agent-runs/{id}/final.mp4
-> 底部音频条:不再渲染,音频结果集中到右侧工作表
-> 旧节点/深度素材面板web/components/nodes/index.tsx、web/components/lightbox.tsx、web/components/storyboard-workbench.tsx底层保留当前不作为主入口
-> API 契约web/lib/api.ts
后端主链路:
api/main.py
-> Auth session / Feishu OAuth / Job owner / AgentRun owner / KeyFrame / KeyElement / StoryboardScene / AudioScript
-> api/db.py / Postgres: app_users / canvas_projects / canvas_workflows / job_index / generated_assets / prompt_library_index / asset_library_index / agent_run_index / audit_events
-> 下载 / 上传 / 音频提取 / ASR / 翻译 / 声音背景音分析 / 抽帧 / Vision brief / GPT 图像生成 / 产品视角识别 / 分镜保存 / 首尾帧生成 / 后续 Azure OpenAI 配音预留
-> jobs/&lt;jobId&gt;/state.json + agent_runs/&lt;runId&gt;/state.json + 图片文件落盘API 层按登录用户过滤列表和详情</pre>
</section>
<section id="ui-map" data-search>
<h2>界面区域到源码</h2>
<div class="flow">
<div class="flow-row">
<div><strong>你看到的区域</strong><span>SKG 首页</span></div>
<div><strong>主要源码</strong><span><code>web/app/page.tsx</code>;前端 API client 在 <code>web/lib/api.ts</code>;轻量创作后端在 <code>api/main.py</code><code>/creative/jobs/image</code><code>/creative/copy</code><code>/prompt/polish</code>,实际图片和视频生成继续复用 <code>/jobs/{id}/frames/{idx}/generate</code><code>/jobs/{id}/frames/{idx}/storyboard/video</code></span></div>
<div><strong>适合怎么描述</strong><span>“首页只有一个对话框,三个模式是文生图、文生视频、图生视频;图生视频上传图片后手写提示词生成”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>个人生成画布 / 工作流面板</span></div>
<div><strong>主要源码</strong><span><code>web/canvas-app/src/views/Canvas.vue</code><code>web/canvas-app/src/components/WorkflowPanel.vue</code><code>web/canvas-app/src/stores/projects.js</code><code>web/canvas-app/src/stores/workflows.js</code>;后端项目接口在 <code>/canvas-projects</code>,我的工作流接口在 <code>/canvas-workflows</code></span></div>
<div><strong>适合怎么描述</strong><span>“画布当前节点怎么保存成我的工作流、我的工作流为什么空、插回画布时节点位置和连线怎么处理、同一飞书账号换电脑后为什么能看到同一批个人模板”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>任务详情页</span></div>
<div><strong>主要源码</strong><span><code>web/app/detail/page.tsx</code>;通过 <code>/detail/?job=&lt;id&gt;</code> 读取任务;复用 <code>getJob</code><code>generateImage</code><code>generateStoryboardVideo</code><code>generateCreativeCopy</code><code>deleteGeneratedImage</code><code>deleteGeneratedVideo</code></span></div>
<div><strong>适合怎么描述</strong><span>“某个任务里参考图、生成图、视频候选、提示词、营销图文方案怎么沉淀,继续生成或删除入口在哪里”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>一分钟二创出片终端</span></div>
<div><strong>主要源码</strong><span><code>web/app/agent/page.tsx</code>;后端状态机、日志、最终成片文件和审片图在 <code>api/main.py</code><code>/agent-runs</code> 系列接口。</span></div>
<div><strong>适合怎么描述</strong><span>“新快速页只要链接、产品图、实时终端和最终视频,后台自己做下载、抽帧、规划、生成、重跑、审片、合成;旧工作台保留给高级编辑”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>信息流广告复刻工作表</span></div>
<div><strong>主要源码</strong><span>旧高级能力保留在 <code>AdRecreationBoard</code> in <code>web/components/ad-recreation-board.tsx</code>;品牌 token、暖光背景、主/次按钮、stat 卡片和空状态角色样式在 <code>web/app/globals.css</code>。当前默认首页不再挂载该组件。</span></div>
<div><strong>适合怎么描述</strong><span>“需要恢复或改造旧复刻台时,再明确说信息流复刻工作表、素材输入列、源视频工作区、转换层、分镜工作台”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>源视频工作区</span></div>
<div><strong>主要源码</strong><span><code>AudioIntakePanel</code> / <code>SourceSubjectPipeline</code> in <code>web/components/ad-recreation-board.tsx</code>;参考帧和主体元素缩略图复用 <code>MediaAssetTile</code>;后端复用 <code>triggerTranscribe</code><code>AudioScript</code><code>analyzeJob</code><code>addManualFrame</code><code>deleteFrame</code><code>generateSubjectAssets</code></span></div>
<div><strong>适合怎么描述</strong><span>“竖版原视频尺寸、逐句时间轴、连续响度波形、临时画面胶片、参考帧池竖排、主体元素重生/删除/hover 放大还需要怎么调整”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>信息流复刻分镜工作台</span></div>
<div><strong>主要源码</strong><span><code>AudioStoryboardPlanPanel</code><code>ProductReferenceCard</code><code>MissingProductViewSlot</code><code>buildAudioStoryboardRows</code><code>selectProductItemsForRow</code><code>subjectAssetRefsForPlanning</code><code>subjectBriefForEndpoint</code><code>endpointAssetRef</code><code>buildEndpointFramePrompt</code><code>buildStoryboardSceneFromAudioRow</code><code>generateEndpointFrameForRow</code><code>saveRowStoryboardDraft</code><code>saveAllStoryboardDrafts</code><code>EndpointFrameSlot</code><code>StoryboardVideoSlots</code> in <code>web/components/ad-recreation-board.tsx</code>;产品图、首尾帧和视频候选缩略图统一复用 <code>MediaAssetTile</code>,包括顶层 hover 放大和删除入口。产品白底图上传复用 <code>uploadStoryboardAsset</code>,视角自动识别调用 <code>analyzeProductViews</code>,缺角度自动补图调用 <code>generateProductAngleAsset</code>。当前单条/批量按钮只保存规划;首尾帧按钮调用 <code>generateSceneAsset</code>,传 <code>subject_brief</code> 和端点选择后的 1-2 张 <code>product_images</code>,不再传主体图或 contact sheet再用 <code>PUT /frames/{idx}/storyboard</code> 保存 asset 首尾帧引用;首尾帧删除只移除本条规划中的引用,避免继续误用旧资产。语言策略由 <code>AudioStoryboardRow</code> 的英文主字段 + <code>*Zh</code> 镜像字段承载:<code>role</code> 内部是 <code>hook/pain/proof/solution/cta/bridge</code><code>buildEndpointFramePrompt</code><code>StoryboardScene</code> 主值默认英文,中文只用于团队阅读;首尾帧提交前前端 <code>translateText</code> 兜底,后端 <code>_ensure_english</code> 再兜底。<code>web/app/page.tsx</code> 的视频提交回调有暂停保护,旧入口误触也不会请求 <code>/storyboard/video</code></span></div>
<div><strong>适合怎么描述</strong><span>“按音频逐句生成产品分镜、每行怎样改写口播、哪几句不需要产品或人物、首帧/尾帧该怎么停、首尾帧是否已经生成并准确、产品素材池识别/补图后的备注是否准确、哪些分镜后续才值得进入单条视频候选”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>三字段候选生成行 / 可折叠视频候选区 / 高级抽屉</span></div>
<div><strong>主要源码</strong><span><code>AudioStoryboardPlanPanel</code><code>CompactStoryboardField</code><code>StoryboardVideoSlots</code><code>StoryboardVideoPreview</code> in <code>web/components/ad-recreation-board.tsx</code>;前端接口是 <code>quickPlanStoryboard</code><code>refineStoryboard</code><code>batchGenerateAll</code><code>generateStoryboardVideo(count)</code> in <code>web/lib/api.ts</code>;后端接口和后台线程在 <code>api/main.py</code></span></div>
<div><strong>适合怎么描述</strong><span>“默认行只露三字段,产品/批量/三字段/候选/高级能不能折叠候选是否默认不占大面积AI 改写是否先预览、生成 4 个视频是否追加、哪个候选被选中、整片批量生成进度和失败重试、首尾帧细节是否只在高级里出现”。</span></div>
</div>
<div class="flow-row">
<div><strong>你看到的区域</strong><span>全局资源中心浮窗</span></div>
<div><strong>主要源码</strong><span><code>LibraryDrawer</code> in <code>web/components/resource-library/library-drawer.tsx</code>;入口、保存到库和应用回写在 <code>AdRecreationBoard</code><code>AudioStoryboardPlanPanel</code><code>ProductReferenceCard</code><code>EndpointFrameSlot</code>;前端接口在 <code>web/lib/api.ts</code>后端目录扫描、索引、CRUD、删除保护和复制到 job 在 <code>api/main.py</code></span></div>
<div><strong>适合怎么描述</strong><span>“资源库浮窗的尺寸/位置/Tab 记忆、提示词 5 列、素材 4 列、最近 24 小时条、复制提示词、素材应用到当前 job、保存产品/首尾帧/规划 prompt 到库、删除前引用确认、节点不能拖动这些交互要怎么改”。</span></div>
</div>
<div class="flow-row">
<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>“这一组关键帧如何共同生成一个统一主体包;某张关键帧的水印、去主体场景图、产品融合镜头组和质量风险应该如何审核”。</span></div>
</div>
<div class="flow-row">
<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>“每个分镜需要哪些图片槽、哪些改造说明,哪些 SKG 产品图要作为视频生成参考”。</span></div>
</div>
</div>
</section>
<section id="data-model" data-search>
<h2>数据模型</h2>
<div class="grid-2">
<div class="card">
<h3>Job</h3>
<p>一个视频任务。前端维护多个 <code>jobs[]</code>,当前激活的是 <code>activeJobId</code>。URL 查询参数会持久化多个 job。</p>
<pre>Job {
id, url, status, progress, message,
owner_id, owner_name, owner_email, owner_provider, tenant_key,
video_url, source_audio_url, duration, width, height,
frames: KeyFrame[],
transcript: TranscriptSegment[],
audio_script: AudioScript,
subject_agent: SubjectAgentState,
storyboard_images?: StoryboardImage[],
product_refs?: ProductRefStateItem[]
}</pre>
</div>
<div class="card">
<h3>AgentRun</h3>
<p>一键出片终端的后台状态机。它不替代 <code>Job</code>,而是持有一个 <code>job_id</code> 并记录终端日志、阶段、生成视频 ID、审片图和最终 mp4。</p>
<pre>AgentRun {
id, job_id,
owner_id, owner_name, owner_email, owner_provider, tenant_key,
status: draft | queued | executing | reviewing | completed | failed,
stage, progress,
logs: AgentRunLog[],
video_ids: string[],
final_video_url,
contact_sheet_url,
error,
created_at, updated_at
}</pre>
</div>
<div class="card">
<h3>CanvasProject</h3>
<p>根域名画布的项目持久化模型。前端仍保留本地缓存,但服务端 <code>canvas_projects</code> 是公司内部沉淀的主存储;默认可见性为 <code>private</code>,只有 owner 可改写,后续可扩展 team/company 可见项目。</p>
<pre>CanvasProject {
id,
owner_id, owner_name, owner_email, owner_provider, tenant_key,
name,
thumbnail,
visibility: private | team | company,
canvas_data: { nodes, edges, viewport, ... },
version,
created_at, updated_at, deleted_at
}</pre>
</div>
<div class="card">
<h3>CanvasWorkflow</h3>
<p>我的工作流的云端个人模板模型。它保存可复用的画布结构,不保存一次生成出来的媒体结果;前端写入前会清掉图片 URL、视频 URL、任务进度、错误、LLM 输出和生成结果列表,插回画布时重新生成节点 ID 并重连边。</p>
<pre>CanvasWorkflow {
id,
owner_id, owner_name, owner_email, owner_provider,
name, description, thumbnail,
workflow_data: { nodes, edges, viewport },
version,
created_at, updated_at, deleted_at
}</pre>
</div>
<div class="card">
<h3>Postgres 索引表</h3>
<p>Postgres 不替代大文件和完整 job state它负责跨用户、跨浏览器和后续后台管理需要的结构化索引。任务详情、媒体文件和资源库原始 manifest 仍保留在现有目录里。</p>
<pre>app_users
canvas_projects
canvas_workflows
job_index
generated_assets
prompt_library_index
asset_library_index
agent_run_index
audit_events</pre>
</div>
<div class="card">
<h3>KeyFrame</h3>
<p>关键帧是整个产品的核心单位。<code>index</code> 是稳定 ID手动加帧后不连续不能用数组下标代替。</p>
<pre>KeyFrame {
index, timestamp, url,
description,
transparent_human_score,
cleaned_url, cleaned_applied,
quality_report,
scene_assets: SceneAsset[],
elements: KeyElement[],
storyboard: StoryboardScene,
generated_images: GeneratedImage[]
}</pre>
</div>
<div class="card">
<h3>SubjectAgentState</h3>
<p>转换层生图对话的项目内记忆。它跟随 <code>Job</code> 写入 <code>state.json</code>,保存当前参考帧、模型套件、识别摘要、对话要求、最终英文提示词、方向和数量;主界面会用这些字段驱动同一输入区里的“提示词就绪”和“确认生成 N 张”按钮,用户点击后才生成主体套图。</p>
<pre>SubjectAgentState {
model_bundle: "gpt" | "gemini",
source_frame_indices: number[],
analysis?: SubjectAgentAnalysis,
messages: SubjectAgentMessage[],
selected_mode: "realistic" | "cartoon" | "elements" | "custom",
selected_traits: string[],
requirements_zh,
generation_prompt_en,
quantity,
updated_at
}</pre>
</div>
<div class="card">
<h3>TransparentHumanFrameScore</h3>
<p>透明骨架人主题的抽帧验收结果。只有 <code>target=transparent_human</code> 时会在抽帧阶段写入;普通抽帧目标不要求该字段。</p>
<pre>TransparentHumanFrameScore {
transparent_body_score: 0-25,
skeleton_visible_score: 0-25,
human_prominence_score: 0-15,
clarity_score: 0-15,
commercial_style_score: 0-10,
product_usefulness_score: 0-10,
total_score,
qualified,
reject_reason
}</pre>
</div>
<div class="card">
<h3>KeyElement</h3>
<p>从关键帧识别结果里确认出来的主体候选。当前素材准备流程只保留一个统一主体;多张关键帧通过 <code>source_frame_indices</code> 作为该主体的参考帧。</p>
<pre>KeyElement {
id,
name_zh, name_en, position,
source: auto | manual | region,
region,
cutouts: string[],
cutout_id,
subject_kind: object | living,
subject_assets: SubjectAsset[],
subject_consensus_brief,
subject_consensus_brief_zh
}</pre>
</div>
<div class="card">
<h3>AudioScript</h3>
<p>第一步音频解析的结构化产物。<code>pipeline_transcribe</code> 提取 <code>audio.wav</code> 后先保存原语言转写(支持中文、英文和其他多语言)、中文镜像、讲话人画像、口播节奏和背景音乐/环境声/音效分析。<code>rewritten_text</code> 是英文新口播,<code>rewritten_text_zh</code> 只作为团队审稿镜像;<code>voice_url</code> 等字段仍保留给后续新配音阶段。</p>
<pre>AudioScript {
status: idle | rewriting | completed | failed,
source_text,
source_zh,
rewritten_text,
rewritten_text_zh,
speaker_profile,
rhythm_profile,
background_audio_profile,
product_brief,
rewrite_model,
voice_provider: azure_openai,
voice_model,
voice_id,
voice_url,
error
}</pre>
</div>
<div class="card">
<h3>SceneAsset / SubjectAsset</h3>
<p>画面工作台素材准备阶段生成的组图资产。实际图片保存在 <code>jobs/&lt;jobId&gt;/assets</code>,可作为 <code>asset</code> 类型复制到分镜槽位。</p>
<pre>SceneAsset {
id, label, url,
width, height, quality, size,
scene_mode: remove_subject | similar | style,
scene_style,
quality_report
}
SubjectAsset {
id, view, label, url,
background: white | black,
width, height, size,
source_frame_indices[],
status: queued | in_progress | completed | failed,
progress, error,
pack_id, pack_label, pack_mode, pack_created_at
}</pre>
<p><code>SubjectTemplateItem</code> 保存用户确认过的主体视图包。<code>prompt_brief</code> 是后端从模板图反推的英文文字特征,后续相似生成优先读取它,而不是再次把模板图作为强参考图传给 image-edit<code>prompt_brief_zh</code> 仅用于模板库卡片和团队阅读。</p>
<pre>SubjectTemplateItem {
id, name, description, note,
prompt_brief,
prompt_brief_zh,
subject_style: transparent_human | source_actor | cartoon_subject,
primary_image,
images: SubjectTemplateImage[]
}</pre>
</div>
<div class="card">
<h3>ProductLibraryItem</h3>
<p>内置 SKG 白底图库条目。实际图片保存在 <code>api/product_library/skg-products/images</code>,被选中时会复制到 <code>jobs/&lt;jobId&gt;/assets</code>,再以普通 <code>ImageRef(kind="asset")</code> 进入产品参考组。</p>
<pre>ProductLibraryItem {
id, handle, title, product_type,
image_index, filename, url,
width, height,
white_score,
source_path,
tags[]
}</pre>
</div>
<div class="card">
<h3>PromptLibraryItem / AssetLibraryItem</h3>
<p>全局资源中心的数据模型。提示词库只保存文本和中英镜像,发给模型时使用 <code>prompt_en</code>;素材库保存主体、产品、场景和视频四类全局资源。库目录和 job 目录完全平级,索引文件只是缓存,服务启动会扫描目录重建。素材应用到 job 时永远复制文件,返回普通 <code>ImageRef(kind="asset")</code> 或 job 内视频引用,不把 <code>library_*</code> 引用写入 job state。</p>
<pre>PromptLibraryItem {
id,
category: scene_desc | video_desc | subject_desc | skg_script | product_angle,
name,
tags[],
prompt_en,
prompt_zh,
use_count,
source_job_id,
created_at,
updated_at
}
AssetLibraryItem {
id,
kind: subjects | products | scenes | videos,
name,
name_zh,
note,
tags[],
source_job_id,
use_count,
created_at,
updated_at,
is_official,
prompt_brief,
prompt_brief_zh,
subject_style,
product_type,
asset_role,
duration,
aspect_ratio,
images: AssetLibraryImage[],
poster,
video_url
}</pre>
</div>
<div class="card">
<h3>ImageRef.asset_meta</h3>
<p>产品图上传或从产品图库复制到 job 时,后端会把原图转为统一 AI 工作副本,避免超高清原图拖慢识别、生图和生视频链路。黑底/白底不强行改色;只有透明底会铺白。前端用该字段展示工作图尺寸、自动转换动作和分辨率风险。</p>
<pre>asset_meta {
standard: "AI工作副本最长边≤1600px建议长边≥900px短边≥600pxJPEG q92",
original_width, original_height,
width, height,
original_bytes, work_bytes,
max_side, min_long_side, min_short_side, quality,
actions[],
warnings[],
normalized
}</pre>
</div>
<div class="card">
<h3>ProductViewAnalysisItem</h3>
<p>产品素材池识别结果。它不判断不同产品身份,只服务同一款挂脖肩颈按摩仪的生视频选图和方向约束;左/右按佩戴者身体左右不按图片左右。前端会把上传图、识别标注、AI 补图、备注和删除结果写入 <code>Job.product_refs</code>,后端保存到 <code>state.json</code>,避免刷新、热更新或服务重启后丢失产品素材池。</p>
<pre>ProductViewAnalysisItem {
index,
view: front | left_45 | right_45 | side_thickness | inner_contacts | back_bottom,
background,
use_tags[],
orientation: {
product_left, product_right,
top, bottom,
inner_side, outer_side,
opening_direction
},
landmarks[],
note,
risk,
confidence
}
ProductRefStateItem {
id,
ref: ImageRef,
view,
background,
useTags[],
orientation,
landmarks[],
note,
risk,
source: upload | ai,
assetMeta,
confidence
}</pre>
</div>
<div class="card">
<h3>ProductFusionShot</h3>
<p>产品融合镜头组的单行数据。每个关键帧最多 6 行,用户选择一个内置角色后只微调场景/产品使用/享受描述和秒数;四张桌面 SKG 产品角度图会在顶部显式展示为真实产品真源,所选角色 7 张参考图作为人物身份参考,生成时作为 Seedance 参考图提交。</p>
<pre>ProductFusionShot {
id,
first_image,
last_image,
product_images[4],
action_text,
duration,
image_model: gpt-image-2,
video_model: seedance,
// legacy: product_image, person_image, product_region, scene_image, guide_image
}</pre>
</div>
<div class="card">
<h3>StoryboardScene</h3>
<p>分镜编排结果,不是复刻说明。当前主流程里 <code>first_image</code>/<code>last_image</code> 只接受生成后的 asset 首尾帧;旧 keyframe 首尾帧引用会被前端忽略。<code>subject_images</code> 保存相似主体白底视图,<code>product_images</code> 保存该行挑选的产品参考。</p>
<pre>StoryboardScene {
duration,
visual_mode: person_only | person_product | product_only | environment,
needs_product,
needs_subject,
subject_brief,
skg_copy_en,
skg_copy_zh,
scene_one_line_en,
scene_one_line_zh,
action_one_line_en,
action_one_line_zh,
selected_video_id,
first_frame_plan,
last_frame_plan,
product_placement,
first_image,
last_image,
product_images[],
subject_images[],
subject_image,
scene_image,
product_image,
action_image,
subject,
product,
scene,
action
}</pre>
</div>
</div>
</section>
<section id="api-map" data-search>
<h2>接口地图</h2>
<table>
<thead>
<tr><th>功能</th><th>接口</th><th>前端调用</th><th>说明</th></tr>
</thead>
<tbody>
<tr><td>网页登录 / 飞书免登录</td><td><code>GET /auth/config</code><code>GET /auth/feishu/start</code><code>GET /auth/feishu/callback</code><code>POST /auth/login</code><code>GET /auth/check</code><code>GET /auth/me</code><code>POST /auth/logout</code></td><td><code>web/app/login/page.tsx</code>、Nginx <code>auth_request</code></td><td>登录页先读 <code>/api/auth/config</code> 判断是否显示飞书按钮和密码表单;飞书客户端内且 <code>feishu_enabled=true</code> 时前端自动跳转授权入口,普通浏览器保留手动飞书按钮。飞书 OAuth 成功后后端用 open_id / union_id / email 生成多用户会话并设置 HttpOnly Cookie。当前生产 <code>PASSWORD_AUTH_ENABLED=false</code>,因此 <code>password_enabled=false</code>,账号密码表单不展示,<code>POST /auth/login</code> 返回未配置。生产 Nginx 对工作台和 <code>/api/</code><code>/auth/check</code> 做统一校验,未登录页面跳 <code>/login/?next=$request_uri</code>API 返回 JSON 401。</td></tr>
<tr><td>运行配置 / 模型标注</td><td><code>GET /health</code></td><td><code>getRuntimeHealth</code><code>ModelTrace</code></td><td>返回 <code>database</code> 健康状态和 <code>models</code>ASR、<code>asr_language</code>(默认 <code>auto</code>,表示中文/英文/多语言自动识别)、<code>asr_base_url</code><code>asr_remote_enabled</code><code>asr_local_fallback_enabled</code><code>asr_audio_fallback_enabled</code><code>faster_whisper</code>、本机 ASR、ASR fallback、翻译、GPT 改写、GPT 画面理解、产品视角识别 <code>product_view</code>、主图像模型 <code>gpt-image-2</code>、图片故障兜底 <code>image_fallbacks</code>、图片尺寸 <code>image_size_options</code>、短时熔断状态 <code>image_circuit</code>、主体 6 视图模型链路、Azure OpenAI TTS、视频别名、视频画幅 <code>video_size_options</code>、真实可用视频时长 <code>video_duration_options</code>、单条最大秒数 <code>video_max_duration_seconds</code> 和 Seedance 服务商。当前 <code>REWRITE_MODEL</code><code>AUDIO_REWRITE_MODEL</code><code>VISION_MODEL</code> 默认使用 <code>gpt-4o</code>;如果旧环境变量仍写 <code>gemini-*</code>,后端会归一化回 <code>GPT_TEXT_MODEL</code> / <code>REWRITE_MODEL</code>。语音只走 Azure OpenAI TTS<code>models.voice_tts_paths</code> 会回传当前尝试的语音路径,方便区分路径错误和语音服务不可用。前端所有当前主路径里会调用模型的按钮旁显示模型名,点击弹出小窗口查看模型链路和输入输出逻辑;不返回 API Key、数据库密码或敏感凭证。</td></tr>
<tr><td>历史列表</td><td><code>GET /jobs</code></td><td><code>listJobs</code></td><td>当前登录用户可见 job 精简列表id/url/status/thumbnail/mtime/owner…按 state.json mtime 倒序。前端 URL 无 <code>?job=</code> 时拉它回填本人历史;带 <code>limit</code> 可截断。开启数据隔离时,飞书用户只看到自己的任务,历史无 owner 的旧任务只对备用账号可见。</td></tr>
<tr><td>创建任务</td><td><code>POST /jobs</code></td><td><code>createJob</code></td><td>提交 TK 链接,后台开始下载;后端会把当前登录用户写入 <code>Job.owner_*</code>,后续详情、素材文件、删除和生成接口都通过统一中间件校验归属。下载阶段默认不带 cookies生产环境必须显式保持 <code>YTDLP_COOKIES_FILE=</code><code>YTDLP_COOKIES_FROM_BROWSER=</code> 为空,避免容器内误读被打进镜像的开发 <code>api/.env</code></td></tr>
<tr><td>画布项目</td><td><code>GET /canvas-projects</code><br><code>POST /canvas-projects</code><br><code>PUT /canvas-projects/{id}</code><br><code>GET /canvas-projects/{id}</code><br><code>DELETE /canvas-projects/{id}</code><br><code>POST /canvas-projects/import</code></td><td><code>web/canvas-app/src/stores/projects.js</code></td><td>根域名画布项目的服务端持久化接口。列表和详情按当前登录用户过滤;写入时保存画布 JSON、缩略图、可见性、版本和更新时间删除为软删除。首次上线后本地 <code>localStorage</code> 旧项目会通过 import 导入到当前用户,之后服务端 Postgres 是主存储。</td></tr>
<tr><td>我的工作流</td><td><code>GET /canvas-workflows</code><br><code>POST /canvas-workflows</code><br><code>PUT /canvas-workflows/{id}</code><br><code>DELETE /canvas-workflows/{id}</code></td><td><code>web/canvas-app/src/stores/workflows.js</code><br><code>WorkflowPanel.vue</code></td><td>工作流面板“我的工作流”页的个人模板接口。列表、保存、更新和删除都按当前登录用户过滤;保存的是清理过运行态的 <code>workflow_data.nodes/edges/viewport</code>,用于跨设备复用画布结构。插回画布时前端会按当前视口中心重排节点、重建节点 ID并用旧 ID 到新 ID 的映射重连边,避免和现有画布节点冲突。</td></tr>
<tr><td>画布生成</td><td><code>POST /creative/jobs/image</code><br><code>POST /jobs/{id}/frames/upload</code><br><code>POST /jobs/{id}/frames/{idx}/generate</code><br><code>POST /jobs/{id}/frames/{idx}/storyboard/video</code><br><code>GET /jobs/{id}</code></td><td><code>web/canvas-app/src/hooks/useApi.js</code></td><td>画布项目结构保存在 <code>/canvas-projects</code>;一旦生成图片或视频,就通过同一套 creative job / frame / storyboard video 接口写入当前登录用户自己的 job 目录。文生图会创建空白 creative job 后生成图片;图生视频会把上传图转成 frame 并作为视频参考图提交,提交视频后用 <code>skg:{jobId}:{videoId}</code> 作为画布侧任务 id 轮询 <code>/jobs/{id}</code>,直到视频状态完成或失败。视频任务最终 prompt 会条件说明参考图人物是 AI 生成的虚拟角色,避免员工上传的 AI 人像素材被当成真实肖像处理。</td></tr>
<tr><td>AI 润色 / LLM 节点</td><td><code>POST /prompt/polish</code></td><td><code>web/canvas-app/src/hooks/useApi.js</code><br><code>web/canvas-app/src/api/chat.js</code></td><td>中性的提示词润色和通用文本生成接口。根画布和文本节点传 <code>mode=image</code>、默认输出英文提示词LLM 节点和自动执行意图分析传 <code>mode=chat</code>、保持输入语言。接口会清掉上一次润色遗留的模板尾巴,只保留用户明确写出的主体、品牌、产品、地点、平台、风格和意图;用户没写 <code>SKG</code> 时绝不主动加入 SKG也不主动补产品、平台、广告语境、slogan 或 hashtag。人物安全词按输入条件加入原文明确有人像、模特、角色、数字人等语义时才声明“虚构 AI 角色、非真人、非公众人物”;原文明确无人时才保留无人物约束;原文没写人时不主动造人,也不主动追加无人物禁令;输入提到参考图、首帧或尾帧时,提示词只条件保留已有可见人物,不凭空新增人物。</td></tr>
<tr><td>一键出片终端</td><td><code>POST /agent-runs</code><br><code>GET /agent-runs</code><br><code>GET /agent-runs/{id}</code><br><code>GET /agent-runs/{id}/final.mp4</code><br><code>GET /agent-runs/{id}/contact.jpg</code></td><td><code>web/app/agent/page.tsx</code></td><td>快速出片页的唯一主接口。前端提交 TikTok 链接和最多 6 张产品图;后端创建同 owner 的 <code>Job</code><code>AgentRun</code>后台执行下载、产品图归一化、透明骨架主体参考复制、12 段镜头计划、视频生成、失败镜头自动重跑一次、审片接触表和 ffmpeg 最终合成。列表、详情、最终 mp4 和接触表同样按 owner 隔离。</td></tr>
<tr><td>重试下载</td><td><code>POST /jobs/{id}/download/retry</code></td><td><code>retryJobDownload</code></td><td>用于 TK 链接下载失败且没有 <code>video_url</code> 的素材;清空错误、重新进入下载状态,并在后台再次执行 <code>pipeline_download</code>。上传视频不能重下载,需要重新上传文件。</td></tr>
<tr><td>上传视频</td><td><code>POST /jobs/upload</code></td><td><code>uploadJob</code></td><td>保存 source.mp4然后同样进入下载完成状态当前上传后也加入第一步队列下载完成后自动解析音频。</td></tr>
<tr><td>删除输入视频</td><td><code>DELETE /jobs/{id}</code></td><td><code>deleteJob</code></td><td>从任务队列、URL 和磁盘 <code>jobs/&lt;id&gt;</code> 目录移除整个 job包括源视频、关键帧、元素提取图和生成视频。</td></tr>
<tr><td>解析视频</td><td><code>POST /jobs/{id}/analyze?frames=&amp;target=&amp;mode=&amp;quality=</code></td><td><code>analyzeJob</code></td><td>抽参考帧能力。当前开始流程会在视频下载完成后自动调用一次,默认 <code>frames=12</code><code>target=motion</code><code>quality=accurate</code><code>mode=replace</code>,形成全局动作/节奏参考帧池;原版视频旁的“抽参考 12 帧”也会用同一参数显式重跑。<code>target</code> 仍支持透明骨架人、综合、清晰主体、转场变化、表情瞬间、动作峰值。</td></tr>
<tr><td>音频文案轨</td><td><code>POST /jobs/{id}/transcribe</code></td><td><code>triggerTranscribe</code></td><td>若尚未拆轨,先从 <code>source.mp4</code> 提取 <code>audio.wav</code> 并回填 <code>source_audio_url</code>;远端启用时把 <code>audio.wav</code> 上传到 <code>ASR_BASE_URL</code> 的 OpenAI Audio Transcriptions 兼容接口,用 <code>ASR_MODEL</code> 提取原始文案。<code>ASR_LANGUAGE</code> 默认空值/auto不传固定语种让远端和本地 ASR 自动识别中文、英文和其他多语言;只有确认素材固定语种时才填写 ISO-639-1 代码。微软官方路径包括 <code>/openai/deployments/{deployment}/audio/transcriptions?api-version=...</code><code>/openai/v1/audio/transcriptions?api-version=preview</code>;当前 SKG 网关探测这些路径均未返回可用 ASR<code>gpt-4o-transcribe</code> 返回 <code>DeploymentNotFound</code>。当前生产因此复制本地成功策略:<code>ASR_REMOTE_ENABLED=false</code><code>ASR_LOCAL_FALLBACK_ENABLED=true</code>,直接走容器内 CPU 版多语言 <code>faster-whisper</code> 生成真实逐句时间轴;<code>ASR_AUDIO_FALLBACK_ENABLED=false</code>,避免 Gemini 多模态假字幕。后端会拒绝重复文本、逐秒假字幕或覆盖率过低的结果,质量校验支持中文等非空格分词文本,不再按英文字符集误判。中文镜像由 <code>TRANSLATE_MODEL</code> 按 ASR 原语言段落补齐,原文已经是中文时保留为简体中文镜像;失败时保留原文时间轴且中文可为空。再用 <code>ASR_FALLBACK_MODEL</code> 读取 <code>audio.wav</code> 和已有转写时间轴,多模态音频分析讲话人、语速节奏、停顿、背景音乐/环境声/音效,写入 <code>speaker_profile</code><code>rhythm_profile</code><code>background_audio_profile</code>;若模型分析失败,则用转写段落、时长和语速做本地估算兜底。当前第一步不默认生成 SKG 新口播和 Azure OpenAI 配音。失败后只要后台 worker 不在运行,就允许重新触发;前端也不再把失败状态下残留的半成品 transcript 当成音频完成。</td></tr>
<tr><td>分镜脚本改写</td><td><code>POST /jobs/{id}/script/rewrite</code></td><td><code>rewriteStoryboardScript</code></td><td>根据原英文参考文案、当前英文新口播、英文 role enum、时间段和作者想法改写英文口播作者想法若含中文后端会先经 <code>_ensure_english</code> 兜底翻译。<code>mode=segment</code> 只改一段;<code>mode=all</code> 一次改完整片,要求整片前后连贯。后端按 <code>AUDIO_REWRITE_MODEL</code><code>ASR_FALLBACK_MODEL</code><code>TRANSLATE_MODEL</code> 依次尝试,全部失败时用英文本地模板保留可编辑文案。接口返回 <code>items[index,text,text_zh]</code>,其中 <code>text</code> 是写入模型链路的英文主值,<code>text_zh</code> 只供团队审稿镜像显示;点击保存规划后写入 <code>StoryboardScene.action</code></td></tr>
<tr><td>原始音频文件</td><td><code>GET /jobs/{id}/audio.wav</code></td><td><code>sourceAudioUrl</code></td><td>返回拆轨得到的 wav当前主界面不再渲染底部吸附音频条右侧复刻工作表会读取该文件生成参考图式横向响度波形并和原视频、逐句时间轴联动波形标题栏显示当前播放秒数、总时长和鼠标指针停点秒数。</td></tr>
<tr><td>改写配音文件</td><td><code>GET /jobs/{id}/audio-script.mp3</code></td><td><code>apiAssetUrl(job.audio_script.voice_url)</code></td><td>后续新配音阶段保留的 TTS 产物;服务端固定走 <code>VOICE_PROVIDER=azure_openai</code>,通过 <code>AZURE_OPENAI_BASE_URL</code> 的 OpenAI 协议生成 mp3并按 <code>AZURE_TTS_PATHS</code> 依次尝试 <code>/audio/speech</code><code>/v1/audio/speech</code> 等路径。当前第一步不默认生成该文件。</td></tr>
<tr><td>手动加帧</td><td><code>POST /jobs/{id}/frames?t=</code></td><td><code>addManualFrame</code></td><td>按视频时间戳抽一帧index 递增但 frames 按 timestamp 排序。当前主界面会把原版视频播放器的播放秒数传给 <code>AudioIntakePanel</code> 标题栏右侧的“当前点抽帧”;胶片缩略图双击或拖入参考帧池也调用同一接口,成功后胶片显示已添加。</td></tr>
<tr><td>上传转换层参考图</td><td><code>POST /jobs/{id}/frames/upload</code></td><td><code>uploadReferenceFrame</code></td><td>把用户拖入转换层的本地图片转成 JPEG 参考帧并写入 <code>job.frames</code>,随后前端把新 frame index 加入转换层参考输入区;这让对话式生图可以直接用桌面图片、左侧参考帧或胶片帧作为同一套 1-3 张参考图。</td></tr>
<tr><td>删除参考帧</td><td><code>DELETE /jobs/{id}/frames/{idx}</code></td><td><code>deleteFrame</code></td><td>删除单张抽帧参考帧并清掉对应选择态;当前主界面每张缩略图右下角提供删除入口,方便手动抽错后直接修正。接口返回状态消息必须称为“参考帧/关键帧”,不能写成“分镜”,避免和逐句 storyboard 行混淆。</td></tr>
<tr><td>Vision 识别</td><td><code>POST /frames/{idx}/describe</code></td><td><code>describeFrame</code></td><td>写入 frame.description后续可从 objects 加候选元素。</td></tr>
<tr><td>清洗水印</td><td><code>POST /frames/{idx}/cleanup</code></td><td><code>cleanupFrame</code></td><td>支持全图和区域清洗,生成 cleaned 待应用版本;前端批量清洗会顺序调用该接口,不自动覆盖原图。单帧清洗状态按 frame.index 隔离,清洗某一张不会禁用其他关键帧的清洗按钮。</td></tr>
<tr><td>应用清洗版</td><td><code>POST /frames/{idx}/cleanup/apply</code></td><td><code>applyCleanedFrame</code></td><td>把 cleaned 待应用版本覆盖到原关键帧,并保留首次原图备份;前端“一键替换待应用”会顺序调用该接口应用所有已有清洗版。</td></tr>
<tr><td>应用清洗</td><td><code>POST /cleanup/apply</code></td><td><code>applyCleanedFrame</code></td><td>物理覆盖 frames/{idx}.jpg并备份原图。</td></tr>
<tr><td>元素增改删</td><td><code>POST/PATCH/DELETE /elements</code></td><td><code>addElement/updateElement/deleteElement</code></td><td>让用户修正 Vision 错误,避免候选结果锁死。</td></tr>
<tr><td>元素提取</td><td><code>POST /elements/{element_id}/cutout</code></td><td><code>cutoutElement</code></td><td>调用图像模型生成独立白底素材图,每次累积一张 cutout。</td></tr>
<tr><td>转换层生图智能体</td><td><code>POST /jobs/{id}/subject-agent/analyze</code><br><code>POST /jobs/{id}/subject-agent/message</code></td><td><code>analyzeSubjectAgent</code><br><code>sendSubjectAgentMessage</code></td><td>转换层主路径接口:<code>analyze</code> 根据 1-3 张参考帧输出主体/服装/风格特征和 trait chips<code>message</code> 从用户对话里识别方向、数量和要求,返回中文需求与英文 <code>generation_prompt_en</code>。前端只在用户确认 prompt 后再调用 <code>generateSubjectAssets</code> 生成右侧主体套图。</td></tr>
<tr><td>主体资产包</td><td><code>POST /elements/{element_id}/subject-assets</code><br><code>DELETE /elements/{element_id}/subject-assets/{asset_id}</code></td><td><code>generateSubjectAssets</code><br><code>deleteSubjectAsset</code></td><td>根据转换层对话状态重新绘制统一主体资产包;当前主界面通过上方参考帧池 + 宽幅转换层提交参考图、对话要求和确认后的 prompt生成结果进入下方主体元素结果栏保留套图输出、轮询、文件夹分组、单张重生和删除。当前源视频工作区使用 <code>subject_style=source_actor</code> 承接形象锁定、创意复刻和自主描述,使用 <code>subject_style=cartoon_subject</code> 承接对话识别出的卡通重构;旧 <code>transparent_human</code> 仍为兼容类型但不是当前转换层默认入口。<code>reconstruction_mode=similar</code> 是参考创新路径:后端先用 <code>VISION_MODEL</code> 把关键帧反推成主体 brief只要有参考帧就把这些帧作为 <code>/images/edits</code> 的 image refs 一起提交,日志会显示 <code>endpoint=/images/edits</code><code>image_refs&gt;0</code>,不再偷偷降级为纯文字生图。卡通重构在后端额外加入原创卡通/插画主体约束,明确不输出真实人物复制 likeness。生成完成后后端会把生成视图反推/写入 <code>KeyElement.subject_consensus_brief</code>,作为后续首尾帧的唯一主体身份文字依据。<code>reconstruction_mode=same</code> 是源形象锁定路径:自主描述空文本或对话要求形象锁定时可使用该路径,后端把参考帧作为 primary visual evidence尽量保留同一可见主体、体态、发型、服装和配色。每个 <code>view</code> 单独调用一次生图明确禁止多视图拼图、contact sheet、多主体、多面板、标签或对比排版。单次图片请求受 <code>IMAGE_REQUEST_TIMEOUT_SECONDS</code> 控制,默认 60 秒;<code>gpt-image-2</code> 超时、429、5xx、DNS 或连接失败时可兜底 <code>gemini-3-pro-image-preview</code>,连续 2 次主模型上游类失败后 600 秒内短时熔断。仅当 <code>image_model_preference=auto</code> 时才启用兜底和熔断;用户显式选择 GPT 或 Gemini 时只走所选模型,方便已知某个上游不可用时直接切换。主体同一套图内一旦触发 Gemini后续视图沿用 Gemini避免风格混杂和重复等待主模型超时。主体 prompt 会要求从参考图继承性别、人种/肤色、年龄体态和角色气质等广义特征,但生成同一个全新主体;多视图必须保持同一脸部设定、发型、体态、服装类型、配色、材质、剪裁和配饰,不允许每个视角换衣服。后端新增 pack bible 固定字段,把主体、发型、肤色、体态、服装、鞋、配饰和禁止换装项注入每个视角;<code>_normalize_asset_image(fill_subject=true)</code> 裁白边后会按目标画布放大主体,而不是只用 <code>thumbnail()</code> 缩小,目标是让全身主体占画布高度约 88-94%。后端不再要求整包全成功才写入:单个视图失败时会保留已成功生成的主体图,返回“部分生成完成”,只有一张都没生成出来才返回错误。<code>replace_views=true</code> 时会替换同一视角旧图;删除接口会移除对应 subject asset 记录并删除本地 jpg 文件。</td></tr>
<tr><td>主体套图状态</td><td><code>SubjectAsset.status</code><br><code>pack_id</code></td><td><code>web/app/page.tsx</code><br><code>SourceSubjectPipeline</code></td><td><code>generateSubjectAssets</code> 现在先写入同一个 <code>pack_id</code> 下的 queued 占位卡并立即返回,后台按视角逐张生成,单张完成就把该占位替换成 completed 图片。前端轮询会把 queued / in_progress 主体资产纳入运行状态;主体元素结果栏按 pack 显示套图文件夹,点击某个文件夹后横向展开该套图,其他套图顺位进入右侧可滚动列表。</td></tr>
<tr><td>首尾帧资产</td><td><code>POST /frames/{idx}/scene-asset</code></td><td><code>generateSceneAsset</code></td><td>同一接口兼容旧场景图和新首尾帧;当前信息流复刻流程传 <code>asset_role=first_frame/last_frame</code><code>subject_brief</code> 和最多 1-2 张 <code>product_images</code>。首尾帧不再传主体图、不再把主体图和产品图拼成 contact sheet主体只走文字 brief允许新动作、新景别、新表情和新环境。若本条需要产品后端只把产品参考图作为 <code>gpt-image-2</code> image-edit 的硬视觉真源;若不需要产品,则走纯文字生图。关键帧只作为行数据承载位置。生成结果保存在 <code>scene_assets</code>,前端再写入 <code>StoryboardScene.first_image/last_image</code></td></tr>
<tr><td>产品图库</td><td><code>GET /product-library/skg</code></td><td><code>listProductLibrary</code></td><td>读取内置 SKG 白底图库 manifest返回产品标题、品类、尺寸、白底评分和预览图 URL。</td></tr>
<tr><td>产品图入库到 job</td><td><code>POST /jobs/{id}/assets</code><code>POST /jobs/{id}/assets/product-library</code></td><td><code>uploadStoryboardAsset</code><code>copyProductLibraryAsset</code></td><td>上传产品图或把内置产品图库条目复制为当前 job 的普通 asset。后端统一生成最长边 1600px、JPEG 92 的 AI 工作副本,透明底铺白,过大/过小图片会在 <code>ImageRef.asset_meta</code> 里返回转换动作和风险;黑底/白底背景本身不强行转换。注意该接口只写图片文件,产品素材池列表另由 <code>PUT /jobs/{id}/product-refs</code> 持久化。</td></tr>
<tr><td>产品素材池保存</td><td><code>PUT /jobs/{id}/product-refs</code></td><td><code>saveProductRefs</code></td><td>把当前 job 的产品素材池列表、识别视角、用途标签、方向、结构点、备注、AI 补图和删除结果保存到 <code>Job.product_refs</code> / <code>state.json</code>。前端上传、识别完成、补角度、编辑备注和删除时都会同步保存;刷新页面或热更新后从 job 恢复,不再要求重新上传和重新识别。</td></tr>
<tr><td>产品视角识别</td><td><code>POST /jobs/{id}/assets/product-views/analyze</code></td><td><code>analyzeProductViews</code></td><td>读取同一产品素材池,按批次把多张图一次性提交给 <code>PRODUCT_VIEW_MODEL=gpt-image-2</code> 做视角标注,不限制只看前 6 张;识别对象被固定为套在脖子上的 U 形肩颈按摩仪。返回 <code>view</code><code>background</code><code>use_tags</code><code>orientation</code><code>landmarks</code>、中文备注、生成风险和置信度;<code>orientation</code> 明确佩戴者左/右、上/下、内外侧和开口方向对应图中哪边,避免把图片左右误当产品左右。批量识别失败会按单图重试,仍失败或文件缺失时写入本地默认视角,并在 <code>risk</code>/<code>note</code> 标明兜底原因。前端不再要求用户手动选择视角,也不做不同产品身份判断。</td></tr>
<tr><td>产品缺角度补图</td><td><code>POST /jobs/{id}/assets/product-angle</code></td><td><code>generateProductAngleAsset</code></td><td>用当前同一产品素材池作为参考,通过 <code>gpt-image-2</code> 自动补全缺失视角,输出新的 <code>ImageRef(kind="asset")</code>。前端不再固定传第一张图,而是按目标视角给已上传/已标注参考图打分,优先选择真实上传图、目标相邻视角、侧厚/触点/底部对应用途标签和低风险高置信图,最多传 6 张;后端通过 <code>/images/edits</code> multipart 的多张 <code>image[]</code> 直接提交给主模型,不再把参考图拼成一张板,降低模型误解成拼图/多产品的概率。Prompt 会约束白底产品图、左右非对称、厚度、内侧触点和肩颈真实佩戴比例,并禁止输出拼图/多产品;遇到上游 429 / saturated、5xx、超时或网络错误会按熔断规则兜底 <code>gemini-3-pro-image-preview</code>400/401/403/404 和参数错误不兜底。</td></tr>
<tr><td>角色库</td><td><code>GET /character-library/skg</code></td><td><code>listCharacterLibrary</code></td><td>读取内置 5 个透明骨架人角色 manifest每个角色含正面、左右 45 度、侧面、背面、半身近景和背部特写 7 张参考图,以及用于相似主体文字生图的 <code>prompt_brief</code></td></tr>
<tr><td>主体模板库</td><td><code>GET /subject-templates</code><br><code>GET /subject-templates/images/{filename}</code><br><code>POST /jobs/{id}/subject-templates</code></td><td><code>listSubjectTemplates</code><br><code>subjectTemplateImageUrl</code><br><code>saveSubjectTemplate</code></td><td>数据库化可复用主体库。前端模板库展示这里保存的主体模板;“保存为主体模板”会把当前 job 的相似主体白底视图按名称、备注、主体类型、原 job/frame/element 和 asset 列表复制到 <code>JOBS_DIR/_subject_templates</code>,并由后端用 Vision LLM 从这些图反推 <code>prompt_brief</code>。以后相似生成通过 <code>subject_template_id</code> 读取这个 brief 作为文字创意方向,不再把模板图直接上传给 image-edit。</td></tr>
<tr><td>全局提示词库</td><td><code>GET /prompt-library</code><br><code>GET /prompt-library/{id}</code><br><code>POST /prompt-library</code><br><code>PATCH /prompt-library/{id}</code><br><code>DELETE /prompt-library/{id}</code><br><code>POST /prompt-library/{id}/use</code></td><td><code>listPromptLibrary</code><br><code>createPromptLibraryItem</code><br><code>usePromptLibraryItem</code><br><code>deletePromptLibraryItem</code></td><td>浮窗提示词 Tab 的 5 类文本资源。<code>prompt_en</code> 是实际复制/提交给模型的英文主值,<code>prompt_zh</code> 只给团队阅读;点击复制会调用 <code>/use</code> 增加使用次数。节点位置由前端按“常用 + 月份倒序”硬编码排列,不接受拖拽或自定义排序。</td></tr>
<tr><td>全局素材库</td><td><code>GET /asset-library/{kind}</code><br><code>GET /asset-library/{kind}/{id}</code><br><code>POST /asset-library/{kind}</code><br><code>PATCH /asset-library/{kind}/{id}</code><br><code>GET /asset-library/{kind}/{id}/refs</code><br><code>DELETE /asset-library/{kind}/{id}?force=true</code><br><code>POST /asset-library/{kind}/{id}/copy-to-job/{job_id}</code><br><code>GET /asset-library/{kind}/{id}/file/{filename}</code></td><td><code>listAssetLibrary</code><br><code>createAssetLibraryItem</code><br><code>getAssetLibraryRefs</code><br><code>deleteAssetLibraryItem</code><br><code>copyAssetLibraryToJob</code></td><td>素材库四类 <code>subjects/products/scenes/videos</code>。应用到当前 job 时后端复制文件到 <code>jobs/&lt;jobId&gt;/assets</code><code>storyboard-videos</code> 并增加 <code>use_count</code>,前端只写普通 job 内引用。删除前必须先查 <code>/refs</code>;被 job 引用时不带 <code>force=true</code> 返回 409强删会把库目录移动到 <code>_trash/asset_library</code></td></tr>
<tr><td>最近资源</td><td><code>GET /resource-library/recent?hours=24</code></td><td><code>getResourceLibraryRecent</code></td><td>资源中心顶部最近 24 小时横条,混合返回提示词和素材节点。它只做快速定位和新资源提醒,不改变提示词/素材两套库的独立数据边界。</td></tr>
<tr><td>角色图入库到 job</td><td><code>POST /jobs/{id}/assets/character-library</code></td><td><code>copyCharacterLibraryAssets</code></td><td>把所选角色的 7 张参考图复制为当前 job asset返回 <code>subject_images</code>,产品融合生成视频时作为人物身份参考图提交。</td></tr>
<tr><td>产品融合引导图</td><td><code>POST /jobs/{id}/product-fusion/guide</code></td><td><code>createProductFusionGuide</code></td><td>旧流程兼容接口:读取产品图和白底人物图,按 <code>product_region</code> 合成位置引导图。当前内置角色 + 产品 + 描述流程不再主动调用它。</td></tr>
<tr><td>产品融合描述词</td><td><code>POST /jobs/{id}/product-fusion/descriptions</code></td><td><code>generateProductFusionDescriptions</code></td><td>兼容接口:可生成产品融合动作描述库。当前前端默认直接用本地 36 条镜头语言模板预填 6 行镜头,并通过“换一组”按钮按 6 条一组轮换。</td></tr>
<tr><td>分镜保存</td><td><code>PUT /frames/{idx}/storyboard</code></td><td><code>updateStoryboard</code></td><td>保存三字段中英镜像、选中视频 ID、4 图槽、时长、改造说明,以及高级抽屉里的镜头类型、人物描述、人物/产品开关、首帧规划、尾帧规划和产品出现方式。当前音频分镜行会额外写 <code>storyboard_row_idx</code>,避免多条分镜共用同一参考帧时互相覆盖。</td></tr>
<tr><td>三字段自动展开</td><td><code>POST /jobs/{job_id}/frames/{idx}/storyboard/quick-plan</code></td><td><code>quickPlanStoryboard</code></td><td>输入 <code>skg_copy_*</code><code>scene_one_line_*</code><code>action_one_line_*</code><code>subject_brief</code>,用 <code>REWRITE_MODEL</code> 展开为完整 <code>StoryboardScene</code>,只作为视频 prompt 来源,不直接持久化。</td></tr>
<tr><td>AI 改文案</td><td><code>POST /jobs/{job_id}/frames/{idx}/storyboard/refine</code></td><td><code>refineStoryboard</code></td><td>输入当前三字段和中文反馈,返回新的三字段中英镜像。前端必须先弹改前/改后预览,用户点应用后才写入行状态。</td></tr>
<tr><td>单条视频候选生成</td><td><code>POST /jobs/{job_id}/frames/{idx}/storyboard/video</code></td><td><code>generateStoryboardVideo</code></td><td>新增 <code>count</code><code>seed</code><code>storyboard_row_idx</code>,默认一次创建 4 个 <code>GeneratedVideo</code> 任务并立即返回 job每个候选独立排队、生成、失败或成功。前端提交 prompt 前用 quick-plan 展开,高级首尾帧存在时继续带上,不存在时后端用参考帧/主体图/产品图透明兜底。最终提交给视频模型前,后端会为参考图追加 AI 虚拟角色条件提示:参考图若包含人物、脸、身体、手、头像或角色,就按虚构 AI 角色处理,不按真人或公众人物处理。视频候选显示必须优先按 <code>storyboard_row_idx</code> 归属到音频分镜行,而不是只按 <code>frame_idx</code></td></tr>
<tr><td>整片一键生成候选</td><td><code>POST /jobs/{job_id}/storyboard/batch-generate-all</code></td><td>当前主路径改为逐行调用 <code>generateStoryboardVideo</code></td><td>用户选择“每行 N 条”后,前端按音频分镜逐行提交,确保每个候选都带 <code>storyboard_row_idx</code>。后端批量接口保留为兼容能力,默认 <code>concurrency=1</code>,但当前 UI 不再用它做主路径。</td></tr>
<tr><td>生图</td><td><code>POST /frames/{idx}/generate</code></td><td><code>generateImage</code></td><td>基于关键帧或已选生成图做 image-to-image目前可用。</td></tr>
</tbody>
</table>
</section>
<section id="node-contract" data-search>
<h2>节点职责边界</h2>
<table>
<thead>
<tr><th>节点</th><th>当前职责</th><th>不该承担</th><th>改动主要文件</th></tr>
</thead>
<tbody>
<tr>
<td><span class="tag blue">复刻工作表</span></td>
<td>承载当前第一步主路径:素材输入列按文件任务管理素材;点击“开始”后自动下载源视频,下载完成后触发音频提取、原文案转写、中文翻译、讲话人/节奏/背景音分析和抽帧参考;讲话人/节奏/背景音作为后台数据保留,不默认占用主界面。</td>
<td>不要在当前开始流程里自动抽帧、自动写分镜、自动生成元素或自动合成视频;不要恢复右侧空白画布占位。</td>
<td><code>web/components/ad-recreation-board.tsx</code><code>web/app/page.tsx</code></td>
</tr>
<tr>
<td><span class="tag violet">旧节点 / 深度素材面板</span></td>
<td><code>InputNode</code><code>VisualLabNode</code><code>AudioNode</code><code>ComposeNode</code><code>FrameLightbox</code> 等底层能力暂保留,避免本次大改同时破坏抽帧、音频、素材处理和历史 job 数据。</td>
<td>不要作为当前用户主路径展示;后续迁移时按看板工作流重新整理。</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>
<td><span class="tag blue">内容生产画布</span></td>
<td>承载个人自由排列的创作空间用户在画布上通过提示词、推荐词、AI 润色、自动执行、工作流模板或手动节点连接生成文本、图片和视频节点,结果按节点位置沉淀。画布项目服务端保存到 Postgres浏览器本地只缓存生成资产进入后端个人 job。</td>
<td>当前不做实时多人协同编辑或管理员总览;默认项目仍是个人私有,可见性字段先预留 team/company。API 设置不能接回上游外部注册链接,生成调用必须继续走本项目后端 <code>/api</code> 和登录会话。</td>
<td><code>web/canvas-app/</code><code>web/canvas-app/src/stores/projects.js</code><code>api/main.py</code><code>api/db.py</code></td>
</tr>
<tr>
<td><span class="tag gray">音频条</span></td>
<td>复刻工作表顶部触发音频解析;全文文案依据和音频解析结果摘要不再默认渲染;主展示以源视频工作区为准:竖版原视频在左,逐句时间轴在原视频下方,音频波形和参考帧池在右;底部 <code>AudioStrip</code> 当前不渲染。</td>
<td>当前第一步不要默认展示底部音频条、新配音播放器、独立原文案提取大卡片,或把未生成的 Azure OpenAI 配音当作已完成结果。</td>
<td><code>web/components/audio-strip.tsx</code><code>pipeline_transcribe</code><code>AudioScript</code></td>
</tr>
<tr>
<td><span class="tag green">候选片段</span></td>
<td>当前分镜主路径的视频候选结果:单条可选择生成数量,候选在每行右侧横向持续追加;点击候选卡只打开视频预览,卡片右上角提供下载按钮,另支持重生/删除/清空候选;整片一键按每行数量排队提交。</td>
<td>不要要求客户先手动生成首帧/尾帧;不要把 prompt 全文塞进默认候选区,除非用户展开高级。</td>
<td><code>/storyboard/video</code><code>generated_videos</code><code>AdRecreationBoard</code></td>
</tr>
</tbody>
</table>
</section>
<section id="current-state" data-search>
<h2>当前已通与阻塞</h2>
<div class="grid-2">
<div class="card">
<h3>已通</h3>
<ul>
<li>TK 链接 / 上传创建 job。</li>
<li>视频下载或本地保存;后端会检测可用 ffmpeg/ffprobePATH 版本不可用时可 fallback 到本机静态 ffmpeg避免 Homebrew 动态库损坏导致素材输入失败。</li>
<li>手动按时间戳加关键帧。</li>
<li>关键帧清洗水印,全图或区域清洗。</li>
<li>Vision 识别关键帧,输出 scene、objects、style、suggested_prompt并作为主体候选来源。</li>
<li>“开始”会在下载完成后自动触发音频处理不再默认自动抽帧、Vision 扫描或保存分镜初稿。</li>
<li>主体候选确认、改名、删除和主体资产包生成能力保留在底层旧面板和接口中,当前第一步主界面不主动展示。</li>
<li>分镜工作台 4 图槽和改造说明自动保存。</li>
<li>音频文案轨:点击开始或提取音频后提取原文案、中文翻译、讲话人、语速节奏、背景音乐/环境声/音效;结果集中在右侧工作表展示。</li>
<li>GPT Image 生图;当前 <code>IMAGE_MODEL</code> 和主体 6 视图链路默认使用 <code>gpt-image-2</code>,单次图片网关请求默认 60 秒超时主模型超时、429、5xx 或网络错误时允许 <code>gemini-3-pro-image-preview</code> 兜底,并有 2 次失败 / 600 秒短时熔断。</li>
<li>三字段分镜候选生成:默认行左侧露文案、场景一句话、人物+产品+动作,右侧直接展示横向视频轨;中文镜像失焦后会自动优化英文主值;支持 AI 改写预览、单条选择数量生成、追加生成、选中候选和整片按行排队提交。</li>
<li>全局资源中心:提示词库和素材库可从顶部“资源库”打开;提示词可复制并计数,素材应用到 job 时会复制成本 job 内普通 asset。</li>
<li>画布:<code>https://marketing.skg.com</code> 登录后直接进入个人生成画布,支持文生图、文生视频、图生视频三种节点化生成;画布项目服务端保存到 Postgres生成资产继续写入当前登录用户自己的后端 job。</li>
</ul>
</div>
<div class="card">
<h3>阻塞 / 占位</h3>
<ul>
<li>ASR优先走当前 OpenAI-compatible 音频转写入口;如果该网关没有 <code>/audio/transcriptions</code>,自动 fallback 到 <code>ASR_FALLBACK_MODEL</code>(默认 <code>gemini-2.5-flash</code>)的多模态音频识别。</li>
<li>Voice当前语音通道固定是 <code>VOICE_PROVIDER=azure_openai</code>,通过 <code>AZURE_OPENAI_BASE_URL=https://ai.skg.com/azure</code> 的 OpenAI 协议生成 TTS后端按 <code>AZURE_TTS_PATHS</code> 依次尝试路径。第一步暂不默认调用。</li>
<li>Audio Product Brief默认是通用 SKG 放松产品卖点;当前第一步只保留配置,后续分镜/新配音阶段再使用。</li>
<li>Video Gen当前视频通道固定优先 Seedance<code>VIDEO_API_BASE_URL=https://ai.skg.com/doubao</code> 走 content JSON 异步任务,提交后写入候选片段并轮询到完成。</li>
<li>Compose还没做本地 ffmpeg 字幕/TTS 合成。</li>
</ul>
</div>
</div>
<div class="callout warn" style="margin-top:14px">
<p>最重要的产品判断:当前先把“链接/上传 → 下载 → 音频原文案与声音背景音分析”跑顺;视觉抽帧、分镜和视频生成不要再反过来挤进第一步。</p>
</div>
</section>
<section id="request-language" data-search>
<h2>需求描述模板</h2>
<div class="todo">
<div class="todo-item">
<h3>改复刻工作表</h3>
<p>“我在素材输入列或右侧复刻工作表,开始后下载、转写、翻译、讲话人/节奏/背景音、分镜行和生成结果哪些状态要怎么展示。”</p>
</div>
<div class="todo-item">
<h3>改音频字段</h3>
<p>“每条音频解析结果需要哪些字段例如原文案、中文翻译、说话人、语速、停顿、BGM、环境声、音效、置信度。”</p>
</div>
<div class="todo-item">
<h3>进入下一步</h3>
<p>“音频解析完成后,什么时候才进入抽帧、分镜规划、产品融入、关键元素 6 视图或视频生成。”</p>
</div>
<div class="todo-item">
<h3>改数据/接口</h3>
<p>“这个动作需要持久化到 state.json字段加在 Job/KeyFrame/KeyElement/StoryboardScene 哪一层,刷新后要恢复。”</p>
</div>
<div class="todo-item">
<h3>改工作区语义</h3>
<p>“这个工作区的业务职责要改,不只是 UI 文案;请同步更新标题、说明、可点击行为、状态推导和本源码解析页。”</p>
</div>
<div class="todo-item">
<h3>改画布</h3>
<p>“我在根域名画布里,左侧或底部对话框、节点类型、生成结果排列、排队状态、下载或删除行为要怎么改。”</p>
</div>
</div>
</section>
<section id="change-log" data-search>
<h2>变更记录</h2>
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
<div class="changelog">
<article class="change">
<header>
<h3>2026-05-26 · AI 润色改为意图分类和冲突校验</h3>
<span class="tag amber">API</span>
<span class="tag violet">Canvas</span>
</header>
<div class="body">
<p><strong>问题:</strong>旧润色会把人物/无人物安全词作为模板尾巴直接拼到可见 prompt。用户二次润色时这些尾巴会污染意图判断出现 <code>A person...</code> 后面又接 <code>do not introduce people</code> 这类自相矛盾结果;同时系统背景也不能替用户主动加入 SKG。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>/prompt/polish</code> 改成“清理旧模板尾巴 → 分类人物/无人/物体/场景/动物/未知主体 → 按图片或视频结构改写 → 输出冲突校验/修复”。用户没写 <code>SKG</code> 时明确禁止主动加入 SKG用户没写人时不把未知主体润成 person也不追加“必须无人物”的兜底禁令用户明确有人时才把人物描述成虚构 AI 角色;用户明确无人时才保留无人物约束。</p>
<p><strong>影响:</strong>AI 润色继续保持中性专业但不会再把公司背景、SKG、产品、平台或人物安全尾巴硬塞给所有提示词。员工要 SKG 或具体产品时,需要自己写进输入;写了就保留并润色。视频生成的参考图链路会额外声明图中人物是 AI 生成的虚拟角色,以便员工继续使用 AI 人像素材做图生视频。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 我的工作流接入云端个人模板</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">Backend</span>
<span class="tag green">Data</span>
</header>
<div class="body">
<p><strong>问题:</strong>工作流面板只有公共模板,“我的工作流”为空;用户在当前画布里整理好的节点组合无法保存成自己的模板,也不能在另一台电脑用同一账号复用。</p>
<p><strong>改动:</strong>新增 Postgres 表 <code>canvas_workflows</code><code>GET/POST/PUT/DELETE /canvas-workflows</code> 接口,按当前登录用户保存个人工作流模板。前端新增 <code>web/canvas-app/src/stores/workflows.js</code><code>WorkflowPanel.vue</code> 的“我的工作流”页提供保存当前、刷新和删除;<code>Canvas.vue</code> 保存当前节点/连线,插入个人模板时重新生成节点 ID、按当前视口重排并按 ID 映射重连边。</p>
<p><strong>影响:</strong>同一飞书账号换电脑后应能看到自己的工作流模板;保存时会清掉生成图片、视频、进度、错误和 LLM 输出等运行态,只保留可复用结构和配置。当前仍是个人私有模板,不是公共模板库或公司共享模板库。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 推荐词扩展为 30 组共享短词池</h3>
<span class="tag violet">Canvas</span>
<span class="tag rose">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>推荐词刷新已经可用,但词池只有少数组,用户连续点击时会很快回到同一批,感觉“刷来刷去就这两组”。</p>
<p><strong>改动:</strong>新增 <code>web/canvas-app/src/config/suggestions.js</code>,把首页 <code>Home.vue</code> 和画布 <code>Canvas.vue</code> 的推荐词统一改为 <code>QUICK_SUGGESTION_GROUPS</code>,当前 30 组 / 120 个短词,每次仍显示 4 个并按组轮换。</p>
<p><strong>影响:</strong>用户可以连续刷新更多主题、场景、产品、镜头和风格灵感;推荐栏仍保持单行短词展示,不会顶起输入框。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 推荐词刷新改为短词轮换</h3>
<span class="tag violet">Canvas</span>
<span class="tag rose">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>画布和首页推荐词是固定数组,旁边刷新按钮没有绑定事件;推荐词如果过长或换行,会把底部输入区撑高。</p>
<p><strong>改动:</strong><code>web/canvas-app/src/views/Canvas.vue</code><code>Home.vue</code> 改为 4 个一组的短推荐词池点击刷新按钮时切换到下一组推荐栏固定单行高度chip 设置最大宽度和截断,按钮加 <code>title</code> / <code>aria-label</code></p>
<p><strong>影响:</strong>用户可连续点刷新切换推荐词,推荐区不会因为文案长度换行或顶起 composer。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · AI 润色按人物意图加安全词</h3>
<span class="tag amber">API</span>
<span class="tag violet">Canvas</span>
</header>
<div class="body">
<p><strong>问题:</strong>为了降低 Seedance 人脸/肖像风控,不能把“虚构 AI 角色、非真人”无条件加到所有润色结果里;无人画面如果出现 <code>character</code><code>avatar</code> 等词,反而可能诱导模型新增人物。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增人物意图检测原始输入含人像、模特、角色、数字人、脸、portrait、model、character 等语义时润色才补“fully fictional synthetic AI character / virtual avatar / not based on any real person”原始输入没有人物语义或明确写无人时润色改为补“保持 object-only / scene-only / product-only composition不新增 people、faces、bodies、hands、avatars、characters、crowds”。</p>
<p><strong>影响:</strong>无人物产品图、场景图、食物图、街景图不会因为合规词被润成有人画面;有人物需求仍会尽量声明为虚构 AI 角色,减少被上游误判为真实肖像的概率。参考图里已有清晰人脸时,最终仍由上游图片安全检测决定,失败后继续显示可读的人脸风控提示。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · AI 润色从 SKG 广告文案拆出</h3>
<span class="tag amber">API</span>
<span class="tag violet">Canvas</span>
</header>
<div class="body">
<p><strong>问题:</strong>画布 <code>AI 润色</code> 之前复用 <code>/creative/copy</code>,该接口本来是 SKG 营销短视频文案生成器,会默认加入 SKG、健康科技、按摩产品、TikTok/Reels 广告语境,导致普通图片或视频提示词也被带偏。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增中性 <code>POST /prompt/polish</code><code>web/canvas-app/src/hooks/useApi.js</code> 改为调用该接口并传入 <code>system_prompt</code><code>mode</code><code>target_language</code>。根画布和文本节点显式用 <code>mode=image</code> 输出英文生成提示词LLM 节点和自动执行意图分析用 <code>mode=chat</code> 保持输入语言。</p>
<p><strong>影响:</strong>AI 润色只优化用户原本写的主体、风格、镜头、光线、构图和细节,不再主动添加用户没提到的品牌、产品或营销话术;<code>/creative/copy</code> 继续保留给明确的 SKG 营销文案生成场景。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 视频生成失败改为员工可读提示</h3>
<span class="tag amber">API</span>
<span class="tag rose">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>Seedance / Doubao 视频上游返回 <code>InputImageSensitiveContentDetected.PrivacyInformation</code>、HTTP 400、429、timeout 等机器错误时,画布错误框原样展示会让员工误以为账号、模型或网关坏了,需要人工解释。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增视频错误归一化逻辑,提交失败、轮询失败和后台任务异常都会先转换成可读中文,再写入 <code>GeneratedVideo.error</code>。例如参考图被上游判成疑似真实人脸时,会提示系统已按 AI 虚拟角色提交但上游仍可能误判,并建议换低识别度首帧、裁掉或弱化脸部后重试。</p>
<p><strong>影响:</strong>前端现有视频失败卡、画布轮询错误框和详情里的 <code>video.error</code> 会自动显示中文解释;原始上游错误只写入 API 日志,方便管理员排查,不再要求用户把英文错误码发给开发者翻译。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 生产登录改为仅飞书</h3>
<span class="tag amber">Auth</span>
<span class="tag blue">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>飞书接入后继续保留共享密码入口,后续成员容易误用旧密码账号,导致新内容继续落到 <code>password:skg</code>,和飞书个人身份沉淀混在一起。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>PASSWORD_AUTH_ENABLED</code> 总开关;生产设为 <code>false</code> 后,<code>/auth/config</code> 返回 <code>password_enabled=false</code>,登录页不再展示账号密码表单,<code>/auth/login</code> 不再可用,旧密码 Cookie 会在校验时失效。<code>WEB_AUTH_SESSION_SECRET</code> 仍保留用于飞书会话签名,旧备用账号配置只作为应急恢复材料,不作为当前入口。</p>
<p><strong>验证:</strong>生产 <code>/api/auth/config</code> 返回 <code>password_enabled=false</code><code>feishu_enabled=true</code><code>GET /api/auth/feishu/start?next=/</code> 返回 302 到飞书授权页;<code>POST /api/auth/login</code> 返回 503 <code>账号密码登录未配置</code></p>
<p><strong>影响:</strong>新成员只能通过飞书登录,后续新建任务和画布都会绑定真实飞书 owner。旧 <code>password:skg</code> 数据不会自动归属到任何飞书用户,后续应通过公司可见/认领/管理员迁移处理。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 旧密码账号内容迁到飞书账号</h3>
<span class="tag green">Data</span>
<span class="tag amber">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>密码登录停用后,旧共享账号 <code>password:skg</code> 下的视频和画布不会自动出现在飞书账号里,用户会误以为之前生成的视频丢失。</p>
<p><strong>改动:</strong>在生产服务器先备份 Postgres 和 <code>data/jobs</code>,再把 <code>password:skg</code> 的 22 个 job、3 个画布项目和对应生成资产索引迁到飞书用户 <code>万康</code><code>feishu:ou_78276b4fd9dd818d8f70bc00d03ddbdf</code>。job 的 <code>state.json</code> 也同步改写 owner 字段,并重启 API 让内存态重新加载。</p>
<p><strong>影响:</strong>万康飞书登录后应能看到旧密码账号下的历史视频和画布;无 owner 的 4 个更早旧 job 保持不动,避免把来源不明确的内容误归属。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 接入 Postgres 做公司内部沉淀</h3>
<span class="tag blue">Backend</span>
<span class="tag amber">Deploy</span>
<span class="tag green">Data</span>
</header>
<div class="body">
<p><strong>问题:</strong>画布项目只存在浏览器 <code>localStorage</code> 时,换电脑、清缓存或多人长期使用都难以形成公司内部资产沉淀;任务和资源虽然已有文件持久化,但缺少结构化索引和审计。</p>
<p><strong>改动:</strong>新增 <code>api/db.py</code> 和 Postgres schema<code>app_users</code><code>canvas_projects</code><code>job_index</code><code>generated_assets</code><code>prompt_library_index</code><code>asset_library_index</code><code>agent_run_index</code><code>audit_events</code><code>api/main.py</code> 启动时建表并索引已有 job / AgentRun / 提示词 / 素材;新增 <code>/canvas-projects</code> CRUD 和 import 接口;登录、创建任务、资源库操作、画布保存都会写用户索引或审计。<code>web/canvas-app/src/stores/projects.js</code> 改为服务端项目优先,<code>localStorage</code> 只做缓存和旧项目导入。</p>
<p><strong>部署:</strong><code>docker-compose.prod.yml</code><code>docker-compose.standalone.yml</code> 增加 <code>postgres:16-alpine</code>;生产环境在 <code>deploy/.env.production</code> 写入 <code>DATABASE_URL</code> 和 Postgres 密码,数据目录为服务器 <code>./data/postgres</code><code>deploy-prod-safe.sh</code> 会在容器存在时导出 <code>pg_dump</code><code>verify-prod-docker.sh</code> 要求 <code>database.connected=true</code></p>
<p><strong>影响:</strong>画布项目开始具备跨浏览器、跨设备恢复的服务端主存储;默认仍按 owner 私有隔离,后续可在同一表上扩展 team/company 可见性。完整 job state 和媒体文件仍保留在原有文件目录,避免把大文件一次性搬进数据库。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-26 · 恢复最初生图配置</h3>
<span class="tag amber">API</span>
<span class="tag rose">UI</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>连续加入真实像素、低/中/高画质、Gemini 官方 1K/2K/4K 尺寸和显式模型选择后,生图配置变复杂,员工容易选到不稳定或不符合预期的组合。</p>
<p><strong>改动:</strong>按用户要求恢复到最初简单版:<code>api/main.py</code> 只返回四个图片尺寸 <code>auto</code><code>1024x1536</code><code>1024x1024</code><code>1536x1024</code><code>GenerateReq</code> 不再接收画质字段;<code>web/canvas-app/src/config/models.js</code> 恢复 <code>auto</code><code>gpt-image-2</code><code>gemini-3-pro-image-preview</code> 三个图片模型和单一“标准”画质。</p>
<p><strong>影响:</strong><code>auto</code> 图片模型重新启用后端既有策略:优先 GPT Image 2遇到超时、限流或上游异常时可按熔断/兜底策略走 Gemini不再显示自定义尺寸输入也不再展示 Gemini 1K/2K/4K 长列表。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 媒体模型选择对齐真实后端能力</h3>
<span class="tag amber">API</span>
<span class="tag rose">UI</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>恢复上游画布后,前端仍可能显示 Nano Banana、Seedream、Kling、Veo 等上游或旧缓存模型,但这些并不是当前 SKG 后端已经验证可用的生成通道,用户一旦选择就会失败或误解模型能力。</p>
<p><strong>改动:</strong><code>web/canvas-app/src/config/models.js</code> 将图片模型收口为 <code>auto</code><code>gpt-image-2</code><code>gemini-3-pro-image-preview</code>,视频模型收口为 <code>seedance</code> / <code>Seedance 2.0 Fast</code>;图片尺寸、视频画幅和视频时长对齐后端真实能力。<code>useModelConfig.js</code> 和 Pinia 模型 store 会忽略浏览器本地自定义图片/视频模型,并把旧 localStorage 选中值回退到默认可用模型。</p>
<p><strong>后端:</strong><code>api/main.py</code><code>video_model_options()</code> 按真实模型去重:当前 <code>kling</code><code>veo</code> 等别名如果都指向 <code>doubao-seedance-2-0-fast-260128</code><code>/health</code> 只返回一个 Seedance 选项,不再假装有多个不同视频模型。</p>
<p><strong>影响:</strong>员工在画布里只能选到当前可直接生成的图片/视频模型;后续新增 Kling、Veo 或其他图片模型时,必须先在后端完成真实模型配置和探针验证,再更新前端白名单。</p>
<p><strong>部署:</strong><code>84d9de6</code> 已部署到生产;外部根域名和 <code>/p/test</code> 登录保护复验通过,容器内 <code>/health</code> 确认图片模型为 <code>auto,gpt-image-2,gemini-3-pro-image-preview</code>,视频模型只返回 <code>Seedance 2.0 Fast</code>,生产静态 bundle 未命中旧上游模型名。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 恢复上游画布能力API 保持 SKG 接入</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>三模式精简版把成熟画布削弱过多,用户明确要求 <code>api 没关系</code>,其他上游画布能力直接恢复,不再把早先的 SKG 简化想法强行融入当前成熟版面。</p>
<p><strong>改动:</strong><code>chatfire-AI/huobao-canvas</code> 恢复 <code>ApiSettings.vue</code>、模型/渠道/工作流配置、<code>useWorkflowOrchestrator</code>、首页、画布和图片/视频/文本/LLM 节点。保留 SKG logo-only 品牌、根路径 <code>/</code>、内部路由 <code>/p/:id?</code><code>useApi.js</code> 的 SKG 后端适配API 设置弹窗去掉上游注册链接和外部品牌文案。</p>
<p><strong>影响:</strong>画布重新具备推荐词、AI 润色、自动执行、工作流模板、首帧/尾帧/参考图节点、多角度分镜、故事板、绘本和批量下载等成熟能力;生成请求仍使用当前登录会话调用本项目 <code>/api</code>,员工不需要个人 API Key。</p>
<p><strong>部署:</strong><code>cce9779</code> 已部署到生产;外部根域名、旧 <code>/canvas/</code> 跳转和 <code>/p/test</code> 登录保护均复验通过,容器静态 bundle 已命中恢复能力并未命中上游注册链接或火宝欢迎文案。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 根域名直接进入个人生成画布</h3>
<span class="tag rose">UI</span>
<span class="tag amber">Deploy</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户已经确定用成熟画布版快速上架,不希望员工先进入单对话框首页再点 <code>/canvas/</code>,也不希望我们继续大幅重写成熟画布结构。</p>
<p><strong>改动:</strong><code>web/canvas-app</code> 的 Vite base 和 Vue Router base 改为根路径;<code>web/package.json</code> 生产构建改为先构建画布、再 Next 静态导出、最后用 <code>web/scripts/sync-canvas-root.mjs</code> 把画布产物覆盖到 <code>web/out</code> 根目录。<code>deploy/nginx.conf</code><code>/canvas/</code> 改成 308 兼容跳转到根域名。</p>
<p><strong>影响:</strong><code>https://marketing.skg.com</code> 登录后直接进入个人生成画布;旧 <code>/canvas/</code> 链接不会失效但不再是主入口。后续优先围绕模式文案、API 接入、队列和结果显示做小改,不反复拆成熟画布。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 生成入口收敛为三模式</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong><code>首帧生视频</code><code>首尾帧生视频</code> 是模型提交细节,不应该作为用户主入口;和成熟生成产品里的“文生图、文生视频、图生视频”概念相比显得混乱。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code><code>web/canvas-app/src/views/Canvas.vue</code> 的模式按钮统一收敛为文生图、文生视频、图生视频。图生视频只显示“上传图片”,内部仍用 <code>first_image</code> 提交视频;画布视频节点状态也从首帧/尾帧细节收敛为图片状态。</p>
<p><strong>影响:</strong>终端用户不再需要理解首帧、尾帧等实现概念;生成能力保留文生图、文生视频和单图参考的视频生成。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 移除画布底部推荐提示词</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
</header>
<div class="body">
<p><strong>问题:</strong>画布底部输入框下方常驻“推荐”提示词 chips占据底部视线和操作空间用户已经在写提示词时会被这些内容阻挡。</p>
<p><strong>改动:</strong><code>web/canvas-app/src/views/Canvas.vue</code> 删除底部推荐提示词区域;<code>web/canvas-app/src/views/Home.vue</code> 同步删除入口页输入框下方推荐 chips只保留输入框、必要模式/上传控件和发送按钮。</p>
<p><strong>影响:</strong>生成能力不变,仍由用户手写提示词;画布底部更干净,避免推荐项遮挡主要操作。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 可见品牌收敛为 SKG logo</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong><code>生图生视频</code><code>SKG 生成画布</code> 和过长中文系统名放在主界面上都显得别扭,削弱“打开就会用”的简单感。</p>
<p><strong>改动:</strong>终端可见品牌位改为 logo-only主页顶部、登录框左上角、画布首页都只展示 SKG logo网页 title 和画布 title 缩为 <code>SKG</code>;首页进入画布的按钮文案缩短为“画布”。</p>
<p><strong>影响:</strong>产品语义仍是公司内部营销内容生产平台,功能路径不变;只是把品牌表达从文字命名改成 SKG 字标,减少页面认知负担。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 可见命名改为营销内容生产平台</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong><code>SKG 生图生视频</code> 过窄,只覆盖图片和视频生成,不利于承接详情页图文方案、提示词沉淀和后续内容资产整理。</p>
<p><strong>改动:</strong>当时将可见系统名统一改为 <code>SKG 营销内容生产平台</code>;画布入口和画布首页改为 <code>内容生产画布</code>。同步更新网页标题、登录页品牌、首页入口按钮、画布首页标题、项目元数据、规则和源码解析当前章节。</p>
<p><strong>影响:</strong>功能路径不变,仍是首页单对话框生成、<code>/canvas/</code> 进入个人画布、<code>/detail/?job=</code> 查看任务详情。随后已被上方 logo-only 方案覆盖。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 可见命名改成生图生视频和生成画布</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>“营销内容工作台”像后台管理系统“SKG 无限画布”也看不出具体用途,内部同事第一次打开无法立即判断这里是生图、生视频和整理生成结果的地方。</p>
<p><strong>改动:</strong>当前可见系统名统一改为 <code>SKG 生图生视频</code>;画布入口和画布首页统一改为 <code>生成画布</code>。同步更新网页标题、登录页品牌、首页入口按钮、画布首页标题、项目元数据、规则和源码解析当前章节。</p>
<p><strong>影响:</strong>功能路径不变,仍是首页单对话框生成、<code>/canvas/</code> 进入个人生成画布、<code>/detail/?job=</code> 查看任务详情。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 接入 SKG 内部无限画布</h3>
<span class="tag rose">UI</span>
<span class="tag amber">API</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>默认首页适合“一次生成一个结果”,但内部多人使用时,用户还需要把多次生成的图片、视频、提示词和参考图放在一个自由空间里整理,避免结果都挤在同一条对话或详情页里。</p>
<p><strong>改动:</strong>新增 <code>web/canvas-app/</code>,将 huobao-canvas 的 Vue Flow 画布交互改造为 SKG 内部版可见品牌、GitHub 入口、API Key 设置和外部服务商配置都已移除,保留项目列表、生成画布、节点连接、四模式 prompt composer 和生成结果节点。构建链路新增 <code>pnpm build:canvas</code><code>web/scripts/sync-canvas-dist.mjs</code>,生产 Nginx 新增受登录保护的 <code>/canvas/</code> fallback 路由;首页顶部增加“生成画布”入口。</p>
<p><strong>影响:</strong>当时画布项目先保存在浏览器 <code>localStorage</code>不是团队共享也不做跨设备同步2026-05-26 已升级为服务端 Postgres 持久化。生成图片和视频仍调用本项目 <code>/api</code>,按当前登录用户写入个人 job。第三方来源说明只保存在 <code>THIRD_PARTY_NOTICES.md</code>,不进入终端用户 UI。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 视频生成进入个人公平队列</h3>
<span class="tag blue">Backend</span>
<span class="tag rose">UI</span>
<span class="tag amber">Queue</span>
</header>
<div class="body">
<p><strong>问题:</strong>多人使用时,如果视频提交后直接开后台线程,单个用户连续提交多个视频可能占满生成通道,其他人也看不清自己的任务是在排队还是已经生成。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增进程内视频队列:全局默认同时 2 个视频生成、单用户同时 1 个视频生成;<code>GeneratedVideo</code> 增加 <code>queue_position</code><code>queue_size</code><code>queue_message</code>,排队、启动、完成、失败和删除都会回写状态。<code>web/app/page.tsx</code> 把首页视频结果卡的英文状态改成“排队中 / 前方 N 个任务 / 你的上一个视频生成中 / 生成中 N% / 可播放”。</p>
<p><strong>影响:</strong>当前只做个人队列反馈,不做团队共享或管理员总览。队列是单 API 容器内存队列,不依赖 Redis容器重启后未完成视频会标记为失败并提示重新生成。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 首页完成视频结果卡直接可播放</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Media</span>
</header>
<div class="body">
<p><strong>问题:</strong>生产最近视频任务已经完成并写入 mp4但首页结果卡里的视频缩略图没有原生播放控件用户点击完成视频时看起来没有任何反馈只能依赖 hover 自动预览。</p>
<p><strong>改动:</strong><code>MediaAssetTile</code> 新增 <code>videoControls</code> 参数;<code>web/app/page.tsx</code> 在首页最新视频状态为 <code>completed</code> 时启用 controls并在失败状态显示失败空态和错误详情。</p>
<p><strong>影响:</strong>首页生成视频完成后可直接在结果卡里播放;详情页和旧分镜候选仍按原有缩略图 / hover 预览 / 下载按钮语义工作。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 生产启用飞书 OAuth 登录</h3>
<span class="tag amber">Auth</span>
<span class="tag blue">Ops</span>
</header>
<div class="body">
<p><strong>改动:</strong>生产服务器 <code>/opt/skg-marketing-studio/deploy/.env.production</code> 已补齐飞书 OAuth 应用配置,保留 <code>WEB_AUTH_SESSION_SECRET</code><code>AUTH_DATA_ISOLATION_ENABLED=true</code>,并仅重建 <code>skg-marketing-api</code> 使环境变量生效;敏感 App Secret 不进入仓库。</p>
<p><strong>验证:</strong><code>/api/auth/config</code> 返回 <code>feishu_enabled=true</code><code>password_enabled=true</code><code>data_isolation_enabled=true</code><code>GET /api/auth/feishu/start?next=/</code> 返回 302 跳转到飞书授权页;容器内 <code>/health</code> 返回 <code>auth_modes.feishu=True</code></p>
<p><strong>影响:</strong>飞书登录链路正式可用,飞书用户新建任务时 <code>owner_id</code> 会按飞书身份写入,后端列表、详情和素材接口继续按 owner 校验;备用账号密码登录仍保留。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 飞书客户端内登录页自动发起免登录</h3>
<span class="tag amber">Auth</span>
<span class="tag rose">UI</span>
<span class="tag blue">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>当前后端已经有飞书 OAuth 的 start / callback / user info / 会话 Cookie 链路,但登录页仍要求用户点一次“飞书免登录”。用户希望在飞书里打开应用时自动免登录。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 读取 <code>/api/auth/config</code> 后,如果 <code>feishu_enabled=true</code> 且 UA 命中飞书 / Lark 客户端,会用 <code>sessionStorage</code> 防循环后自动跳转 <code>/api/auth/feishu/start?next=...</code>;普通浏览器继续显示飞书按钮和备用账号。<code>deploy/nginx.conf</code> 的未登录跳转改为 <code>/login/?next=$request_uri</code>,飞书或备用账号登录成功后回到原页面。</p>
<p><strong>影响:</strong>该能力仍依赖生产服务器配置 <code>FEISHU_APP_ID</code><code>FEISHU_APP_SECRET</code><code>FEISHU_REDIRECT_URI</code><code>WEB_AUTH_SESSION_SECRET</code>,并要求飞书开放平台登记回调 <code>https://marketing.skg.com/api/auth/feishu/callback</code>。未配置时 <code>feishu_enabled=false</code>,页面不会自动跳转。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 修复空白创作任务请求体解析失败</h3>
<span class="tag amber">API</span>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>文生图或文生视频没有上传首帧时,前端仍用空 <code>FormData</code><code>createCreativeImageJob</code>;部分浏览器 / 代理链路会把它变成缺 boundary 的 <code>multipart/form-data</code>FastAPI 在进入 <code>/creative/jobs/image</code> 业务函数前直接返回 <code>400 There was an error parsing the body</code></p>
<p><strong>改动:</strong><code>web/lib/api.ts</code> 在无文件时改发 JSON 空对象;有文件时才发 multipart。<code>api/main.py</code><code>/creative/jobs/image</code> 不再通过 <code>File(...)</code> 强制预解析请求体,改为按 <code>Content-Type</code> 手动兼容 JSON / 无 body / 正常 multipart并对空 multipart 容错为创建空白底图任务。</p>
<p><strong>影响:</strong>首页四模式里文生图、文生视频这类无首帧路径都能稳定先创建轻量 job首帧 / 首尾帧模式仍正常上传图片,坏图片继续返回 400。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 首页按模型能力暴露尺寸和时长</h3>
<span class="tag amber">API</span>
<span class="tag rose">UI</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>首页视频时长写死显示 20 秒和 30 秒,但后端当前 Ark / Doubao 视频提交会把时长截到最高 15 秒Poe / 自定义视频通道最高只发 12 秒;文生图也没有尺寸选择,前端无法把竖图、方图或横图需求传给图片模型。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>/health</code> 新增 <code>image_size_options</code><code>video_size_options</code><code>video_duration_options</code><code>video_max_duration_seconds</code><code>GenerateReq</code> 新增 <code>size</code>,文生图和图生图都会把尺寸传给 <code>/images/generations</code><code>/images/edits</code><code>web/app/page.tsx</code> 改为按后端能力渲染图片尺寸、视频画幅和视频时长,视频不再显示无法单条提交的 30 秒。</p>
<p><strong>影响:</strong>当前 Doubao / Seedance 生产配置下,首页视频时长只应显示 5、8、10、12、15 秒;如果业务要 30 秒视频,应新增“分段生成 15 + 15 秒并合成”的工作流,而不是把单次生成时长直接改成 30。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 默认首页收敛为单对话框四模式生成</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>即梦式版本仍有侧栏、模式附属按钮和结果浮层,用户希望再简单一点:只保留对话框,让使用者自己写提示词,再选择对应生成方式。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code> 改为单中央对话框,默认只露出文生视频、文生图、首帧生视频、首尾帧生视频四个按钮、提示词输入框、必要上传位、时长和生成按钮。首尾帧模式复用 <code>uploadReferenceFrame</code> 上传尾帧,并把 <code>last_image</code> 传给 <code>generateStoryboardVideo</code></p>
<p><strong>影响:</strong>默认首页不再展示营销图文、灵感、最近任务、侧栏或自动设置;默认使用方式是“选模式 → 上传必要帧 → 写提示词 → 生成”。详情页仍保留任务沉淀和继续生成能力。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 首页接入图片和视频模型选择</h3>
<span class="tag amber">API</span>
<span class="tag rose">UI</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>平台准备给团队直接使用时,用户需要自己选择模型;前端虽然可以传视频 <code>model</code>,但首页没有模型选择,生图接口也忽略了前端传入的 <code>model</code></p>
<p><strong>改动:</strong><code>api/main.py</code><code>/health</code> 新增 <code>image_options</code><code>video_options</code><code>/frames/{idx}/generate</code><code>model</code> 字段选择 <code>auto / gpt-image-2 / gemini-3-pro-image-preview</code><code>web/app/page.tsx</code> 读取模型选项并在单对话框底部显示模型下拉,生图和生视频提交时都带上用户选择。</p>
<p><strong>验证:</strong>本地后端真实探针已确认 <code>gpt-image-2</code> 文生图成功;同一 job 提交 <code>seedance</code> 5 秒视频后拿到上游 provider id并最终回填 mp4。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 默认首页复刻即梦 generate 极简布局</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>上一版多人创作首页仍把入口、参考图、任务和结果全部展开,首次进入时信息密度偏高,不像用户指定的即梦登录后生成页那样“一眼知道往输入框里写”。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code> 改为窄导航栏 + 会话侧栏 + 中央 prompt composer。文生图、图生图、文生视频、图生视频不再作为大入口卡平铺而是合并成视频 / 图片 / 图文模式;参考图上传放到输入框左侧倾斜卡片;产品、人群、平台、时长和语气折叠进“自动”;结果改为右下角浮层,完整沉淀继续进入 <code>/detail/?job=&lt;id&gt;</code></p>
<p><strong>影响:</strong>默认产品使用方式改为“直接写需求或上传参考图 → 选视频/图片/图文 → 发送 → 右下角看当前结果或进详情页”。源码解析页和 <code>RULES.md</code> 已同步新默认心智。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 生图生视频统一英文提示词和完整正投影视图</h3>
<span class="tag amber">API</span>
<span class="tag violet">UI</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>生视频链路已经会把中文要求转成英文再提交外部视频 API但首页直接生图仍可能把中文混合 prompt 直接传给图片 API物体六视图也只对俯视/仰视加了正投影约束,前后左右仍有“正面/背面/左侧/右侧”的口语化表述。</p>
<p><strong>改动:</strong><code>/jobs/{job_id}/frames/{idx}/generate</code> 在调用图片 API 前统一对完整 prompt 执行 <code>_ensure_english()</code>,和生视频 <code>_enqueue_storyboard_videos</code> 的英文提交规则保持一致。物体主体套图的默认六面视图改为 <code>正投影主视图 / 正投影后视图 / 正投影左视图 / 正投影右视图 / 正投影俯视图 / 正投影仰视图</code>,并对前后左右也加入 <code>orthographic elevation</code><code>no perspective</code><code>no tilt</code><code>no 3/4 angle</code><code>no isometric view</code> 约束。</p>
<p><strong>影响:</strong>用户仍可以用中文描述生图或生视频需求;后端实际提交给图片/视频模型的提示词会优先归一为英文。以后“专利六视图”应明确理解为六面正投影视图,不等同于摄影角度图;真人/短视频主体参考包仍保留口播、45 度、肩颈近景等非专利视角。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-25 · 主体套图俯视和仰视改为正投影口径</h3>
<span class="tag amber">API</span>
<span class="tag violet">UI</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>原先主体/产品套图里把 <code>top</code><code>bottom</code> 简写为“顶部/底部”或“俯视/仰视”,模型容易理解成斜上方、斜下方或 3/4 透视镜头,不符合外观专利六面正投影视图的要求。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>SUBJECT_VIEW_LABELS</code>、默认物体 6 视图和主体生图 prompt 已改为“正投影俯视图 / 正投影仰视图”,并在生成单图时加入 <code>orthographic top/bottom view</code><code>no perspective</code><code>no tilt</code><code>no 3/4 angle</code> 约束。<code>web/components/lightbox.tsx</code><code>web/components/ad-recreation-board.tsx</code><code>web/components/storyboard-editor.tsx</code> 同步更新显示文案和后续分镜提示词。</p>
<p><strong>影响:</strong>以后描述专利六面图需求时应说“正投影俯视图 / 正投影仰视图”,不要只说“俯视角度 / 仰视角度”;这些视图是垂直投影,不是带角度的摄影机视角。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-24 · 默认首页完整重设计为多人创作平台</h3>
<span class="tag rose">UI</span>
<span class="tag green">Product</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>前一版已经从复刻管线降到直接创作台,但仍是三模式表单,缺少团队成员各自工作、四类主入口和任务详情沉淀。用户确认这次要完全推倒旧复刻优先设计,平台先服务多人快速生图、生视频和图文创作。</p>
<p><strong>改动:</strong>重写 <code>web/app/page.tsx</code> 为多人创作首页:左侧四入口(文生图、图生图、文生视频、图生视频)、参考图和我的任务,中间创作台,右侧当前任务图片/视频/图文结果。新增 <code>web/app/detail/page.tsx</code>,静态路由 <code>/detail/?job=&lt;id&gt;</code> 按 job 展示参考图、全量生成图、视频候选、营销图文方案和历史提示词,并支持继续生成、删除和复制。进入重设计前创建本地数据备份 <code>.backups/pre-redesign-20260524-012047</code>、源码快照包、dirty patch并把 Git 标签 <code>backup/pre-redesign-20260524-012047</code> 推到 Gitea。</p>
<p><strong>影响:</strong>默认产品使用方式改为“选择入口 → 填写要求 → 生成 → 详情页沉淀”;旧 <code>AdRecreationBoard</code><code>/agent/</code> 只作为高级复刻能力保留。<code>.project.json</code><code>RULES.md</code> 已同步多人创作平台定位。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-24 · 项目更名并接入飞书免登录与多用户隔离</h3>
<span class="tag blue">Auth</span>
<span class="tag violet">API</span>
<span class="tag green">Product</span>
</header>
<div class="body">
<p><strong>问题:</strong>平台后续会给公司内多人使用,单一共享账号会让任务、素材和一键出片记录混在一起;项目定位也已经从 TK 二创验证转为长期使用的 SKG 营销内容工作台。</p>
<p><strong>改动:</strong>项目名统一改为 <code>SKG 营销内容工作台</code>。后端新增飞书 OAuth<code>/auth/config</code> 暴露登录方式,<code>/auth/feishu/start</code> 跳转飞书授权,<code>/auth/feishu/callback</code> 换取 <code>user_access_token</code> 并读取用户信息,再签发 HttpOnly 会话 Cookie账号密码登录保留为备用。<code>Job</code><code>AgentRun</code> 新增 <code>owner_id</code><code>owner_name</code><code>owner_email</code><code>owner_provider</code><code>tenant_key</code> 字段,新建任务绑定当前登录用户;中间件统一拦截 <code>/jobs/{id}</code>、复制到 job 和 <code>/agent-runs/{id}</code> 路径,非 owner 返回 404。列表接口只返回本人数据。</p>
<p><strong>影响:</strong>飞书用户之间数据互不可见;历史无 owner 的旧任务作为旧版本数据保留,仅备用账号可见。生产需要在飞书开放平台登记回调 <code>https://marketing.skg.com/api/auth/feishu/callback</code>,并把 <code>FEISHU_APP_ID</code><code>FEISHU_APP_SECRET</code><code>WEB_AUTH_SESSION_SECRET</code> 放入服务器 <code>deploy/.env.production</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-23 · 默认首页重置为直接创作台</h3>
<span class="tag rose">UI</span>
<span class="tag violet">API</span>
<span class="tag green">Product</span>
</header>
<div class="body">
<p><strong>问题:</strong>原首页围绕 TK 复刻、抽帧、转换层、主体 6 视图和分镜工作台展开,功能完整但对普通团队成员太重。真实需求更接近“能直接生图、生视频、自动写文案,或给一张图加一句描述快速出图/出片”。</p>
<p><strong>改动:</strong>进入新设计前先把当前数据复制到 <code>.backups/pre-redesign-20260523-235959</code>,并创建本地 tag <code>pre-redesign-20260523</code> 指向旧版代码 <code>0d5c326</code>。默认 <code>web/app/page.tsx</code> 改成 <code>SKG 营销内容工作台</code> 三模式首页:生视频、生图、写文案;图片和视频缩略图继续复用 <code>MediaAssetTile</code><code>api/main.py</code> 新增 <code>POST /creative/jobs/image</code> 创建参考图/空白图创作任务,新增 <code>POST /creative/copy</code> 生成短视频文案和可直接投喂模型的 image/video prompt<code>web/lib/api.ts</code> 同步新增类型和 client。</p>
<p><strong>影响:</strong>旧信息流复刻工作台和 Agent Cut 代码不删除,作为最后版本和后续可复用能力保留;默认产品使用方式已经变成“输入一句要求,必要时加一张图,直接生成图片/视频/文案”。后续需求应优先描述首页三模式创作体验,不要默认回到旧转换层或 09 步管线。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-22 · 音频解析改为中文和多语言自动识别</h3>
<span class="tag blue">ASR</span>
<span class="tag green">Audio</span>
</header>
<div class="body">
<p><strong>问题:</strong>音频解析默认把 <code>ASR_LANGUAGE</code> 固定为 <code>en</code>,本地容器兜底也使用 <code>faster-whisper tiny.en</code> 并强制 <code>language="en"</code>;中文或其他语言视频容易被压成英文识别路径,且质量校验按英文字符集计算重复率,会误伤中文字幕。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>ASR_LANGUAGE</code> 默认改为空值/auto远端和 <code>faster-whisper</code> 都只在显式配置语种时才传语言提示;本地 <code>faster-whisper</code> 默认模型改为多语言 <code>base</code>Gemini 音频兜底也要求保留原语种而不是翻译成英文。ASR 质量校验改为 Unicode 文本 key翻译 prompt 改为“原语言字幕 → 简体中文”,中文原文会作为中文镜像保留。</p>
<p><strong>影响:</strong>后续音频解析默认支持中文、英文和其他多语言原文识别,分镜时间轴的 <code>en</code> 字段实际承载“原语言文案”,<code>zh</code> 字段承载中文镜像。若某个素材明确固定语种,可通过 <code>ASR_LANGUAGE=zh</code><code>en</code> 等 ISO-639-1 代码强制识别。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 分镜视频候选点击改为预览下载</h3>
<span class="tag rose">UI</span>
<span class="tag blue">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>分镜工作台右侧视频候选卡把普通点击绑定为“选用该视频”,用户只是想打开查看候选片段时会被误判成选择操作。</p>
<p><strong>改动:</strong><code>StoryboardVideoSlots</code> / <code>StoryboardVideoPreview</code> 取消候选卡的默认选择点击,完成态视频卡点击只打开视频预览;候选提交后立即写入当前任务,完成后自动回填 mp4候选轨显示“自动保存”。卡片右上角常显下载按钮并保留重生、删除和清空候选。旧 <code>VideoCandidate</code> 也同步改为缩略图点击预览,并在操作区提供“下载”。<code>MediaAssetTile</code> 增加 <code>actionsAlwaysVisible</code>,用于这类必须直接可见的媒体操作。</p>
<p><strong>影响:</strong>分镜候选视频默认是“自动保存 / 看一下 / 下载”,不是“点一下就选中”或“生成后还要保存”。后续若要做整片合成的候选选用,应提供单独明确的“选用”控件,不再抢占视频缩略图点击。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 新增一分钟二创出片终端</h3>
<span class="tag violet">Agent</span>
<span class="tag green">Pipeline</span>
</header>
<div class="body">
<p><strong>背景:</strong>用户希望默认不再进入多段人工链路,而是只提交 TikTok 链接和产品图,后台像 Codex 接管一样解决下载、规划、生成和合成问题,最终直接看成片。</p>
<p><strong>改动:</strong>新增 <code>web/app/agent/page.tsx</code> 快速出片页和 <code>api/main.py</code><code>AgentRun</code> 状态机。后台固定 12 段约 1 分钟策略,执行下载、产品素材、主体参考、抽帧、镜头规划、视频生成、失败重跑、审片接触图和最终 mp4 合成;前端只显示实时终端和成片播放器。</p>
<p><strong>影响:</strong>旧信息流复刻工作台仍保留给高级编辑;新页是“只看成品”的默认候选入口。后续描述该路径时应引用“一键出片终端 / AgentRun / agent-runs 接口”,不要再要求用户逐步处理关键帧、分镜和候选视频。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 工作台顶栏品牌文案调整</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Copy</span>
</header>
<div class="body">
<p><strong>问题:</strong>玻璃工作台顶栏仍使用英文 <code>Marketing Production Console</code> 和“信息流广告复刻工作台”标题,和当前中文品牌表达不一致。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 顶栏三行文案改为 <code>未来健康 · 营销内容工作台</code><code>营销内容工作台</code><code>信息流广告复刻生产</code></p>
<p><strong>影响:</strong>只改变顶栏显示文案,不改变素材、音频解析、分镜、资源库、主题切换或任何接口数据。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 侧栏中段悬停与多色玻璃卡</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Figma Reference</span>
</header>
<div class="body">
<p><strong>问题:</strong>侧栏按整高工具条处理后不像原版参考图,资源库和明暗按钮被压到底部;顶部指标卡也偏全灰,缺少原版紫、黄绿、琥珀、青绿色光斑的玻璃层次。</p>
<p><strong>改动:</strong><code>WorkbenchRail</code> 改成贴近工作台全高的胶囊侧栏,鼠标移入或键盘聚焦时自动展开素材输入抽屉,点击素材任务可固定展开;资源库和明暗切换移到侧栏上方图标组。<code>Metric</code> 新增多色 variant顶部五个指标分别使用紫、黄绿、琥珀、青绿和绿色光斑。</p>
<p><strong>影响:</strong>侧栏不再作为短中段导航存在;后续应按“全高悬停抽屉 + 顶部真实动作图标”描述。配色增强只影响视觉层,不改变素材任务、资源库、主题切换或指标数据。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 明暗主题色阶重新扫描修正</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Theme</span>
</header>
<div class="body">
<p><strong>问题:</strong>玻璃工作台暗色模式整体灰雾偏重;明亮模式仍有黑色 stat 卡、黑底按钮和偏白音频波形残留,导致同一版面里明暗语义混在一起。</p>
<p><strong>改动:</strong><code>web/app/globals.css</code> 重新分离暗色和明亮模式的 shell、panel、glass、stat、action 与音频波形 token暗色压低背景和卡片底色明亮模式改为暖白面板、浅色指标卡和绿黄主按钮。<code>AudioWaveform</code> 改用 <code>skg-audio-waveform</code> 主题类,不再把黑底、白色包络和播放线写死在组件里。</p>
<p><strong>影响:</strong>这是纯视觉修正,不改变素材任务、字幕时间轴、波形点击跳转、胶片抽帧、模型调用或任何接口数据;后续调色应优先调整主题 token不要在组件里新增孤立的黑白 Tailwind 色值。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 逐句时间轴向下加高</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Timeline</span>
</header>
<div class="body">
<p><strong>问题:</strong>原版视频下方的竖向逐句时间轴只有 270px高度偏短一屏可见字幕段落太少。</p>
<p><strong>改动:</strong><code>SOURCE_TRANSCRIPT_MAX_HEIGHT</code> 从 270 调整为 360让时间轴向下多展示几行列表仍保留内部滚动英文和中文仍最多两行。</p>
<p><strong>影响:</strong>只影响源视频工作区左侧字幕审片面板的可见高度,不改变字幕数据、点击跳转、当前句高亮或音频波形联动。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 素材输入改为侧边滑出面板</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>侧栏图标条里的流程状态图标容易被理解成可导航入口,但多数没有独立操作;素材输入也不应该常驻挤占主画布,而应该由真实入口唤出。</p>
<p><strong>改动:</strong><code>WorkbenchRail</code> 默认收起为 65px 中段胶囊工具条,只保留素材任务、资源库和主题切换三个真实动作;鼠标移入、键盘聚焦或点击素材任务后,<code>skg-board-rail</code> 增加 <code>is-open</code> 并从左侧展开 320px <code>MaterialColumn</code> 抽屉。旧的源视频、音频、参考帧、主体套图和视频候选状态图标不再放在侧栏里冒充按钮,状态继续由顶部指标和主工作区各自展示。</p>
<p><strong>影响:</strong>后续描述布局时应说“左侧中段胶囊工具条悬停滑出素材输入面板,右侧主画布视频拆解工作区”;素材任务状态、开始分析、上传和任务切换接口不受影响。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-21 · 按 Figma Glassmorphism 全面改造工作台外观</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Figma MCP</span>
</header>
<div class="body">
<p><strong>依据:</strong>通过本地 <code>figma-desktop</code> MCP 读取 <code>Dashboard Glassmorphism (Community)</code>:主 frame <code>2:3</code><code>1366x768</code>,普通指标卡约 <code>288x160</code><code>rgba(255,255,255,.1)</code><code>backdrop-blur(5px)</code><code>20px</code> 圆角,左侧栏为 <code>#383838</code>、宽 <code>65px</code>、右侧 <code>70px</code> 圆角。</p>
<p><strong>改动:</strong><code>web/app/globals.css</code> 新增黑灰玻璃工作台 token 和 <code>skg-board-shell</code><code>skg-board-rail</code><code>skg-glass-card</code> 等类;<code>AdRecreationBoard</code> 新增 <code>WorkbenchRail</code>,把资源库和主题切换收进左侧胶囊栏,顶部改为轻量生产控制条,素材列、视频拆解面板、源视频工作区、转换层和主体结果栏统一套深色玻璃面。</p>
<p><strong>影响:</strong>这是纯前端视觉壳改造,不改变后端接口、模型路由、任务状态、转换层确认逻辑或媒体缩略图交互;后续描述 UI 需求时可说“按 2026-05-21 玻璃工作台外壳继续扩展”,不要再回到旧的顶部大 brand strip。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 取消工作台固定画布缩放</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>外层 1800x1000 固定画布和整页 <code>zoom</code> 虽然能让不同显示器看到相同框架,但会压缩真实排版、让文字发虚,并让页面在实际浏览器里显得比例失衡。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 删除 <code>BOARD_FRAME_WIDTH</code> / <code>BOARD_FRAME_HEIGHT</code>、缩放档位、<code>ResizeObserver</code> 和内层 <code>zoom</code> 画布;外层改为 <code>min-height: 100vh</code><code>width: 100%</code><code>max-width: 1920px</code> 的正常桌面流式容器,并保留 <code>min-width: 1280px</code> 作为最低操作宽度。</p>
<p><strong>影响:</strong>浏览器宽度变化时页面不再按固定画布整体缩放,文字由原生字号渲染,视觉大小和真实空间更接近浏览器本身;窄窗口会横向滚动兜底,而不是把整套复杂工作台继续缩小。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 取消转换层固定长高</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>转换层固定 640px 和 160px 输入框让页面显得过长,主体元素被推远,整体观感变重。</p>
<p><strong>改动:</strong><code>SOURCE_CONVERSION_HEIGHT</code> 改为 <code>SOURCE_CONVERSION_MAX_HEIGHT</code>,转换层不再写死 <code>height</code>,而是按内容自然高度显示,最多到 560px 后内部滚动;生成要求输入框从 160px 收回到 128px。</p>
<p><strong>影响:</strong>对话式生成入口保留“发送消息 / 确认生成”闭环,但界面不再为了固定长款占满垂直空间,主体元素栏会更早露出。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 生成确认回到对话框</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Conversion</span>
</header>
<div class="body">
<p><strong>问题:</strong>生成要求区已经是对话式输入,但提示词生成后仍跳出确认弹窗,用户还要理解额外确认层。</p>
<p><strong>改动:</strong>生成要求输入框先提升到 160px随后为视觉观感收敛到 128px<code>subject-agent/message</code> 返回出图提示词后不再自动弹窗,标题右侧只显示“提示词就绪”,底部主按钮直接从“发送消息”切换成“确认生成 N 张”。用户在输入框里输入“确认生成 / 出图 / 开始生成”这类短句时也走同一个生成动作。</p>
<p><strong>影响:</strong>转换层的生成闭环收敛到同一个对话框:继续输入就是改需求,直接点确认生成就是提交套图;数量控件、保留元素和主体元素输出逻辑不变。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层继续加长</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>生成要求 composer 放大后560px 的转换层在已有参考图、识别结果和待确认入口同时存在时仍显得偏短。</p>
<p><strong>改动:</strong><code>SOURCE_CONVERSION_MAX_HEIGHT</code> 取消固定高并收敛为 560px 最大高度;参考帧池滚动高度同步使用该值,转换层内部仍保持自身滚动。</p>
<p><strong>影响:</strong>参考输入、识别结果和生成要求之间有更多垂直呼吸空间,发送区不再贴得太紧;主体元素结果栏仍放在转换层下方。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 生成要求区精简为发送框</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Conversion</span>
</header>
<div class="body">
<p><strong>问题:</strong>“当前要求”、保留元素副本和对话记录计数重复表达了识别结果和确认弹窗里的信息,增加了转换层噪音。</p>
<p><strong>改动:</strong>删除生成要求区内的摘要卡、保留元素副本和“对话记录已收起”提示;该区域只保留文本输入、张数控件和发送按钮;文本输入高度提升到 112px张数步进和发送按钮提升到 40px出图提示词就绪后底部主按钮直接切换为“确认生成 N 张”。</p>
<p><strong>影响:</strong>转换层更像对话式生图输入区,输入框和底部操作区更适合直接书写生成要求,保留元素只在识别结果 chip 中管理,确认出图流程不变。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层拉高并修正内部堆叠</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>转换层内部的模型选择、参考输入、识别结果和生成要求在 500px 高度内会互相挤压,和左侧参考帧池视觉高度也不完全一致。</p>
<p><strong>改动:</strong>把转换层和参考帧池共用高度从 500px 拉到 560px并在生成要求区域放大后继续扩到 640px转换层内部改为 <code>gap</code> 堆叠,并让主要板块 <code>shrink-0</code>,内容超出时由转换层自身滚动承接。</p>
<p><strong>影响:</strong>转换层与参考帧池在同一水平区域内更接近同长,内部板块不再因为 <code>flex-1</code> 被压缩重叠。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层移除内嵌待确认提示词卡</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Conversion</span>
</header>
<div class="body">
<p><strong>问题:</strong>转换层里同时展示完整待确认 prompt、大号“确认并生成”按钮和固定确认弹窗职责重复并挤压生成要求输入区。</p>
<p><strong>改动:</strong>删除转换层内嵌的“待确认提示词”卡和黑色确认按钮;<code>subject-agent/message</code> 返回 prompt 后仍自动打开固定确认弹窗,用户关闭后可通过“生成要求”标题右侧的小型“待确认 · N 张”入口重新打开。</p>
<p><strong>影响:</strong>确认出图的安全步骤不变,但转换层高度更稳定,当前要求区不再重复显示完整英文 prompt。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 撤销源视频工作区临时布局调节</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>临时滑杆和浏览器本地参数让工作区比例不稳定,容易把转换层、主体元素和逐句时间轴调乱。</p>
<p><strong>改动:</strong>删除 <code>SourceWorkspaceLayoutPanel</code>、“布局调节”按钮和 <code>localStorage["skg-source-workspace-layout:v1"]</code> 读写;源视频工作区改回代码固定比例:左侧原视频列 380px、9:16 视频高 500px、逐句时间轴最大高 360px、参考帧池 140px、转换层自然高度、最大 560px 内部滚动、主体空态 78px。</p>
<p><strong>影响:</strong>用户不再需要在线调比例,不同浏览器也不会继续受旧本地参数影响;任务数据、模型调用和素材生成接口不受影响。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 源视频工作区增加临时布局调节</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>左侧 9:16 原版视频和逐句时间轴实际信息密度偏低,但固定列宽难以一次判断准确;转换层高度又会影响主体元素位置。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 新增“布局调节”面板,临时开放左列宽、视频高度、逐句时间轴高度、参考帧池宽度、转换层高度和主体空态高度 6 个滑杆,写入当前浏览器 <code>localStorage["skg-source-workspace-layout:v1"]</code>。转换层高度改为受控区域,内容过多时内部滚动。</p>
<p><strong>影响:</strong>用户可以先在线调整到合适比例,后续再把确认后的参数固化为默认值;任务数据、模型调用和素材生成接口不受影响。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 主体元素下移为结果栏</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>主体元素是转换层的生成结果,却长期占据右侧整列;空态时浪费 500px 高度,也把对话式转换层挤得过窄。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code> 改为上方两列:左侧紧凑参考帧池,右侧宽幅转换层;主体元素移动到下方结果栏,空态压缩为 80px已生成时横向展示当前套图并在右侧切换套图包。</p>
<p><strong>影响:</strong>转换层获得主要操作宽度;主体套图仍保留文件夹分组、单张重生、删除和 <code>MediaAssetTile</code> 顶层 hover 预览。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 工作台缩放后按视口高度居中</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Responsive</span>
</header>
<div class="body">
<p><strong>问题:</strong>常见尺寸档位让宽度稳定了,但 1440x900、1728x1117、2560x1440 这类窗口里,缩放后的 <code>1800x1000</code> 工作台高度小于视口却仍贴顶,底部会出现明显空白,视觉上像整体尺寸调错。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 记录工作台视口宽高,在缩放后画布可完整放入视口高度时上下居中;当缩放后高度等于或超过视口时仍保持顶部对齐和滚动。</p>
<p><strong>影响:</strong>不同显示器继续使用同一套框架和缩放档位,但短窗口不再贴顶留黑边,高窗口会保留更均衡的上下呼吸感。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 工作台缩放改为常见尺寸档位</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Responsive</span>
</header>
<div class="body">
<p><strong>问题:</strong>连续按宽度铺满虽然消除了左右空白,但 2048px 等窗口会被放得过大,界面失去最初的比例和呼吸感。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 新增 <code>BOARD_SCALE_PRESETS</code>,按可见宽度选择最接近且能放下的人工档位:<code>0.72/0.76/0.8/0.86/0.92/1/1.06/1.16/1.24/1.34/1.48/1.6</code>,继续用 CSS <code>zoom</code> 渲染。</p>
<p><strong>影响:</strong>1440、1728、1920/2048、2200、2560 这类常见尺寸会更稳定,保留适度侧边留白;拖动浏览器不会每个像素都改变比例,视觉更接近人工设计稿。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 工作台缩放改为清晰文字渲染</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Rendering</span>
</header>
<div class="body">
<p><strong>问题:</strong>工作台按宽度铺满后仍使用 <code>transform: scale()</code> 缩放整个 <code>1800x1000</code> 画布,小数比例下浏览器会把文字、边线和图标一起作为缩放层处理,用户看到文字有发虚感。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 保留同一套 <code>boardScale</code>、外层占位尺寸和宽度优先策略,但内层画布改用 CSS <code>zoom</code> 承接缩放,减少整屏 transform 位图化带来的文字模糊。</p>
<p><strong>影响:</strong>不同显示器仍看到同一套框架;左右空白和纵向滚动策略不变,但文字、按钮和小号状态标签应比 transform 缩放更清晰。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 工作台框架固定并按视口等比缩放</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>工作台之前直接跟随浏览器视口宽高,并在 <code>xl/2xl</code> 断点下改变列宽、视频高度、主体管线和分镜网格;用户换显示器或缩放浏览器时,看到的框架会变。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 外层改为可缩放视口,内部保留 <code>1800x1000</code> 基准画布;移除工作台内会影响核心框架的响应式断点,统一使用同一套桌面列宽和高度,再按可见宽度计算 0.72-1.6 倍的等比缩放,优先消除左右空白,必要时允许纵向滚动。背景网格和氛围层改为固定层,滚动画布时视觉不被裁切;逐句时间轴的当前句同步滚动只作用于字幕列表内部,避免小窗口打开时把整个工作台顶端自动滚掉。</p>
<p><strong>影响:</strong>不同显示器和浏览器宽度下,素材输入列、源视频工作区、参考帧池、转换层、主体元素和分镜行保持同一框架;大屏会自动放大并尽量铺满宽度,小窗口会等比缩小,高度不足时用纵向滚动承接,不再为了完整高度留下大块左右空白。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 生产部署增加数据保护脚本</h3>
<span class="tag amber">Deploy</span>
<span class="tag rose">Safety</span>
</header>
<div class="body">
<p><strong>问题:</strong>手动 <code>rsync --delete</code> 如果没有排除服务器 <code>data/jobs</code> 和真实 <code>deploy/.env.production</code>,会把生产案例、资源库或登录配置删掉。</p>
<p><strong>改动:</strong>新增 <code>scripts/deploy-prod-safe.sh</code> 作为生产部署唯一入口。脚本部署前会在服务器创建 <code>/opt/skg-marketing-studio-backups/skg-marketing-preserve-*.tgz</code>,备份真实 env、案例、资源库和 secrets同步时用 <code>rsync --filter='P ...'</code> 和 exclude 双重保护 <code>data/</code><code>jobs/</code><code>secrets/</code><code>api/jobs</code><code>deploy/.env.production</code> 和本地开发文件。</p>
<p><strong>影响:</strong>后续发布不再手写裸 <code>rsync --delete</code>;脚本会自动 Docker 重建并调用 <code>verify-prod-docker.sh</code>。若误操作,先从最新 <code>skg-marketing-preserve-*.tgz</code> 恢复。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层改为提示词确认后生成</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>补充:</strong>转换层排版改为“参考输入区在上、消息对话区在下”的对话式生图 composer。参考输入区可接收左侧参考帧拖拽、胶片拖拽和本地图片拖拽上传本地图片通过 <code>POST /jobs/{id}/frames/upload</code> 写入 <code>job.frames</code> 后加入当前转换层参考图。</p>
<p><strong>影响:</strong>“生成提示词”按钮语义收敛为底部“发送消息”,用户先围绕参考图发需求,系统再返回待确认英文 prompt右侧主体元素套图输出、轮询、文件夹分组、单张重生和删除不变。</p>
<p><strong>补充:</strong>可见快捷需求 chip 不再显示,输入框也不再枚举固定示例句,改成中性的“补充调整要求”;默认让识别结果 chip 承担元素保留,用户需要换人物、卡通化、放大人物或删除某元素时直接在对话里补充。生成数量改成发送区旁边的张数控件,默认 6 张,当前上限 10 张。参考输入空态和已选参考图缩略图压小,依靠 <code>MediaAssetTile</code> hover 放大预览查看细节。</p>
<p><strong>补充:</strong>识别结果里的特征 chip 改为纯本地“保留元素”选择,点亮表示会随下一条消息提交给模型,再点同一 chip 取消,右侧“清空”一次性取消全部;点击 chip 本身不再触发 <code>/subject-agent/message</code>,避免每点一次都等待模型造成卡顿。</p>
<p><strong>补充:</strong>转换层不再把最近 5 条 user/assistant 消息作为聊天泡逐条显示;“我们将不再强制……”这类模型确认句默认收起,界面只保留识别结果里的保留元素 chip 和生成要求 composer最终英文 prompt 不再自动弹窗,界面只显示“提示词就绪”并把底部主按钮切换为确认生成。</p>
<p><strong>问题:</strong>用户希望转换层只做清晰的“上传图/选图 → 分析图 → 对话确认需求 → 生成出图提示词 → 底部出现确认生成按钮 → 点击生成多角度统一套图”闭环,不能拖入参考后自动开跑,也不能继续保留旧四投放区。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code> 恢复轻量对话式转换层:参考帧池缩略图新增 <code>+</code> 操作送入转换层;转换层内可选 GPT/Gemini 套件、分析 1-3 张参考图、查看特征 chips、通过对话生成英文 prompt。<code>subject-agent/message</code> 返回后不再自动弹窗,生成要求区底部主按钮切换为“确认生成 N 张”;用户点该按钮才调用 <code>generateSubjectAssets</code></p>
<p><strong>影响:</strong>右侧主体元素输出、套图文件夹、逐张回填、单张重生和删除逻辑不变。生成数量、方向和风格继续由对话解析,最终英文 prompt 由对话生成并进入就绪状态,用户从同一输入区直接确认生成。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层设计清空待重构</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>现有转换层界面和智能体交互方向被判定为错误,继续在该板块上补控件会扩大错误设计。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code> 的中间转换层渲染清空为待重构占位,移除模型选择、参考图、分析按钮、对话记录、输入框、生成按钮和拖拽接收。</p>
<p><strong>影响:</strong>右侧主体元素区的已有图片输出、套图文件夹、轮询、单张重生和删除逻辑不变;后端 subject-agent 和主体生成函数暂时保留在代码里,但当前主界面不再通过转换层触发。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层方向和数量并入对话</h3>
<span class="tag violet">UI</span>
<span class="tag amber">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>转换层已经是生图智能体,但界面仍保留四个方向卡片、卡通风格下拉和数量按钮,用户需要在“对话”和“控件”之间来回确认,违背“模糊需求通过对话收口”的目标。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code> 删除方向卡片和独立数量控件,只显示当前识别出的方向与张数摘要;用户在对话里写“形象锁定 / 卡通重构 / 创意复刻 / 自主描述”和“生成几张”,后端 <code>subject-agent/message</code> 会解析并更新 <code>selected_mode</code><code>quantity</code>、中文需求和英文生成 prompt。</p>
<p><strong>影响:</strong>数量、方向和风格以后都通过转换层对话完成;点击“生成 N 张”时使用对话状态,不再要求用户点卡片或另选数量。卡通风格若在对话里说明,会进入英文 prompt不再被前端默认 3D 风格硬覆盖。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层改为项目内生图对话智能体</h3>
<span class="tag violet">UI</span>
<span class="tag amber">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>旧多方向卡片加 textarea 的转换层不适合“参考图先分析、再边聊边确定生图需求”的模糊决策流程;同时 GPT/Gemini 切换必须成套影响分析对话模型和生图模型,不能只切图片模型。</p>
<p><strong>改动:</strong><code>Job</code> 新增 <code>subject_agent</code> 状态保存当前项目的模型套件、参考帧、AI 分析、对话消息、选中方向、特征 chip、数量和英文生成 prompt。后端新增 <code>POST /jobs/{job_id}/subject-agent/analyze</code><code>POST /jobs/{job_id}/subject-agent/message</code><code>SourceSubjectPipeline</code> 的转换层改成“GPT 套件 / Gemini 套件 + 参考图 + 开始分析 + 生图对话 + 数量 + 生成”结构。选 GPT 时分析/对话走 GPT、图片走 <code>gpt-image-2</code>;选 Gemini 时分析/对话走 Gemini、图片走 <code>gemini-3-pro-image-preview</code></p>
<p><strong>影响:</strong>转换层不再把生图模型偏好写入浏览器全局或 job-scoped <code>localStorage</code> 作为主状态;当前项目的生图记忆跟随 <code>state.json</code>。用户可以在对话里改“生成几张、保留什么、删除什么、人物放大、服装统一、形象锁定/创意复刻/卡通/自主描述”,确认后仍复用 <code>generateSubjectAssets</code> 逐张生成并进入右侧主体元素套图文件夹。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层参考帧改为项目隔离和图像参考生图</h3>
<span class="tag violet">UI</span>
<span class="tag amber">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>转换层提示词记忆和生图模型选择用全局 <code>localStorage</code> key切换不同项目时会带入上一项目的偏好同时 <code>reconstruction_mode=similar</code> 虽然提交了参考帧,但最终走纯文字 <code>/images/generations</code>,导致不同项目容易生成同一套泛化人物。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code> 把主体提示词记忆和生图模型偏好改为按 <code>job.id</code> 存储;自主描述为空且已拖入参考帧时,前端切到 <code>reconstruction_mode=same</code> 并提交源形象锁定 prompt。<code>generateSubjectAssets</code> 对有参考帧的 <code>similar</code> 路径先生成 source brief再把参考帧作为 <code>/images/edits</code> 的 image refs 一起提交;<code>same</code> 路径使用源形象 pack bible锁定参考帧中的可见主体、体态、发型、服装和配色。</p>
<p><strong>影响:</strong>不同项目之间不会再共享转换层提示词 chip 或模型选择;真人/卡通/元素重构仍是参考创新,但参考帧会真正进入生图请求;自主描述不写字时表示按拖入参考帧复刻同一主体,而不是随机生成新人。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 主体 6 视图增加 pack bible 和主体占比放大</h3>
<span class="tag amber">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>Gemini 显式生图时6 个视角是 6 次独立 <code>/images/generations</code> 请求,容易每张自行脑补服装;模型也可能输出“小人 + 大白边”,即使最终文件是 2048 高也显得主体很小。</p>
<p><strong>改动:</strong><code>generateSubjectAssets</code> 在每套主体图中注入同一份 pack bible固定同一新主体、发型、肤色、体态、服装、鞋、配饰和禁止换装项全身视图 prompt 明确要求主体占画布高度 88-94%。<code>_normalize_asset_image(fill_subject=true)</code> 裁出白底主体后改为可放大 resize 到目标画布上限,不再只用 <code>thumbnail()</code> 导致小人保持原比例。</p>
<p><strong>影响:</strong>显式 Gemini 仍然是逐视角独立生成,但每次调用收到同一份具体主体/服装约束;保存后的主体资产会尽量贴近画布高度,减少大白边。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-20 · 转换层增加模型选择、提示词记忆和主体服装锁定</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag amber">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户已经知道某个生图模型不可用时,需要能在转换层直接切换;同时主体 6 视图容易出现不同衣服、不同人设,提示词每次也要重复输入。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code> 新增自动 / GPT / Gemini 三档生图模型选择,当时偏好写入全局 <code>localStorage["skg:subject-image-model:v1"]</code>,后续已改为当前 job 作用域;提示词输入当时保存到全局 <code>localStorage["skg:subject-prompt-memory:v1"]</code>,后续也已改为当前 job 作用域。后端 <code>_image_model_candidates</code> 支持显式模型偏好,<code>auto</code> 保留 gpt-image-2 主模型、Gemini 兜底和短时熔断,显式 GPT / Gemini 只走所选模型。</p>
<p><strong>影响:</strong>主体重构默认继承参考图里的性别、人种/肤色、年龄体态和角色气质这些广义特征,但生成同一个全新主体;六视图 prompt 强制统一脸部设定、发型、体态、服装类型、配色、材质、剪裁和配饰,避免一套图里每张衣服都不同。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 生图增加 Gemini 故障兜底和短时熔断</h3>
<span class="tag violet">API</span>
<span class="tag amber">Reliability</span>
<span class="tag rose">Config</span>
<span class="tag cyan">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong><code>gpt-image-2</code> 当前上层通道会读超时,但完全禁用兜底会导致主体套图和补图无法继续;同时不能把主模型直接改成 Gemini。</p>
<p><strong>改动:</strong><code>api/main.py</code> 保持 <code>gpt-image-2</code> 为主模型,新增 <code>IMAGE_FALLBACK_MODEL=gemini-3-pro-image-preview</code><code>IMAGE_FALLBACK_ENABLED</code><code>IMAGE_CIRCUIT_FAILURE_THRESHOLD</code><code>IMAGE_CIRCUIT_COOLDOWN_SECONDS</code>。只有主模型超时、429、5xx 或网络错误时才兜底400/401/403/404 和参数错误不兜底。连续 2 次主模型上游类失败后600 秒内直接走 Gemini主模型成功后自动清空失败计数。主体同一套图内一旦触发 Gemini后续视图沿用 Gemini。</p>
<p><strong>影响:</strong><code>/health</code> 返回 <code>image_fallbacks</code><code>image_circuit</code><code>ModelTrace</code> 显示 <code>gpt-image-2 / gemini-3-pro-image-preview</code> 和熔断规则。该变更是故障兜底,不是默认改模型。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · gpt-image-2 请求超时改为快速失败</h3>
<span class="tag violet">API</span>
<span class="tag amber">Reliability</span>
<span class="tag rose">Config</span>
</header>
<div class="body">
<p><strong>问题:</strong><code>gpt-image-2</code> 上游图片网关无响应时,文字生图仍通过 SDK 默认等待,编辑生图也按 120 秒重复尝试;主体 6 视图第一张卡住后,用户侧长时间看不到逐张失败或后续进度。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>IMAGE_REQUEST_TIMEOUT_SECONDS</code>,默认 60 秒;<code>_image_text_call</code> 统一改为直接调用 <code>/images/generations</code><code>_image_edit_call</code> 和旧分镜生图也复用同一超时。超时、DNS、连接失败这类传输错误不再盲目重试三轮会把当前视图标失败并继续处理后续视图。<code>/health</code> 回传当前图片超时配置。</p>
<p><strong>影响:</strong>当时不改模型,所有图片入口仍固定只使用 <code>gpt-image-2</code>;后续已增加 Gemini 故障兜底和短时熔断,但错误仍会明确指向当前 <code>IMAGE_BASE_URL</code> 上的主模型通道超时或不可用。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 主体元素改为套图文件夹并逐张回填</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag amber">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>主体 6 视图生成一次性等待太久,且多次生成后所有图片平铺会迅速挤满主体元素区。</p>
<p><strong>改动:</strong><code>SubjectAsset</code> 新增 <code>status/progress/error</code><code>pack_id/pack_label/pack_mode/pack_created_at</code><code>generateSubjectAssets</code> 先写 queued 占位卡并后台按视角逐张生成。<code>web/app/page.tsx</code> 轮询主体资产运行态,<code>SourceSubjectPipeline</code> 按 pack 显示套图文件夹,点击文件夹在最上层展开该套,其他套图进入下方可滚动列表。</p>
<p><strong>影响:</strong>用户可以连续生成多套真人/卡通/元素/自主描述主体图,不会被平铺图片淹没;生成过程会逐张出现,单张失败不阻塞其他视角。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 转换层拖入参考不再自动生成</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>拖入第一张参考帧就立即开始生成,会打断用户继续补第二、第三张参考帧和填写方向文字。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code><code>addConversionFrame</code> 只更新对应重构入口的参考队列和提示,不再调用 <code>generateSubjectPack</code>;旧版本曾保留独立生成按钮,用户放好参考后再手动提交。</p>
<p><strong>影响:</strong>当前正确操作是“拖入 1-3 张参考帧 → 可写方向文字/选卡通风格 → 点击生成 6 视图”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 转换层改为四类主体重构入口</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag amber">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>旧转换层暴露“透明骨架 / 真人、完整 10 / 常用 4”等生成参数和当前“参考创新生产套图、避免侵权”的目标不一致用户更需要把少量参考帧拖到明确方向上右侧马上看到生成结果。</p>
<p><strong>改动:</strong><code>SourceSubjectPipeline</code> 的转换层曾改成多方向入口;文字描述会参与 prompt卡通重构可选择风格。主体元素区按重构类型分组展示结果。<code>web/lib/api.ts</code><code>api/main.py</code> 扩展 <code>subject_style=cartoon_subject</code>,后端对卡通主体额外加入原创卡通/插画约束。</p>
<p><strong>影响:</strong>后续描述这块应说“把参考帧拖到某个重构方向,生成全新主体 6 视图”,不要再说“抠图”“模仿”“透明骨架/真人开关”或“10 张/4 张选择”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 胶片双击加帧和音频失败重试</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>胶片只能拖入参考帧池,不能双击快速加入;回到同一素材时胶片会重新扫视频;音频解析失败后如果留下半成品 transcript 或 transcribing 状态,开始队列和解析按钮可能不再真正触发重试。</p>
<p><strong>改动:</strong><code>TimelineFilmstrip</code> 支持双击调用 <code>addManualFrame</code>,已加入的胶片显示“已添加”;胶片预览按 job、视频、密度和时长做内存缓存未切换低/中/高时复用已有截图。<code>web/app/page.tsx</code> 用音频失败状态排除半成品结果,<code>api/main.py</code><code>trigger_transcribe</code> 只按实际 worker 和 <code>audio_script.status=rewriting</code> 判定忙碌,失败后可重新提交。</p>
<p><strong>影响:</strong>用户可以双击胶片快速选参考帧;返回素材或切回同密度时不重复扫视频;音频失败后可直接点击解析音频或开始分析重试。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 优化参考帧池预览和多参考转换</h3>
<span class="tag violet">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>参考帧池默认 hover 大图向右弹出,遮挡转换层;竖版缩略图用完整 contain 显示后仍然偏大;转换层拖入多张参考帧后也可能挤占生成控制区。</p>
<p><strong>改动:</strong><code>MediaAssetTile</code> 新增 <code>previewPlacement</code><code>previewMaxWidth</code><code>SourceSubjectPipeline</code> 的参考帧池改为左侧紧凑 hover 预览,参考帧池和转换层缩略图固定为约 72-80px 宽,同时保持 <code>aspect-[9/16]</code><code>object-contain</code>,转换层参考列表增加滚动上限。</p>
<p><strong>影响:</strong>参考帧仍可点击多选、拖入转换层和删除;多张参考会在转换层内滚动,不会把下方生成按钮挤走。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 右侧改为参考帧池到主体元素管线</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>旧下方“相似主体 / 主体模板库”过重,用户实际只想从少量关键帧里挑 1-2 张作为参考,直接生成新的主体套图。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 右侧新增 <code>SourceSubjectPipeline</code>,把主体流程改为三栏:竖向参考帧池、转换层和主体元素。参考帧池可从胶片正式加帧,也可拖关键帧到转换层;转换层拖入后自动调用 <code>generateSubjectAssets</code> 参考创新生成套图,保留透明骨架/真人、完整 10 / 常用 4、统一方向和手动重生主体元素区展示生成结果并支持单张重生、删除和 hover 放大。</p>
<p><strong>影响:</strong><code>SourceReferenceBuildPanel</code> 不再主路径渲染,主体模板库入口从首屏移除;后续描述这里时,应按“参考帧池 → 转换层 → 主体元素”来提需求。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 逐句时间轴移到原版视频下方</h3>
<span class="tag violet">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>逐句时间轴放在右侧会和波形、胶片、参考帧池抢空间,且单行截断导致文案内容看不全。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 把左侧原版视频列调整为 430-460px并把逐句时间轴放到原版视频下方时间轴抽成 <code>TranscriptTimelinePanel</code>,英文和中文字段由单行 <code>truncate</code> 改为最多两行 <code>line-clamp-2</code>,时间列收窄为 68px。</p>
<p><strong>影响:</strong>视频、字幕、波形播放线仍联动;右侧下方只保留参考帧池。后续描述源视频工作区时,应把“逐句时间轴”理解为原版视频下方的字幕审片面板。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 音频波形下新增临时画面胶片</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户通常只需要 1-2 张关键帧,但只靠自动抽帧或当前播放点补帧,浏览大量候选画面不够快,直接抽很多帧又会污染正式关键帧池。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code><code>AudioWaveform</code> 下方同框新增无标题 <code>TimelineFilmstrip</code>,前端从源视频临时截取低/中/高密度胶片缩略图,并按 <code>frame.time / duration</code> 的百分比定位到和波形一致的横向时间点;低/中/高密度按钮上移到波形上方波形和胶片之间不再显示分隔横线缩略图增加轻微上下错落。hover 时关闭额外弹出预览,改为在原胶片坐标生成固定顶层克隆,使用同一张胶片卡放大到约 4.8 倍;当前单击只跳转视频时间点,双击或拖进参考帧池才调用现有手动抽帧入口。</p>
<p><strong>影响:</strong>临时胶片不会写入 <code>job.frames</code>,只有拖入后才成为正式关键帧;胶片轨道和波形共用同一个无标题框架,基线更贴近波形,并显示同一条当前播放线;胶片 hover 克隆挂到 <code>document.body</code>,避免被工作区、滚动容器或相邻面板裁切。后续如果要调摆放密度、倾斜角度、上下错落或放大倍率,改 <code>FILMSTRIP_DENSITIES</code><code>FILMSTRIP_TILT_CLASSES</code><code>FILMSTRIP_VERTICAL_OFFSET_CLASSES</code><code>FILMSTRIP_HOVER_SCALE</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 逐句时间轴改为窄版面板</h3>
<span class="tag violet">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>宽屏下逐句时间轴占满右侧剩余空间,表格过宽、阅读距离过长,也挤压源视频拆解区的密度。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 给逐句时间轴容器增加桌面最大宽度,约束在 620-680px 范围内;参考帧池、波形、字幕点击跳转和当前句滚动逻辑不变。</p>
<p><strong>影响:</strong>时间轴视觉宽度约减半,后续若要再调整只改该容器的 <code>max-width</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 隐藏音频解析摘要卡</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>源视频工作区顶部的“音频解析结果”折叠条和讲话人/节奏/背景音三张摘要卡仍占据首屏,和用户当前要看的原视频、波形、参考帧和逐句时间轴重复抢空间。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 移除“音频解析结果”details 和三张 <code>ProfileTile</code> 摘要卡。<code>AudioScript</code> 里的讲话人、节奏和背景音数据仍由后端生成并保留,主界面不再默认展示这组诊断信息。</p>
<p><strong>影响:</strong>源视频工作区打开后直接进入原视频、波形、参考帧池和逐句时间轴;需要排查模型分析时再从数据或后续专门详情入口查看。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 隐藏工作区顶部状态提示条</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>源视频工作区顶部同时展示 01-09 流程条、素材/视频/音频/文案/参考帧 chips、文案依据下拉和 03-06 四个状态卡,占用过多首屏空间,用户需要的是直接进入视频、音频文案、抽帧和分镜操作。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 不再渲染 <code>WorkflowOrderBar</code>,并移除右侧源视频区顶部的需求 chips、文案依据 details 和四个 <code>PipelineLane</code> 状态卡。后台状态计算、步骤编号和后续分镜/生成逻辑保持不变。</p>
<p><strong>影响:</strong>这些状态仍可作为代码里的判定依据,但默认不在此工作区显现;首屏高度让给真实操作内容。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 视频候选按音频分镜行隔离</h3>
<span class="tag blue">API</span>
<span class="tag green">Video</span>
<span class="tag cyan">Storyboard</span>
</header>
<div class="body">
<p><strong>问题:</strong>多条音频分镜会映射到同一张参考帧,旧视频候选只按 <code>frame_idx</code> 过滤,导致第一行生成的视频也出现在后面共用参考帧的分镜行里。</p>
<p><strong>改动:</strong><code>StoryboardScene</code><code>GeneratedVideo</code><code>/storyboard/video</code> 请求增加 <code>storyboard_row_idx</code>;前端显示候选和读取已保存分镜时优先按该行号隔离,旧无行号候选只归到同参考帧的第一条兼容行。</p>
<p><strong>影响:</strong>点击某一行生成视频后,只会在该音频分镜行右侧出现候选;整片生成也改为逐行提交,避免候选跨行串位。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 分镜行改成左文字右视频轨</h3>
<span class="tag cyan">Storyboard</span>
<span class="tag green">Video</span>
<span class="tag violet">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>三字段和视频候选上下堆叠时每条分镜一展开就占用大量纵向空间4-grid 候选区也让长分镜列表很难连续操作。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 改为每条分镜左侧编辑三字段、右侧直接显示视频候选横向轨。候选生成数量可按单行选择,整片生成也可选择每行数量并按行排队。中文镜像字段失焦后会调用 AI 改写链路优化对应英文主值,失败时退回翻译。</p>
<p><strong>影响:</strong>用户可以按行扫文案、按行横向看所有候选,生成新候选会持续向右追加,不再撑高分镜行;英文仍是视频 prompt 主值,中文作为团队编辑入口和镜像。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 云端音频解析复制本地真实转写路径</h3>
<span class="tag blue">API</span>
<span class="tag cyan">Audio</span>
<span class="tag red">Deploy</span>
</header>
<div class="body">
<p><strong>问题:</strong>本地音频解析成功时实际链路是远端失败后落到 <code>mlx_whisper</code>,而生产强制 <code>ASR_BASE_URL=https://ai.skg.com/azure/v1</code> + <code>ASR_MODEL=gpt-4o-transcribe</code> 且关闭本地兜底。生产探测官方 Azure OpenAI 音频路径 <code>/openai/v1/audio/transcriptions?api-version=preview</code><code>/openai/deployments/{deployment}/audio/transcriptions?api-version=...</code> 仍不可用,当前部署名返回 <code>DeploymentNotFound</code></p>
<p><strong>改动:</strong>当时远端 ASR 请求新增 <code>ASR_LANGUAGE</code> 并固定英文素材优先;翻译请求也套用 <code>ASR_TIMEOUT_SECONDS</code>。生产配置临时改成 <code>ASR_REMOTE_ENABLED=false</code><code>ASR_LOCAL_FALLBACK_ENABLED=true</code><code>ASR_AUDIO_FALLBACK_ENABLED=false</code>,云端用容器内 <code>faster-whisper</code> 复制本地“真实本机转写”路径。2026-05-22 后该路径已改为默认 <code>auto</code> 语种和多语言模型。</p>
<p><strong>影响:</strong>音频解析不再卡在不存在的 Azure deployment当前云端 CPU 实测同一失败 job 的 <code>audio.wav</code> 可在约 13.6 秒转出 17 段。等 SKG 网关提供真实 Azure ASR deployment 后,再把 <code>ASR_REMOTE_ENABLED=true</code> 并恢复对应部署名。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 删除抽帧状态文案改为参考帧</h3>
<span class="tag blue">API</span>
<span class="tag cyan">Source Video</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户在参考帧池删除自动抽出来的帧时,后端 <code>DELETE /jobs/{job_id}/frames/{idx}</code> 返回的 job message 写成“删除分镜”,让人误以为删掉了逐句分镜。</p>
<p><strong>改动:</strong><code>delete_frame</code> 的状态消息改为“已删除参考帧 N”。前端按钮本来就是“删除关键帧”删除逻辑仍只移除 <code>job.frames</code> 里的抽帧参考帧和对应文件。</p>
<p><strong>影响:</strong>删除抽帧不会被误报成删除分镜;分镜行和参考帧池在用户提示和文档里继续保持清晰边界。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 视频候选文案改为生成 4 个视频</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Storyboard</span>
</header>
<div class="body">
<p><strong>问题:</strong>旧文案里的“抽卡”容易被理解成图片、抽帧或只生成缩略图,不够明确。</p>
<p><strong>改动:</strong>前端按钮、候选区标题、Toast、空状态和后端 job message 统一改为“生成 4 条视频 / 4 个视频候选”。接口逻辑不变:<code>count=4</code> 表示一次创建 4 个独立 <code>GeneratedVideo</code> 视频任务。</p>
<p><strong>影响:</strong>用户看到的是“生成候选视频”,不是“抽图片”;后续不再用“张”描述视频候选数量。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 固化生产 Docker Web 验收</h3>
<span class="tag red">Deploy</span>
<span class="tag violet">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>线上 Web 不是开发态 Next 服务,而是 Docker 内 Nginx 承载的 <code>next export</code> 静态产物。只在本地跑 <code>npm run build</code> 或 dev server无法覆盖 Nginx 登录跳转、<code>/api</code> 反代、静态资源路径、生产构建参数和镜像内环境污染。</p>
<p><strong>改动:</strong>新增 <code>scripts/verify-prod-docker.sh</code>,直接在 VPS 的 <code>skg-marketing-web</code> / <code>skg-marketing-api</code> 容器内检查路由状态、静态包是否残留本地 API 地址、API health、<code>/app/.env</code> 是否泄漏,以及 yt-dlp cookies 参数是否被开发环境污染。<code>RULES.md</code> 明确 Web 改动部署后必须跑该脚本。</p>
<p><strong>影响:</strong>后续前端上线验收以生产 Docker 形态为准;本地 dev 只用于开发预览,不能作为生产适配结论。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 修正生产 TikTok 下载 cookies 污染</h3>
<span class="tag red">Deploy</span>
<span class="tag cyan">Source Video</span>
</header>
<div class="body">
<p><strong>问题:</strong>生产 API 镜像把本地开发 <code>api/.env</code> 复制进 <code>/app/.env</code>,其中 <code>YTDLP_COOKIES_FROM_BROWSER=chrome</code><code>load_dotenv()</code> 读入。容器没有 Chrome cookies 数据库,导致公开视频也在下载阶段失败,错误为 <code>could not find chrome cookies database</code></p>
<p><strong>改动:</strong><code>.dockerignore</code> 明确排除 <code>api/.env</code><code>api/.env.local</code><code>api/.env.production</code>,生产 env 显式保持 <code>YTDLP_COOKIES_FILE=</code><code>YTDLP_COOKIES_FROM_BROWSER=</code> 为空。需要登录态时只允许配置服务器 cookies 文件,不允许在生产容器使用 <code>YTDLP_COOKIES_FROM_BROWSER=chrome</code></p>
<p><strong>影响:</strong>公开视频下载恢复为无 cookies 直连;受限视频仍可通过服务器私有 cookies 文件处理。后续 rsync / Docker build 必须排除本地 <code>api/.env</code>否则开发代理、cookies 或 API 配置会污染生产镜像。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 分镜工作台折叠化和候选区紧凑化</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Storyboard</span>
</header>
<div class="body">
<p><strong>问题:</strong>三字段候选生成改造后,候选区默认直接铺开 4-grid占用每条分镜的大量纵向空间产品素材池、批量控制、三字段和候选区也缺少统一折叠入口长列表操作时很难快速扫分镜。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 新增板块折叠状态:产品素材池、批量控制 / 作者想法、每行三字段、每行视频候选和高级抽屉都可独立收起。视频候选默认不展开;无候选且未展开时不渲染大区域,有候选时只显示横向迷你缩略条和已选 / 生成中状态,点“候选”或候选标题后才展开 4-grid 大预览。</p>
<p><strong>影响:</strong>默认工作台从“每条都展示候选面板”改为“行头控制 + 按需展开”。客户仍可一键生成 4 个视频候选、追加生成、选中、删除或清空候选,但长分镜列表不会被空候选槽撑高;高级用户展开高级区时继续看到完整视频候选和首尾帧细节。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 分镜生成简化为三字段候选生成流</h3>
<span class="tag blue">API</span>
<span class="tag violet">UI</span>
<span class="tag cyan">Storyboard</span>
</header>
<div class="body">
<p><strong>问题:</strong>原分镜行把新口播、画面规划、人物描述、首帧、尾帧、产品出现方式和视频槽全部摊开,客户要先理解 6 字段和首尾帧闸门才能生成视频,单条也只能按旧候选逻辑慢慢提交。</p>
<p><strong>改动:</strong>默认行改为三字段:文案、场景一句话、人物+产品+动作;每行提供 AI 改写、生成 4 条视频和高级抽屉。高级抽屉保留原 6 字段、首尾帧 prompt 和首尾帧资产槽。候选区改为 4-grid支持生成中状态、hover 视频预览、选中、追加生成、单条重生、删除和清空。顶部新增整片一键生成候选,默认每条 4 个候选并后台提交。</p>
<p><strong>影响:</strong><code>StoryboardScene</code> 新增 <code>skg_copy_*</code><code>scene_one_line_*</code><code>action_one_line_*</code><code>selected_video_id</code>。后端新增 <code>quick-plan</code><code>refine</code><code>batch-generate-all</code>,并让 <code>/storyboard/video</code> 支持 <code>count</code>/<code>seed</code>。默认 prompt 仍以英文主值提交,中文只作为镜像显示。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 新增全局资源中心和复制式跨 job 素材库</h3>
<span class="tag blue">API</span>
<span class="tag violet">UI</span>
<span class="tag cyan">Library</span>
</header>
<div class="body">
<p><strong>问题:</strong>主体、产品、首尾帧、视频候选和常用 prompt 都散落在单个 job 里,跨任务复用时容易重新上传、重新生成或误把旧 job 文件当公共引用,后续也缺少删除保护和使用次数记录。</p>
<p><strong>改动:</strong>后端新增和 <code>jobs/</code> 平级的 <code>asset_library/</code><code>prompt_library/</code>,服务启动扫描目录重建索引;提示词提供 CRUD、复制计数和 5 类分类;素材提供主体/产品/场景/视频四类 CRUD、引用检查、强删移入 <code>_trash/</code><code>copy-to-job</code>。前端新增 <code>LibraryDrawer</code> 浮窗,顶部“资源库”按钮打开;浮窗记忆尺寸、位置和 Tab提示词按“常用 + 月份”分列,素材按月份分列,节点不能拖动;提示词支持一键复制英文/中文/双语,素材应用到当前 job 时只走复制模式。生产 compose 同步挂载 <code>/data/asset_library</code><code>/data/prompt_library</code><code>/data/_trash</code>,避免容器重建后库文件丢失。</p>
<p><strong>影响:</strong>后续保存产品图、首尾帧、主体模板和分镜 prompt 时优先进入资源中心。库素材永远不要直接写成 job 引用,必须通过 <code>POST /asset-library/{kind}/{id}/copy-to-job/{job_id}</code> 复制成普通 job asset删除库素材前必须查 <code>/refs</code> 并让用户确认。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 工作台视觉统一到登录页品牌语言</h3>
<span class="tag amber">UI</span>
<span class="tag violet">Theme</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页已经形成 SKG 卡通角色、暖橘光晕、金色聚焦色、米白主按钮和“未来健康 · 营销内容工作台”的品牌承诺,但进入信息流工作台后仍是纯黑高密度界面,主 CTA 和选中态混用 cyan、emerald、rose、violet 等颜色,视觉上像两个产品。</p>
<p><strong>改动:</strong><code>web/app/globals.css</code> 把登录页的金色、米白、暖光、8px 圆角、按钮阴影和明暗模式中性色抽成 <code>skg-board-theme</code> token并新增 <code>skg-board-brand</code><code>skg-stat-card</code><code>skg-primary-action</code><code>skg-secondary-action</code><code>skg-empty-state</code> 等复用类。<code>AdRecreationBoard</code> 顶部改为 SKG brand strip右侧统计改成米白 stat 卡片,开始分析、生成主体、整片改写、保存规划等主动作统一为登录页同源按钮;主体模板选中、处理中状态和重点输入焦点收敛到品牌金色,空状态复用 <code>AnimatedLoginCharacters</code></p>
<p><strong>影响:</strong>后续描述工作台视觉时应按“登录页同源 SKG 营销内容工作台”理解,不再把它当成独立黑色工具页;新增图片/视频/表格板块时,主动作优先用 <code>skg-primary-action</code>,次动作用 <code>skg-secondary-action</code>,选中和聚焦用 SKG 金色,功能色只留给明确成功、等待、失败、信息语义。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 模型 prompt 语言策略切到英文主值</h3>
<span class="tag cyan">Prompt</span>
<span class="tag violet">UI</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>最终产物是英文 TikTok 二创广告,但前端默认分镜规划、首尾帧 prompt 和用户可编辑规划字段里混入中文,导致 <code>gpt-image-2</code>、脚本改写和后续视频模型收到中英混杂指令。</p>
<p><strong>改动:</strong><code>AudioStoryboardRow.role</code> 改为 <code>hook/pain/proof/solution/cta/bridge</code> 英文枚举UI 仍显示中文角色标签。<code>buildAudioStoryboardRows</code><code>buildVisualPlan</code><code>buildFirstFramePlan</code><code>buildLastFramePlan</code><code>buildSubjectDescription</code><code>buildEndpointFramePrompt</code><code>buildStoryboardSceneFromAudioRow</code> 的模型主字段改为英文,并新增 <code>*Zh</code> 镜像用于团队阅读。首尾帧提交前前端会对含中文 prompt 调 <code>translateText</code> 兜底,后端新增 <code>_ensure_english</code> 并挂到 <code>generate_scene_asset</code><code>generate_subject_assets</code>、脚本改写和音频分析入口。</p>
<p><strong>影响:</strong>发给 LLM / 生图 / 视频模型的主 prompt 默认全英文中文只作为团队审稿镜像、UI 标签和 toast。<code>AudioScript</code> 新增 <code>rewritten_text_zh</code><code>KeyElement</code> 新增 <code>subject_consensus_brief_zh</code><code>SubjectTemplateItem</code> 新增 <code>prompt_brief_zh</code><code>POST /jobs/{id}/script/rewrite</code> 返回 <code>text</code> 英文主值和 <code>text_zh</code> 中文镜像。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 首尾帧改为主体 brief + 产品少量硬参考</h3>
<span class="tag cyan">Prompt</span>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>首尾帧旧链路把 5 张主体图和 6 张产品图拼成 contact sheet 再做 image-edit主体姿态和景别被参考图锁死模型还容易把拼图布局误认为输出布局。</p>
<p><strong>改动:</strong><code>KeyElement</code> 新增 <code>subject_consensus_brief</code>;主体视图生成后由 Vision LLM 从 10 张图或模板 <code>prompt_brief</code> 写入统一主体 brief。<code>generateEndpointFrameForRow</code> 生成首尾帧时只传 <code>subject_brief</code> 和最多 1-2 张产品图,不再传 <code>subject_images</code><code>generate_scene_asset</code> 在首/尾帧里只对产品图走 image-edit没有产品图时走纯文字生图。前端状态栏改成“依据主体 brief · N 张产品参考”,首尾帧 slot 的 info 图标可查看 brief 全文。</p>
<p><strong>影响:</strong>主体可以在首尾帧里演新动作、新景别和新场景,但仍保持同一人设;产品结构继续由少量硬参考图锁住左右非对称、按键、触点、厚度和真实佩戴比例。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 主体生成加入随机/手动人设控制</h3>
<span class="tag violet">UI</span>
<span class="tag cyan">Prompt</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>“生成主体视图”只有自由文本方向,用户想控制男女老少、着装、地域人种、肤色等人设时必须手写,而且随机生成容易在多张视图里漂移。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 新增“主体设定”控件:默认随机组合,也可手动指定性别表现、年龄段、着装风格、地域人种、肤色、体型比例、发型和气质场景。点击生成时前端把随机项解析成一套固定 profile传入 <code>generateSubjectAssets</code><code>subject_profile</code>;后端 <code>GenerateSubjectAssetsReq</code> 新增 <code>SubjectProfilePreference</code>,并把这套设定写入 <code>gpt-image-2</code> prompt要求整包视图统一使用同一人设。</p>
<p><strong>影响:</strong>用户可以快速得到有差异但稳定的人物设定;随机模式仍省操作,手动模式可精确控制 casting后续生成首尾帧时也更容易从主体库里挑到明确类型的人物。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 相似主体生成结果可见性修复</h3>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>相似主体默认一次生成 10 张,旧后端只在整包全部成功后才写入 <code>subject_assets</code>;只要其中某个视角失败,前面已生成的图也不会显示。前端默认“不用模板”时仍渲染整块模板网格,也会把生成按钮和结果区域压到可视区域下方。</p>
<p><strong>改动:</strong><code>generate_subject_assets</code> 改为逐视角容错:单张失败时继续生成下一张,并把已成功的主体图写回 state返回“部分生成完成”只有一张都没成功才返回错误。<code>SourceReferenceBuildPanel</code> 在“不用模板”模式收起模板网格,已有主体缩略图优先显示在生成区顶部,并在生成中显示锁定的素材 ID明确切换其他模块不会改变本次请求。</p>
<p><strong>影响:</strong>用户不再因为一个视角失败而看到空结果;默认源视频创新路径下,生成按钮和主体缩略图更容易在当前屏幕内看到。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 模型链路描述对齐真实后端</h3>
<span class="tag cyan">Model</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>前端模型弹窗和状态文档对音频、产品、相似主体、脚本改写和视频入口的描述过于简化,容易误解为所有模型调用都会成功、都会上传参考图或会直接提交视频。</p>
<p><strong>改动:</strong><code>ModelTrace</code> 文案改成真实链路ASR 是远端 whisper、本机 mlx_whisper、Gemini 多模态三级;翻译失败会保留原文时间轴;音频画像使用 <code>audio.wav</code> + 转写时间轴并有本地估算兜底;产品识别批量失败会单图重试再写本地默认视角;相似主体先用 GPT 视觉 brief有参考帧时再带参考图走 <code>/images/edits</code>;脚本改写全部模型失败后用本地模板;视频入口当前主工作台暂停直接提交。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>web/components/dashboard.tsx</code><code>api/README.md</code><code>.project.json</code><code>.memory/status.md</code> 和本页同步按实际后端行为描述,后续排查模型问题时优先看弹窗里的“兜底/失败行为”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 删除个人语音通道残留</h3>
<span class="tag violet">API</span>
<span class="tag cyan">Model</span>
</header>
<div class="body">
<p><strong>问题:</strong>旧个人语音通道虽然已不再作为活动路径,但仓库里仍有健康检查字段、前端类型、环境注释和文档历史文字残留,容易被误认为可用模型方案。</p>
<p><strong>改动:</strong><code>/health</code> 不再返回旧语音通道相关字段;<code>web/lib/api.ts</code> 删除对应类型;<code>api/.env.example</code><code>deploy/.env.production.example</code><code>RULES.md</code><code>.memory/status.md</code> 和本页移除相关名称和配置引用。语音只保留 Azure OpenAI TTS。</p>
<p><strong>影响:</strong>后续模型分工里只描述 Azure OpenAI TTS不要再把个人语音通道作为候选、fallback 或环境变量方案。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 语音通道固定 Azure OpenAI TTS</h3>
<span class="tag violet">API</span>
<span class="tag cyan">Model</span>
</header>
<div class="body">
<p><strong>问题:</strong>语音生成测试失败时无法区分是 Azure OpenAI TTS 路径配置不对,还是整条语音服务不可用;同时 TTS 分支仍可能被环境变量或前端文案误导。</p>
<p><strong>改动:</strong>后端固定 <code>VOICE_PROVIDER=azure_openai</code>,删除旧 TTS 活动配置和 fallback<code>_azure_openai_tts_sync</code> 改为按 <code>AZURE_TTS_PATHS</code> 依次尝试多个 OpenAI-compatible 语音路径,失败时返回每个 URL 的 HTTP 状态或连接错误。<code>/health</code> 新增 <code>models.voice_tts_paths</code></p>
<p><strong>影响:</strong>后续排查语音时先看失败信息里的路径和状态码:如果所有路径 404/405优先调 <code>AZURE_TTS_PATHS</code>;如果连接/鉴权/上游错误,则按 Azure 语音服务可用性或 Key 排查。前端和状态文档不再展示旧语音方案。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 画面理解和文案改写改用 GPT</h3>
<span class="tag violet">API</span>
<span class="tag cyan">Model</span>
</header>
<div class="body">
<p><strong>问题:</strong>关键帧画面理解和分镜/口播改写仍可能被旧环境变量覆盖到 Gemini不符合后端模型分工。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>GPT_TEXT_MODEL</code> / <code>gpt_model_env</code>,默认 <code>VISION_MODEL</code><code>REWRITE_MODEL</code><code>AUDIO_REWRITE_MODEL</code><code>gpt-4o</code>;旧 <code>gemini-*</code> 覆盖值会自动归一化回 GPT。</p>
<p><strong>影响:</strong><code>/health</code> 会向前端模型标注暴露 GPT 模型名Gemini 仅保留在 ASR fallback / 翻译链路。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 相似主体改为文字 brief 创新生成</h3>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>相似主体生成把最多 10 张源视频帧或模板图作为 <code>image[]</code> 上传给 <code>/images/edits</code>,参考图视觉权重过强,结果容易变成源视频人物或内置模板的复刻品。</p>
<p><strong>改动:</strong><code>generate_subject_assets</code> 当时在 <code>reconstruction_mode=similar</code> 时先用 <code>VISION_MODEL</code> 把关键帧、内置形象或数据库模板转成非身份化文字 brief再调用 <code>gpt-image-2</code> 的文字生图路径;后续当前转换层已有参考帧时会改走 <code>/images/edits</code> 图像参考路径。<code>same</code> 模式保留 image-edit 源形象锁定路径。<code>SubjectTemplateItem</code> 和内置形象 manifest 新增 <code>prompt_brief</code>,保存模板时会从生成视图反推 brief。前端 <code>SourceReferenceBuildPanel</code> 改成“用模板 / 不用模板”模式开关、120px 竖排模板卡、底部一行保存表单,以及独立的“生成主体视图”区域,支持全部 10 / 常用 4 / 自定义视图。</p>
<p><strong>影响:</strong>后续说“相似主体”应理解为文字 brief 驱动的创新生图,不是把关键帧或模板图送去 image edit 复制。需要精确复刻时必须显式走 <code>reconstruction_mode=same</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 主工作台增加明亮模式</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Style</span>
</header>
<div class="body">
<p><strong>问题:</strong>工作台已对齐登录页暗色质感,但长时间编辑分镜、产品图和首尾帧时,需要一个更亮的检查环境,方便看表格、缩略图和文案密度。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 顶部增加“明亮/暗色”切换按钮,使用 <code>localStorage["skg-board-theme"]</code> 记住用户选择;<code>globals.css</code> 增加 <code>skg-board-theme--light</code>,把暗场玻璃面板映射为暖白底、浅绿金色光感、深色文本和浅色边框,保留登录页同源的材质感。</p>
<p><strong>影响:</strong>只改变工作台视觉模式,不改变素材下载、音频解析、抽帧、主体模板、产品素材池、首尾帧或模型链路;<code>web/app/page.tsx</code> 同步移除旧全局浮动主题按钮,避免右下角出现第二套不相关的主题入口。后续新增图片/视频板块仍应复用同一套媒体悬停放大和删除逻辑。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 微软 ASR 强制模式</h3>
<span class="tag violet">API</span>
<span class="tag cyan">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>生产验收要求音频解析走微软 Azure OpenAI而不是远端失败后静默落到容器内 <code>faster-whisper</code> 或 Gemini 多模态兜底。</p>
<p><strong>改动:</strong><code>api/main.py</code> 增加 <code>ASR_LOCAL_FALLBACK_ENABLED</code><code>ASR_AUDIO_FALLBACK_ENABLED</code> 两个运行期开关;<code>GET /health</code> 回传这两个值。生产示例把 <code>ASR_BASE_URL</code> 固定为 <code>https://ai.skg.com/azure/v1</code><code>ASR_MODEL</code> 固定为 <code>gpt-4o-transcribe</code>,并关闭本地 / 多模态兜底。</p>
<p><strong>影响:</strong>微软 ASR 未部署或部署名不匹配时,音频步骤会直接暴露 Azure 的错误,例如 <code>DeploymentNotFound</code>;不会再用其它通道生成看似成功的逐句时间轴。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 独立 ASR 上传网关配置</h3>
<span class="tag violet">API</span>
<span class="tag cyan">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>生产只配置 <code>LLM_BASE_URL=https://ai.skg.com/ezlink/v1</code>,文本网关不一定提供 <code>/audio/transcriptions</code> 文件上传接口,导致音频文案步骤无法真实转写。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>ASR_BASE_URL</code> / <code>ASR_API_KEY</code> 和独立 ASR OpenAI client音频转写只通过该 client 上传 <code>audio.wav</code>,不再绑死 <code>LLM_BASE_URL</code><code>deploy/.env.production.example</code> 增加生产 ASR 网关示例。</p>
<p><strong>影响:</strong>文本/视觉模型仍走 <code>LLM_BASE_URL</code>,音频文件上传可单独切换到支持 Audio Transcriptions 的网关;<code>/health</code> 会回传 <code>asr_base_url</code> 供排障。</p>
<p><strong>补充:</strong>当云端音频网关不兼容 OpenAI Audio Transcriptions 时,可设置 <code>ASR_REMOTE_ENABLED=false</code>,直接使用容器内 <code>faster-whisper</code> 做 CPU 转写。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · ASR 客户端级超时硬化</h3>
<span class="tag violet">API</span>
<span class="tag cyan">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>云端全流程中远端 <code>whisper-1</code> 转录可能长期停在“转录中”,页面没有 transcript也不会进入后续分镜。</p>
<p><strong>改动:</strong><code>api/main.py</code> 的远端 ASR、Gemini 音频转写兜底和音频画像调用改为 <code>llm().with_options(timeout=ASR_TIMEOUT_SECONDS)</code> 后再发起请求,避免只在调用参数里传 <code>timeout</code> 时被兼容网关或 SDK 路径忽略。</p>
<p><strong>影响:</strong>远端 ASR 超时后应进入本机 ASR 或多模态音频兜底,并最终给出成功 transcript 或明确失败状态;不会无限占用“音频文案”步骤。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-19 · 云端 TikTok cookies 文件挂载</h3>
<span class="tag violet">Deploy</span>
<span class="tag cyan">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>生产环境没有桌面浏览器,不能依赖 <code>YTDLP_COOKIES_FROM_BROWSER</code> 读取本机 Chrome 登录态;受限 TikTok 链接仍会在云端返回“Log in for access”。</p>
<p><strong>改动:</strong><code>docker-compose.prod.yml</code> 将宿主机 <code>./secrets/tiktok_cookies.txt</code> 挂载到 API 容器 <code>/run/secrets/tiktok_cookies.txt</code><code>deploy/.env.production.example</code> 默认设置 <code>YTDLP_COOKIES_FILE=/run/secrets/tiktok_cookies.txt</code><code>.gitignore</code> 忽略 <code>secrets/</code>,避免 cookies 入库。</p>
<p><strong>影响:</strong>云端只需要维护一个服务器私有 cookies 文件,配置后可对失败素材执行“重新下载”;<code>yt-dlp</code> 会在任务结束时回写 cookies因此该挂载不能设为只读。cookies 过期时更换该文件并重启 API 容器即可。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · TK 受限视频支持 cookies 和失败重试</h3>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>部分 TikTok 链接会返回“Log in for access”后端已经接到任务并创建 job但裸 <code>yt-dlp</code> 下载没有登录态,任务只能失败;失败素材再次点击“开始分析”也没有重新触发下载。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>YTDLP_COOKIES_FILE</code> / <code>YTDLP_COOKIES_FROM_BROWSER</code><code>POST /jobs/{id}/download/retry</code><code>web/lib/api.ts</code> 新增失败文案格式化和 <code>retryJobDownload</code><code>web/app/page.tsx</code> 在失败素材上清掉自动触发标记,无 <code>video_url</code> 时重新下载,有 <code>video_url</code> 时重新跑音频和视觉路;<code>AdRecreationBoard</code> 按状态显示“重新下载 / 重新解析”。</p>
<p><strong>影响:</strong>用户无需删除素材再粘贴同一链接;配置 cookies 后可直接对原失败素材重新下载。cookies 属于敏感登录态,只能放本机或服务器私有路径。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 主工作台视觉质感对齐登录页</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Style</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页已经形成黑底、草场绿、SKG 金色和玻璃登录卡片的质感,但进入工作台后仍偏黑红/青色,前后像两个不同系统。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 根节点增加 <code>skg-board-theme</code>,顶部、流程条、素材列和主工作区使用 <code>skg-board-panel</code> / <code>skg-board-topbar</code><code>globals.css</code> 增加暗场径向光、细网格、金绿边框、玻璃阴影和金色 focus 状态。流程状态和主按钮从玫红/高饱和青色收敛到登录页同源的 SKG 金色与草绿色。</p>
<p><strong>影响:</strong>这次只改颜色、质感和系统感觉,不改变素材输入、抽帧、音频解析、主体模板、分镜规划等功能链路。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 相似主体区接入主体模板数据库</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>“相似主体 / 主体模板”之前把内置模板选择和本次生成结果混在同一块里;后端主体数据库可用后,前端需要真的支持“已有模板可复用、新生成主体可人工命名后入库”。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 拆成“主体模板库”和“本次生成 / 入库草稿”两块:左侧同时读取 <code>GET /subject-templates</code> 数据库模板和 <code>GET /character-library/skg</code> 内置形象;右侧可填写模板名称、备注,并通过 <code>POST /jobs/{id}/subject-templates</code> 把本次生成主体视图保存入库。<code>generateSubjectAssets</code> 新增 <code>subject_template_id</code>,可用数据库模板继续生成新的相似主体。</p>
<p><strong>影响:</strong>后续主体复用流程应理解为:先从数据库模板库选已有形象或用源视频关键帧生成新主体;新主体只有在人工确认、命名和备注后,才进入主体模板库供其他二创任务复用。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 去除参考帧池右侧已选重复栏</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>参考帧池和右侧“已选关键帧”展示同一批图,造成重复占位;后续主体生成本来就是读取 <code>selectedFrames</code>,不需要第二套图片列表。</p>
<p><strong>改动:</strong><code>SourceKeyframePicker</code> 删除右侧已选栏,把空间还给参考帧池。参考帧缩略图自身用勾选状态表示是否已选,顶部只显示“已选 X / 默认全部”的数量提示。</p>
<p><strong>影响:</strong>后续调用逻辑不变:有勾选就只传勾选帧,没有勾选就默认使用全部参考帧。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 关键帧移到逐句时间轴左侧</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>关键帧和已选关键帧放在源视频工作区下方,用户看字幕时间轴时还要上下跳,下面主体生成区也被关键帧池占掉太多空间。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 在逐句时间轴左侧新增 <code>SourceKeyframePicker</code>,把参考帧池、已选关键帧、自动抽帧 12 张、删除关键帧和 hover 放大都放到时间轴旁。<code>SourceReferenceBuildPanel</code> 去掉下方重复关键帧池,改成只展示“相似主体 / 主体模板”、内置形象选择、主体类型、统一方向和生成后的相似主体白底视图。</p>
<p><strong>影响:</strong>后续这里应理解为“上方完成源视频观看、字幕对齐和关键帧选择;下方只做主体模板和主体资产生成”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 放大原版视频并右侧显示已选关键帧</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>原版视频预览偏小,用户看人物动作和手动抽帧点不够直观;关键帧勾选后只在原网格里变状态,不方便确认实际会传给相似主体重构的帧。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 把原版竖屏视频列和播放器高度放大一档。<code>SourceReferenceBuildPanel</code> 在关键帧网格右侧新增“已选关键帧”窄列,点击左侧关键帧会同步出现在右侧,点击右侧缩略图可取消选择;没有选择时仍默认用全部关键帧。</p>
<p><strong>影响:</strong>后续描述这里时,可以把左侧理解为“全部参考帧池”,右侧理解为“本次主体重构将使用的已选帧”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 前端流程编号改为状态判定驱动</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>页面里的 01、02 只是硬编码视觉标签,用户无法判断编号代表什么流程,也看不出每一步是按什么条件通过、运行、阻塞或暂停。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 新增 <code>buildWorkflowSteps</code>,统一定义 01 素材输入、02 源视频下载、03 音频文案、04 抽帧参考、05 相似主体、06 产品素材池、07 分镜文案、08 画面首尾帧、09 视频候选。顶部新增 <code>WorkflowOrderBar</code> 展示顺序、状态和判定依据;素材输入、源视频区、四路状态条、产品素材池、分镜文案、画面规划和视频候选列共用 <code>WorkflowStepBadge</code> / <code>PipelineLane</code> 的同一编号。</p>
<p><strong>影响:</strong>以后说“改 03 音频文案”或“08 首尾帧怎么判定”时,可以直接定位到这套步骤配置;编号不再散落在 JSX 里。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 首尾帧按人物描述选择主体视角</h3>
<span class="tag rose">UI</span>
<span class="tag violet">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>首尾帧不能直接把全部主体视图和产品图丢给模型;每条视频真正需要先确定场景、人、产品和动作,再按需求选择人物参考视角。产品特写不需要人物时,也不能被后端默认强加透明骨架人。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 的每条分镜补充可编辑人物描述;没有描述时按分镜角色自动补统一相似主体说明。<code>selectSubjectRefsForRow</code> 会根据人物描述、镜头类型、首尾帧状态、佩戴/后颈/近景等关键词,从已生成的 6/10 张主体视图里自动选择最多 5 张最相关视角。后端 <code>generate_scene_asset</code> 只有收到主体图时才加人物身份约束;没有主体图时不会给产品特写硬塞透明骨架人。</p>
<p><strong>影响:</strong>后续描述需求时应把人物理解为“主体资产池里按镜头需求调度视角”,不是“每条首尾帧都传全部人物图”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 媒体素材交互收口为统一组件</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Contract</span>
</header>
<div class="body">
<p><strong>问题:</strong>每新增图片/视频板块都临时写缩略图,导致 hover 放大、删除按钮、预览浮层层级和图片完整显示规则反复不一致。</p>
<p><strong>改动:</strong>新增 <code>web/components/media-asset-tile.tsx</code> 作为项目内媒体素材基底组件,并把当前主路径的关键帧、相似主体图、产品素材图、首尾帧和历史视频候选接入同一套缩略图、顶层 hover 预览、删除/重生按钮和忙碌遮罩逻辑;<code>AGENTS.md</code><code>RULES.md</code> 同步新增媒体素材 UI Contract。</p>
<p><strong>影响:</strong>以后当前项目凡是图片、视频、抽帧、产品图、AI 生成图、首尾帧或视频候选,默认不能再手写孤立交互;只允许在统一组件参数上调整尺寸和显示内容。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 暂停直接视频提交,改为首尾帧闸门</h3>
<span class="tag rose">UI</span>
<span class="tag violet">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>分镜还没有真实首尾帧时直接一键提交视频,会把几十上百条视频任务一次性打到 Seedance成本不可控同时旧逻辑还会把关键帧继续当作后续视频参考偏离“关键帧只用于主体重构”的新流程。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 取消当前主路径里的“生成本条 · Seedance”和“一键提交全部”改成“保存本条规划 / 保存全部规划”。画面规划列新增 <code>EndpointFrameSlot</code> 首尾帧闸门,按行生成首帧和尾帧;<code>endpointAssetRef</code> 忽略旧的 <code>keyframe</code> 首尾帧,只认可 asset 类型首尾帧。<code>generateEndpointFrameForRow</code> 调用 <code>generateSceneAsset</code>,传相似主体白底视图和按行挑选的产品图。<code>web/app/page.tsx</code> 加暂停保护,旧视频回调误触也不会请求视频接口。</p>
<p><strong>影响:</strong>后续正确链路是“抽帧选主体 → 生成相似主体视图 → 上传/识别产品 → 写新口播和画面规划 → 逐条生成首尾帧 → 人工审核首尾帧 → 再开放单条视频生成”。历史视频候选仍展示,但不会继续自动追加。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 开始分析改为音频与视觉并行资产流</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>信息流复刻不应该等音频字幕完成后才抽帧;字幕是分镜文案前置条件,但主体参考帧和产品资产可以并行准备。旧体验让用户感觉流程线性且复杂。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code> 新增并行启动逻辑:视频下载完成后触发 <code>triggerTranscribe</code> 解析音频,同时触发 <code>analyzeJob</code> 自动抽 12 张参考帧。<code>api/main.py</code> 允许抽帧在音频转写状态下排队执行,并在音频路已运行时跳过重复拆音轨,避免并发写同一个 <code>audio.wav</code><code>AdRecreationBoard</code> 顶部新增音频文案路、视频视觉路、主体资产和产品资产状态条,素材输入按钮改成“开始分析”。</p>
<p><strong>影响:</strong>当时 <code>AudioStoryboardPlanPanel</code> 新增“一键提交全部”以验证批量提交思路;该视频提交入口已在后续“首尾帧闸门”改动中暂停,当前主流程只保存规划并先生成首尾帧。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 分镜画面规划加入首尾帧和人物/产品开关</h3>
<span class="tag rose">UI</span>
<span class="tag violet">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>“画面规划 / 产品融入”默认把每句都理解成产品 + 人物视频,导致痛点铺垫、场景过渡和产品特写无法按信息流广告真实节奏拆开。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 每行新增镜头类型、人物/产品开关、首帧规划、尾帧规划和产品出现方式;默认按角色把开场/痛点设为人物情绪、利益证明设为人物+产品、转化收口设为产品特写。<code>StoryboardScene</code> 新增 <code>visual_mode</code><code>needs_product</code><code>needs_subject</code><code>first_frame_plan</code><code>last_frame_plan</code><code>product_placement</code>,后端保存到 <code>state.json</code></p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>web/app/page.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>RULES.md</code><code>docs/source-analysis.html</code>。单条生成时,只有勾选产品才会自动选最多 6 张产品图;只有勾选人物才会传相似主体参考图,避免不需要产品/人物的镜头被硬塞错内容。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 生图网关增加显式代理和网络错误提示</h3>
<span class="tag violet">API</span>
<span class="tag amber">Ops</span>
</header>
<div class="body">
<p><strong>问题:</strong>相似主体高清视图包调用 <code>gpt-image-2</code> 时出现 <code>ConnectError: nodename nor servname provided</code>。排查确认 <code>IMAGE_BASE_URL</code> 配置正确、模型列表可访问,错误发生在连接层;本地 launchd 启动的 API 进程没有稳定继承交互 shell 的代理环境。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>AI_HTTP_PROXY</code> / <code>IMAGE_HTTP_PROXY</code> 支持OpenAI-compatible client 和所有图片 <code>/images/edits</code> / 图片 URL 下载统一走 <code>ai_http_client</code><code>/health</code> 新增 <code>models.ai_proxy_configured</code>,只显示是否配置代理,不暴露代理地址。图片 DNS、ConnectError、连接拒绝、网络不可达等传输层失败会归类为 503并提示检查本机网络或在 <code>api/.env</code> 配置代理后重启后端。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>deploy/.env.production.example</code><code>RULES.md</code><code>docs/source-analysis.html</code>。后续如果生图失败但模型名仍是 <code>gpt-image-2</code>,先区分 429 上游饱和、400 参数错误和 503 网络/代理错误,不要直接改 prompt 或换模型。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 相似主体升级为内置形象 + 肩颈高清视图包</h3>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>相似主体仍按“6 视图”理解,只覆盖全身角度;肩颈按摩设备需要更清晰的脖颈、肩线、后颈和上背特写。桌面已有 5 套策划好的透明骨架形象,但主工作表没有提供内置形象选择。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 在“相似主体白底视图”中加入 5 套内置形象选择,默认仍可走源视频相似,选择内置形象后把 <code>character_id</code> 传给 <code>generateSubjectAssets</code>。主体生成默认请求 10 张 <code>2048</code> 高清图,包含全身多视角、肩颈正/左右近景和后颈肩背特写。后端不再把主体多参考图拼成参考板,而是把内置形象图和关键帧作为独立 <code>image[]</code> 交给 <code>gpt-image-2</code>,并在 prompt 中明确“创新变体,不照抄”。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>RULES.md</code><code>docs/source-analysis.html</code>。后续描述这个功能时应叫“相似主体高清视图包”,不要再只说 6 视图。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 产品视角识别切到 gpt-image-2 并重做补图参考选择</h3>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>同一产品素材池的视角标注仍显示通用 Vision 模型;缺角度补图固定拿第一张产品图作为参考,少侧面或内侧时容易用错误视角硬推,生成结果偏离产品真实结构。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>PRODUCT_VIEW_MODEL=gpt-image-2</code><code>analyze_product_view</code> / <code>analyze_product_views_batch</code> 改用该模型并在 <code>/health</code> 返回 <code>models.product_view</code><code>generateProductAngleAsset</code> 前端请求新增 <code>source_refs</code><code>source_notes</code><code>AudioStoryboardPlanPanel</code> 按目标视角给产品图打分,优先真实上传图、相邻视角、用途标签、置信度和低风险图,最多传 6 张。后端把多张参考图作为独立 <code>image[]</code> 提交给 <code>gpt-image-2</code> 生成目标角度,避免只照抄第一张,也避免参考板被模型当成拼图格式。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>RULES.md</code><code>api/.env.example</code><code>deploy/.env.production.example</code><code>docs/source-analysis.html</code>。后续补产品角度必须从同一产品素材池里挑多张证据图,不要再默认第一张。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · gpt-image-2 图生图改用 edits 并处理上游饱和</h3>
<span class="tag violet">API</span>
<span class="tag amber">Reliability</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品补角度遇到 <code>gpt-image-2</code> 上游 429 saturated 时,后端只间隔 1 秒重试 3 次,很容易直接失败;相似主体重构还把参考图作为 JSON <code>image</code> 参数提交到 <code>/images/generations</code>,在 GPT 图片接口下会返回 <code>Unknown parameter: 'image'</code></p>
<p><strong>改动:</strong><code>_image_edit_call</code> 和旧分镜图生图入口改为调用 <code>/images/edits</code>,用 multipart <code>image</code> 文件传参考图,继续固定模型 <code>gpt-image-2</code>。图像调用新增 429 / saturated 识别和退避重试;产品补角度单次请求提高到 5 次尝试,耗尽后返回 503 和“上游负载饱和,请稍后重试”的可读错误。<code>web/lib/api.ts</code> 对产品补角度和主体资产接口解析 FastAPI <code>detail</code>,不再把整段 JSON 原样堆进 toast。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>docs/source-analysis.html</code>。后续生图仍然只走 <code>gpt-image-2</code>,但图生图必须走 edits 形态,不能再往 generations JSON 里传 <code>image</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 相似主体缩略图压缩并支持单张重生/删除</h3>
<span class="tag rose">UI</span>
<span class="tag violet">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>关键帧和相似主体白底视图缩略图仍偏大;主体 6 视图只能整组重生,某一张不准时无法单独替换;白底视图也缺少删除入口。竖版视频参考生成的主体图还需要明确保持竖版画布。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 把关键帧网格改成更小的自适应列,把白底主体视图改成 9:16 固定小缩略图;每个主体视图新增重生和删除按钮。前端只展示每个 view 的最新一张;单张重生调用 <code>generateSubjectAssets</code> 并传 <code>views=[当前 view]</code><code>replace_views=true</code>,不会追加成第 7 张。后端 <code>GenerateSubjectAssetsReq</code> 新增 <code>replace_views</code>,并新增 <code>DELETE /subject-assets/{asset_id}</code>;竖屏参考帧会把 9:16 竖版画布要求写入 prompt输出继续按源帧纵横比归一化。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>docs/source-analysis.html</code>。后续“重新生成某张主体图”应走同视角替换,不要追加多套六视图。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 生图模型统一锁定为 gpt-image-2</h3>
<span class="tag violet">API</span>
<span class="tag rose">UI</span>
<span class="tag amber">Config</span>
</header>
<div class="body">
<p><strong>问题:</strong>部分生图路径仍保留 <code>gpt-image-1.5</code> 或 Gemini 图片模型 fallback旧分镜生图区也还能选择 Gemini 模型;这会导致同一流程里不同图片入口走不同模型。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>GPT_IMAGE_MODEL</code><code>IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODELS</code> 全部锁定为 <code>gpt-image-2</code><code>_image_edit_call</code><code>_image_text_call</code><code>/frames/{idx}/generate</code> 即使收到旧前端 model 字段或旧 models 列表,也只使用 <code>gpt-image-2</code>。旧 Dashboard 生图选择器改成只展示 <code>gpt-image-2</code>,模型链路标注不再显示图片模型 fallback。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/components/ad-recreation-board.tsx</code><code>web/components/dashboard.tsx</code><code>api/.env.example</code><code>deploy/.env.production.example</code><code>RULES.md</code><code>docs/source-analysis.html</code>。该阶段凡是“生图”,包括清洗、元素提取、主体 6 视图、产品补角度、首尾帧/场景图和旧分镜生图,都只能走 <code>gpt-image-2</code>;后续在保持 <code>gpt-image-2</code> 主模型不变的前提下增加了 Gemini 故障兜底。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 主体 6 视图改为单图预览和单视角生成</h3>
<span class="tag rose">UI</span>
<span class="tag violet">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>相似主体 6 视图生成后只能点击打开原图,不能像参考帧和产品图一样鼠标停留看大图;同时后端单张生成 prompt 里仍提到 six-view pack容易让图片模型把多个视角拼进同一张图。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 给生成后的白底主体视图增加 <code>createPortal</code> 顶层悬停预览,缩略图保持小尺寸密排,停留时显示大图、视角标签和尺寸。<code>generateSubjectAssets</code> 把身份锁定说明改为“六视图属于同一主体,但每个输出文件只展示当前请求视角”,并为每次 view 生成加入单图规则,禁止 contact sheet、六视图拼图、多主体、多面板、标签或对比排版。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>api/main.py</code><code>docs/source-analysis.html</code>。后续用户说“6 视图”,应理解为 6 个独立图片文件,而不是一张六宫格大图。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 模型链路拆分为 GPT 生图、Azure 语音和 Seedance 视频</h3>
<span class="tag violet">API</span>
<span class="tag cyan">Workflow</span>
<span class="tag amber">Config</span>
</header>
<div class="body">
<p><strong>问题:</strong>之前图片、文本、音频分析共用 <code>LLM_BASE_URL</code>,语音通道不够清晰,视频虽然已接豆包/Seedance但模型标注没有把“生图 GPT / 语音 Azure / 视频 Seedance”三条高优先级链路清楚拆开。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>IMAGE_BASE_URL</code><code>IMAGE_API_KEY</code><code>VOICE_PROVIDER</code><code>AZURE_OPENAI_BASE_URL</code><code>AZURE_OPENAI_API_KEY</code><code>AZURE_TTS_MODEL</code> 等配置;图片调用改走图片专用 OpenAI-compatible client文字生图走 <code>/images/generations</code>,图生图后续已收敛到 <code>/images/edits</code>,默认 <code>gpt-image-2</code>TTS 新增 Azure OpenAI 协议 <code>/audio/speech</code> 通道,默认 <code>VOICE_PROVIDER=azure_openai</code><code>GET /health</code> 回传图片、主体、语音和视频的实际模型与 base URL 供前端模型标注使用。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>RULES.md</code><code>.project.json</code><code>docs/source-analysis.html</code>。真实 key 仍只写本地 <code>api/.env</code> / 生产环境变量,不能入库。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-18 · 6 视图改用 GPT 图片模型并修复前端展示</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag violet">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>“相似透明骨架主体”实际已经在后端生成并写入 job但前端的相似主体识别只匹配“相似主体 / similar subject”等窄名称无法命中“相似透明骨架主体 / similar transparent skeleton humanoid subject”所以界面仍显示 <code>0/6</code>,重复点击还会新增重复元素。同时 6 视图模型链路仍跟随通用 <code>IMAGE_MODEL</code>,默认显示 Gemini 图片模型,不符合主体资产必须走 GPT 图片模型的要求。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>GPT_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODEL</code><code>SUBJECT_ASSET_IMAGE_MODELS</code>,当时把 <code>generateSubjectAssets</code> 切到 GPT 图片链路;后续已进一步收敛为所有生图入口都只使用 <code>gpt-image-2</code><code>GET /health</code> 返回 <code>subject_image</code><code>subject_image_fallbacks</code><code>web/components/ad-recreation-board.tsx</code> 扩大相似主体元素匹配规则,并在优先帧找不到时回退到全局关键帧里查找已生成主体资产,避免图片被当前选择集隐藏;模型标注改为显示主体专用 GPT 图片链路。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续主体 6 视图不应复用通用补图模型链,必须走主体资产专用 GPT 图片模型配置。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 模型调用入口增加模型名和链路说明</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag violet">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>音频解析、产品识别、补图、6 视图、脚本改写和生视频都会调用模型,但界面没有明确显示当前使用哪个模型,也看不到调用链路和回退逻辑。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>GET /health</code> 扩展返回 <code>local_asr</code><code>vision</code><code>image</code><code>image_fallbacks</code><code>web/lib/api.ts</code> 新增 <code>RuntimeHealth</code><code>RuntimeModels</code><code>getRuntimeHealth</code><code>web/components/ad-recreation-board.tsx</code> 新增 <code>ModelTrace</code>,在主路径每个模型动作旁直接显示模型名,点击后弹出小窗口显示逻辑链路。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续任何新增模型动作都必须同时接入 <code>ModelTrace</code>,不要只在代码里隐藏模型名。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 6 视图改为相似主体并默认使用全部关键帧</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag violet">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>“生成 6 视图”只有在用户先手动勾选关键帧后才可点击;但当前流程里自动抽出的 12 张帧本身就是相似主体参考池。生成提示也偏普通广告演员,没有锁定透明骨架人这类主体的透明皮肤、可见骨架和统一身份。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 改为有关键帧即可生成:未勾选时默认使用全部关键帧,勾选后只用已选帧;生成区新增“透明骨架 / 普通真人”主体类型和统一方向输入。<code>generateSubjectAssets</code> 后端提示词新增 identity lock要求六视图先锁定同一个主体设定再保持同一性别表现、年龄段、体型、材质、风格和视觉身份透明骨架模式必须保持透明皮肤包裹白色骨架且排除普通真人、纯骷髅和恐怖/医疗解剖风。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>api/main.py</code><code>docs/source-analysis.html</code>。后续这块不要再理解成“抽帧后复制人物”,而是“抽帧提供主体证据,再生成统一的新相似主体六视图”。</p>
</div>
</article>
<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>顶部“音频文案依据 / 当前步骤 / 音频解析结果”仍然抢占主操作空间;左侧素材列重复放横版原视频预览,竖版视频会产生大黑边;“自动抽帧”入口不够显眼,用户会误以为能力被去掉。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 把状态压成紧凑标签,文案依据保持默认折叠;<code>MaterialColumn</code> 不再重复显示原视频预览;<code>AudioIntakePanel</code> 把音频解析结果改成默认折叠,主区域改为左侧 9:16 竖版播放器、右侧音频波形 + 逐句时间轴,播放器内覆盖“当前点抽帧”;<code>SourceReferenceBuildPanel</code> 保留并强化“自动抽帧 12 张”主按钮,缩略图更小更密,下面再放相似主角 6 视图。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续源视频分析先看竖版播放器、波形和逐句时间轴;参考帧池负责自动 12 帧、手动补帧后的删除和相似主角选择。</p>
</div>
</article>
<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>参考帧区占在原视频和逐句时间轴中间,导致视频和字幕联动不直观;抽帧缩略图仍偏大,一屏可比较的帧数少;“生成 6 视图”和抽参考按钮平齐,语义上像同一级操作。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 改成上层“原版视频 + 逐句时间轴”左右排列,下层 <code>SourceReferenceBuildPanel</code> 横向铺开参考帧;时间轴侧栏改成时间 + 原文/中文双行,减少宽度占用;参考帧缩略图用 6/8/12 列响应式排列;“生成 6 视图”移动到相似主角白底视图区。<code>AudioWaveform</code> 新增鼠标悬停时间回传,标题栏显示当前播放、总时长和指针停点秒数。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续查看原片时先看视频和右侧逐句时间轴;参考帧池是下一行横向候选区。</p>
</div>
</article>
<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>“当前点抽帧”放在关键帧池顶部不符合操作路径;抽出来的竖版画面被小缩略图裁切,用户无法快速判断整张帧是否适合做主角/动作参考。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 把“当前点抽帧”移动到原版视频标题栏右侧,并扩大原视频、参考帧和时间轴这组区域高度;<code>SourceReferenceBuildPanel</code> 只保留批量抽参考、生成 6 视图和删除,缩略图改为 <code>aspect-[9/16]</code> + <code>object-contain</code> 完整显示,鼠标停留时用 <code>createPortal</code> 在最上层展示放大完整帧。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续需要补某一秒画面时从视频播放器操作;需要判断帧内容时直接悬停缩略图看大图。</p>
</div>
</article>
<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>上一版把原视频旁的自动抽帧从动作峰值改成人物主体后,结果更偏中心清晰画面,少了之前信息流广告需要的手势、表情、节奏点和镜头变化。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 的按钮改为“抽参考 12 帧”,调用 <code>analyzeJob</code> 时恢复使用 <code>target=motion</code><code>quality=accurate</code><code>mode=replace</code>。人物仍然是后续相似主角重构的选择对象,但自动抽帧不再只按清晰主体排序。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续这里的自动 12 帧应理解为“广告动作/节奏参考帧池”;若某个具体人物画面没抽到,用“当前点抽帧”手动补。</p>
</div>
</article>
<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>原版视频区域偏小,只能看和播放;如果需要补某个具体画面,用户必须重新跑 12 帧抽取,并且抽错后缺少直接删除入口。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 放大原视频列和视频高度,并把逐句时间轴压成较窄侧栏;当时 <code>SourceReferenceBuildPanel</code> 接收当前播放秒数并提供“当前点抽帧”,后续已移动回原视频标题栏;关键帧缩略图右下角新增删除按钮,复用 <code>deleteFrame</code>,并展示全部已抽帧而不只截取前 12 张。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续描述这里时,应把原版视频旁的关键帧区理解为“广告参考帧池”,支持自动抽参考帧、按播放点补帧和单帧删除。</p>
</div>
</article>
<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>原版视频旁的主动抽帧仍按动作变化优先,不符合当前“主体是人物”的相似主角流程;逐句时间轴侧栏也占用过多横向空间,挤压原视频和关键帧区。</p>
<p><strong>改动:</strong><code>SourceReferenceBuildPanel</code> 的“抽取 12 帧”改为“抽人物 12 帧”,调用 <code>analyzeJob</code> 时使用 <code>target=subject</code><code>quality=accurate</code><code>mode=replace</code><code>AudioIntakePanel</code> 的三列布局把逐句时间轴固定成较窄侧栏,把空间让给原视频和关键帧 / 相似主角区域。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续这里的 12 张关键帧默认是人物主体参考,不再按纯动作峰值理解。</p>
</div>
</article>
<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>分镜行前面的“分镜 / 原内容”信息量很少,却占了过多横向空间;右侧 6 个视频候选槽被压成拥挤的 2x3 小格。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 的行 grid 改为前两列固定窄列,宽屏下视频列固定放大;<code>StoryboardVideoSlots</code> 从 3 列 2 行改为一排 6 个竖版候选预览。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续这个区域按“工作表高密度排版”处理,低信息字段不再占大列宽。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 产品素材池持久化到 job</h3>
<span class="tag blue">API</span>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品图文件虽然已写入 <code>jobs/&lt;jobId&gt;/assets</code>,但产品素材池列表、识别结果、备注和 AI 补图只存在前端 React state代码热更新、刷新或切换任务后会清空用户需要重新上传和重新计算。</p>
<p><strong>改动:</strong><code>Job</code> 新增 <code>product_refs</code>;后端新增 <code>PUT /jobs/{id}/product-refs</code>,前端新增 <code>saveProductRefs</code><code>AudioStoryboardPlanPanel</code> 在上传、识别、补图、备注编辑和删除时同步保存产品素材池,组件重新挂载时从 <code>job.product_refs</code> 恢复。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续更新 UI 或重启本地服务不应再清掉用户已上传的产品图和标注。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 参考帧改为原视频旁全局关键帧与相似主角</h3>
<span class="tag blue">API</span>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>分镜行里的“参考帧 / 关键元素”会把抽帧、人物参考和单条生成混在每一行里,占空间,也容易让用户误解为要逐行抽取并复刻源视频人物。</p>
<p><strong>改动:</strong><code>AudioIntakePanel</code> 在原版视频旁新增 <code>SourceReferenceBuildPanel</code>:一键重新抽取 12 张关键帧,人工选择主角参考后生成 6 张白底相似主角视图;<code>AudioStoryboardPlanPanel</code> 移除每行参考帧/关键元素列,生成视频时按全局关键帧池匹配当前句时间点。<code>generateSubjectAssets</code> 新增 <code>subject_style=source_actor</code><code>reconstruction_mode=similar</code>,用于生成类似但不复刻的正常广告演员。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。旧透明骨架人主体资产仍保留默认模式;信息流主流程的相似主角不走透明骨架人 prompt也不做像素提取或精确身份复刻。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 分镜脚本支持单段和整片 AI 改写</h3>
<span class="tag blue">API</span>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>“原内容 / 新口播文案”两列占用空间偏多,并且新口播只是固定模板,不能按作者想法或原片节奏继续改写;单条生成前也无法微调文案。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 在脚本区上方新增作者想法输入、整片改写和还原初稿;每行新口播改成可编辑 <code>textarea</code>,并提供“单段改写”。新增 <code>POST /jobs/{id}/script/rewrite</code> 和前端 <code>rewriteStoryboardScript</code>,后端根据原参考文案、当前新文案、分镜角色、时间段和作者想法返回中文口播。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。当前改写结果是页面级脚本状态,保存规划或生成首尾帧时会写入对应 <code>StoryboardScene.action</code>;后续如果要刷新后保留,应新增 job 级脚本草稿持久化字段。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 产品图识别加入肩颈按摩仪坐标系</h3>
<span class="tag blue">API</span>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>旧识别只要求模型在“正面/左45/右45/侧厚/触点/背底”里选一类,没有说明这是套在脖子上的 U 形肩颈按摩仪,也没有定义佩戴者左/右、上/下、贴颈内侧和外壳外侧,容易把图片左右当成产品左右,后续生视频会把局部细节和方向搞乱。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>analyzeProductViews</code> 改为同一产品多图批量识别prompt 固定产品为挂脖肩颈按摩仪,并新增 <code>orientation</code><code>landmarks</code> 输出;单图兜底 prompt 也同步要求产品坐标系。<code>web/lib/api.ts</code> 扩展 <code>ProductViewAnalysisItem</code><code>ProductReferenceCard</code> 展示“方向已识别”和结构点,悬停预览显示方向、结构和风险。</p>
<p><strong>影响:</strong>首尾帧生成和后续单条视频生成时,<code>buildStoryboardSceneFromAudioRow</code> 会把“左/右按佩戴者身体左右,上/下按佩戴方向,内侧=贴颈触点,外侧=外壳按键”作为硬规则写入产品提示,并把每张选中产品图的方向和结构点一并交给模型。</p>
</div>
</article>
<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>产品素材池底部图片悬停时,放大预览仍在滚动容器内部,容易被产品区或分镜框架裁掉;预览尺寸也偏小,不利于检查肩颈产品厚度、触点和左右差异。</p>
<p><strong>改动:</strong><code>ProductReferenceCard</code> 将悬停预览从卡片内绝对定位改为 <code>createPortal(document.body)</code> 渲染的固定浮层,宽度提升到最多 380px并根据鼠标位置自动避开视口底部和右侧。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续产品图检查应继续使用该顶层预览,避免把预览重新放回带 <code>overflow</code> 的滚动容器里。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 产品图上传统一生成 AI 工作副本</h3>
<span class="tag blue">API</span>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>同事容易把“上传最高清”理解为最好,但超大原图会拖慢上传、识别和后续生图/生视频,而且并不会让模型更稳定;低分辨率图也需要被标出来,避免后面误以为细节可靠。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>normalize_product_asset_image</code>,产品图上传和产品图库复制都会生成统一 AI 工作副本:最长边 1600px、JPEG 92推荐原图长边 1200-2000px、短边至少 600px。超大图自动压缩低分辨率图自动放大并标风险透明底铺白黑底/白底背景本身不强行转换。接口返回 <code>ImageRef.asset_meta</code> 记录原尺寸、工作图尺寸、自动动作和警告。</p>
<p><strong>影响:</strong><code>web/lib/api.ts</code> 增加 <code>asset_meta</code> 类型;<code>ProductReferenceCard</code> 展示工作图尺寸、“已标准化”和“需留意”标签,悬停预览里显示规格风险。后续视频/图片生成应优先使用该工作副本,不把超高清原图直接交给模型。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 同一产品素材池增加生视频选图标注</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品图识别不能只知道“正面/侧面”,下一步生视频需要知道哪些图适合作为主外观、佩戴比例、触点、厚度、按键或材质参考;同时用户已确认上传图属于同一产品,不需要做不同产品身份识别。</p>
<p><strong>改动:</strong><code>POST /jobs/{id}/assets/product-views/analyze</code> 扩展返回 <code>background</code><code>use_tags</code><code>risk</code>。前端 <code>ProductReferenceCard</code> 展示背景、用途标签和风险标记;<code>selectProductItemsForRow</code> 从单纯按视角轮转改为按分镜角色、视角优先级、用途标签、置信度和风险评分,自动挑选最多 6 张产品参考图。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续接入图库时,图库只负责确定同一产品身份;这里继续负责该产品多图的角度、背景、用途和生视频风险标注。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 产品视角识别容错解析</h3>
<span class="tag blue">API</span>
<span class="tag cyan">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>视觉模型有时能判断视角,但返回的 JSON 含换行、引号或尾部格式问题,后端直接 <code>json.loads</code> 会失败,导致整张图被标成“识别失败”。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增 <code>parse_product_view_response</code>:先按严格 JSON 解析,失败后从原始输出里容错提取 <code>view</code><code>note</code><code>confidence</code>。同时收紧产品视角识别 prompt要求模型输出单行 JSON。</p>
<p><strong>影响:</strong><code>POST /jobs/{id}/assets/product-views/analyze</code> 在模型输出不完全规范时也能保留视角结果,减少无意义 fallback真正无法识别时才按上传顺序兜底。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 产品素材池取消数量上限,单条生成自动选图</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品白底图不能固定最多 6 张。后续分镜很多,产品参考图应作为可持续扩充的素材池;但单条视频生成时也不能把素材池里的全部图片都提交给 AI。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 移除上传数量上限,产品参考区改为不限量素材池,并用滚动网格承载更多图片。<code>analyzeProductViews</code> 和后端 <code>POST /jobs/{id}/assets/product-views/analyze</code> 不再只处理前 6 张;自动补缺失视角也不再因为已有 6 张素材而停止。新增 <code>selectProductItemsForRow</code>,单条分镜生成时按分镜角色、视角优先级和行号轮转,从素材池中自动挑选最多 6 张相关产品图写入 <code>StoryboardScene.product_images</code></p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续需求应区分“产品素材池可无限上传”和“单条生成实际提交的产品参考图集合”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 产品图上传后自动识别视角并补齐缺角度</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品图上传后还让用户手动选择正面、45 度、侧面等视角,操作成本高,也容易把后续生视频的产品结构约束标错;缺失角度也不应该再让用户逐个判断后点击。</p>
<p><strong>改动:</strong>新增 <code>POST /jobs/{id}/assets/product-views/analyze</code> 和前端 <code>analyzeProductViews</code><code>AudioStoryboardPlanPanel</code> 在上传产品白底图后自动识别每张图的视角、写入中文备注和置信度,再自动调用 <code>generateProductAngleAsset</code> 补齐缺失视角。<code>ProductReferenceCard</code> 移除视角下拉,改为只读“自动识别/自动补图”标签,用户只检查备注;<code>MissingProductViewSlot</code> 只作为自动补图失败后的重试入口。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续描述需求时应说“自动识别/补图后的备注是否准确”,不要再按“手选产品视角”理解这个区域。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 产品白底图加入视角备注和 AI 补角度</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Workflow</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>只上传产品白底图还不够,生成视频前需要明确每张图是什么视角、有哪些结构重点;如果某些视角没拍到,也需要通过 AI 先补出可用参考图。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 的产品参考区升级为产品视角工作台每张图都有视角下拉和备注输入鼠标悬停显示放大预览缺失视角显示独立占位并提供“AI 补角度”。新增 <code>POST /jobs/{id}/assets/product-angle</code> 和前端 <code>generateProductAngleAsset</code>,基于已有产品图生成缺失的白底产品视角。单条视频生成会把每张产品图的视角备注写入 <code>StoryboardScene.product</code>,用于约束左右非对称、厚度、内侧触点和肩颈佩戴比例。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。AI 补角度生成的新图保存在 <code>jobs/&lt;jobId&gt;/assets</code>,作为普通 <code>ImageRef(kind="asset")</code> 参与后续分镜视频生成。</p>
</div>
</article>
<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>分镜行前半段占用横向空间偏多,右侧视频候选位太少;同时肩颈产品不是完全对称结构,只靠默认产品图容易生成左右一致、比例不准或佩戴尺寸跑偏。</p>
<p><strong>改动:</strong><code>AudioStoryboardPlanPanel</code> 顶部新增产品白底图上传条,建议上传 5 张、最多 6 张:正面、左 45、右 45、侧面厚度、内侧触点/佩戴比例,非对称明显时补背面或底部。分镜行前几列压缩宽度、字号和缩略图高度;右侧视频区改为 6 个候选槽,便于多次生成和筛选。生成本条时会把上传的产品图写入 <code>StoryboardScene.product_images</code>,并把“保留左右非对称、肩颈真实比例”加入产品融合提示。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。产品白底图当前作为当前页面会话内的复刻参考,单条生成时持久化进对应关键帧分镜;后续如果要跨刷新保留全局产品图组,应新增 job 级产品参考字段。</p>
</div>
</article>
<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>信息流复刻不应该先全量抽帧和提元素,也不能空写分镜;主线应先按音频内容规划产品分镜,再按每条分镜定向抽参考帧,并把生成出的视频回挂到对应分镜。</p>
<p><strong>改动:</strong><code>web/components/ad-recreation-board.tsx</code> 新增 <code>AudioStoryboardPlanPanel</code>:从 <code>job.transcript</code> 生成纵向分镜行,每行内部从左到右展示时间/结构、原内容、新口播文案、画面规划/产品融入、参考帧/关键元素和生成视频。每行“抽参考帧”调用现有手动加帧接口,在对应时间段中点抽取原视频参考帧;“生成本条”会把该行规划保存到对应关键帧分镜,并复用现有 <code>onGenerateVideo</code> 提交 Seedance 候选,候选视频按 <code>frame_idx</code> 回显在该行右侧。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。当前不新增后端字段,继续复用 <code>KeyFrame.storyboard</code><code>GeneratedVideo.frame_idx</code>;后续模型改写、关键元素 6 视图和完整合成可以在该分镜行内继续接入。</p>
</div>
</article>
<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>波形上的播放线只跟随 <code>video.timeupdate</code> 刷新,浏览器触发频率较低,播放时会一顿一顿。</p>
<p><strong>改动:</strong><code>web/components/ad-recreation-board.tsx</code> 在原视频播放期间启用 <code>requestAnimationFrame</code> 同步 <code>currentTime</code>,暂停、结束和卸载时停止帧循环;波形 SVG 点位用 <code>useMemo</code> 缓存,避免播放时重复计算整条包络。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续调整波形交互时,播放线应继续走帧同步,不要只依赖 <code>timeupdate</code></p>
</div>
</article>
<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>只有逐句时间轴难以判断原视频节奏、停顿和爆点位置;普通柱状条也看不出高低音变化,无法辅助判断口播抬升、下沉和爆点。</p>
<p><strong>改动:</strong><code>web/components/ad-recreation-board.tsx</code> 在音频解析结果面板中加入原视频播放器和横向音频波形,布局为左侧原视频、右侧逐句时间轴、底部连续响度波形。视频播放时根据 <code>currentTime</code> 高亮并自动滚动当前字幕行;点击音频波形或字幕行会跳转原视频时间。音频波形只从真实 <code>audio.wav</code> 解码生成,用连续灰色包络显示响度和停顿;解码前显示加载状态,失败时明确提示,不再用假波形兜底。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续如果要做爆点标记,应基于当前连续响度波形和字幕时间轴继续加 marker而不是恢复底部音频条。</p>
</div>
</article>
<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>音频解析第一步顶部“音频文案依据”和主区域“原文案提取”同时展示全文,和逐句时间轴重复;逐句表格、讲话人/节奏/背景音分析也默认占用太多工作台版面。</p>
<p><strong>改动:</strong><code>web/components/ad-recreation-board.tsx</code> 将顶部“音频文案依据”改为默认折叠的 <code>details</code>;移除主面板里的“原文案提取”大卡片,不再把逐句时间轴和声音/背景音拆成两个并列区域;改为一个统一音频解析结果面板,顶部放声音/节奏/背景音 3 个短摘要,下面放紧凑可滚动逐句时间轴。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>docs/source-analysis.html</code>。后续如果需要全文原文案,只展开“音频文案依据”;如果需要查看完整分析文本,用悬停或后续扩展详情入口,不要恢复独立大卡片。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 修复逐句时间轴假字幕</h3>
<span class="tag orange">Bugfix</span>
<span class="tag cyan">ASR</span>
</header>
<div class="body">
<p><strong>问题:</strong>当前 SKG 网关模型列表没有真实 <code>/audio/transcriptions</code> ASR 模型,<code>whisper-1</code> 原始调用返回 404后续 <code>gemini-2.5-flash</code> 多模态 fallback 实际不能听到音频,却返回了 1 秒一段的重复假字幕,导致“逐句时间轴”乱掉。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增本机 <code>mlx_whisper</code> ASR fallback并让它复用可用静态 <code>ffmpeg</code>,避免 Homebrew <code>ffmpeg</code> 动态库损坏。新增 ASR 质量校验:重复文本、逐秒合成时间轴、音频覆盖率过低的结果会被拒绝;转写失败时不再复用旧 transcript 或写入假时间轴。当前任务 <code>ec90faadad27</code> 已重新解析为 25 段真实时间轴。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>RULES.md</code><code>docs/source-analysis.html</code>。后续部署机器需要安装或配置可用 <code>LOCAL_ASR_BIN</code>;否则远端 ASR 不可用时会明确失败,而不是展示假逐句。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-17 · 修复素材输入元数据解析并移除底部音频条</h3>
<span class="tag rose">UI</span>
<span class="tag orange">Bugfix</span>
</header>
<div class="body">
<p><strong>问题:</strong>粘贴 TK 链接后视频已经下载到 <code>source.mp4</code>,但本机 Homebrew <code>ffprobe</code>/<code>ffmpeg</code> 因缺少 <code>libx265.215.dylib</code> 直接崩溃,后端误显示为“下载失败”。同时用户不再需要底部音频展示。</p>
<p><strong>改动:</strong><code>api/main.py</code> 新增媒体二进制选择逻辑,先验证 PATH 里的 <code>ffmpeg/ffprobe</code> 是否可执行,失败时回退到本机静态 <code>ffmpeg</code>;没有可用 <code>ffprobe</code> 时用 <code>ffmpeg -i</code> 解析时长和分辨率。下载阶段把“元数据解析失败”和“下载失败”区分开。ASR 请求增加 <code>ASR_TIMEOUT_SECONDS</code>,后续补充本机 <code>mlx_whisper</code> fallback 后不再直接信任不可听的多模态假字幕。<code>web/app/page.tsx</code> 不再导入和渲染 <code>AudioStrip</code><code>AdRecreationBoard</code> 移除“打开音轨”按钮。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/app/page.tsx</code><code>web/components/ad-recreation-board.tsx</code><code>RULES.md</code><code>docs/source-analysis.html</code>。后续音频预览如果需要恢复,应先明确是否仍放在右侧工作表,而不是默认恢复底部浮层。</p>
</div>
</article>
<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>用户明确否定前一版“开始后自动抽帧、分镜、元素、合成”的推进方式,当前只需要把信息流广告快速复刻的第一步跑通:粘贴 TK 链接或上传视频,下载源视频,提取原音频文案,并分析讲话人、节奏和背景音。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code> 的开始流程改为下载完成后只自动触发 <code>triggerTranscribe</code>;上传视频也加入同一音频解析队列。<code>AdRecreationBoard</code> 主渲染改成左侧素材输入 + 右侧音频解析工作表,不再显示“追加分镜”“开始抽帧”“生成全部视频”。<code>AudioStrip</code> 右侧改为原文案/翻译/声音背景音分析。<code>AudioScript</code> 新增 <code>background_audio_profile</code>,后端 <code>pipeline_transcribe</code> 先保存原文案、中文翻译、讲话人、节奏和背景音分析,当前第一步不默认生成 SKG 新口播或 TTS 配音。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/ad-recreation-board.tsx</code><code>web/components/audio-strip.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>RULES.md</code><code>.project.json</code><code>docs/source-analysis.html</code>。后续需求应先描述“音频解析完成后是否进入下一步”,不要默认把抽帧/分镜/合成塞进开始动作。</p>
</div>
</article>
<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>已被上一条“收窄为第一步音频解析”覆盖;保留此记录用于解释旧代码和历史提交为什么存在。</p>
<p><strong>问题:</strong>用户希望粘贴视频链接后点击一次“开始”,系统就自动完成素材准备:抽帧、音频分析、关键元素扫描和分镜初稿;人工只负责判断规划是否合理、选择关键元素、再单条或全量生成视频。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code> 新增开始生产编排状态:创建/激活 job 后,下载完成自动触发 <code>analyzeJob</code><code>triggerTranscribe</code>,关键帧出来后逐帧调用 <code>describeFrame</code> 并用 <code>updateStoryboard</code> 保存分镜初稿。视频生成时若分镜未显式选择产品图,会自动复制四张 <code>desktop-skg-product-angle-01..04</code> 作为 SKG 产品真源,并把已生成的关键元素 6 视图作为主体参考。<code>AdRecreationBoard</code> 把导入按钮改为“开始”,分镜卡里的候选元素可点击生成提取图 + 6 视图,顶部新增“生成全部视频”。<code>AudioScript</code> 新增 <code>speaker_profile</code><code>rhythm_profile</code>,用于展示讲话人 / 节奏参考。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/ad-recreation-board.tsx</code><code>web/components/nodes/index.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>RULES.md</code><code>.project.json</code><code>docs/source-analysis.html</code>。后续需求应区分“开始生产自动编排”和“人工审核/选择/生成”的边界。</p>
</div>
</article>
<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>四列横向生产流仍把音频文案、关键元素和视频生成拆开,用户真正需要的是每个分镜内部上下游一一对应,快速复刻信息流广告。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 改为左侧素材输入列 + 右侧单一分镜生产板块。右侧顶部保留音频解析、打开音轨、追加分镜和抽帧控制;下方每张 <code>StoryboardSegmentCard</code> 从上到下依次包含音频分镜文案、每个分镜需要的关键元素、视频生成候选。新增 <code>DraftSegmentCard</code> 支持先无限追加草稿分镜,再绑定关键帧后保存和生成候选视频。完整视频合成入口保留在底部,后端合成接口仍待接入。本地开发态隐藏 <code>nextjs-portal</code>,避免 Next Issue 浮层遮挡看板截图。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>web/app/globals.css</code><code>RULES.md</code><code>.project.json</code><code>docs/source-analysis.html</code>。后续需求应按“素材输入列 + 分镜生产板块 + 分镜卡片三层结构”描述。</p>
</div>
</article>
<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>左侧抽屉式看板仍像单点操作面板,不符合用户要的“从左往右推进”的广告生产逻辑。</p>
<p><strong>改动:</strong><code>AdRecreationBoard</code> 改为全屏横向四列:素材输入列按文件任务管理素材;音频解析 / 新分镜文案列把分镜从上到下排列;视频关键元素 / 抽帧生成列把关键帧横向排列,并在每张帧卡上直接生成元素和候选片段;视频合成列汇总音频、候选片段和待接入合成接口。<code>page.tsx</code> 不再渲染右侧空白 ReactFlow 画布。</p>
<p><strong>影响:</strong><code>web/components/ad-recreation-board.tsx</code><code>web/app/page.tsx</code><code>RULES.md</code><code>.project.json</code><code>docs/source-analysis.html</code>。该方向已被上方“单一分镜生产板块”取代,保留为历史记录。</p>
</div>
</article>
<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 无限画布,画布节点和边暂不展示;<code>web/next.config.mjs</code> 清理 Next 16 已不支持的 <code>eslint</code> 配置,避免本地 dev 左下角 Issue 提示遮挡界面。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/ad-recreation-board.tsx</code><code>web/next.config.mjs</code><code>RULES.md</code><code>.project.json</code><code>docs/source-analysis.html</code>。后续需求应优先描述“左侧看板里的哪一段工作表”和“右侧空白画布未来要承载什么”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-16 · 登录页 Logo 静态资源公开访问</h3>
<span class="tag rose">UI</span>
<span class="tag cyan">Deploy</span>
</header>
<div class="body">
<p><strong>问题:</strong>生产登录页引用 <code>/skg-logo-black.svg</code>,但 Nginx 只公开了 <code>/login/</code><code>/_next/</code><code>/assets/</code><code>/oasis-source/</code>,未登录访问该 SVG 会被重定向到 <code>/login/</code>,导致 logo 不显示。</p>
<p><strong>改动:</strong><code>deploy/nginx.conf</code> 新增 <code>location = /skg-logo-black.svg</code>,把官网 SKG 字标作为登录页必需静态资源公开;<code>RULES.md</code> 和本文档同步更新部署事实。</p>
<p><strong>影响:</strong><code>deploy/nginx.conf</code><code>RULES.md</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-16 · 登录页品牌文字和 Logo 水平对齐</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>SKG logo 放大后,右侧“营销内容工作台”的视觉水平线和 logo 内“未来健康”中文部分略有错位。</p>
<p><strong>改动:</strong><code>web/app/globals.css</code> 将系统标识连同分隔线整体下移,桌面端和移动端分别使用不同偏移,保持 logo 尺寸不变。</p>
<p><strong>影响:</strong><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-16 · 登录页系统标识改中文并放大 Logo</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页用户主要是华人,左上角系统标识继续使用英文 <code>Marketing Studio</code> 不够直接SKG logo 在当前画面中略小,分隔线和文字需要更稳地水平对齐。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 将系统标识改为“营销内容工作台”;<code>web/app/globals.css</code> 放大桌面和移动端 logo调整品牌组间距、分隔线高度、中文字号和字距。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录右侧表单下移并收紧按钮间距</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>错误感叹号去掉后,保持登录和提交按钮之间仍保留了旧状态区空位;右侧表单整体也显得偏高。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 只在成功态渲染状态图标,不再为空闲/错误态保留固定高度;<code>web/app/globals.css</code> 将桌面端右侧表单继续下移到更贴近截图反馈的位置,移动端保持原排列。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录错误状态去掉感叹号</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>密码输入错误时,保持登录和按钮之间会出现一个感叹号图标,视觉上干扰登录表单。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 删除错误态 <code>AlertCircle</code> 图标渲染;错误反馈仍由动态角色的 error 状态承担,成功态勾选图标保留。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页补系统标识和保持登录</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>只放 SKG 字标还缺少具体系统识别;保持会话复选框没有文字说明;表单上方单独的小锁图标占位多余。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 在 SKG 字标旁加入 <code>Marketing Studio</code> 系统标识,复选框旁恢复“保持登录”,并删除表单上方独立锁图标;<code>web/app/globals.css</code> 增加品牌标识组合布局与移动端尺寸。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页加入 SKG 字标并增强角色立体感</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页缺少 SKG 品牌识别;动态角色仍偏扁平,和玻璃草场背景的空间感不够一致。</p>
<p><strong>改动:</strong>新增 <code>web/public/skg-logo-black.svg</code>,来源为官网 <code>cn.skg.com/logo-black.svg</code><code>web/app/login/page.tsx</code> 在组合登录框内加入 SKG 标识;<code>web/app/globals.css</code> 为标识做反相白色玻璃处理,并给四个动态角色加高光、暗边、内阴影、接触投影和透视层次。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>web/public/skg-logo-black.svg</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录框固定在视口中心</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>组合登录框仍放在普通文档流的居中容器里,背景滚动或视口变化时视觉上会偏离画面中心。</p>
<p><strong>改动:</strong><code>web/app/globals.css</code><code>login-source-overlay</code> 改为固定覆盖全屏层;组合登录框自身用 <code>position:fixed; left/top:50%; transform:translate(-50%,-50%)</code> 直接锚定视口中心,避免网格或背景滚动状态造成偏移。</p>
<p><strong>影响:</strong><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录小人完整显示并恢复背景响应</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>左右排列后动态小人左侧区域偏窄,角色边缘被裁;上轮为处理底部黑边把 Oasis 背景硬锁成单屏,削弱了背景随鼠标和滚动变化的感觉。</p>
<p><strong>改动:</strong><code>web/app/globals.css</code> 扩大组合登录框和左侧角色列,提升角色区域高度与缩放比例;<code>web/components/login/oasis-canvas.tsx</code> 改为原生鼠标事件和 <code>postMessage</code> 双通道转发,输入框上移动鼠标也驱动草地;<code>web/public/oasis-source/index.html</code> 恢复完整滚动进度,保留末端阶段,只禁用原 footer 可见时的 canvas 上移逻辑来去除底部黑边。</p>
<p><strong>影响:</strong><code>web/app/globals.css</code><code>web/components/login/oasis-canvas.tsx</code><code>web/public/oasis-source/index.html</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页背景锁定单屏避免底部黑边</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>Oasis 原源码仍保留多段滚动页面高度,浏览器滚到最后一段或恢复 iframe 滚动位置时,相机会进入 footer 视角并在登录页底部露出黑边。</p>
<p><strong>改动:</strong><code>web/public/oasis-source/index.html</code> 的登录嵌入模式禁用 <code>html/body</code> 滚动,将隐藏的 <code>#scroll-container</code> 固定为单屏,并把隐藏 footer 固定到视口下方且高度归零,避免原脚本触发 canvas 上移。</p>
<p><strong>影响:</strong><code>web/public/oasis-source/index.html</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录框改为动画和表单左右排列</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录框内动画角色和账号/密码表单仍是上下堆叠,不符合“左右排列”的要求。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 给表单加独立布局类;<code>web/app/globals.css</code> 将桌面端组合登录框改为两列网格,左侧放大动画角色,右侧放账号、密码、复选框和提交图标;移动端继续上下排列避免挤压。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页路由文字彻底清空</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录框主体已经没有可见文字,但 <code>/login</code> 仍会继承全站 <code>title</code>/<code>description</code>,背景 iframe 也保留了描述性 <code>title</code>,导致检查页面源码时还能看到文字。</p>
<p><strong>改动:</strong>新增 <code>web/app/login/layout.tsx</code> 将登录路由 metadata 覆盖为空;<code>web/components/login/oasis-canvas.tsx</code> 删除 iframe 的 <code>title</code> 属性;<code>web/next.config.mjs</code> 禁用开发环境左下角 Next Dev Indicator。登录界面只保留动态图形、输入框、复选框和图标按钮。</p>
<p><strong>影响:</strong><code>web/app/login/layout.tsx</code><code>web/components/login/oasis-canvas.tsx</code><code>web/next.config.mjs</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录框改为无可见文字</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>组合登录框内仍显示输入占位、保持会话、按钮文字和状态提示文字,不符合“登录框文字都删掉”的要求。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 删除账号/密钥输入框占位文字、保持会话文本、提交按钮文本、错误/成功状态文本以及表单控件上的可读 <code>aria-label</code>;表单视觉只保留图标、输入区域、复选框和箭头按钮。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页合并小人和表单并清空旧文字</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页不应再出现旧版入口文案、流程状态和指标文字;小人独立玻璃框会扩大遮挡面积,网格背景也不需要。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 将动态角色和登录表单合并进同一个 <code>login-source-auth-panel</code>,删除独立角色框。<code>web/app/globals.css</code> 让浮层容器默认不接收指针事件,只让登录框接收交互;角色区域去掉网格、边框、底部阴影和单独背景。<code>web/components/login/oasis-canvas.tsx</code> 改用 <code>document</code> 捕获阶段监听鼠标移动,并向 Oasis iframe 派发原生 <code>mousemove</code>,让它复用原源码玻璃板覆盖时仍驱动草地的逻辑。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/components/login/oasis-canvas.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页只保留源码背景、登录和动画</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页仍像内容页,保留了旧版入口营销文案、状态胶囊和底部指标,和“直接把源码背景写过来,上面只放登录和动画”的目标不一致。</p>
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 删除左侧营销文案、状态胶囊、标题、说明、Pipeline 和指标栏,只保留 <code>OasisCanvas</code>、动态角色透明框和登录表单。<code>web/app/globals.css</code> 新增 <code>login-page--source</code><code>login-source-overlay</code><code>login-source-character-panel</code> 等样式,让两个功能浮层直接叠在原版 Digital Oasis 背景上。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页草地鼠标转发、面板压缩和角色放大</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>原版 Digital Oasis 鼠标划过草地会产生响应,但登录页的表单和透明面板盖在 iframe 上方时iframe 收不到鼠标坐标;同时右侧登录面板过大,左侧动态角色被压成小装饰。</p>
<p><strong>改动:</strong><code>web/components/login/oasis-canvas.tsx</code> 在捕获阶段监听父页面指针移动,并通过 <code>postMessage</code> 发送到 <code>web/public/oasis-source/index.html</code>,后者复用原 raycaster 逻辑驱动草地。<code>web/app/login/page.tsx</code> 将身份验证面板改成 290-320px 窄列、去掉强制大高度、压缩标题/输入/按钮间距;<code>web/app/globals.css</code> 将动态角色面板放大成左侧透明框架里的主视觉。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/components/login/oasis-canvas.tsx</code><code>web/public/oasis-source/index.html</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页两块主面板复刻 Pillars 透明卡片质感</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Login</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页左右两块面板仍偏旧黑色实体面板,没有用上源码 <code>Pillars</code> 区域的透明玻璃质感。</p>
<p><strong>改动:</strong><code>web/app/globals.css</code> 将左侧内容面板和右侧登录面板统一改为源码 <code>.pillar-card</code> 的核心效果:<code>rgba(10,18,10,.55)</code> 半透明底、<code>rgba(140,180,120,.12)</code> 细边、<code>16px</code> 圆角、<code>blur(16px)</code><code>0 8px 40px rgba(0,0,0,.4)</code> 阴影和 hover 边框/背景变亮输入框、动态图角色面板、Pipeline 胶囊和底部状态条也同步成同一透明体系。</p>
<p><strong>影响:</strong><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页改为 Digital Oasis 动态背景</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Canvas</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户希望采用下载的 <code>remix-3d-website-the-digital-o</code> 视觉方向;登录页需要更有沉浸感,但不能回到具体产品展示。</p>
<p><strong>改动:</strong>新增 <code>web/public/oasis-source/index.html</code>,直接复制下载包原始 WebGPU / Three.js 草场源码;<code>web/components/login/oasis-canvas.tsx</code> 改为 iframe 承载这份源码,登录页只做上层文案和表单覆盖。源码页仅用 CSS 隐藏 demo 站自己的导航、文字和设置面板,保留原草场、景深、风动和鼠标交互。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/components/login/oasis-canvas.tsx</code><code>web/public/oasis-source/index.html</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页移除产品元素并保留动态模块</h3>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>登录页不应该放具体产品图,入口职责是进入内容生产系统,不是展示某个产品。</p>
<p><strong>改动:</strong>移除登录页产品图、G7/Neck Massager 等产品文案和产品摄影卡,改为抽象流程视觉与旧版入口文案。四个动态几何角色作为独立小组件保留,继续响应鼠标眼神跟随、输入、显示密码、错误和成功状态。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页吸收 skg.com 官网视觉元素</h3>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>改动:</strong>参考 skg.com 官网的浅灰产品橱窗、黑白强对比、胶囊按钮、SKG 标识、米白/香槟金健康产品光感,重构生产登录页左侧展示区;角色舞台从纯暗色网格改为官网感浅灰产品展示台,底部模块文案改为 Shop by Need / Recovery / Self-Care 语义。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页角色视觉样式升级</h3>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>改动:</strong>在保留四个几何角色结构的基础上,增加底座阴影、发光边、玻璃高光、内部面板和状态点,让角色更像 SKG 内容工作台里的小模块,而不是纯色几何块。</p>
<p><strong>影响:</strong><code>web/components/login/animated-login-characters.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页角色取消默认上下浮动</h3>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>角色舞台默认挂了 <code>login-stage-breathe</code> 无限循环,导致四个角色在登录页一直上下移动。</p>
<p><strong>改动:</strong>移除默认呼吸动画,角色平时固定贴底;错误态只保留短暂横向抖动,成功态保留一次性反馈动画。</p>
<p><strong>影响:</strong><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页角色组件重构</h3>
<span class="tag rose">UI</span>
<span class="tag violet">Refactor</span>
</header>
<div class="body">
<p><strong>改动:</strong>新增 <code>web/components/login/animated-login-characters.tsx</code>,把登录页四个角色从 <code>web/app/login/page.tsx</code> 抽成独立组件;角色用配置数组渲染,眼睛、嘴型和黄色 SVG 嘴不再散落在页面主体。动效状态从全页 <code>data-mood</code> 改为挂在角色舞台 <code>login-character-stage</code> 上,样式作用域更清晰。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/components/login/animated-login-characters.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页角色眼神幅度增强</h3>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>改动:</strong>放大登录页动画角色的鼠标跟随瞳孔位移,并加大显示密码时眼睛整体看向目标的偏移;同时略微放大白眼球,避免瞳孔大幅移动时显得被裁切。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 登录页动画角色改回风格库原版几何结构</h3>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>首版登录页把风格库的动画角色理解成圆润小球,视觉上偏幼稚,和 <code>14 动画角色登录</code> 的原始气质不一致。</p>
<p><strong>改动:</strong>对照风格库 <code>AnimatedCharacters.vue</code> 和 demo把角色重写为紫色高矩形、黑色矩形、橙色半圆、黄色圆角柱的贴底几何组合保留鼠标视线跟随并让输入、显示密码、错误、成功状态驱动 skew、眼睛和嘴型变化。</p>
<p><strong>影响:</strong><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 生产站点增加应用内登录页</h3>
<span class="tag gray">Runtime</span>
<span class="tag blue">Security</span>
<span class="tag rose">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>公司域名部署后任何人知道地址都能打开工作台并调用生成能力。</p>
<p><strong>改动:</strong>把浏览器 Basic Auth 改为应用内登录页:前端新增 <code>web/app/login/page.tsx</code>,参考风格库 <code>14 动画角色登录</code> 做四个几何角色、鼠标视线跟随、输入 / 显示密码 / 错误 / 成功状态反馈;后端新增 <code>/auth/login</code><code>/auth/check</code><code>/auth/logout</code>,使用 HttpOnly Cookie + HMAC 会话签名;生产 Nginx 通过 <code>auth_request</code> 保护工作台和 <code>/api/</code></p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/app/login/page.tsx</code><code>web/app/globals.css</code><code>web/lib/api.ts</code><code>docker-compose.prod.yml</code><code>deploy/nginx.conf</code><code>deploy/.env.production.example</code><code>api/.env.example</code><code>.project.json</code><code>RULES.md</code><code>docs/deploy-vps.md</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 公司域名生产部署配置</h3>
<span class="tag gray">Runtime</span>
<span class="tag blue">Deploy</span>
</header>
<div class="body">
<p><strong>改动:</strong>把生产入口确定为 <code>https://marketing.skg.com</code>DNS 已解析到 VPS <code>76.13.31.179</code>。新增 Docker Compose 生产配置:前端用 Next 静态导出 + Nginx<code>/api/</code> 反代到唯一容器名 <code>skg-marketing-api:4291</code>,避免与 Coolify 网络里其他项目的泛名 <code>api</code> 冲突;后端任务目录持久化到服务器 <code>./data/jobs</code>Traefik 通过既有 <code>coolify</code> 外部网络接管 80/443。已完成上线验证HTTPS 首页 200<code>/api/health</code> 返回 <code>ok:true</code></p>
<p><strong>影响:</strong><code>Dockerfile.web</code><code>Dockerfile.api</code><code>docker-compose.prod.yml</code><code>deploy/nginx.conf</code><code>deploy/.env.production.example</code><code>docs/deploy-vps.md</code><code>.project.json</code><code>RULES.md</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-15 · 本地启动改为后台不弹 Terminal</h3>
<span class="tag gray">Runtime</span>
</header>
<div class="body">
<p><strong>问题:</strong>通过 macOS Terminal 启动后端会每次弹出一个终端窗口,打开页面时干扰使用。</p>
<p><strong>改动:</strong>新增 <code>scripts/start-dev-background.sh</code><code>scripts/stop-dev-background.sh</code>。启动脚本自动检查前端 4290 和后端 4291缺哪个后台启动哪个日志写入 <code>.logs/</code>PID 写入 <code>.pids/</code>,以后无需通过 <code>osascript</code> 打开 Terminal。</p>
<p><strong>影响:</strong><code>scripts/start-dev-background.sh</code><code>scripts/stop-dev-background.sh</code><code>.gitignore</code><code>RULES.md</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合按真实产品外置合成</h3>
<span class="tag orange">产品融合</span>
<span class="tag blue">Video Gen</span>
</header>
<div class="body">
<p><strong>问题:</strong>Seedance 生成时可能把颈部按摩仪当成可变形装饰物,导致产品样式变化、穿进透明身体或与骨架融合。</p>
<p><strong>改动:</strong>产品融合 prompt 改为英文硬约束:四张 SKG 产品图是真实产品照片和唯一实物真源,生成时按外置刚性设备做 product placement / visual compositing产品必须停留在透明皮肤外侧后颈外侧承托、两端沿左右颈侧向前保留遮挡、接触阴影、透视和真实比例禁止 x-ray blending、穿模、融进骨架或重绘成其他颈带。</p>
<p><strong>界面:</strong>产品融合顶部常驻显示桌面四张真实产品角度图,并支持鼠标停留放大查看。每行视频结果不再只显示最新一个,而是按历史持续追加横向结果条,鼠标停留任一结果会放大预览。</p>
<p><strong>后端:</strong>生视频参考图顺序调整为主参考图之后优先传四张产品图,再传其余人物角色图,提高真实产品外观权重。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/lightbox.tsx</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 修正 SKG 豆包视频网关路径</h3>
<span class="tag orange">Video Gen</span>
<span class="tag blue">Seedance</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品融合视频能进入队列,但后台任务 5% 后失败,错误为 <code>/contents/generations/tasks</code> 返回 404。</p>
<p><strong>根因:</strong><code>https://ai.skg.com/doubao</code> 这个 SKG 豆包网关的真实视频入口是 <code>/api/v3/contents/generations/tasks</code>,不是火山方舟直连 base 下使用的 <code>/contents/generations/tasks</code></p>
<p><strong>改动:</strong>后端默认路径识别 <code>ai.skg.com/doubao</code> 并自动使用 <code>/api/v3/contents/generations/tasks</code><code>/api/v3/contents/generations/tasks/{id}</code><code>/api/v3/contents/generations/tasks/{id}/content</code>;本机 <code>api/.env</code><code>api/.env.example</code> 同步更新。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env</code><code>api/.env.example</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合改为内置角色 + 产品 + 描述生成</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag orange">产品融合</span>
<span class="tag blue">角色库</span>
</header>
<div class="body">
<p><strong>问题:</strong>当前产品融合不再需要手动首帧/尾帧,用户要的是从内置透明骨架人角色、场景描述、产品使用方式和享受状态直接生成视频。</p>
<p><strong>改动:</strong>桌面 <code>skg_anatomy_characters_20260514_120852</code> 的 5 个角色、35 张图内置为 <code>api/character_library/skg-characters</code>。产品融合页新增角色下拉和角色预览,每行只保留场景/产品使用/享受描述、秒数、生成按钮和结果视频选择不同角色时6 行描述会自动改成对应角色的场景气质、产品使用动作和享受状态,并自主写入“本镜头主要参考哪几张角色图、生成什么场景”。生成前自动复制所选角色 7 张参考图和固定 4 张 SKG 产品图到当前 job。</p>
<p><strong>后端:</strong>新增 <code>GET /character-library/skg</code><code>GET /character-library/skg/images/{filename}</code><code>POST /jobs/{job_id}/assets/character-library</code>。视频提交新增 <code>subject_images</code>,无首帧时主人物图以 <code>reference_image</code> role 传入 Ark/Seedance而不是强制作为 <code>first_frame</code></p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/character_library/skg-characters</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合内置多组镜头语言</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag orange">产品融合</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品融合不能只写“人物使用产品”,还要稳定控制出场方式、景别、运镜、产品入画、佩戴贴合、使用感受和收尾;同时 SKG 产品是颈部/肩颈按摩仪,比例和佩戴位置不能跑偏。</p>
<p><strong>改动:</strong>前端内置 36 条产品融合镜头语言模板6 条为一组,对应建立出场、产品入画、佩戴贴合、使用感受、生活延展和收尾记忆;产品融合页新增“换一组”按钮,只刷新 6 行描述词,不改变已选角色和视频结果。视频 prompt 额外写入颈部/后颈/颈肩使用部位和真实尺寸比例约束。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/app/page.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 音频提取直接生成英文产品口播</h3>
<span class="tag gray">Audio</span>
<span class="tag green">TTS</span>
</header>
<div class="body">
<p><strong>问题:</strong>“提取音频”不能只做原音频转文字再改写,用户需要点击后直接得到介绍 SKG 产品的英文文案和配音,长度尽量贴近原音频,并且声音不能生硬。</p>
<p><strong>改动:</strong><code>pipeline_transcribe</code> 提取 <code>audio.wav</code> 后读取原音频时长,用该时长估算英文口播词数;<code>_rewrite_audio_script_sync</code> 改为生成自然、有趣、可直接 TTS 的 SKG 英文产品介绍。ASR/翻译保留为对照和节奏参考ASR 不可用时仍继续生成产品口播。TTS voice_id 改为从 <code>语音音色池</code> 随机选择男声、女声或成熟声。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>api/README.md</code><code>RULES.md</code><code>web/components/nodes/index.tsx</code><code>web/components/audio-strip.tsx</code><code>web/components/dashboard.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合描述词扩成 20 条精准模板</h3>
<span class="tag orange">产品融合</span>
<span class="tag blue">Prompt</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品融合视频的动作描述不能泛泛写“人物使用产品”,需要稳定表达透明骨架人在具体场景中佩戴 SKG 产品,并呈现舒适享受状态。</p>
<p><strong>改动:</strong>前端内置 20 条产品使用描述模板,覆盖卧室、客厅、办公、浴室、阳台、影棚、阅读角等场景;当前产品融合页默认把前 6 条预填到 6 行镜头,用户只在需要时直接改每行描述。</p>
<p><strong>后端:</strong><code>generateProductFusionDescriptions</code> 的兜底模板同步扩为 20 条LLM 提示也改为生成 20 条 35-70 字描述,要求包含场景、佩戴/展示动作和舒适表情,同时排除医疗治疗承诺。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合改为首尾帧加四产品角度垫图</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag orange">产品融合</span>
</header>
<div class="body">
<p><strong>问题:</strong>原产品融合依赖白底人物、手动画区域、场景图和融合引导图,但当前透明骨架人二创流程更需要文字生成首尾帧,再把产品真源作为垫图传给视频模型。</p>
<p><strong>改动:</strong>“场景图”页签改名为“首尾帧”,右侧用地点、风格、参考要素和 prompt 生成首帧/尾帧,生成后自动填入当前产品融合镜头。产品融合 6 行工作表改为首帧、尾帧、四张同一产品不同角度图、描述词、秒数和生成按钮,并提供桌面四角度填当前镜头/填满 6 镜头。</p>
<p><strong>后端:</strong><code>generateSceneAsset</code> 新增 <code>asset_role</code><code>first_frame/last_frame</code> 走文字生图并标记资产角色;<code>ProductFusionShot</code> 新增 <code>first_image</code><code>last_image</code><code>product_images</code>,视频提交直接把首尾帧和四张产品图交给 Seedance产品库新增桌面 <code>skg产品1-4.jpg</code> 四角度条目。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 本地抽帧改为展示友好算力档</h3>
<span class="tag orange">抽帧</span>
<span class="tag gray">Audio</span>
</header>
<div class="body">
<p><strong>问题:</strong>透明骨架人目标逐帧调用 Vision 验收会拖慢抽帧;切回本机算力后,如果自动档直接跑最高极准,也可能在展示时占满机器资源。</p>
<p><strong>改动:</strong><code>transparent_human</code> 目标保留,但抽帧阶段只走本地扫描、评分、去重和时间覆盖,不再逐帧调用 Vision。<code>quality=auto</code> 最高只自动选择精细;极准仍保留为手动选项。抽帧开始拆出 <code>audio.wav</code> 后会启动独立音频线程,视觉抽帧和音频处理并行,互不标失败。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 修复 ReactFlow Hydration 和后端 reload 卡住</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">Dev Server</span>
</header>
<div class="body">
<p><strong>问题:</strong>刷新页面时 Next 报 Hydration mismatchReactFlow 节点服务端默认宽度和客户端 localStorage 里的用户调整宽度不一致;同时提交抖音链接时若后端正在 <code>--reload</code> 等后台任务结束,会出现 4291 端口占用但请求卡住。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code> 增加 <code>clientReady</code>ReactFlow 和底部音频条只在浏览器挂载后渲染,避免服务端 HTML 和客户端本地布局缓存不一致。后端启动说明改为不带 <code>--reload</code> 的稳定命令,并更新 <code>RULES.md</code><code>api/README.md</code> 和本页运行入口。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>RULES.md</code><code>api/README.md</code><code>docs/source-analysis.html</code>。本地 4291 后端已按无 reload 模式重启。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 音频处理改为音频卡片手动触发</h3>
<span class="tag gray">Audio</span>
<span class="tag blue">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>等待抽帧完成后自动启动音频,不符合“先把声音文案拿出来审核”的工作流;用户需要在音频卡片上直接触发。</p>
<p><strong>改动:</strong>移除前端抽帧完成后的自动转写逻辑;<code>AudioNode</code> 保留并固定显示“提取音频 / 重新提取音频”按钮,点击音频卡片也会立即打开底部音频条。后端 <code>/transcribe</code> 不再要求 <code>frames_extracted</code>,视频就绪后可直接从 <code>source.mp4</code> 拆出 <code>audio.wav</code>,并按原音频时长生成 SKG 英文产品介绍和 TTS 随机英文配音;抽帧中触发时不抢主状态,而是用 <code>audio_script.status</code> 表示音频处理中。当当前网关的 <code>whisper-1</code> audio endpoint 返回 404 时,会 fallback 到 Gemini 多模态音频识别ASR 不可用时也会继续按原音频时长生成产品口播,不把可用文案标成前端错误。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 新增底部可伸缩音频条</h3>
<span class="tag gray">Audio</span>
<span class="tag violet">Timeline</span>
</header>
<div class="body">
<p><strong>问题:</strong>音频和文案只在节点或侧栏里展示,审核时缺少“文字和声音时间轴对应”的空间;英文口播和中文翻译也没有上下对齐。</p>
<p><strong>改动:</strong>新增 <code>web/components/audio-strip.tsx</code>,在主工作台底部吸附显示,可拖拽调整高度、可收起。每个音频段按时间横向排列,上方显示英文,中间显示中文翻译,下方显示对应波形条;底部原音频播放器驱动时间轴,播放时绿色指针会沿全局波形移动,并在当前字幕节点内同步走过该段。右侧显示按原音频时长生成的 SKG 英文产品口播、TTS 随机英文配音和产品依据。后端新增 <code>source_audio_url</code><code>GET /jobs/{id}/audio.wav</code> 只读接口,前端用 Web Audio API 解码生成波形峰值。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/audio-strip.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 音频结果改为改前/改后对照展示</h3>
<span class="tag gray">Audio</span>
<span class="tag violet">UI</span>
</header>
<div class="body">
<p><strong>问题:</strong>音频识别成功后只显示改写文案,用户看不到它和原音频之间的变化关系,难以判断“是不是把参考视频转成我们自己的话”。</p>
<p><strong>改动:</strong><code>AudioNode</code> 增加轻量对照摘要:改前显示原音频识别/翻译预览,改后显示 SKG 英文产品口播;侧栏 <code>Rewrite</code> 面板改为完整审核视图,先列原音频逐段 ASR/翻译,再列英文产品介绍稿、产品卖点依据和 TTS 英文配音播放器。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>web/components/dashboard.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 多视频工作流状态按 job 隔离</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">Job State</span>
</header>
<div class="body">
<p><strong>问题:</strong>同时上传多个视频后,前端把已选关键帧和关键帧详情面板作为全局状态保存;切换 active 视频会清空选中帧,或者让详情面板指向另一个视频的同序号帧,容易误以为生图/自动化被终止或串任务。</p>
<p><strong>改动:</strong><code>web/app/page.tsx</code><code>selectedFrames</code><code>expandedFrame</code> 改为按 <code>jobId</code> 存储。切换视频只改变当前视图,不清空其他视频的选择;重新抽帧、删帧、手动加帧只清理或更新对应 job。异步生图、生视频、产品融合返回后按返回的 <code>job.id</code> 写回 <code>jobs[]</code>,不会落到切换后的 active job。轮询条件也把 <code>audio_script.status=rewriting</code> 纳入运行态,保证音频改写/配音阶段切换视频后仍继续刷新。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>docs/source-analysis.html</code>。后端轮询本来已经覆盖所有运行中的 job这轮主要修正前端 UI 工作上下文。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 抽帧后台任务不再卡住 API</h3>
<span class="tag blue">API</span>
<span class="tag orange">抽帧</span>
</header>
<div class="body">
<p><strong>问题:</strong>点击视频抽帧时,后端 4291 端口能连接但 <code>/health</code> 和后续请求长时间不返回,前端看起来像按钮没有反应。</p>
<p><strong>原因:</strong><code>pipeline_download</code><code>pipeline_analyze</code> 声明为 async background task但内部实际是同步 <code>yt-dlp</code><code>ffmpeg</code> 和 Vision 验收Starlette 会在事件循环里执行 async background task导致长抽帧把 API 主循环堵住。</p>
<p><strong>改动:</strong>下载和抽帧 pipeline 改为普通同步函数,让 FastAPI/Starlette 按线程池后台任务执行;<code>analyze_queue_worker</code> 也改为同步 worker。服务启动恢复时如果磁盘里有重启前遗留的 <code>downloading</code><code>splitting</code><code>transcribing</code> 运行态,会恢复成可重试状态,避免按钮一直 disabled。开发运行时改用不带 <code>--reload</code> 的后端命令,避免重载等待后台任务。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>docs/source-analysis.html</code>。已重启本地 4291 后端并验证 <code>/health</code> 立即返回;遗留的 <code>8b37e65521a6</code> job 已恢复为 <code>downloaded</code>,可重新点击抽帧。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 生视频接入 SKG 豆包网关</h3>
<span class="tag orange">Video Gen</span>
<span class="tag blue">Seedance</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户提供 <code>https://ai.skg.com/doubao</code> 作为视频模型网关;该网关应按 Seedance / 方舟内容生成任务格式提交,而不能误走普通 multipart 上传。</p>
<p><strong>改动:</strong><code>video_uses_ark()</code> 现在同时识别火山方舟域名和 <code>ai.skg.com/doubao</code>,统一使用 <code>content</code> JSON文本 prompt、首帧、尾帧和产品参考图作为不同 role 传入。火山方舟直连按 <code>/contents/generations/tasks/{id}</code> 轮询SKG 豆包网关按 <code>/api/v3/contents/generations/tasks/{id}</code> 轮询。<code>api/.env.example</code> 增加 SKG 豆包视频网关配置示例;生视频轮询上限改为 <code>VIDEO_POLL_TIMEOUT_SECONDS</code>,默认 900 秒,避免慢任务过早失败。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>docs/source-analysis.html</code>。本机 <code>api/.env</code> 已配置 <code>VIDEO_API_BASE_URL=https://ai.skg.com/doubao</code> 和视频专用 key。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 音频处理接入 SKG 英文产品口播与 TTS 配音</h3>
<span class="tag gray">Audio</span>
<span class="tag green">TTS</span>
</header>
<div class="body">
<p><strong>问题:</strong>音频处理节点之前只说明“音轨 → ASR → 翻译 → 改写”,没有按原音频时长生成的产品介绍产物,也没有配音输出;用户无法直接拿到符合 SKG 产品语境的英文口播。</p>
<p><strong>改动:</strong><code>Job</code> 新增 <code>audio_script</code><code>pipeline_transcribe</code> 提取 <code>audio.wav</code> 后按原音频秒数生成 SKG 英文产品介绍文案,并在配置 <code>语音 API Key</code> 时调用 TTS T2A 输出 <code>/jobs/{id}/audio-script.mp3</code>。TTS voice_id 从英文男声、女声、成熟声池随机选择;前端 <code>AudioNode</code> 和侧栏 Rewrite 区显示模型链路、英文产品文案和配音播放器。</p>
<p><strong>边界:</strong>TTS 官方 Speech API 当前接入的是 TTS 配音,不替代 ASR原始音频文案提取仍走现有 OpenAI-compatible audio transcription 入口。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>api/README.md</code><code>web/lib/api.ts</code><code>web/components/nodes/index.tsx</code><code>web/components/dashboard.tsx</code><code>web/app/page.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 默认抽帧张数改为 12 帧</h3>
<span class="tag violet">InputNode</span>
<span class="tag blue">抽帧</span>
</header>
<div class="body">
<p><strong>问题:</strong>透明骨架人主题需要更稳定的素材覆盖,默认 5 帧太少,容易缺少可用于主体、场景和产品融合的角度。</p>
<p><strong>改动:</strong>后端 <code>KEYFRAME_COUNT</code> 默认值、前端抽帧 fallback、API client 默认参数都改为 12抽帧设置里的张数选项把 12 放到第一位。透明骨架人目标仍会对每个候选做 Vision 验收,不合格候选自动换下一帧。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 抽帧新增透明骨架人 AI 验收目标</h3>
<span class="tag violet">InputNode</span>
<span class="tag blue">Vision</span>
</header>
<div class="body">
<p><strong>问题:</strong>透明人二创不能只靠清晰度抽帧,也不能只要出现“骨头”就算合格;需要确认同一人形角色同时具备透明/半透明外壳、干净白色骨架、足够大且清晰、非恐怖广告感。</p>
<p><strong>改动:</strong><code>FrameExtractTarget</code> 新增 <code>transparent_human</code> 并设为当前 UI 默认目标。后端抽帧先按本地清晰度、中心主体、对比度和去重扩大候选池,再逐张从原视频抽高清帧交给 Vision 评分;评分维度包括透明身体、可见骨架、人物占比、清晰度、商业风格和产品可用性。不合格帧会被删除并自动换下一候选,直到凑够目标张数或候选耗尽。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>web/components/lightbox.tsx</code><code>web/lib/workflow-target.ts</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 清洗页增加一键替换待应用清洗版</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">清洗</span>
</header>
<div class="body">
<p><strong>问题:</strong>一键清洗后会生成多张待应用清洗版,但用户仍需要逐张点“替换原图”,素材准备阶段操作成本偏高。</p>
<p><strong>改动:</strong>“原图/清洗”页新增“待替换清洗版”状态和“一键替换待应用 N 张”按钮,并显示批量替换进度和失败数。该按钮只应用已经存在的清洗结果,不重新清洗;不满意的帧仍可先单张重洗或丢弃。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合收敛为首尾帧 + 固定四产品图</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag orange">产品融合</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品融合页继续显示产品角度槽、辅助栏和产品图库会把操作变复杂;当前工作流只需要用户手动补人物首尾帧,产品图固定来自桌面 4 张 SKG 图。</p>
<p><strong>改动:</strong>“产品融合”页每行只保留首帧、尾帧、已预填描述词、秒数、生成按钮和行末视频结果。生成单条或批量视频前,前端自动把内置的 4 张桌面 SKG 产品图复制为当前 job asset 并写入 <code>product_images[4]</code>;视频 prompt 增加 <code>产品融合镜头ID</code> 标记,用来把生成结果显示回对应行。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>web/components/dashboard.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合镜头组改为纵向 6 行工作表</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag orange">产品融合</span>
</header>
<div class="body">
<p><strong>问题:</strong>产品融合要生成 6 条视频,但旧排版只在列表里显示摘要,真正编辑区只展开当前镜头,用户无法从上到下同时检查 6 条镜头的产品、人物、场景和描述是否一一对应。</p>
<p><strong>改动:</strong>“产品融合”页左侧改为纵向 6 行镜头工作表。每行包含产品图槽、白底人物图槽、人物图内产品区域画框、场景图槽、动作描述、秒数选择和单条生成按钮右侧只保留模型状态、当前镜头状态、AI 草拟 6 条、批量排队和当前镜头产品图库。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合改为 6 行区域约束镜头组</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag orange">Seedance</span>
</header>
<div class="body">
<p><strong>问题:</strong>只把产品图作为参考图无法解决尺寸和位置融合,模型不知道产品应该放在人物或场景里的哪个区域。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 的“产品融合”页改为 6 行镜头组。每行绑定产品图、白底人物图、手动画出的产品区域、场景图、描述词和视频秒数;图片槽支持上传和粘贴,产品图也可从内置 SKG 白底图库选用。右侧固定显示图片模型 <code>GPT Image 2</code> 和视频模型 <code>Seedance</code>,支持 AI 草拟 6 条动作描述和批量排队;单条生成入口放在每一行镜头内。</p>
<p><strong>后端:</strong>新增 <code>POST /jobs/{job_id}/product-fusion/guide</code><code>POST /jobs/{job_id}/product-fusion/descriptions</code>。前者把产品图按 <code>product_region</code> 合成到白底人物图上,生成普通 <code>asset</code> 引导图;后者用 LLM 或本地模板生成 6 条动作描述草稿。前端再把引导图作为 Seedance 首帧,并把产品图、人物图、场景图作为参考图提交。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/lightbox.tsx</code><code>web/components/nodes/index.tsx</code><code>web/components/dashboard.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 产品融合槽位接入应用内剪贴板</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">Clipboard</span>
</header>
<div class="body">
<p><strong>问题:</strong>卡片上的“复制”写入的是应用内 <code>ImageRef</code> 剪贴板,而产品融合槽位只监听系统剪贴板文件,导致复制后的素材无法在产品融合里粘贴。</p>
<p><strong>改动:</strong><code>NodeData</code><code>FrameLightbox</code> 新增 <code>clipboard</code> 传递链路;产品融合三类槽位的“粘贴”按钮优先使用应用内剪贴板,应用剪贴板为空时再提示可选中槽位后 Cmd+V 粘贴系统图片。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>web/components/dashboard.tsx</code><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 增加产品融合和 SKG 内置白底图库</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag orange">产品融合</span>
</header>
<div class="body">
<p><strong>问题:</strong>生成视频需要稳定的 SKG 产品真源,不能每次都依赖临时上传或从参考视频里找产品图;桌面已有整理过的 SKG 产品图,应作为内置数据库使用。</p>
<p><strong>改动:</strong>从桌面 <code>skg_product_downloads/all_products</code> 的 gallery 中筛出 41 张白底产品图,生成 <code>api/product_library/skg-products/manifest.json</code> 和压缩预览图。<code>FrameLightbox</code> 新增“产品融合”页签,<code>StoryboardWorkbench</code> 的 SKG 产品参考区也接入同一个 <code>ProductLibraryPicker</code>,支持搜索、品类筛选、尺寸预览和一键加入。</p>
<p><strong>后端:</strong>新增 <code>GET /product-library/skg</code><code>GET /product-library/skg/images/{filename}</code><code>POST /jobs/{job_id}/assets/product-library</code>。选中库内产品图时,后端会复制成当前 job 的 <code>asset</code>,后续仍通过既有 <code>product_images</code> 进入生视频接口。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/product_library/skg-products</code><code>web/lib/api.ts</code><code>web/components/product-library-picker.tsx</code><code>web/components/lightbox.tsx</code><code>web/components/storyboard-workbench.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 场景图改为全图参考和关键词 Prompt</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">Prompt</span>
</header>
<div class="body">
<p><strong>问题:</strong>场景图页不能只围绕当前单张图;它需要看到全部关键帧,并通过地点、风格、参考要素等关键词组合出可控 prompt再生成场景。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 的“场景图”页左侧改为全部关键帧网格:点击图片设为生成目标,点击“选”加入场景参考。右侧新增地点、生成方式、风格、额外关键词和参考要素 chips并自动拼出可编辑场景 prompt。</p>
<p><strong>后端:</strong><code>generateSceneAsset</code> 请求新增 <code>prompt</code><code>source_frame_indices</code>;多张参考帧会拼成 contact sheet 给图像模型,同时把用户 prompt 注入场景生成提示词。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 主体资产改为参考重绘六张标准图</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">Assets</span>
</header>
<div class="body">
<p><strong>问题:</strong>主体资产不是抠图,也不是只看当前单帧生成多角度;主体页需要看到全部参考帧,并用这些参考重新绘制一个完整主体。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 在“主体资产”页左侧显示参考帧网格,优先纳入所有已清洗帧,额外已选帧也会并入;小图排列,可点击切换当前帧。右侧仍负责统一主体确认和生成。人物/生物默认视图改为六张身份标准图:正面、背面、左侧、右侧、左前 45°、右前 45°并把表情补充和动作补充折成独立分组需要时再勾选。</p>
<p><strong>后端:</strong><code>generateSubjectAssets</code> prompt 改为“参考重绘”,明确禁止裁剪/抠图/粘贴源像素,要求主体完整居中、纯白/黑背景、无其他元素,并占画面约 85-95% 高度;落盘时会裁掉纯背景空白并放大主体。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/components/nodes/index.tsx</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 主体资产改为统一主体参考帧生成</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">Workflow</span>
</header>
<div class="body">
<p><strong>问题:</strong>当前流程里抽多张关键帧的目的不是让每张图各自生成一个主体,也不是从单张图硬造多角度,而是用多张关键帧共同还原同一个主体的多角度、动作和表情。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 的主体页改成“统一主体”逻辑:全局只保留一个主体候选;生成主体资产包时默认传全部关键帧作为 <code>source_frame_indices</code>,用户手动选择关键帧时只传已选帧。场景图仍然按当前关键帧逐张生成,因此是一个主体包 + 多个场景图。</p>
<p><strong>后端:</strong><code>generateSceneAsset</code> 查找主体名称时改为从整个 job 的已生成主体资产中读取,而不是只看当前帧,确保任意关键帧生成场景图时都能移除统一主体。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/components/nodes/index.tsx</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 场景图改为主体资产之后生成</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>场景图如果先于主体资产生成,只能做普通背景清理,无法准确知道要移除哪个主体,也不利于后续生成相似但不同或同构换风格的场景。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 页签顺序改为“原图/清洗 → 主体资产 → 场景图 → 审核”;画面工作台缩略图和进度文案也同步为主体资产先于场景图。场景图页新增“去主体原场景 / 相似新场景 / 同构换风格”和风格选择,且在没有主体资产时提示先生成主体资产。</p>
<p><strong>后端:</strong><code>generateSceneAsset</code> 请求新增 <code>scene_mode</code><code>scene_style</code>;后端提示词会优先读取已生成主体资产对应的主体名称,生成去主体并补背景的场景图,再按模式决定是否做相似变化或风格变化。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/components/nodes/index.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 关键帧素材面板统一右侧操作栏</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">Layout</span>
</header>
<div class="body">
<p><strong>问题:</strong>“原图/清洗、主体资产、场景图、审核”都应遵循同一结构:左侧负责看图和框选,右侧负责操作、状态和结果;旧布局把部分操作塞在左侧下方,导致左侧满、右侧空。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 统一为左侧主图、右侧操作栏。清洗按钮、批量清洗、清洗结果预览、场景图生成/复制、主体识别/主体资产包和审核状态都在右侧;切换到非清洗页时会退出框选模式,避免画框状态残留。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 单帧清洗不再全局锁住其他帧</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">Bugfix</span>
</header>
<div class="body">
<p><strong>问题:</strong>单独清洗某一张关键帧时,前端使用全局 <code>cleaning</code> 布尔状态,导致切到其他关键帧后清洗按钮仍被禁用。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 改用 <code>cleaningFrameIds</code><code>frame.index</code> 记录正在清洗的帧,只禁用当前正在清洗的那一张;其他帧可以继续单独清洗。区域清洗完成时也只在用户仍停留于同一帧时清空当前框选,避免异步完成误清别的帧操作状态。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 清洗页支持一键批量清洗</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>画面工作台里的关键帧已经在抽帧阶段筛过,逐张点“清洗水印”会拖慢素材准备;但自动清洗又不能直接覆盖原图,否则模型误改时不好回退。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 的“原图/清洗”页新增“批量清洗”卡片,一键顺序清洗所有未处理素材帧,并显示进度和失败数。批量清洗只生成 <code>cleaned</code> 待审核版本,不自动应用;没清干净的帧再用单帧框选区域做手工补洗。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code>。后端暂未新增 batch API前端复用 <code>POST /jobs/{id}/frames/{idx}/cleanup</code> 顺序执行。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 关键帧详情改为素材准备面板</h3>
<span class="tag violet">FrameLightbox</span>
<span class="tag blue">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>面板标题仍叫“关键帧详情 · 元素提取”,里面还露出普通 cutout 抠图、AI 提取、元素清单等旧流程入口;“选用此帧 / 加入目标帧”也无法说明实际价值,因为抽帧阶段已经筛过图,进入画面工作台的关键帧默认就应该参与素材准备。</p>
<p><strong>改动:</strong><code>KeyframePanelNode</code> 标题改为“关键帧素材准备”;<code>FrameLightbox</code> 的主体页改成“主体识别 / 主体清单 / 主体资产包”移除普通抠图列表、AI 提取按钮、详情内的目标帧开关以及手动/框选加主体入口,改为只显示“已在素材准备流程”的状态说明;<code>VisualLabNode</code> 上方缩略图也不再展示普通抠图分组,只展示关键帧、主体包、场景图和视频任务。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。底层旧 cutout 数据和接口暂保留兼容历史任务,但不再作为新素材准备流程的可见入口。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 画面工作台改为素材准备看板</h3>
<span class="tag violet">Visual Lab</span>
<span class="tag blue">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>画面工作台从展示缩略图扩展为素材生产中枢后,关键帧、主体资产包、场景图和视频任务继续混在一个列表里会让流程不清晰;关键帧详情面板也把清洗、识别、主体生成和场景生成都堆在一屏。</p>
<p><strong>改动:</strong><code>VisualLabNode</code> 改成素材准备进度看板,显示关键帧素材、主体资产、场景图和分镜/视频四个入口。<code>FrameLightbox</code> 新增“原图/清洗、主体资产、场景图、审核”四个页签,素材审核信息从普通列表中拆出来。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>web/components/lightbox.tsx</code><code>docs/source-analysis.html</code>。这轮只重排工作台信息架构,批量自动准备队列仍留到下一阶段。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 画面工作台增加主体资产包和场景图</h3>
<span class="tag violet">Visual Lab</span>
<span class="tag blue">Assets</span>
</header>
<div class="body">
<p><strong>问题:</strong>抽帧阶段已经筛过图,画面工作台第一步应把已选关键帧转成可用于生视频的干净素材:同一主体一套多视角/动作/表情图,再基于主体生成每帧去主体场景图,而不是继续手动逐张抠普通 cutout。</p>
<p><strong>改动:</strong><code>KeyFrame</code> 新增 <code>scene_assets</code><code>quality_report</code><code>KeyElement</code> 新增 <code>subject_kind</code><code>subject_assets</code>。后端新增 <code>generateSubjectAssets</code><code>generateSceneAsset</code>,主体资产支持白/黑背景、原尺寸/固定尺寸、物体视角、人物/生物动作与喜怒哀乐等表情;当已选关键帧共同指向一个主体时,前端会把这些帧作为 <code>source_frame_indices</code> 传入,后端拼接参考板。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/components/lightbox.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。生成出的素材保存在 <code>jobs/&lt;jobId&gt;/assets</code>,可作为 <code>asset</code> 类型复制到后续分镜槽位。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 抽帧精度自动选择并支持后端排队</h3>
<span class="tag violet">Input</span>
<span class="tag blue">Queue</span>
</header>
<div class="body">
<p><strong>问题:</strong>抽帧精度不应该每次都让用户判断;点击一个视频抽帧后,其他视频不应被全局禁用,而应该可以先后排队。另外打开视频抽帧侧边面板后,也应能自动抽帧。</p>
<p><strong>改动:</strong><code>quality</code> 新增 <code>auto</code> 默认值,后端按 CPU 核数、内存和视频时长解析为快速或精细为了展示稳定auto 不再自动进入极准,极准仅在用户手动选择时启用。后端新增内存队列 <code>ANALYZE_QUEUE</code>,多个 <code>analyze</code> 请求按顺序执行;前端轮询所有运行中的 job不只轮询当前 active job。<code>VideoFramePanelNode</code> 内也加入同一套自动抽帧工具条。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。队列目前是进程内队列,重启后不会恢复未执行的排队任务。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 抽帧工具条降噪并修复追加失败</h3>
<span class="tag violet">Input</span>
<span class="tag blue">Bugfix</span>
</header>
<div class="body">
<p><strong>问题:</strong>每个缩略图上方同时放目标、张数、精度和按钮太拥挤;另外追加抽帧时可能没有新增图片。</p>
<p><strong>根因:</strong>追加模式下 <code>frames</code> 目录已经存在,但后端仍使用非 <code>exist_ok</code><code>mkdir</code>,触发 <code>File exists</code> 后任务进入解析失败。</p>
<p><strong>改动:</strong>工具条默认只显示目标、抽帧/追加和设置按钮;张数、精度折叠到设置里。后端改为允许已存在的 <code>frames</code> 目录,追加模式不再因目录存在失败。缩略图高度增大到 192px横屏/竖屏都按真实比例显示;抽帧评分也按视频原比例缩放,不固定 16:9。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。已用临时 job 验证 append已有 1 张关键帧时追加 3 张后变为 4 张。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 自动抽帧增加本地精度模式</h3>
<span class="tag violet">Input</span>
<span class="tag blue">Performance</span>
</header>
<div class="body">
<p><strong>问题:</strong>当前抽帧默认偏轻量,用户机器是 Apple M2 Max、12 核 CPU、38 核 GPU、64GB 内存,足以承受更密集的本地候选扫描;但需要在 UI 里提示算力档位,避免长视频误用重模式。</p>
<p><strong>改动:</strong>后端 <code>/jobs/{id}/analyze</code> 新增 <code>quality</code> 参数:快速为 2fps/360px精细为 8fps/720px极准为 12fps/960px高精度模式还会提高本地评分图分辨率保留竖屏比例不再把竖屏压扁到 16:9。每个缩略图工具条新增精度选择。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。实测本机 64.5s、1080×1920、60fps 视频12fps/960px 扫描 774 张候选约 2.61s;重型 ffmpeg 场景/模糊滤镜约 21.63s,因此继续使用本地 PIL/NumPy 评分更划算。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 每个输入视频缩略图绑定自己的抽帧工具条</h3>
<span class="tag violet">Input</span>
<span class="tag blue">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>统一放在缩略图浮条上方的抽帧工具条仍然不够明确,用户无法一眼判断当前会抽哪一个视频。</p>
<p><strong>改动:</strong>抽帧目标、张数、精度和抽帧按钮改为渲染在每个视频缩略图正上方,并且每个 job 独立保存目标、张数和精度设置。点击某个缩略图上方的抽帧按钮时,前端直接把该 <code>jobId</code> 传给 <code>analyzeJob</code>,同时切换 active job 并进入该 job 的进度轮询。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。后续与输入视频相关的快捷操作都应优先贴近对应缩略图,不再依赖全局当前选择的心智。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 自动抽帧快捷工具条移到缩略图上方</h3>
<span class="tag violet">Input</span>
<span class="tag blue">Frame Target</span>
</header>
<div class="body">
<p><strong>问题:</strong>自动抽帧入口放在 Input 卡片正文里,离视频缩略图和预览工作区较远;用户需要在看缩略图时快速切目标、切张数并反复抽取。</p>
<p><strong>改动:</strong>输入视频缩略图浮条上方新增自动抽帧快捷工具条,包含抽帧目标、张数快捷项和抽帧按钮。前端新增 <code>frameCount</code> 状态并把目标 / 张数传给 <code>analyzeJob</code>;已有关键帧时默认用 <code>mode=append</code> 追加抽取。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。后端追加模式会保留已有关键帧,避开非常接近的时间点,并用新的 frame index 落盘。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 自动抽帧支持目标化扫描</h3>
<span class="tag violet">Input</span>
<span class="tag blue">Frame Target</span>
</header>
<div class="body">
<p><strong>问题:</strong>单一“自动抽帧”无法表达这次要清晰人物、下次要转场变化或表情瞬间的不同目标;但把抽帧做成复杂参数面板会破坏 Input 卡片的轻量工作流。</p>
<p><strong>改动:</strong>Input 节点新增抽帧目标,默认“综合关键帧”,可切换清晰主体、转场变化、表情瞬间、动作峰值。后端 <code>/jobs/{id}/analyze</code> 新增 <code>target</code> 参数先低清低帧率扫描候选再按目标评分、pHash 去重、时序分桶,最后只对选中的时间点从原视频抽高质量关键帧。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。当前“人物/动物表情”是本地近似评分,后续可把候选小图接入视觉模型重排。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 视频抽帧面板默认静音播放</h3>
<span class="tag violet">Input</span>
<span class="tag blue">Video</span>
</header>
<div class="body">
<p><strong>问题:</strong>输入视频缩略图改为单击打开抽帧面板后,面板播放器如果自动播放带声音,会打断工作流。</p>
<p><strong>改动:</strong><code>VideoFramePanelNode</code> 的主播放器增加 <code>muted</code>,默认静音自动播放;用户仍可通过浏览器视频 controls 自行取消静音。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · Canvas Panel 颜色跟随来源卡片且视频单击打开</h3>
<span class="tag violet">Canvas Panel</span>
<span class="tag blue">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>视频抽帧面板和画面工作台详情面板框架一致后,标题栏颜色也需要分别跟各自来源卡片一致;输入视频缩略图仍需要双击才能打开抽帧面板,操作偏重。</p>
<p><strong>改动:</strong>视频抽帧面板标题栏使用 <code>--grad-input</code>,画面工作台详情面板标题栏使用 <code>--grad-ai</code>输入视频缩略图改为单击打开抽帧面板hover 仍负责尺寸预览。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。后续面板颜色应从来源卡片类型继承。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 画面工作台详情面板统一为 Canvas Panel 框架</h3>
<span class="tag violet">Visual Lab</span>
<span class="tag blue">Canvas Panel</span>
</header>
<div class="body">
<p><strong>问题:</strong>关键帧详情 / 元素提取面板虽然默认左侧吸附,但外层仍是旧橙色“钉住”框架,和视频抽帧面板的紫色标题栏、画布/左/右/底吸附按钮不一致。</p>
<p><strong>改动:</strong><code>KeyframePanelNode</code> 改为和 <code>VideoFramePanelNode</code> 同一套 Canvas Panel 外壳:紫色标题栏、画布模式、吸附左侧、吸附右侧、吸附底部、缩放、关闭和右下角拖拽缩放。面板定位状态从单一 <code>pinned</code> 语义升级为 <code>framePanelDock</code></p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。后续工作面板应复用这套 Canvas Panel 框架。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 关键帧详情嵌入态去掉双标题栏</h3>
<span class="tag violet">Visual Lab</span>
<span class="tag blue">FrameLightbox</span>
</header>
<div class="body">
<p><strong>问题:</strong>画面工作台详情面板外层已有“关键帧详情 · 元素提取”工具栏,内层 <code>FrameLightbox</code> 嵌入态又显示一条橙红分镜导航栏,视觉上像两个窗口叠在一起。</p>
<p><strong>改动:</strong><code>FrameLightbox</code><code>embedded</code> 模式下不再渲染自己的顶部工具栏和外边框;上一帧 / 下一帧导航移动到 <code>KeyframePanelNode</code> 外层标题栏。</p>
<p><strong>影响:</strong><code>web/components/lightbox.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。非嵌入式 lightbox 保留原标题栏。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 画面工作台详情面板默认左侧吸附</h3>
<span class="tag violet">Visual Lab</span>
<span class="tag blue">Dock</span>
</header>
<div class="body">
<p><strong>问题:</strong>画面工作台点击关键帧后,关键帧详情 / 元素提取面板仍默认作为画布节点出现,还会触发 <code>fitView</code> 拉动画布;和视频抽帧面板默认左侧吸附的规则不一致。</p>
<p><strong>改动:</strong><code>framePanelDock</code> 初始值改为 <code>left</code>;从关闭状态打开关键帧详情时默认吸附左侧。若面板已打开,用户切回画布模式后继续切换关键帧,不会被强制吸回左侧;默认吸附状态下也不再触发画布 <code>fitView</code></p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>docs/source-analysis.html</code>。画面工作台处理面板现在与视频抽帧面板保持同一默认策略:先贴左侧,需要时再切回画布。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 视频抽帧面板默认左侧吸附</h3>
<span class="tag violet">Canvas Panel</span>
<span class="tag blue">Dock</span>
</header>
<div class="body">
<p><strong>问题:</strong>视频抽帧面板默认以画布节点方式打开时,用户还要再点一次吸附左侧,和“处理面板先贴边、需要时再拖回画布”的工作习惯不一致。</p>
<p><strong>改动:</strong><code>videoPanelDock</code> 初始值改为 <code>left</code>;从关闭状态双击输入视频缩略图时默认吸附左侧。面板已打开后,用户手动切换到画布 / 右侧 / 底部不会被找回动作覆盖。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>docs/source-analysis.html</code>。后续同类处理面板应优先考虑“默认贴边,必要时切回画布”的交互。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 视频抽帧面板支持删除已抽关键帧</h3>
<span class="tag violet">Canvas Panel</span>
<span class="tag blue">Frames</span>
</header>
<div class="body">
<p><strong>问题:</strong>视频抽帧面板只能新增关键帧,用户看到“已抽关键帧”后不能在同一工作上下文里清理误抽的帧。</p>
<p><strong>改动:</strong>已抽关键帧缩略图右上角增加删除按钮,点击后按 <code>jobId + frameIdx</code> 删除对应关键帧;删除期间显示小号 loading。图片类素材仍沿用快速删除策略不弹确认。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。视频抽帧面板不再依赖当前 active job 来决定删除目标。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 吸附工作面板贴近视口边缘</h3>
<span class="tag violet">Canvas Panel</span>
<span class="tag blue">Dock</span>
</header>
<div class="body">
<p><strong>问题:</strong>视频抽帧面板吸附左侧 / 右侧时顶部仍留出明显空白;这不是实际板块遮挡,而是面板吸附样式里硬编码了 <code>top: 72</code> 和对应的高度预留。关键帧面板也保留了旧 storyboard 顶栏避让逻辑。</p>
<p><strong>改动:</strong>新增统一吸附边距常量,视频抽帧面板和关键帧详情面板吸附时都贴近视口边缘,仅保留 8px 安全边距;移除关键帧面板对旧 <code>data-storyboard-dock</code> / <code>data-storyboard-bar</code> 的避让查询。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。后续画布工作面板的吸附语义统一为“贴边”,不是为顶部旧板块预留空间。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 输入视频双击改为画布抽帧面板</h3>
<span class="tag violet">Canvas Panel</span>
<span class="tag blue">Input</span>
</header>
<div class="body">
<p><strong>问题:</strong>输入视频缩略图双击原来只在 Input 节点上方展开一个临时播放器,不能作为无限画布工作台移动、找回或吸附,后续其他板块也无法复用这种交互。</p>
<p><strong>改动:</strong>新增 <code>videoFramePanel</code> ReactFlow 节点和 <code>VideoFramePanelNode</code>。双击输入视频缩略图会打开/找回画布内抽帧面板,面板可拖动、右下角缩放,也可一键吸附到左侧、右侧或底部边缘;面板内支持播放、时间轴定位、当前时间抽帧和查看已抽关键帧。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。原固定全屏 <code>VideoLightbox</code> 不再从 Input 双击路径进入;后续处理板块应复用同类画布工作面板语义。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · Hover 大预览尺寸信息增强</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">HoverPreview</span>
</header>
<div class="body">
<p><strong>问题:</strong>原始尺寸和 Fit 比例之前是左上角小号单行文字,用户在快速扫缩略图时不够醒目,无法马上感知素材分辨率差异。</p>
<p><strong>改动:</strong><code>HoverPreview</code> 左上角改为两层信息徽章:标题固定为“原始尺寸”,主数字用大号等宽字体显示 <code>×</code>,旁边保留 <code>Fit</code> 百分比或 <code>1:1 原寸</code> 状态。</p>
<p><strong>影响:</strong><code>web/components/nodes/hover-preview.tsx</code><code>docs/source-analysis.html</code>。定位和 Fit / 1:1 行为不变,只提升尺寸信息可读性。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 删除确认改为页面内分层交互</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">UX</span>
</header>
<div class="body">
<p><strong>问题:</strong>浏览器原生删除确认会突然出现在页面顶部,和无限画布的操作上下文割裂;图片类素材如果每次删除都确认,也会拖慢快速整理素材的节奏。</p>
<p><strong>改动:</strong>输入视频和生成视频任务删除改为画布内确认层,背景轻遮罩并支持点击背景 / Esc 取消;关键帧和元素提取图属于可快速整理的图片素材,点击删除后直接执行。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。视频删除仍走既有删除接口;图片删除仍走原有回调,只调整确认策略。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 输入视频缩略图支持删除整个 job</h3>
<span class="tag violet">Input</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>画布顶部输入视频缩略图只有切换和预览,没有删除入口;用户清理视频队列时只能删关键帧或生成视频任务,不能删除整个输入视频。</p>
<p><strong>改动:</strong>新增 <code>DELETE /jobs/{id}</code>,前端新增 <code>deleteJob</code><code>onDeleteJob</code>Input 缩略图右上角常驻删除按钮,确认后移除该 job、清理 URL 参数,并删除本地 <code>jobs/&lt;id&gt;</code> 目录。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>web/lib/api.ts</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 大图预览改为尺寸感知 Fit / 1:1</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">HoverPreview</span>
</header>
<div class="body">
<p><strong>问题:</strong>有些源视频和图片分辨率很大,完全按原尺寸展示会撑爆画布;但统一固定预览大小又会丢失素材真实尺寸感知。</p>
<p><strong>改动:</strong><code>HoverPreview</code> 默认按原比例适应可视区域,角标显示原始分辨率和当前 Fit 百分比;点击钉住后可切换到 <code>1:1</code>,超大素材在预览框内滚动查看局部。</p>
<p><strong>影响:</strong><code>web/components/nodes/hover-preview.tsx</code>。视觉缩略图高度同步放大,图片缩略图的复制 / 删除按钮改为常驻大号 icon。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 缩略图滑动条改为大号可拖轨道</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">Thumbnail</span>
</header>
<div class="body">
<p><strong>问题:</strong>节点上方缩略图横排内容多时,原生横向滚动条太细且在画布缩放下不容易点中,用户很难拖动。</p>
<p><strong>改动:</strong>新增 <code>FloatingThumbnailStrip</code> / <code>ThumbnailScrollRail</code>,在缩略图下方显示明显的大号紫色拖动轨道;轨道支持点击跳转、按住拖动和键盘左右移动,并用 <code>nodrag nopan</code> 避免误触发画布拖拽。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>web/app/globals.css</code><code>docs/source-analysis.html</code>。Input、VisualLab 以及保留的旧视觉节点缩略图条共用同一交互。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 三个视觉节点合并为画面工作台</h3>
<span class="tag violet">VisualLab</span>
<span class="tag blue">Canvas</span>
</header>
<div class="body">
<p><strong>问题:</strong>镜头拆解、元素改造、生成视频三个卡片在主画布上占同等权重,但它们只是视觉素材状态展示和入口;真正处理都在点击后的工作台/面板中完成。</p>
<p><strong>改动:</strong>新增 <code>VisualLabNode</code>,把关键帧、元素 cutout、视频任务缩略图合并到一个“画面工作台 · Visual Lab”卡片里DAG 从 <code>Input → Keyframe → Storyboard → VideoGen → Compose</code> 简化为 <code>Input → VisualLab → Compose</code>,音频线仍独立。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code>。底层数据、接口和旧节点组件暂不删除。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 修复节点右下角缩放点击偏移</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">Resize</span>
</header>
<div class="body">
<p><strong>问题:</strong>点击或轻微拖动卡片右下角缩放把手时,节点会突然偏移/跳变,影响在无限画布上精调卡片大小。</p>
<p><strong>原因:</strong><code>getComputedStyle(nodeEl).width/height</code> 读到的已经是 ReactFlow 节点坐标下的布局尺寸,旧逻辑又除了一次 <code>zoom</code>,导致拖动起始宽高被放大或缩小;点击时 1px 级 pointermove 也会立刻写入错误宽高。</p>
<p><strong>改动:</strong>起始宽高直接使用 computed style只有鼠标移动量按 zoom 换算;增加 2px 点击死区,单纯点击缩放角不再改写节点尺寸。</p>
<p><strong>影响:</strong><code>web/components/nodes/resize-handle.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-14 · 缩略图 hover 原尺寸预览贴缩略图上边缘</h3>
<span class="tag violet">Canvas</span>
<span class="tag blue">HoverPreview</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户要的是“鼠标停在卡片缩略图上时,原尺寸图片/视频在该缩略图上方边缘展示,并且仍属于无限画布”;不能贴节点卡片上边缘,也不能放到页面 fixed 层。</p>
<p><strong>改动:</strong><code>HoverPreview</code> 增加缩略图锚点坐标,预览层仍渲染在 ReactFlow 节点 DOM 内,但用缩略图的中心 x 和 top y 定位预览底边贴缩略图上边缘Input 视频、镜头拆解关键帧、元素改造 cutout、生成视频缩略图统一走该逻辑。</p>
<p><strong>影响:</strong><code>web/components/nodes/hover-preview.tsx</code><code>web/components/nodes/index.tsx</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 打开应用自动恢复历史 job</h3>
<span class="tag blue">API</span>
<span class="tag violet">Page</span>
</header>
<div class="body">
<p><strong>问题:</strong>前端只从 URL <code>?job=</code> 读 job 列表,没有任何本地或后端列表回填,打开 <code>/</code> 不带参数就是空白,之前跑过的 job 看不见。</p>
<p><strong>改动:</strong>后端新增 <code>GET /jobs</code> 列表接口(返回 <code>JobSummary</code>id/url/status/progress/duration/width/height/video_url/frame_count/video_count/thumbnail/error/mtime按 state.json mtime 倒序,可带 <code>limit</code>)。前端 <code>page.tsx</code> 启动逻辑URL 有 <code>?job=</code> 时尊重 URL没有时自动调 <code>listJobs()</code> 拿全部历史,反转后让最新 job 落在末尾active。持久化基于 <code>api/jobs/&lt;id&gt;/state.json</code> 磁盘文件,不依赖浏览器存储,换浏览器/清缓存都不会傻。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>JobSummary</code> 类型 + <code>list_jobs</code> endpoint<code>web/lib/api.ts</code><code>JobSummary</code> + <code>listJobs</code>)、<code>web/app/page.tsx</code>(启动 useEffect</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 允许骨骼人使用按摩仪后状态变好</h3>
<span class="tag blue">Prompt</span>
</header>
<div class="body">
<p><strong>问题:</strong>本轮参考素材的主体可能是人形骷髅/透明骨骼人,不应被提示词强行替换成普通真人;核心是“用了 SKG 颈部按摩仪后状态变好”。</p>
<p><strong>改动:</strong>prompt 允许保留人形骷髅作为视觉隐喻,要求正确佩戴 U 形颈部按摩仪,并通过从僵硬/揉脖子/疲惫到抬头/肩颈舒展/放松来表现状态改善;同时禁止恐怖化、血腥化或夸大医疗治愈。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 生视频提示词改为产品图优先</h3>
<span class="tag blue">Prompt</span>
<span class="tag violet">StoryboardWorkbench</span>
</header>
<div class="body">
<p><strong>问题:</strong>已经有真实 SKG 产品图后,提示词还偏“借鉴原视频”,容易让模型保留竞品或改变产品形态。</p>
<p><strong>改动:</strong>生成视频 prompt 明确“上传的 SKG 产品图是唯一产品真源”,首尾帧只控制起止构图、场景和动作,原视频链接只参考节奏和镜头调度;增加产品一致性、产品呈现、禁止非 SKG 产品/竞品包装/随机新增结构等约束。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 产品参考支持本地上传和拖拽</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>SKG 产品参考不能只依赖从关键帧/元素里复制,用户需要手动上传同一产品的多角度图。</p>
<p><strong>改动:</strong><code>SKG 产品参考</code> 区支持点击上传和拖拽上传图片,一次可传多张,最多保留 6 张。后端新增 <code>POST /jobs/{job_id}/assets</code> 保存产品图资产,并返回 <code>kind: "asset"</code><code>ImageRef</code>;生成视频时这些资产会作为产品参考传入 Ark。</p>
<p><strong>影响:</strong><code>web/components/storyboard-workbench.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 首尾帧编排增加 SKG 产品槽</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>首尾帧可以控制视频起止,但还需要单独指定 SKG 产品图,避免模型只模仿原视频动作而没有稳定产品外观。</p>
<p><strong>改动:</strong>分镜编排区新增 <code>SKG 产品</code> 槽,和首帧、尾帧并列;生成视频时把该槽作为 <code>product_image</code> 提交。Ark 请求会附加一张 <code>reference_image</code> 产品参考图;如果接口不接受额外参考图,后端自动回退到首尾帧生成。</p>
<p><strong>影响:</strong><code>web/components/storyboard-workbench.tsx</code><code>web/app/page.tsx</code><code>api/main.py</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 分镜编排改为首尾帧生成</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>赶交付时顶部横向分镜缩略条占空间4 图槽也不如“首帧到尾帧”直接;用户希望直接做首尾帧视频生成。</p>
<p><strong>改动:</strong>移除 <code>StoryboardBar</code> 的横向分镜缩略图区域,只保留标题栏和展开按钮;<code>StoryboardWorkbench</code> 改成首帧 / 尾帧两个槽,首帧默认当前分镜,尾帧默认下一张已选分镜,也可从剪贴板粘贴指定结束画面。后端 <code>/storyboard/video</code> 支持 <code>first_image</code>/<code>last_image</code>Ark 请求同时传 <code>first_frame</code>/<code>last_frame</code>,如果接口不接受尾帧字段则自动回退到单首帧。</p>
<p><strong>影响:</strong><code>web/components/storyboard-bar.tsx</code><code>web/components/storyboard-workbench.tsx</code><code>web/app/page.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 生视频携带原视频链接做节奏参考</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户赶交付,希望直接把上传的原视频链接给视频模型参考,而不是只靠单张关键帧。</p>
<p><strong>改动:</strong>前端提交生视频时增加 <code>source_ref: { kind: "source_video", url: job.url }</code>Ark 请求体在文本 prompt 和首帧之外追加 <code>video_url</code> 参考视频,用于模仿节奏、镜头运动和动作顺序。如果 Ark 返回 400/422 不接受参考视频字段,后端自动回退到“当前关键帧首帧生成”,保证这次不会直接阻断出片。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code><code>docs/source-analysis.html</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 快速出片改为关键帧直生视频</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag blue">Prompt</span>
</header>
<div class="body">
<p><strong>问题:</strong>赶交付时不适合再让 4 图槽决定首帧;如果某个槽里是抠图元素,模型会拿碎元素当第一帧,视频容易不连贯。</p>
<p><strong>改动:</strong>“生成视频”按钮改成直接用当前分镜关键帧作为首帧提交4 图槽和改造目标只作为提示词参考;提示词强调一镜到底、首帧稳定、时间线连续、禁止跳切/换场景/主体变形。后端取关键帧时优先使用未应用的清洗版,否则使用当前 frame 文件。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/storyboard-workbench.tsx</code><code>api/main.py</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 生视频支持火山方舟 Ark 异步任务</h3>
<span class="tag rose">VideoGenNode</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户提供火山方舟 <code>https://ark.cn-beijing.volces.com/api/v3</code> 作为生视频通道;这个通道不是 Poe 的 <code>/videos</code> 形态,而是内容生成异步任务。</p>
<p><strong>改动:</strong>后端识别 Ark base 后,提交改为内容生成任务接口,火山方舟直连使用 <code>POST /contents/generations/tasks</code>SKG 豆包网关使用 <code>POST /api/v3/contents/generations/tasks</code>。请求体使用 <code>content</code> 数组:文本 prompt + 首帧 <code>image_url</code> data URL轮询对应的 <code>{id}</code> 任务地址,成功后读取 <code>content.video_url</code> 下载 MP4。本机默认 Seedance 模型改为 Ark 可见的 <code>doubao-seedance-2-0-fast-260128</code></p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>docs/source-analysis.html</code>。本机 <code>api/.env</code> 需要把 <code>VIDEO_API_BASE_URL</code>/<code>VIDEO_API_KEY</code>/<code>VIDEO_CREATE_PATHS</code>/<code>VIDEO_STATUS_PATH</code> 指向 Ark。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 生视频改接 Poe 视频模型</h3>
<span class="tag rose">VideoGenNode</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>SKG ezlink 的 OpenAI 兼容 base 可列出部分模型,但常规 <code>/videos</code> 入口返回 404/unsupported用户确认可用的视频模型在 Poe 通道里。</p>
<p><strong>改动:</strong>后端新增 <code>POE_API_BASE_URL</code>/<code>POE_API_KEY</code> 配置,未显式配置 <code>VIDEO_API_BASE_URL</code> 时优先走 PoeSeedance / Kling / Veo/Voe 业务别名默认映射到 Poe 真实模型 <code>seedance-2-fast</code><code>kling-omni</code><code>veo-3.1-fast</code>。Poe 提交使用 <code>input_image</code> base64继续轮询 <code>/videos/{id}</code> 并下载 <code>/videos/{id}/content</code></p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>docs/source-analysis.html</code>。密钥只放本地 <code>api/.env</code>,不进入源码解析页。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 生视频提交不再被前端锁死</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>虽然当前探测到常见视频入口返回 404/unsupported但模型层确实有视频模型不能在前端简单判定“未开通”并禁用。</p>
<p><strong>改动:</strong>撤掉分镜编排里的前置禁用;后端允许提交 seedance / kling / veo / voe并支持通过 <code>VIDEO_CREATE_PATHS</code> 逗号分隔配置多个候选生成入口,逐个尝试。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>web/app/page.tsx</code><code>web/components/storyboard-workbench.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 生视频错误提示改为可读原因</h3>
<span class="tag rose">VideoGenNode</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>提交生视频失败时,前端把 <code>generateStoryboardVideo 503 {"detail": ...}</code> 原样展示,用户无法快速判断是配置、端点还是 UI 问题。</p>
<p><strong>改动:</strong><code>generateStoryboardVideo</code> 解析后端 JSON 的 <code>detail</code> 后再抛错后端错误文案区分“模型存在”和“入口不可用”Video Gen 失败卡把 <code>/videos 404</code> 长错误压缩成一句可读原因。</p>
<p><strong>影响:</strong><code>web/lib/api.ts</code><code>web/components/nodes/index.tsx</code><code>api/main.py</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · Video Gen 卡片增加复制和删除</h3>
<span class="tag rose">VideoGenNode</span>
<span class="tag blue">API</span>
</header>
<div class="body">
<p><strong>问题:</strong>Video Gen 节点上方失败/完成任务卡只有整卡点击复制,不够明确;失败任务也无法从界面清掉。</p>
<p><strong>改动:</strong>每个视频任务卡左上角增加复制 prompt 按钮,右上角增加删除任务按钮;后端新增 <code>DELETE /jobs/{job_id}/storyboard-videos/{video_id}</code>,删除 <code>generated_videos</code> 记录并清理本地任务目录。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>web/app/page.tsx</code><code>web/lib/api.ts</code><code>api/main.py</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 分镜编排接入真实生视频任务</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag rose">VideoGenNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>4 图槽已经粘贴参考图后,用户要直接调用生视频 API而不是只生成 prompt 或图片任务。</p>
<p><strong>改动:</strong>分镜编排明细区增加 Seedance / Kling / Veo 3 模型选择和“调用模型生成视频”按钮;后端新增 <code>/jobs/{job_id}/frames/{idx}/storyboard/video</code>。提交后按 <code>VIDEO_CREATE_PATHS</code> 逐个尝试生成入口,成功后轮询并保存 MP4失败时保留任务卡和具体入口错误方便继续排查网关实际路径。<code>VideoGenNode</code> 读取 <code>job.generated_videos</code> 展示排队、生成中、失败和完成视频。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>api/.env.example</code><code>web/components/storyboard-workbench.tsx</code><code>web/components/nodes/index.tsx</code><code>web/app/page.tsx</code><code>web/lib/api.ts</code>。Sora 不再作为默认模型;真实模型 ID 通过 <code>VIDEO_MODEL_SEEDANCE</code><code>VIDEO_MODEL_KLING</code><code>VIDEO_MODEL_VEO3</code> 配置,真实视频 API 地址通过 <code>VIDEO_API_BASE_URL</code>/<code>VIDEO_API_KEY</code> 配置。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 分镜编排下拉区支持上推缩小</h3>
<span class="tag violet">StoryboardWorkbench</span>
</header>
<div class="body">
<p><strong>问题:</strong>分镜编排明细区默认占用太多顶部面积,展开后下方画布空间不足。</p>
<p><strong>改动:</strong>明细区默认高度降为 320px并增加底部拖拽手柄可上推缩到 180px也可下拉放大查看完整内容。</p>
<p><strong>影响:</strong><code>web/components/storyboard-workbench.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 分镜缩略图条与编排明细合并为一个下拉区</h3>
<span class="tag violet">StoryboardBar</span>
<span class="tag violet">StoryboardWorkbench</span>
</header>
<div class="body">
<p><strong>问题:</strong>顶部分镜缩略图条和下方内嵌工作台都带分镜导航,看起来像两个不同板块。</p>
<p><strong>改动:</strong><code>StoryboardBar</code> 成为唯一分镜导航;<code>StoryboardWorkbench</code> 移除自己的标题栏、左侧分镜列表和底部快捷栏,只保留当前分镜的 4 图槽与改造目标明细。</p>
<p><strong>影响:</strong><code>web/components/storyboard-bar.tsx</code><code>web/components/storyboard-workbench.tsx</code><code>web/app/page.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 分镜头编排工作台改为内嵌下拉</h3>
<span class="tag violet">StoryboardWorkbench</span>
<span class="tag violet">StoryboardBar</span>
</header>
<div class="body">
<p><strong>问题:</strong>元素改造节点等入口仍会打开 <code>fixed inset-0</code> 的全屏 <code>StoryboardWorkbench</code>,用户感觉像跳转页面。</p>
<p><strong>改动:</strong>移除 <code>StoryboardWorkbench</code> 的 portal 全屏承载方式,改为渲染在顶部分镜栏下方;所有“打开编排”入口只展开这个内嵌区域。</p>
<p><strong>影响:</strong><code>web/components/storyboard-workbench.tsx</code><code>web/components/storyboard-bar.tsx</code><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 钉住面板停靠到分镜头编排边缘</h3>
<span class="tag orange">KeyframePanelNode</span>
<span class="tag violet">StoryboardBar</span>
</header>
<div class="body">
<p><strong>问题:</strong>关键帧详情钉住在浏览器左侧固定位置时,会遮挡顶部分镜头编排栏展开后的缩略图区域。</p>
<p><strong>改动:</strong><code>StoryboardBar</code> 增加稳定 DOM 标记;钉住面板实时读取该区域下边缘,并吸附到其下方。展开 / 折叠分镜头编排时,钉住面板自动让位。</p>
<p><strong>影响:</strong><code>web/components/storyboard-bar.tsx</code><code>web/components/nodes/index.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 顶部分镜头编排不再跳转全屏工作台</h3>
<span class="tag violet">StoryboardBar</span>
</header>
<div class="body">
<p><strong>问题:</strong>顶部 <code>StoryboardBar</code> 的“进入编排”和分镜缩略图点击会打开全屏 <code>StoryboardWorkbench</code>,打断当前画布流程。</p>
<p><strong>改动:</strong>顶部按钮改为“展开编排”,只下拉展示当前分镜列表;缩略图点击只聚焦该分镜,不再触发全屏跳转。后续已把工作台整体改成内嵌下拉,见上方最新记录。</p>
<p><strong>影响:</strong><code>web/components/storyboard-bar.tsx</code><code>web/app/page.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 钉住关键帧详情改为左侧停靠</h3>
<span class="tag orange">KeyframePanelNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>钉住后仍像自由浮层一样停在画布附近,用户继续缩放画布或调整面板尺寸时容易把它和画布节点混在一起。</p>
<p><strong>改动:</strong>钉住后统一吸附到浏览器左侧边缘,脱离 ReactFlow 画布缩放;钉住瞬间把当前可见大小转换成面板真实尺寸,之后只由右下角拖拽或标题栏按钮调整。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code>;钉住语义从“原地浮在上层”改为“左侧停靠工作面板”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 关键帧详情支持右下角拖拽缩放和上层钉住</h3>
<span class="tag orange">KeyframePanelNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>只有按钮缩放不够直观;钉住后仍作为画布节点,会继续随 ReactFlow 画布缩放。</p>
<p><strong>改动:</strong>增加右下角拖拽缩放手柄;钉住时通过 portal 固定到浏览器上层,脱离 ReactFlow 画布缩放和平移。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code><code>web/app/page.tsx</code>;未钉住时仍是画布节点,钉住后保持屏幕固定位置。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 关键帧详情面板增加钉住按钮</h3>
<span class="tag orange">KeyframePanelNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>面板可以拖动后,用户仍可能误拖;切换图片时希望保持固定工作位置。</p>
<p><strong>改动:</strong>在标题栏增加钉子按钮。钉住后面板节点禁止拖动,切换关键帧只切换内容不移动位置;取消钉住后可继续拖动。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 切换关键帧不再重置详情面板位置</h3>
<span class="tag orange">KeyframePanelNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>用户把关键帧详情面板拖到合适位置后,再点击下一张关键帧会把面板拉回默认位置,造成视觉疲劳。</p>
<p><strong>改动:</strong>已打开的面板只切换内容,不移动位置;只有面板不存在、首次打开时才放到默认位置并自动聚焦。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code>;关闭后重新打开仍会出现在默认位置。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 关键帧详情面板增加缩放控制</h3>
<span class="tag orange">KeyframePanelNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>关键帧详情面板作为画布节点后可以随画布缩放,但面板自身没有尺寸控制,用户无法单独放大或缩小它。</p>
<p><strong>改动:</strong>在面板标题栏增加 <code>-</code>、百分比重置、<code>+</code> 控制,支持 75% 到 135% 的面板级缩放。</p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code>;点击新关键帧仍会找回到默认位置,缩放比例保留。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 关键帧详情从固定左侧抽屉迁到无限画布</h3>
<span class="tag orange">KeyframeNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>关键帧详情 / 元素提取面板固定在左侧 drawer和 ReactFlow 无限画布割裂,也不会跟随画布缩放。</p>
<p><strong>改动:</strong>移除主页面隐藏渲染的 <code>Dashboard</code> drawer 承载方式,新增独立 <code>keyframePanel</code> ReactFlow 节点来挂载 <code>FrameLightbox</code></p>
<p><strong>影响:</strong><code>web/app/page.tsx</code><code>web/components/nodes/index.tsx</code>;点关键帧后面板默认出现在流程左侧空白画布里,不遮挡 Input / Keyframe 主节点;标题栏可拖动,跟随 ReactFlow 平移和缩放。再次点击关键帧缩略图会把面板找回到默认位置,并自动把视野拉到“关键帧 + 面板”。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 元素改造 hover 预览简化为原帧预览</h3>
<span class="tag violet">StoryboardNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>元素改造节点的 hover 预览虽然已改为节点内显示,但仍比关键帧节点复杂,多了“来源原帧 / 提取元素”两栏和元素名称,信息过载。</p>
<p><strong>改动:</strong>改成和镜头拆解关键帧一致的简单预览:只显示来源原帧,底部显示分镜编号和时间。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code>;元素改造板块 hover 现在更轻,不干扰当前判断。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 元素改造 hover 预览改为节点内效果</h3>
<span class="tag violet">StoryboardNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>上一版把元素预览用 <code>createPortal</code> 挂到 <code>body</code>DevTools 里会出现额外 fixed 层,交互形态和关键帧节点不一致。</p>
<p><strong>改动:</strong>改成节点内 <code>group-hover</code> 预览,不再向 <code>body</code> 插入预览层。后续又简化为只展示来源原帧,见上方最新记录。</p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code>;元素改造板块的 DOM 和交互效果更接近关键帧缩略图。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 新增独立源码解析与协作地图</h3>
<span class="tag blue">docs</span>
</header>
<div class="body">
<p><strong>目的:</strong>把产品功能区、源码位置、接口、数据模型、需求描述方式固定下来,减少“描述不准导致改偏”。</p>
<p><strong>影响:</strong>新增 <code>docs/source-analysis.html</code>,不接入 Next 应用,不影响工作台运行。</p>
<p><strong>以后描述:</strong>可以直接引用本页的功能区名称、节点职责和源码文件名。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · Storyboard 元素缩略图 hover 预览修复</h3>
<span class="tag violet">StoryboardNode</span>
</header>
<div class="body">
<p><strong>问题:</strong>元素改造节点上方小图 hover 没有像镜头拆解节点一样显示原图预览,并且首次修复时出现运行时错误。</p>
<p><strong>原因:</strong>节点内部 overflow 裁剪了预览;随后 portal 预览里把变量写成了不存在的 <code>aspectRatio</code></p>
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code>。该记录之后又改为节点内预览,见上方最新记录。</p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 元素识别结果不再锁死</h3>
<span class="tag orange">元素提取</span>
</header>
<div class="body">
<p><strong>问题:</strong>Vision 识别可能错,但元素列表像最终结果;点击提取图会跳页面,打断用户思路。</p>
<p><strong>改动:</strong>支持元素改名、改英文提示、改位置、删除元素、重复提取、删除单张提取图;提取图不再用链接跳新页。</p>
<p><strong>影响:</strong><code>FrameLightbox</code><code>web/lib/api.ts</code><code>PATCH /jobs/{job_id}/frames/{idx}/elements/{element_id}</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 分镜编排入口聚焦到具体分镜</h3>
<span class="tag violet">StoryboardWorkbench</span>
</header>
<div class="body">
<p><strong>问题:</strong>从 Storyboard 或顶部分镜条进入编排时,没有明确定位到用户正在看的那一帧。</p>
<p><strong>改动:</strong>工作台接受 <code>focusedFrame</code>,点击缩略图会打开工作台并聚焦对应分镜。</p>
<p><strong>影响:</strong><code>page.tsx</code><code>StoryboardBar</code><code>StoryboardWorkbench</code><code>StoryboardNode</code></p>
</div>
</article>
<article class="change">
<header>
<h3>2026-05-13 · 视觉管线不再被 ASR 阻断</h3>
<span class="tag green">Pipeline</span>
</header>
<div class="body">
<p><strong>问题:</strong>SKG 网关 audio 不通时,视觉解析也容易被标记失败。</p>
<p><strong>改动:</strong><code>analyze</code> 主流程强调拆轨和关键帧,声音文案轨独立处理。</p>
<p><strong>影响:</strong><code>api/main.py</code><code>page.tsx</code>、节点语义说明。</p>
</div>
</article>
</div>
</section>
<section id="update-rule" data-search>
<h2>更新规则</h2>
<div class="callout">
<p>以后任何改动只要影响产品理解、节点职责、界面行为、数据模型、API、运行方式或用户操作路径都要同步更新本页的对应章节和“变更记录”。</p>
</div>
<table style="margin-top:14px">
<thead>
<tr><th>改动类型</th><th>必须更新本页哪里</th><th>原因</th></tr>
</thead>
<tbody>
<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>用户描述“点击哪里”时必须和真实路径一致。</td></tr>
<tr><td>修 bug</td><td>变更记录;如果暴露新坑,也更新当前已通与阻塞</td><td>让后续同类问题能快速定位。</td></tr>
</tbody>
</table>
</section>
</main>
</div>
<script>
const input = document.getElementById("search");
const searchable = Array.from(document.querySelectorAll("[data-search]"));
input.addEventListener("input", () => {
const q = input.value.trim().toLowerCase();
searchable.forEach((el) => {
const text = el.innerText.toLowerCase();
el.classList.toggle("hidden-by-search", q && !text.includes(q));
});
});
</script>
</body>
</html>