diff --git a/.memory/worklog.json b/.memory/worklog.json index d26c9e5..93a2eca 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1257,6 +1257,19 @@ "message": "auto-save 2026-05-13 10:38 (~5)", "hash": "98d4ecb", "files_changed": 5 + }, + { + "ts": "2026-05-13T10:44:25+08:00", + "type": "commit", + "message": "auto-save 2026-05-13 10:44 (~4)", + "hash": "e4989f6", + "files_changed": 4 + }, + { + "ts": "2026-05-13T02:47:36Z", + "type": "session-heartbeat", + "message": "Claude 会话活跃 · 最近命令:claude · 2 项未提交变更 · 最近提交:auto-save 2026-05-13 10:44 (~4)", + "files_changed": 2 } ] } diff --git a/web/app/page.tsx b/web/app/page.tsx index 1a1b414..b25a0ac 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -14,7 +14,7 @@ import { } from "@/components/nodes" import { ThemeToggle } from "@/components/theme-toggle" import { Dashboard, type DashboardHandle } from "@/components/dashboard" -import { addManualFrame, analyzeJob, createJob, getJob, uploadJob, type Job } from "@/lib/api" +import { addManualFrame, analyzeJob, createJob, getJob, uploadJob, deleteFrame, deleteGeneratedImage, type Job } from "@/lib/api" import { VideoLightbox } from "@/components/video-lightbox" const NODE_TYPES = { @@ -160,6 +160,35 @@ export default function Home() { }) }, []) + const handleDeleteFrame = useCallback(async (idx: number) => { + if (!activeJobId) return + try { + const updated = await deleteFrame(activeJobId, idx) + setJob(updated) + setSelectedFrames((prev) => { + if (!prev.has(idx)) return prev + const next = new Set(prev) + next.delete(idx) + return next + }) + if (expandedFrame === idx) setExpandedFrame(null) + toast.success(`分镜 ${idx + 1} 已删除`) + } catch (e) { + toast.error("删除失败:" + (e instanceof Error ? e.message : String(e))) + } + }, [activeJobId, expandedFrame, setJob]) + + const handleDeleteGenerated = useCallback(async (frameIdx: number, genId: string) => { + if (!activeJobId) return + try { + const updated = await deleteGeneratedImage(activeJobId, frameIdx, genId) + setJob(updated) + toast.success("生成图已删除") + } catch (e) { + toast.error("删除失败:" + (e instanceof Error ? e.message : String(e))) + } + }, [activeJobId, setJob]) + // URL ?job=xxx,yyy 自动恢复多个 job useEffect(() => { const params = new URLSearchParams(window.location.search) @@ -227,7 +256,9 @@ export default function Home() { onSwitchJob: handleSwitchJob, onJobUpdate: setJob as any, onOpenPanel: (key: string) => dashboardRef.current?.openPanel(key), - }), [job, jobs, activeJobId, submitting, analyzing, selectedFrames, expandedFrame, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleAddManualFrame, handleSwitchJob, setJob]) + onDeleteFrame: handleDeleteFrame, + onDeleteGenerated: handleDeleteGenerated, + }), [job, jobs, activeJobId, submitting, analyzing, selectedFrames, expandedFrame, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleAddManualFrame, handleSwitchJob, setJob, handleDeleteFrame, handleDeleteGenerated]) // 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag) const [nodes, setNodes, onNodesChange] = useNodesState(