@particle-academy/fancy-flow 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,11 @@
1
+ import { removeNodes, removeEdges, cloneSubgraph, alignNodes, distributeNodes, reconnectEdge, assignToLane, removeFromLane, duplicateNode, setEdgeLabel, FlowCanvas } from './chunk-5UCWY7G3.js';
2
+ export { ActionNode, DecisionNode, FlowCanvas, FlowViewer, NodeShell, OutputNode, SubgraphNode, TriggerNode, alignNodes, cloneSubgraph, defaultNodeTypes, distributeNodes, reconnectEdge } from './chunk-5UCWY7G3.js';
1
3
  import { useFlowState, useFlowRun, useFlowHistory, applyOutputsToNodes, applyStatusesToNodes } from './chunk-A6RFLGWV.js';
2
4
  export { applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-A6RFLGWV.js';
3
5
  export { runCohort } from './chunk-QZTGV3ZL.js';
4
- import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds, NoteNode, createConnectionValidator } from './chunk-UX65ML3J.js';
6
+ import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds } from './chunk-UX65ML3J.js';
5
7
  export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, NoteNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds, useFlowEditor, useFlowEditorOptional } from './chunk-UX65ML3J.js';
6
- import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, reconnectEdge, index, BackgroundVariant, Background, Controls, MiniMap, ViewportPortal } from './chunk-OWENS2H5.js';
8
+ import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle } from './chunk-OWENS2H5.js';
7
9
  export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
8
10
  import './chunk-USL4FMFU.js';
9
11
  export { runFlow } from './chunk-VLATLVGH.js';
