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 ( - + )} +
+
+ {ref && url ? ( + {label} + ) : ( +
+ 空 · 点下方
「粘贴剪贴板」
填入图片
- - ) - })} -
- )} -
+ )} +
+ +
+ {ref?.label || placeholder} +
+
+ ) + })} +
{/* 生成按钮(Phase 2 占位) */}
@@ -296,7 +279,7 @@ export function StoryboardWorkbench({ job, selectedFrames, open, onClose, onJobU ⚡ 生成此分镜视频片段(Phase 2 待实施)
- 下一阶段:基于上述字段 + 参考图,调视频生成模型(Seedance / Kling / Veo3)生成该分镜的视频片段 + 下一阶段:基于 4 图槽(主体 / 场景 / 产品 / 动作)+ 时长,调视频生成模型(Seedance / Kling / Veo3)生成该分镜的视频片段