auto-save 2026-05-12 16:44 (~4)

This commit is contained in:
2026-05-12 16:44:35 +08:00
parent 1b95cb22ba
commit 63552affc2
4 changed files with 30 additions and 14 deletions

View File

@@ -9,6 +9,7 @@
============================================================ */
:root {
color-scheme: light;
/* ---- Light · 暖白底 ---- */
--bg-canvas-1: #f6f4ed;
--bg-canvas-2: #ece6d8;
@@ -59,6 +60,7 @@
}
.dark {
color-scheme: dark;
/* ---- Dark · 深蓝紫 ---- */
--bg-canvas-1: #0a0d1c;
--bg-canvas-2: #14172e;

View File

@@ -2,6 +2,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import {
ReactFlow, Background, BackgroundVariant, Controls, MiniMap,
useNodesState, useEdgesState,
type Node, type Edge,
} from "@xyflow/react"
import { Toaster, toast } from "sonner"
@@ -137,19 +138,29 @@ export default function Home() {
onToggleFrame: handleToggleFrame,
}), [job, submitting, selectedFrames, handleSubmit, handleUpload, handleToggleFrame])
const nodes: Node[] = useMemo(
() => LAYOUT.map((n) => ({
// 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag
const [nodes, setNodes, onNodesChange] = useNodesState<Node>(
LAYOUT.map((n) => ({
id: n.id,
type: n.type,
position: { x: n.x, y: n.y },
data: nodeData,
draggable: true,
})),
[nodeData],
)
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(
EDGES_RAW.map(([from, to], i) => ({
id: `e${i}`, source: from, target: to, animated: false, type: "default",
})),
)
// 边状态source 节点 done 时 animated
const edges: Edge[] = useMemo(() => {
// Job 数据变化时只更新节点 data 不动 position
useEffect(() => {
setNodes((prev) => prev.map((n) => ({ ...n, data: nodeData })))
}, [nodeData, setNodes])
// 边的 animated 状态跟 Job 进度联动
useEffect(() => {
const doneOf: Record<string, boolean> = {
input: !!job,
download: !!job?.video_url,
@@ -158,14 +169,8 @@ export default function Home() {
asr: !!job && job.transcript.length > 0,
translate: !!job && (job.transcript.some((s) => s.zh) ?? false),
}
return EDGES_RAW.map(([from, to], i) => ({
id: `e${i}`,
source: from,
target: to,
animated: !!doneOf[from],
type: "default",
}))
}, [job])
setEdges((prev) => prev.map((e) => ({ ...e, animated: !!doneOf[e.source] })))
}, [job, setEdges])
return (
<>
@@ -197,6 +202,8 @@ export default function Home() {
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
nodeTypes={NODE_TYPES}
fitView
fitViewOptions={{ padding: 0.12 }}

View File

@@ -74,7 +74,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
onChange={(e) => setUrl(e.target.value)}
placeholder="粘贴 TikTok 链接"
disabled={isLocked}
className="w-full text-[12px] px-2.5 py-2 rounded-md bg-white/40 dark:bg-white/[0.04] border border-black/10 dark:border-white/10 outline-none placeholder:text-[var(--text-faint)] focus:ring-2 focus:ring-[var(--ring)] disabled:opacity-40"
className="w-full text-[12px] px-2.5 py-2 rounded-md bg-white/60 dark:bg-black/40 border border-black/10 dark:border-white/10 outline-none text-[var(--text-strong)] placeholder:text-[var(--text-faint)] focus:ring-2 focus:ring-[var(--ring)] disabled:opacity-40"
/>
<div className="mt-2 flex gap-1.5">
<button