auto-save 2026-05-14 06:44 (~4)

This commit is contained in:
2026-05-14 06:44:41 +08:00
parent 27a3da4b3a
commit c50c4392dc
4 changed files with 70 additions and 35 deletions

View File

@@ -1,19 +1,5 @@
{
"entries": [
{
"files_changed": 3,
"hash": "64db093",
"message": "auto-save 2026-05-12 18:35 (~3)",
"ts": "2026-05-12T18:35:34+08:00",
"type": "commit"
},
{
"files_changed": 2,
"hash": "864781d",
"message": "auto-save 2026-05-12 18:40 (~2)",
"ts": "2026-05-12T18:41:07+08:00",
"type": "commit"
},
{
"files_changed": 3,
"hash": "5a914b9",
@@ -3347,6 +3333,19 @@
"type": "session-heartbeat",
"message": "Codex 会话活跃 · 最近命令codex · 3 项未提交变更 · 最近提交auto-save 2026-05-14 06:33 (~5)",
"files_changed": 3
},
{
"ts": "2026-05-14T06:39:09+08:00",
"type": "commit",
"message": "auto-save 2026-05-14 06:38 (~3)",
"hash": "27a3da4",
"files_changed": 3
},
{
"ts": "2026-05-13T22:43:14Z",
"type": "session-heartbeat",
"message": "Claude 会话活跃 · 最近命令claude · 4 项未提交变更 · 最近提交auto-save 2026-05-14 06:38 (~3)",
"files_changed": 4
}
]
}

View File

@@ -725,11 +725,15 @@ SUBJECT_VIEW_LABELS: dict[str, str] = {
"top": "顶部视角",
"bottom": "底部视角",
"expression_neutral": "中性表情",
"expression_smile": "微笑表情",
"expression_happy": "开心表情",
"expression_angry": "生气表情",
"expression_sad": "难过表情",
"expression_relaxed": "放松表情",
"expression_serious": "严肃表情",
"expression_surprised": "惊讶表情",
"action_walk": "走路动作",
"action_turn": "转身动作",
"action_sit": "坐姿动作",
"action_hold": "手持动作",
"action_use": "使用动作",

View File

@@ -730,7 +730,7 @@ SubjectAsset {
<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 /elements/{element_id}/subject-assets</code></td><td><code>generateSubjectAssets</code></td><td>根据参考帧重新绘制一个统一主体资产包;前端默认把全部关键帧作为 <code>source_frame_indices</code>,如果用户手动选择了关键帧则只传已选帧,后端拼参考板。默认输出六张标准站立/转身参考图,纯白/黑背景,不含其他元素,并裁去空白让主体占满画面。</td></tr>
<tr><td>主体资产包</td><td><code>POST /elements/{element_id}/subject-assets</code></td><td><code>generateSubjectAssets</code></td><td>根据参考帧重新绘制一个统一主体资产包;前端默认把全部关键帧作为 <code>source_frame_indices</code>,如果用户手动选择了关键帧则只传已选帧,后端拼参考板。人物默认输出六张身份标准图,另有表情补充和动作补充分组可选;纯白/黑背景,不含其他元素,并裁去空白让主体占满画面。</td></tr>
<tr><td>场景资产</td><td><code>POST /frames/{idx}/scene-asset</code></td><td><code>generateSceneAsset</code></td><td>在统一主体资产之后,按当前关键帧生成去主体背景板;请求包含 <code>scene_mode</code><code>scene_style</code><code>prompt</code><code>source_frame_indices</code>,可用左侧选择的参考帧 + 右侧关键词生成原场景补背景、相似新场景或同构换风格,保留历史版本用于人工审核。</td></tr>
<tr><td>分镜保存</td><td><code>PUT /frames/{idx}/storyboard</code></td><td><code>updateStoryboard</code></td><td>保存 4 图槽、时长和改造说明。</td></tr>
<tr><td>生图</td><td><code>POST /frames/{idx}/generate</code></td><td><code>generateImage</code></td><td>基于关键帧或已选生成图做 image-to-image目前可用。</td></tr>
@@ -862,7 +862,7 @@ SubjectAsset {
</header>
<div class="body">
<p><strong>问题:</strong>主体资产不是抠图,也不是只看当前单帧生成多角度;主体页需要看到全部参考帧,并用这些参考重新绘制一个完整主体。</p>
<p><strong>改动:</strong><code>FrameLightbox</code> 在“主体资产”页左侧显示参考帧网格,优先纳入所有已清洗帧,额外已选帧也会并入;小图排列,可点击切换当前帧。右侧仍负责统一主体确认和生成。人物/生物默认视图改为六张标准站立/转身图:正面、背面、左侧、右侧、左前 45°、右前 45°。</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>

View File

@@ -1365,25 +1365,57 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o
{isSubjectGenerating ? "生成" : "生成"}
</button>
</div>
<div className="mb-2 flex flex-wrap gap-1">
{viewOptions.map(([value, label]) => {
const active = activeViews.includes(value)
return (
<button
key={value}
type="button"
onClick={() => toggleSubjectView(e.id, value, currentKind)}
className={`rounded border px-1.5 py-0.5 text-[9.5px] transition ${
active
? "border-violet-300/60 bg-violet-500/40 text-white"
: "border-white/10 bg-black/25 text-white/45 hover:text-white"
}`}
>
{label}
</button>
)
})}
</div>
{currentKind === "living" ? (
<div className="mb-2 space-y-2">
{LIVING_VIEW_GROUPS.map((group) => (
<div key={group.title} className="rounded-md border border-white/8 bg-black/20 px-2 py-1.5">
<div className="mb-1 flex items-center justify-between gap-2">
<div className="text-[10px] font-medium text-white/78">{group.title}</div>
<div className="text-[8.5px] text-white/32 truncate">{group.hint}</div>
</div>
<div className="flex flex-wrap gap-1">
{group.options.map(([value, label]) => {
const active = activeViews.includes(value)
return (
<button
key={value}
type="button"
onClick={() => toggleSubjectView(e.id, value, currentKind)}
className={`rounded border px-1.5 py-0.5 text-[9.5px] transition ${
active
? "border-violet-300/60 bg-violet-500/40 text-white"
: "border-white/10 bg-black/25 text-white/45 hover:text-white"
}`}
>
{label}
</button>
)
})}
</div>
</div>
))}
</div>
) : (
<div className="mb-2 flex flex-wrap gap-1">
{viewOptions.map(([value, label]) => {
const active = activeViews.includes(value)
return (
<button
key={value}
type="button"
onClick={() => toggleSubjectView(e.id, value, currentKind)}
className={`rounded border px-1.5 py-0.5 text-[9.5px] transition ${
active
? "border-violet-300/60 bg-violet-500/40 text-white"
: "border-white/10 bg-black/25 text-white/45 hover:text-white"
}`}
>
{label}
</button>
)
})}
</div>
)}
{subjectAssets.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{subjectAssets.slice(-12).map((asset) => (