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

@@ -76,6 +76,13 @@
"message": "auto-save 2026-05-12 16:33 (~1)", "message": "auto-save 2026-05-12 16:33 (~1)",
"hash": "37bf7c9", "hash": "37bf7c9",
"files_changed": 1 "files_changed": 1
},
{
"ts": "2026-05-12T16:39:03+08:00",
"type": "commit",
"message": "auto-save 2026-05-12 16:38 (~1)",
"hash": "1b95cb2",
"files_changed": 1
} }
] ]
} }

View File

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

View File

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