@@ -14,463 +16,9 @@ import { onNodeKindsChanged, listNodeKinds, getNodeKind, defaultConfigFor, valid
14
16
  export { categoryAccent, clearNodeKindOverrides, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKindsChanged, overrideNodeKind, registerNodeKind, validateConfig } from './chunk-PVROYW7C.js';
15
17
  import { getRichInputAdapter } from './chunk-F5RPRB7A.js';
16
18
  export { RichInputPreview, getRichInputAdapter, isRichInputEnabled, onRichInputAdapterChanged, registerRichInputAdapter } from './chunk-F5RPRB7A.js';
17
- import { memo, forwardRef, useState, useMemo, useEffect, useCallback, useRef, useImperativeHandle, useId } from 'react';
19
+ import { forwardRef, useState, useMemo, useEffect, useCallback, useRef, useImperativeHandle, useId, memo } from 'react';
18
20
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
19
21
 
20
- function NodeShellInner({
21
- node,
22
- accent,
23
- tag,
24
- icon,
25
- showInputs = true,
26
- showOutputs = true,
27
- children
28
- }) {
29
- const data = node.data;
30
- const status = data.status ?? "idle";
31
- const inputs = data.inputs ?? defaultInputs(showInputs);
32
- const outputs = data.outputs ?? defaultOutputs(showOutputs);
33
- return /* @__PURE__ */ jsxs(
34
- "div",
35
- {
36
- className: ["ff-node", `ff-node--status-${status}`, node.selected ? "ff-node--selected" : ""].filter(Boolean).join(" "),
37
- style: { borderColor: node.selected ? accent : void 0 },
38
- children: [
39
- /* @__PURE__ */ jsxs("header", { className: "ff-node__header", style: { background: accent }, children: [
40
- /* @__PURE__ */ jsx("span", { className: "ff-node__icon", "aria-hidden": true, children: icon ?? null }),
41
- /* @__PURE__ */ jsx("span", { className: "ff-node__tag", children: tag }),
42
- /* @__PURE__ */ jsx("span", { className: "ff-node__label", children: data.label }),
43
- status !== "idle" && /* @__PURE__ */ jsx(StatusDot, { status })
44
- ] }),
45
- data.description && /* @__PURE__ */ jsx("p", { className: "ff-node__desc", children: data.description }),
46
- children && /* @__PURE__ */ jsx("div", { className: "ff-node__body", children }),
47
- data.statusText && /* @__PURE__ */ jsx("p", { className: "ff-node__status-text", children: data.statusText }),
48
- inputs.map((p, i) => /* @__PURE__ */ jsx(
49
- Handle,
50
- {
51
- type: "target",
52
- position: Position.Left,
53
- id: p.id,
54
- style: portStyle(i, inputs.length),
55
- title: p.label ?? p.id
56
- },
57
- p.id
58
- )),
59
- outputs.map((p, i) => /* @__PURE__ */ jsx(
60
- Handle,
61
- {
62
- type: "source",
63
- position: Position.Right,
64
- id: p.id,
65
- style: portStyle(i, outputs.length),
66
- title: p.label ?? p.id
67
- },
68
- p.id
69
- ))
70
- ]
71
- }
72
- );
73
- }
74
- var NodeShell = memo(NodeShellInner);
75
- function defaultInputs(show) {
76
- return show ? [{ id: "in" }] : [];
77
- }
78
- function defaultOutputs(show) {
79
- return show ? [{ id: "out" }] : [];
80
- }
81
- function portStyle(i, total) {
82
- if (total <= 1) return {};
83
- const slot = 100 / (total + 1) * (i + 1);
84
- return { top: `${slot}%` };
85
- }
86
- function StatusDot({ status }) {
87
- return /* @__PURE__ */ jsx("span", { className: `ff-node__dot ff-node__dot--${status}`, "aria-label": `status ${status}` });
88
- }
89
- function TriggerNodeInner(props) {
90
- return /* @__PURE__ */ jsx(NodeShell, { node: props, accent: "#10b981", tag: "TRIGGER", icon: "\u26A1", showInputs: false });
91
- }
92
- var TriggerNode = memo(TriggerNodeInner);
93
- function ActionNodeInner(props) {
94
- return /* @__PURE__ */ jsx(NodeShell, { node: props, accent: "#3b82f6", tag: "ACTION", icon: "\u25B8" });
95
- }
96
- var ActionNode = memo(ActionNodeInner);
97
- var DEFAULT_BRANCHES = [
98
- { id: "true", label: "true" },
99
- { id: "false", label: "false" }
100
- ];
101
- function DecisionNodeInner(props) {
102
- if (!props.data.outputs) {
103
- props = { ...props, data: { ...props.data, outputs: DEFAULT_BRANCHES } };
104
- }
105
- return /* @__PURE__ */ jsx(NodeShell, { node: props, accent: "#f59e0b", tag: "DECISION", icon: "\u25C7" });
106
- }
107
- var DecisionNode = memo(DecisionNodeInner);
108
- function OutputNodeInner(props) {
109
- return /* @__PURE__ */ jsx(NodeShell, { node: props, accent: "#a855f7", tag: "OUTPUT", icon: "\u25CF", showOutputs: false });
110
- }
111
- var OutputNode = memo(OutputNodeInner);
112
- function SubgraphNodeInner(props) {
113
- const data = props.data;
114
- const childCount = data.childIds?.length ?? 0;
115
- return /* @__PURE__ */ jsx(NodeShell, { node: props, accent: "#0ea5e9", tag: "SUBGRAPH", icon: "\u2750", children: /* @__PURE__ */ jsxs("div", { className: "ff-subgraph__meta", children: [
116
- /* @__PURE__ */ jsxs("span", { children: [
117
- childCount,
118
- " node",
119
- childCount === 1 ? "" : "s"
120
- ] }),
121
- /* @__PURE__ */ jsx("span", { children: data.collapsed === false ? "expanded" : "collapsed" })
122
- ] }) });
123
- }
124
- var SubgraphNode = memo(SubgraphNodeInner);
125
-
126
- // src/components/nodes/index.ts
127
- var defaultNodeTypes = {
128
- trigger: TriggerNode,
129
- action: ActionNode,
130
- decision: DecisionNode,
131
- output: OutputNode,
132
- note: NoteNode,
133
- subgraph: SubgraphNode
134
- };
135
-
136
- // src/components/FlowEditor/graph-ops.ts
137
- function removeNodes(graph, ids) {
138
- if (ids.length === 0) return graph;
139
- const doomed = new Set(ids);
140
- return {
141
- nodes: graph.nodes.filter((n) => !doomed.has(n.id)),
142
- edges: graph.edges.filter((e) => !doomed.has(e.source) && !doomed.has(e.target))
143
- };
144
- }
145
- function removeEdges(edges, ids) {
146
- if (ids.length === 0) return edges;
147
- const doomed = new Set(ids);
148
- return edges.filter((e) => !doomed.has(e.id));
149
- }
150
- function setEdgeLabel(edges, id, label) {
151
- const next = label?.trim();
152
- return edges.map((e) => {
153
- if (e.id !== id) return e;
154
- if (!next) {
155
- const { label: _drop, ...rest } = e;
156
- return rest;
157
- }
158
- return { ...e, label: next };
159
- });
160
- }
161
- function duplicateNode(node, id, offset = 40) {
162
- return {
163
- ...node,
164
- id,
165
- position: { x: node.position.x + offset, y: node.position.y + offset },
166
- // Deep copy so the clone's config edits don't mutate the original.
167
- data: JSON.parse(JSON.stringify(node.data ?? {}))
168
- };
169
- }
170
- function cloneSubgraph(nodes, edges, opts) {
171
- const offset = opts.offset ?? 40;
172
- const idMap = /* @__PURE__ */ new Map();
173
- for (const n of nodes) idMap.set(n.id, opts.makeId());
174
- const clonedNodes = nodes.map((n) => {
175
- const cloned = duplicateNode(n, idMap.get(n.id), offset);
176
- const parentId = n.parentId;
177
- if (parentId && idMap.has(parentId)) cloned.parentId = idMap.get(parentId);
178
- else if (parentId) delete cloned.parentId;
179
- return cloned;
180
- });
181
- const clonedEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((e) => ({ ...e, id: opts.makeId(), source: idMap.get(e.source), target: idMap.get(e.target) }));
182
- return { nodes: clonedNodes, edges: clonedEdges, idMap };
183
- }
184
- function reconnectEdge2(edges, oldEdge, newConnection) {
185
- return reconnectEdge(oldEdge, newConnection, edges, { shouldReplaceId: false });
186
- }
187
- var nodeW = (n) => n.width ?? n.measured?.width ?? 0;
188
- var nodeH = (n) => n.height ?? n.measured?.height ?? 0;
189
- function alignNodes(nodes, edge) {
190
- if (nodes.length < 2) return nodes;
191
- const minL = Math.min(...nodes.map((n) => n.position.x));
192
- const maxR = Math.max(...nodes.map((n) => n.position.x + nodeW(n)));
193
- const minT = Math.min(...nodes.map((n) => n.position.y));
194
- const maxB = Math.max(...nodes.map((n) => n.position.y + nodeH(n)));
195
- const cx2 = (minL + maxR) / 2;
196
- const cy = (minT + maxB) / 2;
197
- return nodes.map((n) => {
198
- let { x, y } = n.position;
199
- if (edge === "left") x = minL;
200
- else if (edge === "right") x = maxR - nodeW(n);
201
- else if (edge === "hcenter") x = cx2 - nodeW(n) / 2;
202
- else if (edge === "top") y = minT;
203
- else if (edge === "bottom") y = maxB - nodeH(n);
204
- else if (edge === "vcenter") y = cy - nodeH(n) / 2;
205
- return { ...n, position: { x, y } };
206
- });
207
- }
208
- function sortNodesParentFirst(nodes) {
209
- const byId = new Map(nodes.map((n) => [n.id, n]));
210
- const seen = /* @__PURE__ */ new Set();
211
- const out = [];
212
- const visit = (n) => {
213
- if (seen.has(n.id)) return;
214
- const pid = n.parentId;
215
- if (pid && byId.has(pid) && !seen.has(pid)) visit(byId.get(pid));
216
- seen.add(n.id);
217
- out.push(n);
218
- };
219
- for (const n of nodes) visit(n);
220
- return out;
221
- }
222
- function absolutePosition(node, nodes) {
223
- const pid = node.parentId;
224
- if (!pid) return node.position;
225
- const parent = nodes.find((n) => n.id === pid);
226
- return parent ? { x: node.position.x + parent.position.x, y: node.position.y + parent.position.y } : node.position;
227
- }
228
- function assignToLane(nodes, nodeId, laneId) {
229
- if (nodeId === laneId) return nodes;
230
- const node = nodes.find((n) => n.id === nodeId);
231
- const lane = nodes.find((n) => n.id === laneId);
232
- if (!node || !lane) return nodes;
233
- const abs = absolutePosition(node, nodes);
234
- const laneAbs = absolutePosition(lane, nodes);
235
- const rel = { x: abs.x - laneAbs.x, y: abs.y - laneAbs.y };
236
- return nodes.map((n) => n.id === nodeId ? { ...n, parentId: laneId, extent: "parent", position: rel } : n);
237
- }
238
- function removeFromLane(nodes, nodeId) {
239
- const node = nodes.find((n) => n.id === nodeId);
240
- if (!node || !node.parentId) return nodes;
241
- const abs = absolutePosition(node, nodes);
242
- return nodes.map((n) => {
243
- if (n.id !== nodeId) return n;
244
- const { parentId: _p, extent: _e, ...rest } = n;
245
- return { ...rest, position: abs };
246
- });
247
- }
248
- function distributeNodes(nodes, axis) {
249
- if (nodes.length < 3) return nodes;
250
- const size = (n) => axis === "h" ? nodeW(n) : nodeH(n);
251
- const coord = (n) => axis === "h" ? n.position.x : n.position.y;
252
- const sorted = [...nodes].sort((a, b) => coord(a) - coord(b));
253
- const start = coord(sorted[0]);
254
- const last = sorted[sorted.length - 1];
255
- const end = coord(last) + size(last);
256
- const totalSize = sorted.reduce((s, n) => s + size(n), 0);
257
- const gap = (end - start - totalSize) / (sorted.length - 1);
258
- const posById = /* @__PURE__ */ new Map();
259
- let cursor = start;
260
- for (const n of sorted) {
261
- posById.set(n.id, cursor);
262
- cursor += size(n) + gap;
263
- }
264
- return nodes.map((n) => {
265
- const p = posById.get(n.id);
266
- return { ...n, position: axis === "h" ? { ...n.position, x: p } : { ...n.position, y: p } };
267
- });
268
- }
269
-
270
- // src/components/canvas/helper-lines.ts
271
- var w = (n) => n.width ?? n.measured?.width ?? 0;
272
- var h = (n) => n.height ?? n.measured?.height ?? 0;
273
- function getHelperLines(change, nodes, distance = 6) {
274
- const result = { snapPosition: { x: void 0, y: void 0 } };
275
- const a = nodes.find((n) => n.id === change.id);
276
- if (!a || !change.position) return result;
277
- const A = {
278
- left: change.position.x,
279
- right: change.position.x + w(a),
280
- top: change.position.y,
281
- bottom: change.position.y + h(a),
282
- width: w(a),
283
- height: h(a)
284
- };
285
- let vDist = distance;
286
- let hDist = distance;
287
- for (const b of nodes) {
288
- if (b.id === a.id) continue;
289
- const B = { left: b.position.x, right: b.position.x + w(b), top: b.position.y, bottom: b.position.y + h(b) };
290
- const ll = Math.abs(A.left - B.left);
291
- if (ll < vDist) {
292
- result.snapPosition.x = B.left;
293
- result.vertical = B.left;
294
- vDist = ll;
295
- }
296
- const rr = Math.abs(A.right - B.right);
297
- if (rr < vDist) {
298
- result.snapPosition.x = B.right - A.width;
299
- result.vertical = B.right;
300
- vDist = rr;
301
- }
302
- const lr = Math.abs(A.left - B.right);
303
- if (lr < vDist) {
304
- result.snapPosition.x = B.right;
305
- result.vertical = B.right;
306
- vDist = lr;
307
- }
308
- const rl = Math.abs(A.right - B.left);
309
- if (rl < vDist) {
310
- result.snapPosition.x = B.left - A.width;
311
- result.vertical = B.left;
312
- vDist = rl;
313
- }
314
- const tt = Math.abs(A.top - B.top);
315
- if (tt < hDist) {
316
- result.snapPosition.y = B.top;
317
- result.horizontal = B.top;
318
- hDist = tt;
319
- }
320
- const bb = Math.abs(A.bottom - B.bottom);
321
- if (bb < hDist) {
322
- result.snapPosition.y = B.bottom - A.height;
323
- result.horizontal = B.bottom;
324
- hDist = bb;
325
- }
326
- const tb = Math.abs(A.top - B.bottom);
327
- if (tb < hDist) {
328
- result.snapPosition.y = B.bottom;
329
- result.horizontal = B.bottom;
330
- hDist = tb;
331
- }
332
- const bt = Math.abs(A.bottom - B.top);
333
- if (bt < hDist) {
334
- result.snapPosition.y = B.top - A.height;
335
- result.horizontal = B.top;
336
- hDist = bt;
337
- }
338
- }
339
- return result;
340
- }
341
- function HelperLines({ horizontal, vertical }) {
342
- if (horizontal === void 0 && vertical === void 0) return null;
343
- return /* @__PURE__ */ jsxs(ViewportPortal, { children: [
344
- vertical !== void 0 && /* @__PURE__ */ jsx(
345
- "div",
346
- {
347
- className: "ff-helper-line",
348
- style: { position: "absolute", transform: `translateX(${vertical}px)`, top: -5e3, height: 1e4, width: 1, pointerEvents: "none" }
349
- }
350
- ),
351
- horizontal !== void 0 && /* @__PURE__ */ jsx(
352
- "div",
353
- {
354
- className: "ff-helper-line",
355
- style: { position: "absolute", transform: `translateY(${horizontal}px)`, left: -5e3, width: 1e4, height: 1, pointerEvents: "none" }
356
- }
357
- )
358
- ] });
359
- }
360
- function preventScrollWhileZooming(event) {
361
- if (event.shiftKey) event.preventDefault();
362
- }
363
- function wheelZoomProps(zoomOnWheel) {
364
- return zoomOnWheel ? { zoomActivationKeyCode: null, preventScrolling: true } : {
365
- zoomActivationKeyCode: "Shift",
366
- preventScrolling: false,
367
- onWheelCapture: preventScrollWhileZooming
368
- };
369
- }
370
- var DEFAULT_FIT_VIEW = { padding: 0.2 };
371
- var DEFAULT_EDGE_OPTIONS = {
372
- type: "smoothstep",
373
- animated: false
374
- };
375
- function FlowCanvas({
376
- nodes,
377
- edges,
378
- background = BackgroundVariant.Dots,
379
- showControls = true,
380
- showMinimap = false,
381
- height = 600,
382
- validateConnections = true,
383
- isValidConnection,
384
- colorMode,
385
- showHelperLines = false,
386
- zoomOnWheel = true,
387
- onNodesChange,
388
- toolbar,
389
- nodeTypes,
390
- edgeTypes,
391
- className,
392
- style,
393
- ...rest
394
- }) {
395
- const mergedNodeTypes = useMemo(
396
- () => ({ ...defaultNodeTypes, ...nodeTypes ?? {} }),
397
- [nodeTypes]
398
- );
399
- const nodesRef = useRef(nodes);
400
- nodesRef.current = nodes;
401
- const builtinValidator = useMemo(
402
- () => validateConnections === false ? void 0 : createConnectionValidator(
403
- () => nodesRef.current,
404
- validateConnections === true ? void 0 : validateConnections
405
- ),
406
- [validateConnections]
407
- );
408
- const resolvedIsValidConnection = isValidConnection ?? builtinValidator;
409
- const mergedEdgeTypes = useMemo(
410
- () => edgeTypes ? { ...edgeTypes } : void 0,
411
- [edgeTypes]
412
- );
413
- const orderedNodes = useMemo(() => sortNodesParentFirst(nodes), [nodes]);
414
- const [helperLines, setHelperLines] = useState({});
415
- const handleNodesChange = useCallback(
416
- (changes) => {
417
- if (showHelperLines) {
418
- const pos = changes.filter((c) => c.type === "position" && c.position);
419
- if (pos.length === 1 && pos[0].dragging) {
420
- const lines = getHelperLines(pos[0], nodesRef.current);
421
- if (lines.snapPosition.x !== void 0) pos[0].position.x = lines.snapPosition.x;
422
- if (lines.snapPosition.y !== void 0) pos[0].position.y = lines.snapPosition.y;
423
- setHelperLines({ horizontal: lines.horizontal, vertical: lines.vertical });
424
- } else {
425
- setHelperLines({});
426
- }
427
- }
428
- onNodesChange?.(changes);
429
- },
430
- [showHelperLines, onNodesChange]
431
- );
432
- return /* @__PURE__ */ jsxs(
433
- "div",
434
- {
435
- className: [
436
- "ff-canvas",
437
- // colorMode drives BOTH react-flow's chrome (below) and our `ff-` styles
438
- // via the shared `.dark` class / light opt-out — one theme signal.
439
- colorMode === "dark" ? "dark" : "",
440
- colorMode === "light" ? "ff-canvas--light" : "",
441
- className ?? ""
442
- ].filter(Boolean).join(" "),
443
- style: { height, ...style },
444
- children: [
445
- toolbar && /* @__PURE__ */ jsx("div", { className: "ff-canvas__toolbar", children: toolbar }),
446
- /* @__PURE__ */ jsx("div", { className: "ff-canvas__surface", children: /* @__PURE__ */ jsxs(
447
- index,
448
- {
449
- nodes: orderedNodes,
450
- edges,
451
- onNodesChange: handleNodesChange,
452
- colorMode,
453
- nodeTypes: mergedNodeTypes,
454
- edgeTypes: mergedEdgeTypes,
455
- fitView: true,
456
- fitViewOptions: DEFAULT_FIT_VIEW,
457
- defaultEdgeOptions: DEFAULT_EDGE_OPTIONS,
458
- proOptions: { hideAttribution: true },
459
- ...wheelZoomProps(zoomOnWheel),
460
- isValidConnection: resolvedIsValidConnection,
461
- ...rest,
462
- children: [
463
- background !== "none" && /* @__PURE__ */ jsx(Background, { variant: background, gap: 20, size: 1, color: "rgba(0,0,0,0.18)" }),
464
- showControls && /* @__PURE__ */ jsx(Controls, { className: "ff-controls", position: "bottom-right" }),
465
- showMinimap && /* @__PURE__ */ jsx(MiniMap, { className: "ff-minimap", pannable: true, zoomable: true }),
466
- showHelperLines && /* @__PURE__ */ jsx(HelperLines, { horizontal: helperLines.horizontal, vertical: helperLines.vertical })
467
- ]
468
- }
469
- ) })
470
- ]
471
- }
472
- );
473
- }
474
22
  var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "layout", "annotation", "custom"];
