From 9957274a5f996da5259d0d7d16960272555d3de8 Mon Sep 17 00:00:00 2001 From: kang Date: Wed, 13 May 2026 00:00:53 +0800 Subject: [PATCH] auto-save 2026-05-13 00:00 (~5) --- .memory/worklog.json | 7 ++++++ web/app/page.tsx | 20 ++++----------- web/components/dashboard.tsx | 45 +++++++++++++++++++++++++++++----- web/components/lightbox.tsx | 26 +++++++++++++------- web/components/nodes/index.tsx | 3 +++ 5 files changed, 71 insertions(+), 30 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index fc9e5e7..8691849 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -496,6 +496,13 @@ "message": "auto-save 2026-05-12 23:49 (~2)", "hash": "25a1e63", "files_changed": 2 + }, + { + "ts": "2026-05-12T23:55:21+08:00", + "type": "commit", + "message": "auto-save 2026-05-12 23:55 (~2)", + "hash": "fd4c78f", + "files_changed": 2 } ] } diff --git a/web/app/page.tsx b/web/app/page.tsx index f796bec..1fee54c 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -15,7 +15,6 @@ import { import { ThemeToggle } from "@/components/theme-toggle" import { Dashboard } from "@/components/dashboard" import { addManualFrame, analyzeJob, createJob, getJob, uploadJob, type Job } from "@/lib/api" -import { FrameLightbox } from "@/components/lightbox" import { VideoLightbox } from "@/components/video-lightbox" const NODE_TYPES = { @@ -215,15 +214,18 @@ export default function Home() { submitting, analyzing, selectedFrames, + expandedFrame, onSubmitUrl: handleSubmit, onUploadFile: handleUpload, onAnalyze: handleAnalyze, onToggleFrame: handleToggleFrame, onExpandFrame: setExpandedFrame, + onCloseExpandedFrame: () => setExpandedFrame(null), onAddManualFrame: handleAddManualFrame, onOpenVideoLightbox: () => setVideoLightboxOpen(true), onSwitchJob: handleSwitchJob, - }), [job, jobs, activeJobId, submitting, analyzing, selectedFrames, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleAddManualFrame, handleSwitchJob]) + onJobUpdate: setJob as any, + }), [job, jobs, activeJobId, submitting, analyzing, selectedFrames, expandedFrame, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleAddManualFrame, handleSwitchJob, setJob]) // 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag) const [nodes, setNodes, onNodesChange] = useNodesState( @@ -305,19 +307,7 @@ export default function Home() { - {/* Lightbox 看大图 */} - {job && ( - setExpandedFrame(null)} - onChange={setExpandedFrame} - onToggleSelect={handleToggleFrame} - onJobUpdate={setJob} - /> - )} + {/* FrameLightbox 已嵌入 dashboard 的 keyframe drawer(embedded mode),不再独立浮动 */} {/* Video lightbox — InputNode 缩略图点击进入 */} = { @@ -142,7 +143,17 @@ export function Dashboard({ data }: Props) { const toggleTile = (key: string) => { setExpanded((prev) => (prev.has(key) ? new Set() : new Set([key]))) } - const closeTile = (_key: string) => setExpanded(new Set()) + const closeTile = (_key: string) => { + setExpanded(new Set()) + data.onCloseExpandedFrame() + } + + // 点关键帧缩略图时(onExpandFrame 触发),自动打开 keyframe drawer + useEffect(() => { + if (data.expandedFrame !== null && !expanded.has("keyframe")) { + setExpanded(new Set(["keyframe"])) + } + }, [data.expandedFrame]) const Tile = ({ tkey }: { tkey: string }) => { const t = TILES.find((x) => x.key === tkey)! @@ -187,13 +198,20 @@ export function Dashboard({ data }: Props) { {/* 合流 */} - {/* 展开面板 — 用 portal 渲染到 body 避免 backdrop-filter 影响 fixed 定位 */} + {/* 展开面板 — keyframe 有选中帧时变宽容纳 lightbox */} {expanded.size > 0 && mounted && createPortal(
- {TILES.filter((t) => expanded.has(t.key)).map((t) => ( + {TILES.filter((t) => expanded.has(t.key)).map((t) => { + const isKeyframeWithExpand = t.key === "keyframe" && data.expandedFrame !== null + return (
- {renderSection(t.key)} + {isKeyframeWithExpand && data.job ? ( + + ) : ( + renderSection(t.key) + )}
- ))} + ) + })} , document.body )} diff --git a/web/components/lightbox.tsx b/web/components/lightbox.tsx index 99dff39..820b361 100644 --- a/web/components/lightbox.tsx +++ b/web/components/lightbox.tsx @@ -14,9 +14,10 @@ interface Props { onChange: (idx: number) => void onToggleSelect: (idx: number) => void onJobUpdate?: (job: Job) => void + embedded?: boolean // true=嵌入到容器里(无 fixed),false=独立浮动卡(默认) } -export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, onChange, onToggleSelect, onJobUpdate }: Props) { +export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, onChange, onToggleSelect, onJobUpdate, embedded = false }: Props) { const [extractPrompt, setExtractPrompt] = useState("") const [describing, setDescribing] = useState(false) const [mounted, setMounted] = useState(false) @@ -66,11 +67,14 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o navigator.clipboard.writeText(text).then(() => toast.success("已复制")) } - return createPortal( + const content = (
e.stopPropagation()} - className="fixed z-[100] rounded-2xl border border-white/15 bg-black/70 backdrop-blur-2xl overflow-hidden flex flex-col" - style={{ + className={`rounded-2xl border border-white/15 overflow-hidden flex flex-col ${embedded ? "" : "fixed z-[100] bg-black/70 backdrop-blur-2xl"}`} + style={embedded ? { + height: "100%", + background: "transparent", + } : { top: 80, right: 16, width: 740, @@ -79,8 +83,11 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o animation: "drawer-in 0.24s cubic-bezier(0.32, 0.72, 0, 1)", }} > - {/* 顶部工具栏 — 切换 / 关闭 */} -
+ {/* 顶部工具栏 — 切换 / 关闭,用 keyframe 橙红配色 */} +
- ←/→ 切换 · Space 选用 · ESC 关闭 · 可拖侧画布看背后 + ←/→ 切换 · Space 选用 · ESC 关闭
-
, - document.body, +
) + + return embedded ? content : createPortal(content, document.body) } diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx index 82400a7..7056a75 100644 --- a/web/components/nodes/index.tsx +++ b/web/components/nodes/index.tsx @@ -15,14 +15,17 @@ export interface NodeData { submitting: boolean analyzing: boolean selectedFrames: Set + expandedFrame: number | null onSubmitUrl: (url: string) => void onUploadFile: (file: File) => void onAnalyze: () => void onToggleFrame: (idx: number) => void onExpandFrame: (idx: number) => void + onCloseExpandedFrame: () => void onAddManualFrame: (t: number) => void onOpenVideoLightbox: () => void onSwitchJob: (id: string) => void + onJobUpdate: (j: Job) => void } /* ---- 状态映射工具 ---- */