auto-save 2026-05-17 10:56 (+1, ~2)
This commit is contained in:
@@ -2,12 +2,11 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useTheme } from "next-themes"
|
||||
import {
|
||||
ReactFlow, Background, BackgroundVariant, Controls, MiniMap,
|
||||
ReactFlow, Background, BackgroundVariant, Controls,
|
||||
useNodesState, useEdgesState,
|
||||
type Node, type Edge,
|
||||
} from "@xyflow/react"
|
||||
import { Toaster, toast } from "sonner"
|
||||
import { LayoutGrid } from "lucide-react"
|
||||
import {
|
||||
InputNode, VisualLabNode, AudioNode,
|
||||
ComposeNode, KeyframePanelNode,
|
||||
@@ -17,6 +16,7 @@ import {
|
||||
} from "@/components/nodes"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
import { AudioStrip } from "@/components/audio-strip"
|
||||
import { AdRecreationBoard } from "@/components/ad-recreation-board"
|
||||
import {
|
||||
addManualFrame, analyzeJob, createJob, getJob, listJobs, uploadJob, deleteJob, deleteFrame, deleteGeneratedImage,
|
||||
deleteGeneratedVideo, deleteCutout, generateStoryboardVideo, triggerTranscribe,
|
||||
@@ -535,7 +535,7 @@ export default function Home() {
|
||||
})
|
||||
updateJobInList(updated)
|
||||
void navigator.clipboard?.writeText(prompt).catch(() => {})
|
||||
toast.success("视频任务已进入 Video Gen 节点")
|
||||
toast.success("视频任务已进入候选片段")
|
||||
} catch (e) {
|
||||
toast.error("提交视频失败:" + (e instanceof Error ? e.message : String(e)))
|
||||
}
|
||||
@@ -666,7 +666,7 @@ export default function Home() {
|
||||
if (jobs.length === 0) return
|
||||
// 状态切到 downloaded 时提示用户点解析(仅一次)
|
||||
if (job?.status === "downloaded" && prevStatusRef.current !== "downloaded") {
|
||||
toast.info("📥 视频已就绪 — 请点 Input 节点里的「点这里开始解析」按钮", { duration: 6000 })
|
||||
toast.info("视频已就绪,请在左侧看板开始抽帧", { duration: 6000 })
|
||||
}
|
||||
prevStatusRef.current = job?.status ?? null
|
||||
|
||||
@@ -767,7 +767,7 @@ export default function Home() {
|
||||
|
||||
// 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag)
|
||||
const savedSizes = useMemo(() => loadNodeSizes(), [])
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node>(
|
||||
const [nodes, setNodes] = useNodesState<Node>(
|
||||
LAYOUT.map((n) => {
|
||||
const s = savedSizes[n.id] ?? {}
|
||||
const w = s.w ?? n.w
|
||||
@@ -868,7 +868,7 @@ export default function Home() {
|
||||
}
|
||||
try { window.localStorage.setItem(NODE_SIZES_KEY, JSON.stringify(sizes)) } catch {}
|
||||
}, [nodes])
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(
|
||||
const [, setEdges] = useEdgesState<Edge>(
|
||||
EDGES_RAW.map(([from, to], i) => ({
|
||||
id: `e${i}`, source: from, target: to, animated: false, type: "default",
|
||||
})),
|
||||
@@ -1002,45 +1002,32 @@ export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<div className="canvas-bg" />
|
||||
<main className="relative h-screen w-screen overflow-hidden flex">
|
||||
{/* 自动整理布局 — 主题切换上方,一键恢复默认位置和宽度 */}
|
||||
<div className="absolute z-30 pointer-events-auto" style={{ bottom: 228, left: 12 }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleResetLayout}
|
||||
className="glass-node h-9 w-9 inline-flex items-center justify-center rounded-xl"
|
||||
style={{ width: 36, height: 36, padding: 0, borderRadius: 12 }}
|
||||
title="自动排版 · 保留每个节点的尺寸,重新排好间距和列布局"
|
||||
>
|
||||
<LayoutGrid className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
{/* 主题切换 — 左下角 Controls 上方(错开) */}
|
||||
<div className="absolute z-30 pointer-events-auto" style={{ bottom: 180, left: 12 }}>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
<main className="relative flex h-screen w-screen overflow-hidden">
|
||||
<AdRecreationBoard data={nodeData} onGenerateVideo={handleQuickGenerateVideo} />
|
||||
|
||||
{/* 右区:DAG 节点流图(原顶部 storyboard dock 已删除) */}
|
||||
<section className="relative flex-1 min-h-0 flex flex-col">
|
||||
{/* 右区:暂时清空,只保留无限画布能力,后续再定义要承载的内容。 */}
|
||||
<section className="relative flex min-h-0 flex-1 flex-col">
|
||||
<div className="absolute z-30 pointer-events-auto" style={{ bottom: 112, left: 12 }}>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
<div className="relative flex-1 min-h-0">
|
||||
{clientReady ? (
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
nodes={[]}
|
||||
edges={[]}
|
||||
onInit={(instance) => { flowRef.current = instance }}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
nodeTypes={NODE_TYPES}
|
||||
colorMode={resolvedTheme === "light" ? "light" : "dark"}
|
||||
fitView
|
||||
fitViewOptions={{ padding: 0.12 }}
|
||||
minZoom={0.2}
|
||||
maxZoom={1.5}
|
||||
nodesDraggable={false}
|
||||
nodesConnectable={false}
|
||||
elementsSelectable={false}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Dots} gap={28} size={1.4} />
|
||||
<Controls position="bottom-left" />
|
||||
<MiniMap position="bottom-right" pannable zoomable nodeStrokeWidth={2} />
|
||||
</ReactFlow>
|
||||
) : (
|
||||
<div className="h-full w-full" suppressHydrationWarning />
|
||||
|
||||
Reference in New Issue
Block a user