互動編排
節點接線畫布
AI 工作流式的節點接線畫布:節點可自由拖曳、埠與埠之間以貝茲曲線相連、從埠拖出新連線、執行時沿線流動光點,支援平移縮放。
把多步驟流程畫成可以直接編輯的圖:每個節點是一張卡片,左側是輸入埠、右側是輸出埠,埠與埠之間以三次貝茲曲線相連。節點可以拖到任何位置,放開時以彈簧補間對齊格線;從輸出埠拖出新線,靠近合法的輸入埠會自動吸附;流程執行時,每條線上有光點沿路徑流動。畫布支援滾輪平移、Ctrl/⌘+滾輪縮放與拖曳背景,節點與連線都是可受控的資料。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add "https://webberui.com/r/node-wire-canvas.json?t=<安裝 token>"Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
400
1.6
編輯中的呼叫預設屬性
<NodeWireCanvas/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add "https://webberui.com/r/node-wire-canvas.json?t=<安裝 token>"或在 components.json 設定 registries 後,改用 @webberui/node-wire-canvas 安裝。
使用
import * as React from "react";
import {
NodeWireCanvas,
type NodeWireEdge,
type NodeWireNode,
} from "@/components/ui/node-wire-canvas";
const initialNodes: NodeWireNode[] = [
{ id: "prompt", x: 0, y: 0, title: "文字提示", outputs: [{ id: "prompt" }] },
{
id: "gen",
x: 280,
y: 40,
title: "影像生成",
inputs: [{ id: "prompt" }],
outputs: [{ id: "image" }],
},
];
const initialEdges: NodeWireEdge[] = [
{ id: "e1", from: { node: "prompt", port: "prompt" }, to: { node: "gen", port: "prompt" } },
];
export function Workflow() {
const [nodes, setNodes] = React.useState(initialNodes);
const [edges, setEdges] = React.useState(initialEdges);
const [running, setRunning] = React.useState(false);
return (
<NodeWireCanvas
nodes={nodes}
onNodesChange={setNodes}
edges={edges}
onEdgesChange={setEdges}
onConnect={(edge) => console.log("新連線", edge.id)}
showFlow={running}
/>
);
}Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
nodes | NodeWireNode[] | — | 節點清單(受控) |
defaultNodes | NodeWireNode[] | [] | 節點初始清單(非受控) |
onNodesChange | (nodes: NodeWireNode[]) => void | — | 節點移動或刪除時回呼,帶入新的完整清單 |
edges | NodeWireEdge[] | — | 連線清單(受控) |
defaultEdges | NodeWireEdge[] | [] | 連線初始清單(非受控) |
onEdgesChange | (edges: NodeWireEdge[]) => void | — | 連線新增或刪除時回呼,帶入新的完整清單 |
onConnect | (edge: NodeWireEdge) => void | — | 使用者建立一條新連線時回呼 |
showFlow | boolean | false | 所有連線都顯示流動光點;關閉時只有起點節點為 running 的連線會流動 |
snap | boolean | true | 放開節點時對齊格線 |
gridSize | number | 20 | 格線間距(px),也是方向鍵微移的步距 |
stiffness | number | 400 | 對齊格線時彈簧的剛性,越大越快到位 |
flowDuration | number | — | 光點跑完一條連線的秒數;未提供時沿用 --wb-nwc-flow-duration(預設 1.6s) |
minZoom | number | 0.4 | 最小縮放倍率 |
maxZoom | number | 2 | 最大縮放倍率 |
defaultViewport | NodeWireViewport | — | 初始視角;未提供時掛載後自動置中全部節點 |
readOnly | boolean | false | 唯讀:不可拖曳節點、建立或刪除連線,仍可平移縮放 |
height | number | 480 | 畫布高度(px) |
ariaLabel | string | "工作流程畫布" | 畫布的無障礙名稱 |
className | string | — | 額外 class,併入根元素 |
NodeWireNode
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 節點唯一識別碼 |
x | number | — | 卡片左上角 x(畫布座標) |
y | number | — | 卡片左上角 y(畫布座標) |
title | string | — | 標題列文字 |
inputs | NodeWirePort[] | [] | 左側輸入埠 |
outputs | NodeWirePort[] | [] | 右側輸出埠 |
status | "idle" | "running" | "done" | "error" | "idle" | 節點狀態:灰點、脈動點加跑馬虛線外框、綠勾、紅叉 |
content | ReactNode | — | 卡片內容,放在埠列下方 |
NodeWirePort
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 埠在該節點內的唯一識別碼 |
label | string | — | 顯示標籤;未提供時顯示 id |
NodeWireEdge
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 連線唯一識別碼;使用者新建的連線為 "來源節點.埠->目標節點.埠" |
from | { node: string; port: string } | — | 起點(輸出埠) |
to | { node: string; port: string } | — | 終點(輸入埠) |
NodeWireViewport
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
x | number | — | 水平平移(px) |
y | number | — | 垂直平移(px) |
zoom | number | — | 縮放倍率 |
CSS 變數
| 變數 | 預設值 | 說明 |
|---|---|---|
--wb-nwc-accent | #6366f1 | 強調色:選取外框、連線預覽、執行中狀態點 |
--wb-nwc-flow-color | 同 --wb-nwc-accent | 流動光點與發光顏色 |
--wb-nwc-flow-duration | 1.6s | 光點跑完一條連線的時間 |
--wb-nwc-march-duration | 0.8s | 執行中外框虛線跑一輪的時間 |
--wb-nwc-dot | rgb(163 163 163 / 0.5) | 背景點格顏色 |
細節
- 座標系:內容層以
translate(panX, panY) scale(zoom)表示整個畫布,背景點格的間距是gridSize × zoom,跟著平移縮放同步。一般滾輪平移、Ctrl/⌘+滾輪(含觸控板雙指縮放)以游標為中心縮放,拖曳空白處也能平移,行為與常見設計工具一致 - 埠位置由資料計算:埠的座標直接從節點
x/y與埠序號算出,不量測 DOM,拖曳節點時相連的線每一幀重算也不會造成 layout thrash。拖曳本身用指標事件自行計算(位移除以 zoom)並寫回資料,而不是只改 transform - 對齊格線:
snap開啟時,放開節點會先把對齊後的座標寫回資料,再以useSpring(stiffness可調)把顯示位置補間到格點,連線同步跟著走 - 連線:每條連線是三次貝茲
M p1 C p1+dx p2−dx p2,dx = max(60, |Δx| / 2)。從輸出埠拖出時以虛線預覽,游標距離合法輸入埠 16px 內會吸附高亮;自己連自己與重複的連線會被拒絕。點選連線後按 Delete 刪除 - 流光:
showFlow為 true,或某條線的起點節點為running時,每條線上有三個光點以 CSSoffset-path沿同一條路徑移動並錯開三分之一週期;畫布捲出畫面時暫停 - 與 patchbay-integrations 的差異:patchbay 是固定兩欄的整合接線板;本元件的節點可以自由布局,連線是可新增、刪除、受控的資料,適合工作流程編輯器
可及性
- 畫布為
role="application",附aria-label(預設「工作流程畫布」)與sr-only操作說明;建立、刪除、取消連線時以aria-live播報結果 - 節點可用 Tab 聚焦(
role="group",名稱含標題與狀態),方向鍵移動一格、Shift 加方向鍵移動五格,Delete 刪除節點與相連的線;readOnly時停用 - 埠是
button:在輸出埠按 Enter 進入連線模式,Tab 到目標輸入埠再按 Enter 完成,Esc 取消;滑鼠也可點一下輸出埠、再點輸入埠完成連線。每條連線也可 Tab 聚焦,按 Delete 刪除 - 鍵盤 + 與 - 縮放、0 重設視角;角落工具列提供縮小、放大與置中全部按鈕
- 使用者系統開啟「減少動態效果」時,不顯示流動光點(改以實色高亮正在流動的連線),對齊格線不補間直接到位,執行中的狀態點不脈動、外框虛線不跑動