diff --git a/.memory/worklog.json b/.memory/worklog.json
index 8c97666..00df4bc 100644
--- a/.memory/worklog.json
+++ b/.memory/worklog.json
@@ -1888,6 +1888,19 @@
"message": "auto-save 2026-05-13 16:17 (~3)",
"hash": "f891cbc",
"files_changed": 3
+ },
+ {
+ "ts": "2026-05-13T16:23:35+08:00",
+ "type": "commit",
+ "message": "auto-save 2026-05-13 16:23 (~6)",
+ "hash": "467e8f6",
+ "files_changed": 6
+ },
+ {
+ "ts": "2026-05-13T08:27:40Z",
+ "type": "session-heartbeat",
+ "message": "Claude 会话活跃 · 最近命令:claude · 3 项未提交变更 · 最近提交:auto-save 2026-05-13 16:23 (~6)",
+ "files_changed": 3
}
]
}
diff --git a/web/app/page.tsx b/web/app/page.tsx
index d69fe7a..1d3c67b 100644
--- a/web/app/page.tsx
+++ b/web/app/page.tsx
@@ -386,6 +386,7 @@ export default function Home() {
open={workbenchOpen}
onClose={() => setWorkbenchOpen(false)}
onJobUpdate={setJob as any}
+ clipboard={clipboard}
/>
diff --git a/web/components/storyboard-workbench.tsx b/web/components/storyboard-workbench.tsx
index 77ec62e..3284720 100644
--- a/web/components/storyboard-workbench.tsx
+++ b/web/components/storyboard-workbench.tsx
@@ -1,7 +1,7 @@
"use client"
import { useEffect, useState, useRef, type ReactNode } from "react"
import { createPortal } from "react-dom"
-import { X, LayoutGrid, Loader2, Check, Sparkle, Wand2 } from "lucide-react"
+import { X, LayoutGrid, Loader2, Check, Wand2 } from "lucide-react"
import {
type Job, type StoryboardScene, type ImageRef,
effectiveFrameUrl, updateStoryboard, resolveImageRefUrl,
@@ -83,21 +83,6 @@ export function StoryboardWorkbench({ job, selectedFrames, open, onClose, onJobU
}, 600)
}
- // 参考图候选 = 全部已推送图
- const pushedImages = job.storyboard_images ?? []
- const refOptions = pushedImages
- .map((p) => {
- const url = p.kind === "keyframe"
- ? effectiveFrameUrl(job.id, { index: p.frame_idx, cleaned_applied: false })
- : (p.element_id && p.cutout_id
- ? (p.cutout_id === p.element_id
- ? cutoutUrl(job.id, p.frame_idx, p.element_id)
- : cutoutUrl(job.id, p.frame_idx, p.element_id, p.cutout_id))
- : "")
- return { ref_id: p.ref_id, url, label: p.label || "", frame_idx: p.frame_idx, kind: p.kind }
- })
- .filter((r) => r.url)
-
const aspect = job.height > 0 ? `${job.width}/${job.height}` : "9/16"
return createPortal(
@@ -111,7 +96,7 @@ export function StoryboardWorkbench({ job, selectedFrames, open, onClose, onJobU
分镜头编排工作台
- {frames.length} 分镜 · {pushedImages.length} 素材
+ {frames.length} 分镜
@@ -184,106 +169,104 @@ export function StoryboardWorkbench({ job, selectedFrames, open, onClose, onJobU
)}
- {/* 右侧详情 */}
+ {/* 右侧详情 — 4 图槽 + 时长 */}
{!focusFrame ? (
从左侧选一个分镜开始编排
) : (
- {/* 顶栏 */}
-
+ {/* 顶栏:分镜信息 + 剪贴板提示 + 时长 */}
+
- 分镜 {focusSeq} {focusFrame.timestamp.toFixed(2)}s
+ 分镜 {focusSeq}
+ {focusFrame.timestamp.toFixed(2)}s
+
+
+ {clipboard ? (
+
+ 📋 剪贴板:{clipboard.label || (clipboard.kind === "keyframe" ? "关键帧" : "元素")}
+
+ ) : (
+
剪贴板为空 · 在 DAG / lightbox 上点 📋 复制
+ )}
+
- {/* 左大图 + 右字段 */}
-
-
-

-
- {focusFrame.cleaned_applied ? "✨ 已清洗版" : "原图"}
-
-
-
-
-
- queueSave({ ...form, subject: v })} />
- queueSave({ ...form, product: v })} />
- queueSave({ ...form, scene: v })} />
- queueSave({ ...form, duration: v })} />
-
-
queueSave({ ...form, action: v })}
- rows={4}
- />
-
-
-
- {/* 参考图选择 */}
-
-
-
-
- 选用参考图
-
- · 选用 {form.reference_ids.length} / 可选 {refOptions.length}
-
-
-
- {refOptions.length === 0 ? (
-
- 暂无可选参考图 · 到关键帧节点 / 元素提取图 / 分镜头编排节点等处点 ⬆ 上推到这里
-
- ) : (
-
- {refOptions.map((r) => {
- const checked = form.reference_ids.includes(r.ref_id)
- return (
-
- 下一阶段:基于上述字段 + 参考图,调视频生成模型(Seedance / Kling / Veo3)生成该分镜的视频片段
+ 下一阶段:基于 4 图槽(主体 / 场景 / 产品 / 动作)+ 时长,调视频生成模型(Seedance / Kling / Veo3)生成该分镜的视频片段