auto-save 2026-05-14 00:54 (+4, ~2)

This commit is contained in:
2026-05-14 00:54:42 +08:00
parent e8a653e524
commit 7804fd13f8
6 changed files with 1049 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ import {
type Node, type Edge,
} from "@xyflow/react"
import { Toaster, toast } from "sonner"
import { LayoutGrid } from "lucide-react"
import {
InputNode, KeyframeNode, ASRNode,
TranslateNode, RewriteNode, StoryboardNode, VideoGenNode, ComposeNode, KeyframePanelNode,
@@ -498,6 +499,54 @@ export default function Home() {
))
}, [pinnedNodes, setNodes])
// 自动排版:保留每个节点的当前宽高(用户为方便看而调过的尺寸),只重新计算 position
// 让卡片按管线列分组、列间和列内留出统一间距,不重叠。
const handleResetLayout = useCallback(() => {
const zoom = flowRef.current?.getZoom?.() ?? 1
const measure = (id: string): { w: number; h: number } => {
const el = document.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLElement | null
if (!el) return { w: 320, h: 220 }
const r = el.getBoundingClientRect()
return { w: r.width / zoom, h: r.height / zoom }
}
// 按管线列分组(顶 → 底):图层 1 输入 → 5 合成
const COLUMNS: string[][] = [
["input"],
["keyframe", "asr"],
["storyboard", "translate"],
["videogen", "rewrite"],
["compose"],
]
const GAP_X = 80
const GAP_Y = 56
const START_X = 40
const START_Y = 60
const positions: Record<string, { x: number; y: number }> = {}
let cursorX = START_X
for (const col of COLUMNS) {
let cursorY = START_Y
let colMaxW = 0
for (const id of col) {
const { w, h } = measure(id)
positions[id] = { x: cursorX, y: cursorY }
cursorY += h + GAP_Y
if (w > colMaxW) colMaxW = w
}
cursorX += colMaxW + GAP_X
}
setNodes((prev) => prev.map((n) => {
if (n.id === KEYFRAME_PANEL_ID) return n
const p = positions[n.id]
if (!p) return n
return { ...n, position: { x: p.x, y: p.y } }
}))
setTimeout(() => flowRef.current?.fitView?.({ padding: 0.12, duration: 400 }), 30)
toast.success("已自动排版 · 保留每个节点的尺寸")
}, [setNodes])
// 持久化每个节点宽 / 高到 localStorageKeyframePanelNode 自己管尺寸,不写回)
useEffect(() => {
const sizes: Record<string, NodeSize> = {}
@@ -591,6 +640,18 @@ export default function Home() {
<>
<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 />