auto-save 2026-05-12 16:02 (+2, ~6)

This commit is contained in:
2026-05-12 16:05:47 +08:00
parent 064083ef0a
commit b0ffd03b72
9 changed files with 1080 additions and 214 deletions

View File

@@ -0,0 +1,79 @@
"use client"
import { type ReactNode } from "react"
import { Handle, Position } from "@xyflow/react"
import { CheckCircle2, Loader2, AlertCircle } from "lucide-react"
export type NodeKind = "input" | "process" | "ai" | "output"
export type NodeStatus = "pending" | "running" | "done" | "failed"
interface Props {
type: NodeKind
status: NodeStatus
icon?: ReactNode
title: string
subtitle?: string
width?: number
selected?: boolean
hasTarget?: boolean
hasSource?: boolean
children?: ReactNode
}
const STATUS_DOT: Record<NodeStatus, string> = {
pending: "status-dot",
running: "status-dot status-dot--running",
done: "status-dot status-dot--done",
failed: "status-dot status-dot--failed",
}
const STATUS_LABEL: Record<NodeStatus, string> = {
pending: "待运行",
running: "运行中",
done: "完成",
failed: "失败",
}
export function NodeShell({
type,
status,
icon,
title,
subtitle,
width = 280,
selected,
hasTarget = true,
hasSource = true,
children,
}: Props) {
return (
<div
className={`glass-node ${selected ? "glass-node--selected" : ""} ${status === "running" ? "glass-node--running" : ""}`}
data-type={type}
style={{ width }}
>
{hasTarget && <Handle type="target" position={Position.Left} />}
<div className="glass-node__header">
{icon ? <span className="inline-flex h-5 w-5 items-center justify-center">{icon}</span> : null}
<span className="text-[13px]">{title}</span>
<span className="ml-auto flex items-center gap-1.5">
{status === "running" ? <Loader2 className="h-3 w-3 animate-spin" /> :
status === "done" ? <CheckCircle2 className="h-3 w-3" /> :
status === "failed" ? <AlertCircle className="h-3 w-3" /> : null}
<span className={STATUS_DOT[status]} />
</span>
</div>
<div className="glass-node__body">
{subtitle && (
<div className="text-[11px] mb-2 glass-node__kbd uppercase tracking-widest">
{subtitle} · {STATUS_LABEL[status]}
</div>
)}
{children}
</div>
{hasSource && <Handle type="source" position={Position.Right} />}
</div>
)
}