475
23
  var CATEGORY_LABELS = {
476
24
  trigger: "Triggers",
@@ -1487,7 +1035,7 @@ function FlowEditorInner({
1487
1035
  [flow]
1488
1036
  );
1489
1037
  const onReconnect = useCallback(
1490
- (oldEdge, conn) => flow.setEdges((eds) => reconnectEdge2(eds, oldEdge, conn)),
1038
+ (oldEdge, conn) => flow.setEdges((eds) => reconnectEdge(eds, oldEdge, conn)),
1491
1039
  [flow]
1492
1040
  );
1493
1041
  const isLaneNode = useCallback(
@@ -1498,10 +1046,10 @@ function FlowEditorInner({
1498
1046
  (orientation = "horizontal", title) => {
1499
1047
  const vertical = orientation === "vertical";
1500
1048
  const lanes = flow.nodes.filter(isLaneNode);
1501
- const w2 = vertical ? 280 : 680;
1502
- const h2 = vertical ? 480 : 168;
1049
+ const w = vertical ? 280 : 680;
1050
+ const h = vertical ? 480 : 168;
1503
1051
  const end = lanes.reduce(
1504
- (m, l) => Math.max(m, vertical ? l.position.x + (l.width ?? w2) : l.position.y + (l.height ?? h2)),
1052
+ (m, l) => Math.max(m, vertical ? l.position.x + (l.width ?? w) : l.position.y + (l.height ?? h)),
1505
1053
  0
1506
1054
  );
1507
1055
  const id = newNodeId();
@@ -1510,8 +1058,8 @@ function FlowEditorInner({
1510
1058
  id,
1511
1059
  type: "@particle-academy/lane",
1512
1060
  position: vertical ? { x: lanes.length ? end + 12 : 0, y: 0 } : { x: 0, y: lanes.length ? end + 12 : 0 },
1513
- width: w2,
1514
- height: h2,
1061
+ width: w,
1062
+ height: h,
1515
1063
  data: { kind: "@particle-academy/lane", label: name, config: { title: name, orientation } }
1516
1064
  };
1517
1065
  flow.setNodes((all) => [...all, node]);
@@ -1927,135 +1475,6 @@ function EdgeLabelEditor({
1927
1475
  }
1928
1476
  );
1929
1477
  }
1930
- var STATUS_LABEL = {
1931
- ok: "ok",
1932
- running: "running",
1933
- failed: "failed",
1934
- skipped: "skipped",
1935
- pending: "pending"
1936
- };
1937
- function FlowViewer({
1938
- graph,
1939
- variant = "canvas",
1940
- height = 480,
1941
- showMinimap = false,
1942
- showControls = true,
1943
- statuses,
1944
- selectedNodeId = null,
1945
- onSelectNode,
1946
- className,
1947
- classNames = {},
1948
- style,
1949
- empty
1950
- }) {
1951
- const nodeTypes = useMemo(() => buildNodeTypes(), []);
1952
- const rows = useMemo(
1953
- () => graph.nodes.map((node, index2) => {
1954
- const kind = getNodeKind(node.data?.kind ?? node.type ?? "");
1955
- return {
1956
- node,
1957
- index: index2,
1958
- title: kind?.label ?? node.data?.label ?? node.data?.kind ?? node.id,
1959
- description: kind?.description ?? null,
1960
- accent: kind?.accent ?? categoryAccent(kind?.category ?? "custom"),
1961
- status: statuses?.[node.id] ?? null
1962
- };
1963
- }),
1964
- [graph.nodes, statuses]
1965
- );
1966
- if (graph.nodes.length === 0) {
1967
- return /* @__PURE__ */ jsx("div", { className: cx("ff-viewer ff-viewer--empty", className, classNames.root), style, children: empty ?? /* @__PURE__ */ jsx("span", { className: "ff-viewer__empty", children: "This flow has no nodes." }) });
1968
- }
1969
- if (variant === "list") {
1970
- return /* @__PURE__ */ jsx(
1971
- "div",
1972
- {
1973
- className: cx("ff-viewer ff-viewer--list", className, classNames.root),
1974
- style,
1975
- "data-flow-viewer": "list",
1976
- children: /* @__PURE__ */ jsx("ol", { className: cx("ff-viewer__list", classNames.list), children: rows.map(({ node, index: index2, title, description, accent, status }) => {
1977
- const selected = node.id === selectedNodeId;
1978
- const Row = onSelectNode ? "button" : "div";
1979
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
1980
- Row,
1981
- {
1982
- ...onSelectNode ? { type: "button", onClick: () => onSelectNode(node) } : {},
1983
- className: cx(
1984
- "ff-viewer__row",
1985
- selected && "ff-viewer__row--selected",
1986
- onSelectNode && "ff-viewer__row--interactive",
1987
- classNames.row
1988
- ),
1989
- "data-flow-viewer-node": node.id,
1990
- "data-flow-viewer-status": status ?? void 0,
1991
- "aria-current": selected || void 0,
1992
- children: [
1993
- /* @__PURE__ */ jsx(
1994
- "span",
1995
- {
1996
- className: cx("ff-viewer__index", classNames.rowIndex),
1997
- style: { backgroundColor: accent },
1998
- "aria-hidden": true,
1999
- children: index2 + 1
2000
- }
2001
- ),
2002
- /* @__PURE__ */ jsxs("span", { className: "ff-viewer__body", children: [
2003
- /* @__PURE__ */ jsx("span", { className: cx("ff-viewer__title", classNames.rowTitle), children: title }),
2004
- description && /* @__PURE__ */ jsx("span", { className: cx("ff-viewer__desc", classNames.rowDescription), children: description })
2005
- ] }),
2006
- status && /* @__PURE__ */ jsx(
2007
- "span",
2008
- {
2009
- className: cx(
2010
- "ff-viewer__status",
2011
- `ff-viewer__status--${status}`,
2012
- classNames.rowStatus
2013
- ),
2014
- children: STATUS_LABEL[status]
2015
- }
2016
- )
2017
- ]
2018
- }
2019
- ) }, node.id);
2020
- }) })
2021
- }
2022
- );
2023
- }
2024
- return /* @__PURE__ */ jsx(
2025
- "div",
2026
- {
2027
- className: cx("ff-viewer ff-viewer--canvas", className, classNames.root),
2028
- style,
2029
- "data-flow-viewer": "canvas",
2030
- children: /* @__PURE__ */ jsx(
2031
- FlowCanvas,
2032
- {
2033
- nodes: graph.nodes,
2034
- edges: graph.edges,
2035
- nodeTypes,
2036
- height,
2037
- showControls,
2038
- showMinimap,
2039
- nodesDraggable: false,
2040
- nodesConnectable: false,
2041
- nodesFocusable: Boolean(onSelectNode),
2042
- edgesFocusable: false,
2043
- elementsSelectable: Boolean(onSelectNode),
2044
- deleteKeyCode: null,
2045
- selectionKeyCode: null,
2046
- multiSelectionKeyCode: null,
2047
- connectOnClick: false,
2048
- zoomOnScroll: false,
2049
- onNodeClick: onSelectNode ? (_, node) => onSelectNode(node) : void 0,
2050
- fitView: true
2051
- }
2052
- )
2053
- }
2054
- );
2055
- }
2056
- function cx(...parts) {
2057
- return parts.filter(Boolean).join(" ");
2058
- }
2059
1478
  function defineNode(render) {
2060
1479
  function Wrapped(props) {
2061
1480
  return render({
@@ -2090,6 +1509,6 @@ function NodePort({ side, type, id, style, title, className }) {
2090
1509
  // src/index.ts
2091
1510
  registerBuiltinKinds();
2092
1511
 
2093
- export { ActionNode, ConfigFieldRenderer, DecisionNode, FlowCanvas, FlowEditor, FlowRunControls, FlowRunFeed, FlowViewer, NodeConfigPanel, NodePalette, NodePort, NodeShell, OutputNode, SubgraphNode, TriggerNode, alignNodes, cloneSubgraph, defaultNodeTypes, defineNode, distributeNodes, paletteDropHandlers, reconnectEdge2 as reconnectEdge };
1512
+ export { ConfigFieldRenderer, FlowEditor, FlowRunControls, FlowRunFeed, NodeConfigPanel, NodePalette, NodePort, defineNode, paletteDropHandlers };
2094
1513
  //# sourceMappingURL=index.js.map
2095
1514
  //# sourceMappingURL=index.js.map