@burdenoff/fluidgrids-fe-sdk 2026.1105.2 → 2026.1105.4

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.
Files changed (69) hide show
  1. package/README.md +42 -3
  2. package/dist/ExecutionDetailPage-OOHJQR2L.js +30 -0
  3. package/dist/ExecutionsListPage-6HS5VQOV.js +24 -0
  4. package/dist/WorkflowBuilder-PICIS7FI.js +35 -0
  5. package/dist/WorkflowsList-SD5XQC4Q.js +23 -0
  6. package/dist/{agentTools-5N3ZEZDW.js → agentTools-UH4LU6LA.js} +9 -7
  7. package/dist/{chunk-6ZK4MRVA.js → chunk-43GSC5Y6.js} +1 -1
  8. package/dist/{chunk-CH3YUGWR.js → chunk-4XAXF5VP.js} +4 -4
  9. package/dist/{chunk-6IS7EXIY.js → chunk-5GG2FEKW.js} +2 -2
  10. package/dist/{chunk-64JN2TVX.js → chunk-5Z4F24QT.js} +10 -39
  11. package/dist/chunk-6VIG44VF.js +32 -0
  12. package/dist/{chunk-QFC6E3WD.js → chunk-ASYYYJG2.js} +4 -4
  13. package/dist/{chunk-OXXXUAWM.js → chunk-CTKWMPQQ.js} +1 -1
  14. package/dist/{chunk-QFGZXWTM.js → chunk-EIMTOK6E.js} +3 -3
  15. package/dist/{chunk-AMXJLVZN.js → chunk-EXJW7EKR.js} +2 -2
  16. package/dist/{chunk-O7CJJNW6.js → chunk-GBJWPSLJ.js} +70 -5
  17. package/dist/{chunk-X25NCWG2.js → chunk-GBQC5WDH.js} +16 -8
  18. package/dist/{chunk-3XUWPDKJ.js → chunk-GPABUQTD.js} +650 -636
  19. package/dist/{chunk-3QCRKKTI.js → chunk-HSTP2RX3.js} +267 -2149
  20. package/dist/{chunk-US5QSLPV.js → chunk-JKLZ6YZ3.js} +22 -11
  21. package/dist/{chunk-GKFMK7FF.js → chunk-JRLBPR5U.js} +3 -3
  22. package/dist/{chunk-IL343FKX.js → chunk-KHS2DZHH.js} +1 -1
  23. package/dist/{chunk-SALZ3LQB.js → chunk-KJT5TAOE.js} +3 -3
  24. package/dist/{chunk-AASHQI77.js → chunk-KZCNJAMK.js} +2 -1
  25. package/dist/{chunk-TNFBRXU7.js → chunk-LLCVVRCE.js} +9 -8
  26. package/dist/{chunk-5CFCFA6O.js → chunk-M72SMQOS.js} +8 -7
  27. package/dist/{chunk-TCIBTMJI.js → chunk-NZMUKAXG.js} +3 -69
  28. package/dist/{chunk-2KUZBEWW.js → chunk-OLRIWGYM.js} +8 -7
  29. package/dist/{chunk-62CCBMDC.js → chunk-OTOQFZFO.js} +40 -39
  30. package/dist/{chunk-EYQ7GJRW.js → chunk-PASKZCII.js} +3 -3
  31. package/dist/{chunk-3IXU7WUC.js → chunk-PDODDDHY.js} +1 -1
  32. package/dist/chunk-SDZEAIIE.js +9 -0
  33. package/dist/chunk-TGWDFLEX.js +1932 -0
  34. package/dist/{chunk-AU7LACOY.js → chunk-TYI2XOSI.js} +1 -1
  35. package/dist/{chunk-L2N6DDOI.js → chunk-U7UNWR3D.js} +2 -2
  36. package/dist/{chunk-HK76ZUAK.js → chunk-UIS7KGHW.js} +4 -4
  37. package/dist/{chunk-QPLFALVV.js → chunk-UWCD7JUU.js} +8 -7
  38. package/dist/{chunk-CNKTQ5MG.js → chunk-UYTPURFW.js} +16 -50
  39. package/dist/{chunk-AJVU5YW6.js → chunk-WBRW67HX.js} +4 -3
  40. package/dist/{chunk-J3YQCEP3.js → chunk-WC526MLS.js} +3 -3
  41. package/dist/{chunk-XFH6SLDK.js → chunk-XNARCHMT.js} +2 -2
  42. package/dist/{chunk-W5WML3OO.js → chunk-XWINH6CR.js} +1 -1
  43. package/dist/{chunk-Y6M7K4UG.js → chunk-Y4UCHFGK.js} +1 -1
  44. package/dist/{chunk-OCJTTHTS.js → chunk-Y7FKJJMF.js} +1 -9
  45. package/dist/{client-YVZMLEQX.js → client-62VHXHKN.js} +2 -2
  46. package/dist/code-editor-field-WWPDMF4O.js +3 -0
  47. package/dist/executions.js +17 -15
  48. package/dist/{global-client-N5EIFW6T.js → global-client-PJ5LANJA.js} +2 -2
  49. package/dist/history.js +11 -8
  50. package/dist/index.d.ts +10 -4
  51. package/dist/index.js +69 -52
  52. package/dist/{node-detail-view-WIWW5N6N.js → node-detail-view-P5NV5CMO.js} +14 -12
  53. package/dist/nodes.js +3 -3
  54. package/dist/observability.js +10 -8
  55. package/dist/{quick-config-panel-O64TXXK3.js → quick-config-panel-ZNSBVF4G.js} +11 -9
  56. package/dist/settings.js +11 -9
  57. package/dist/templates.js +17 -14
  58. package/dist/{workflow-builder-7vv9dCdp.d.ts → workflow-builder-C-ekFbwR.d.ts} +37 -4
  59. package/dist/workflow-builder.d.ts +1 -1
  60. package/dist/workflow-builder.js +19 -16
  61. package/dist/workflow-preview.d.ts +158 -0
  62. package/dist/workflow-preview.js +721 -0
  63. package/dist/workflows-list.js +12 -10
  64. package/package.json +5 -1
  65. package/dist/ExecutionDetailPage-HQB2SWBP.js +0 -28
  66. package/dist/ExecutionsListPage-RSLL634L.js +0 -22
  67. package/dist/WorkflowBuilder-E3PNZX74.js +0 -32
  68. package/dist/WorkflowsList-473FRRP2.js +0 -21
  69. package/dist/code-editor-field-XWUVOKTA.js +0 -2
@@ -0,0 +1,721 @@
1
+ import { canvasFromDefinition } from './chunk-6VIG44VF.js';
2
+ import { LogicNodeCard, NodeCard, NodeCardHeader, NodeCardBody, InputHandles, OutputHandles, STICKY_NOTE_TYPE, outputHandlesSpacerHeight, isNodeTrigger, resolveOutputHandles, resolveInputHandles, resolveOperationName, WAIT_FOR_SIGNAL_NODE_TYPE, CODE_WORKFLOW_NODE_TYPE, readSignalName } from './chunk-TGWDFLEX.js';
3
+ import './chunk-Y7FKJJMF.js';
4
+ import { useTr } from './chunk-UULVIT25.js';
5
+ import { memo, useMemo, useState, useCallback, useSyncExternalStore, useRef, useEffect } from 'react';
6
+ import { getBezierPath, ReactFlowProvider, MarkerType, ReactFlow, Background, useReactFlow, useStore } from '@xyflow/react';
7
+ import '@xyflow/react/dist/style.css';
8
+ import { cn } from '@burdenoff/fe-libs/utils';
9
+ import { nodeCategoryColor } from '@burdenoff/fe-libs/shared/utils';
10
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
+
12
+ var PREVIEW_NODE_TYPE = "preview";
13
+ var PREVIEW_EDGE_TYPE = "preview";
14
+ var LOGIC_TYPE_PATTERN = /(^|[:/])(if|switch|merge|loop|filter|branch|router|condition|aggregate)/;
15
+ function isRecord(value) {
16
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17
+ }
18
+ function text(value) {
19
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
20
+ }
21
+ function finite(value) {
22
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
23
+ }
24
+ function humanizeNodeType(nodeType) {
25
+ const last = nodeType.split(/[:/]/).filter(Boolean).pop() ?? nodeType;
26
+ const words = last.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_\-.]+/).filter(Boolean);
27
+ if (words.length === 0) return nodeType;
28
+ return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
29
+ }
30
+ var FALLBACK_ICONS = [
31
+ [/schedule|cron|timer/, "mdi:clock-outline"],
32
+ [/webhook/, "mdi:webhook"],
33
+ [/form/, "mdi:form-select"],
34
+ [/mcp/, "mdi:server-network"],
35
+ [/(^|[:/])api($|[:/_-])|manual/, "mdi:play-circle-outline"],
36
+ [/(^|[:/])if($|[:/_-])|condition|branch|router/, "mdi:call-split"],
37
+ [/switch/, "mdi:source-branch"],
38
+ [/merge/, "mdi:call-merge"],
39
+ [/loop|iterate|each/, "mdi:repeat"],
40
+ [/filter/, "mdi:filter-outline"],
41
+ [/aggregate/, "mdi:sigma"],
42
+ [/wait|delay|sleep|signal/, "mdi:timer-sand"],
43
+ [/code|script|function|javascript|python/, "mdi:code-braces"],
44
+ [/http|rest|graphql|request/, "mdi:web"],
45
+ [/gmail|email|mail|smtp/, "mdi:email-outline"],
46
+ [/slack/, "mdi:slack"],
47
+ [/github/, "mdi:github"],
48
+ [/sheet|excel|csv/, "mdi:table"],
49
+ [/db|sql|postgres|mysql|mongo|table/, "mdi:database-outline"],
50
+ [/ai|llm|gpt|openai|anthropic|claude|agent/, "mdi:robot-outline"],
51
+ [/notify|notification|message|chat|sms/, "mdi:message-outline"]
52
+ ];
53
+ var FALLBACK_NODE_ICON = "mynaui:puzzle";
54
+ function fallbackNodeIcon(nodeType) {
55
+ const key = nodeType.toLowerCase();
56
+ for (const [pattern, icon] of FALLBACK_ICONS) {
57
+ if (pattern.test(key)) return icon;
58
+ }
59
+ return FALLBACK_NODE_ICON;
60
+ }
61
+ function cardData(raw) {
62
+ const root = raw;
63
+ const data = isRecord(raw.data) ? raw.data : {};
64
+ const merged = {};
65
+ for (const key of ["operation_name", "operation_type", "args", "version"]) {
66
+ if (root[key] !== void 0) merged[key] = root[key];
67
+ }
68
+ return { ...merged, ...data };
69
+ }
70
+ function logicSubtitle(nodeType, data) {
71
+ const args = isRecord(data.args) ? data.args : void 0;
72
+ const state = isRecord(args?.state) ? args.state : void 0;
73
+ if (nodeType === WAIT_FOR_SIGNAL_NODE_TYPE) return readSignalName(state);
74
+ const rules = state?.rules;
75
+ if (Array.isArray(rules))
76
+ return `${rules.length} rule${rules.length === 1 ? "" : "s"}`;
77
+ return text(state?.mode);
78
+ }
79
+ function previewCardModel(raw, resolveMeta) {
80
+ const data = cardData(raw);
81
+ const nodeType = text(raw.type) ?? text(data.nodeType) ?? "";
82
+ const manifest = isRecord(data.nodeData) ? data.nodeData : {};
83
+ const resolved = nodeType ? resolveMeta?.(nodeType) : void 0;
84
+ const appName = text(resolved?.title) ?? text(manifest.name) ?? (nodeType ? humanizeNodeType(nodeType) : "");
85
+ const icon = text(resolved?.icon) ?? text(manifest.icon) ?? (nodeType ? fallbackNodeIcon(nodeType) : FALLBACK_NODE_ICON);
86
+ const description = text(manifest.description) ?? text(resolved?.description) ?? "";
87
+ const label = text(data.label);
88
+ const trigger = isNodeTrigger({ type: nodeType, data });
89
+ const lowerType = nodeType.toLowerCase();
90
+ const isLogicType = LOGIC_TYPE_PATTERN.test(lowerType);
91
+ const accent = nodeCategoryColor(
92
+ trigger ? "trigger" : isLogicType ? "logic" : "action"
93
+ );
94
+ const outputHandles = resolveOutputHandles(data, nodeType);
95
+ const inputHandles = resolveInputHandles(data);
96
+ const kind = manifest.kind;
97
+ const isWaitForSignal = nodeType === WAIT_FOR_SIGNAL_NODE_TYPE;
98
+ const isCodeWorkflow = nodeType === CODE_WORKFLOW_NODE_TYPE;
99
+ const isControlFlow = isWaitForSignal || !isCodeWorkflow && nodeType.startsWith("internal:") && (Array.isArray(kind) ? kind.includes("control") : isLogicType);
100
+ const operationName = resolveOperationName(data.operation_name, "");
101
+ const showsDescriptionOnly = isCodeWorkflow || operationName === "execute" && nodeType.startsWith("internal:") || trigger && !operationName;
102
+ return {
103
+ variant: isControlFlow ? "logic" : "app",
104
+ nodeType,
105
+ title: label ?? appName,
106
+ appName,
107
+ icon,
108
+ operation: showsDescriptionOnly ? null : operationName || null,
109
+ description,
110
+ subtitle: isControlFlow ? logicSubtitle(nodeType, data) : void 0,
111
+ trigger,
112
+ disabled: data.disabled === true,
113
+ accent,
114
+ accentKey: nodeType || appName,
115
+ inputHandles,
116
+ outputHandles,
117
+ handleSpacer: outputHandlesSpacerHeight(
118
+ Math.max(outputHandles.length, inputHandles.length)
119
+ )
120
+ };
121
+ }
122
+ function branchTone(id) {
123
+ const key = id.toLowerCase();
124
+ if (key === "true" || key === "success" || key === "approved")
125
+ return "success";
126
+ if (key === "false" || key === "done") return "neutral";
127
+ if (key === "error" || key === "failed" || key === "declined") return "error";
128
+ return void 0;
129
+ }
130
+ function withEdgeSourceHandles(card, edgeHandleIds) {
131
+ const known = new Set(card.outputHandles.map((handle) => handle.id));
132
+ const missing = [...new Set(edgeHandleIds)].filter((id) => !known.has(id));
133
+ if (missing.length === 0) return card;
134
+ const isDefault = card.outputHandles.length === 1 && card.outputHandles[0]?.label === "";
135
+ const named = missing.map((id) => ({
136
+ id,
137
+ label: id,
138
+ tone: branchTone(id)
139
+ }));
140
+ if (isDefault && named.length === 1)
141
+ return { ...card, outputHandles: [{ id: named[0].id, label: "" }] };
142
+ const outputHandles = isDefault ? named : [...card.outputHandles, ...named];
143
+ return {
144
+ ...card,
145
+ outputHandles,
146
+ handleSpacer: outputHandlesSpacerHeight(
147
+ Math.max(outputHandles.length, card.inputHandles.length)
148
+ )
149
+ };
150
+ }
151
+ function previewCanvasFromDefinition(definitionJson, resolveMeta) {
152
+ const canvas = canvasFromDefinition(definitionJson);
153
+ if (!canvas) return null;
154
+ const steps = canvas.nodes.filter((node) => node.type !== STICKY_NOTE_TYPE);
155
+ const ids = new Set(steps.map((node) => node.id));
156
+ const rawEdges = canvas.edges.filter(
157
+ (edge) => ids.has(edge.source) && ids.has(edge.target)
158
+ );
159
+ const sourceHandles = /* @__PURE__ */ new Map();
160
+ for (const edge of rawEdges) {
161
+ if (!edge.sourceHandle) continue;
162
+ const list = sourceHandles.get(edge.source) ?? [];
163
+ list.push(edge.sourceHandle);
164
+ sourceHandles.set(edge.source, list);
165
+ }
166
+ const cards = /* @__PURE__ */ new Map();
167
+ const nodes = steps.map((raw) => {
168
+ const card = withEdgeSourceHandles(
169
+ previewCardModel(raw, resolveMeta),
170
+ sourceHandles.get(raw.id) ?? []
171
+ );
172
+ cards.set(raw.id, card);
173
+ const position = isRecord(raw.position) ? raw.position : {};
174
+ return {
175
+ id: raw.id,
176
+ type: PREVIEW_NODE_TYPE,
177
+ position: {
178
+ x: finite(position.x),
179
+ y: finite(position.y)
180
+ },
181
+ data: { card },
182
+ draggable: false,
183
+ connectable: false,
184
+ selectable: false,
185
+ deletable: false
186
+ };
187
+ });
188
+ const edges = rawEdges.map((edge) => {
189
+ const sourceCard = cards.get(edge.source);
190
+ const targetCard = cards.get(edge.target);
191
+ const targetHandle = edge.targetHandle && targetCard?.inputHandles.some((h) => h.id === edge.targetHandle) ? edge.targetHandle : targetCard?.inputHandles[0]?.id ?? null;
192
+ const sourceHandle = edge.sourceHandle ?? sourceCard?.outputHandles[0]?.id ?? null;
193
+ return {
194
+ id: edge.id,
195
+ source: edge.source,
196
+ target: edge.target,
197
+ sourceHandle,
198
+ targetHandle,
199
+ type: PREVIEW_EDGE_TYPE,
200
+ selectable: false,
201
+ deletable: false,
202
+ focusable: false
203
+ };
204
+ });
205
+ return { nodes, edges };
206
+ }
207
+ function previewRevealOrder(nodes, shown = /* @__PURE__ */ new Set()) {
208
+ return nodes.filter((node) => !shown.has(node.id)).slice().sort(
209
+ (left, right) => left.position.x - right.position.x || left.position.y - right.position.y
210
+ ).map((node) => node.id);
211
+ }
212
+ function edgesBetweenVisible(edges, visible) {
213
+ return edges.filter(
214
+ (edge) => visible.has(edge.source) && visible.has(edge.target)
215
+ );
216
+ }
217
+ function newestNodeId(previous, next, order, current) {
218
+ let added = null;
219
+ for (const id of order) {
220
+ if (next.has(id) && !previous.has(id)) added = id;
221
+ }
222
+ if (added) return added;
223
+ return current && next.has(current) ? current : null;
224
+ }
225
+ function applyMeasurements(current, changes) {
226
+ let next = null;
227
+ for (const change of changes) {
228
+ if (change.type !== "dimensions" || !change.dimensions) continue;
229
+ const before = (next ?? current).get(change.id);
230
+ const { width, height } = change.dimensions;
231
+ if (before?.width === width && before.height === height) continue;
232
+ next ??= new Map(current);
233
+ next.set(change.id, { width, height });
234
+ }
235
+ return next ?? current;
236
+ }
237
+
238
+ // src/builder/preview/preview-styles.ts
239
+ var PREFIX = "fgp";
240
+ var PREVIEW_CLASS = {
241
+ root: `${PREFIX}-root`,
242
+ node: `${PREFIX}-node`,
243
+ ring: `${PREFIX}-ring`,
244
+ edgeTrack: `${PREFIX}-edge-track`,
245
+ edgeArrow: `${PREFIX}-edge-arrow`,
246
+ edgeFlow: `${PREFIX}-edge-flow`,
247
+ edgeDot: `${PREFIX}-edge-dot`
248
+ };
249
+ var NODE_ENTER_MS = 260;
250
+ var EDGE_DRAW_MS = 360;
251
+ var EDGE_DRAW_DELAY_MS = 90;
252
+ var CAMERA_MS = 350;
253
+ var C = PREVIEW_CLASS;
254
+ var PREVIEW_STYLES = `
255
+ .${C.node} {
256
+ position: relative;
257
+ transform-origin: 50% 50%;
258
+ animation: ${PREFIX}-pop ${NODE_ENTER_MS}ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
259
+ }
260
+ @keyframes ${PREFIX}-pop {
261
+ from { opacity: 0; transform: scale(0.82) translateY(6px); }
262
+ to { opacity: 1; transform: scale(1) translateY(0); }
263
+ }
264
+ .${C.ring} {
265
+ position: absolute;
266
+ inset: -6px;
267
+ border-radius: 18px;
268
+ pointer-events: none;
269
+ opacity: 0;
270
+ box-shadow:
271
+ 0 0 0 2px var(--fgp-accent),
272
+ 0 0 18px 4px color-mix(in srgb, var(--fgp-accent) 35%, transparent);
273
+ transition: opacity 600ms ease-out;
274
+ }
275
+ .${C.node}[data-fresh="true"] > .${C.ring} {
276
+ opacity: 1;
277
+ transition: opacity 120ms ease-out;
278
+ animation: ${PREFIX}-ring-in 700ms ease-out both;
279
+ }
280
+ @keyframes ${PREFIX}-ring-in {
281
+ 0% { box-shadow: 0 0 0 0 var(--fgp-accent), 0 0 0 0 color-mix(in srgb, var(--fgp-accent) 55%, transparent); }
282
+ 45% { box-shadow: 0 0 0 3px var(--fgp-accent), 0 0 0 12px color-mix(in srgb, var(--fgp-accent) 0%, transparent); }
283
+ 100% { box-shadow: 0 0 0 2px var(--fgp-accent), 0 0 18px 4px color-mix(in srgb, var(--fgp-accent) 35%, transparent); }
284
+ }
285
+ .${C.node}[data-pulse="true"] > .${C.ring} {
286
+ opacity: 1;
287
+ animation: ${PREFIX}-pulse 1400ms ease-in-out infinite;
288
+ }
289
+ @keyframes ${PREFIX}-pulse {
290
+ 0%, 100% { box-shadow: 0 0 0 2px var(--fgp-accent), 0 0 10px 2px color-mix(in srgb, var(--fgp-accent) 25%, transparent); }
291
+ 50% { box-shadow: 0 0 0 2px var(--fgp-accent), 0 0 24px 8px color-mix(in srgb, var(--fgp-accent) 45%, transparent); }
292
+ }
293
+ .${C.edgeTrack} {
294
+ fill: none;
295
+ stroke: var(--fgp-accent);
296
+ stroke-width: 2;
297
+ stroke-opacity: 0.6;
298
+ stroke-dasharray: 1;
299
+ stroke-dashoffset: 0;
300
+ animation: ${PREFIX}-draw ${EDGE_DRAW_MS}ms cubic-bezier(0.4, 0, 0.2, 1) ${EDGE_DRAW_DELAY_MS}ms both;
301
+ }
302
+ @keyframes ${PREFIX}-draw {
303
+ from { stroke-dashoffset: 1; }
304
+ to { stroke-dashoffset: 0; }
305
+ }
306
+ .${C.edgeArrow} {
307
+ animation: ${PREFIX}-fade 180ms ease-out ${EDGE_DRAW_DELAY_MS + EDGE_DRAW_MS - 80}ms both;
308
+ }
309
+ @keyframes ${PREFIX}-fade {
310
+ from { opacity: 0; }
311
+ to { opacity: 1; }
312
+ }
313
+ .${C.edgeFlow} {
314
+ fill: none;
315
+ stroke: var(--fgp-accent);
316
+ stroke-width: 2;
317
+ stroke-dasharray: 6 8;
318
+ pointer-events: none;
319
+ animation:
320
+ ${PREFIX}-fade 200ms ease-out both,
321
+ ${PREFIX}-march 550ms linear infinite;
322
+ }
323
+ @keyframes ${PREFIX}-march {
324
+ from { stroke-dashoffset: 0; }
325
+ to { stroke-dashoffset: -14; }
326
+ }
327
+ .${C.edgeDot} {
328
+ fill: var(--fgp-accent);
329
+ pointer-events: none;
330
+ filter: drop-shadow(0 0 3px color-mix(in srgb, var(--fgp-accent) 60%, transparent));
331
+ }
332
+ @media (prefers-reduced-motion: reduce) {
333
+ .${C.node},
334
+ .${C.node} > .${C.ring},
335
+ .${C.edgeTrack},
336
+ .${C.edgeArrow},
337
+ .${C.edgeFlow} {
338
+ animation: none !important;
339
+ transition: none !important;
340
+ }
341
+ .${C.edgeDot} { display: none; }
342
+ }
343
+ `;
344
+ function noop() {
345
+ }
346
+ function CardBody({ id, card }) {
347
+ const tr = useTr();
348
+ const fallbackName = tr("fluidgrids.builder.nodeCard.defaultAppName", "Node");
349
+ const title = card.title || fallbackName;
350
+ if (card.variant === "logic") {
351
+ return /* @__PURE__ */ jsx(
352
+ LogicNodeCard,
353
+ {
354
+ nodeId: id,
355
+ title,
356
+ subtitle: card.subtitle,
357
+ icon: card.icon,
358
+ accent: card.accent,
359
+ selected: false,
360
+ executionStatus: "idle",
361
+ isTrigger: card.trigger,
362
+ isExecuting: false,
363
+ inputHandles: card.inputHandles,
364
+ outputHandles: card.outputHandles,
365
+ isConnectable: false,
366
+ onDoubleClick: noop,
367
+ disabled: card.disabled
368
+ }
369
+ );
370
+ }
371
+ return /* @__PURE__ */ jsxs(
372
+ NodeCard,
373
+ {
374
+ isTrigger: card.trigger,
375
+ accent: card.accent,
376
+ disabled: card.disabled,
377
+ children: [
378
+ /* @__PURE__ */ jsx(
379
+ NodeCardHeader,
380
+ {
381
+ icon: card.icon,
382
+ appName: card.appName || fallbackName,
383
+ accentKey: card.accentKey,
384
+ label: title
385
+ }
386
+ ),
387
+ card.operation ? /* @__PURE__ */ jsx(
388
+ NodeCardBody,
389
+ {
390
+ operationName: card.operation,
391
+ description: card.description
392
+ }
393
+ ) : /* @__PURE__ */ jsx("div", { className: "rounded-b-xl border-t border-border/60 px-3 pb-3 pt-2.5", children: /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-snug text-muted-foreground", children: card.description }) }),
394
+ card.handleSpacer > 0 && /* @__PURE__ */ jsx("div", { style: { height: card.handleSpacer } }),
395
+ !card.trigger && /* @__PURE__ */ jsx(
396
+ InputHandles,
397
+ {
398
+ nodeId: id,
399
+ handles: card.inputHandles,
400
+ accent: card.accent,
401
+ isConnectable: false
402
+ }
403
+ ),
404
+ /* @__PURE__ */ jsx(
405
+ OutputHandles,
406
+ {
407
+ nodeId: id,
408
+ handles: card.outputHandles,
409
+ accent: card.accent,
410
+ isConnectable: false
411
+ }
412
+ )
413
+ ]
414
+ }
415
+ );
416
+ }
417
+ function PreviewNodeComponent({ id, data }) {
418
+ const { card, fresh, pulse } = data;
419
+ const style = { "--fgp-accent": card.accent };
420
+ return /* @__PURE__ */ jsxs(
421
+ "div",
422
+ {
423
+ className: PREVIEW_CLASS.node,
424
+ "data-fresh": fresh ? "true" : "false",
425
+ "data-pulse": pulse ? "true" : "false",
426
+ "data-node-type": card.nodeType,
427
+ style,
428
+ children: [
429
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: PREVIEW_CLASS.ring }),
430
+ /* @__PURE__ */ jsx(CardBody, { id, card })
431
+ ]
432
+ }
433
+ );
434
+ }
435
+ var PreviewNode = memo(PreviewNodeComponent);
436
+ var DOT_TRAVEL_S = 1.6;
437
+ function PreviewEdgeComponent({
438
+ id,
439
+ sourceX,
440
+ sourceY,
441
+ targetX,
442
+ targetY,
443
+ sourcePosition,
444
+ targetPosition,
445
+ markerEnd,
446
+ data
447
+ }) {
448
+ const [path] = getBezierPath({
449
+ sourceX,
450
+ sourceY,
451
+ sourcePosition,
452
+ targetX,
453
+ targetY,
454
+ targetPosition
455
+ });
456
+ const accent = data?.accent ?? "var(--color-edge-default)";
457
+ const style = { "--fgp-accent": accent };
458
+ return /* @__PURE__ */ jsxs("g", { style, "data-testid": `workflow-preview-edge-${id}`, children: [
459
+ /* @__PURE__ */ jsx(
460
+ "path",
461
+ {
462
+ id,
463
+ d: path,
464
+ pathLength: 1,
465
+ className: PREVIEW_CLASS.edgeTrack
466
+ }
467
+ ),
468
+ /* @__PURE__ */ jsx("g", { className: PREVIEW_CLASS.edgeArrow, children: /* @__PURE__ */ jsx("path", { d: path, fill: "none", stroke: "transparent", markerEnd }) }),
469
+ data?.running && /* @__PURE__ */ jsxs(Fragment, { children: [
470
+ /* @__PURE__ */ jsx("path", { d: path, className: PREVIEW_CLASS.edgeFlow }),
471
+ !data.reducedMotion && /* @__PURE__ */ jsx("circle", { r: 3.5, className: PREVIEW_CLASS.edgeDot, children: /* @__PURE__ */ jsx(
472
+ "animateMotion",
473
+ {
474
+ dur: `${DOT_TRAVEL_S}s`,
475
+ repeatCount: "indefinite",
476
+ path
477
+ }
478
+ ) })
479
+ ] })
480
+ ] });
481
+ }
482
+ var PreviewEdge = memo(PreviewEdgeComponent);
483
+ var QUERY = "(prefers-reduced-motion: reduce)";
484
+ function mediaQuery() {
485
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(QUERY) : null;
486
+ }
487
+ function subscribe(onChange) {
488
+ const query = mediaQuery();
489
+ if (!query) return () => {
490
+ };
491
+ query.addEventListener("change", onChange);
492
+ return () => query.removeEventListener("change", onChange);
493
+ }
494
+ function snapshot() {
495
+ return mediaQuery()?.matches ?? false;
496
+ }
497
+ function usePrefersReducedMotion() {
498
+ return useSyncExternalStore(subscribe, snapshot, () => false);
499
+ }
500
+ var NODE_TYPES = { [PREVIEW_NODE_TYPE]: PreviewNode };
501
+ var EDGE_TYPES = { [PREVIEW_EDGE_TYPE]: PreviewEdge };
502
+ var MIN_ZOOM = 0.1;
503
+ var DEFAULT_MAX_ZOOM = 1.5;
504
+ var DEFAULT_PADDING = 0.25;
505
+ var KEY_SEPARATOR = "\0";
506
+ function idsKey(ids) {
507
+ return [...new Set(ids)].sort().join(KEY_SEPARATOR);
508
+ }
509
+ function idsFromKey(key) {
510
+ return new Set(key.split(KEY_SEPARATOR).filter(Boolean));
511
+ }
512
+ function CameraFollow({
513
+ visibleIds,
514
+ visibleKey,
515
+ padding,
516
+ maxZoom,
517
+ reducedMotion
518
+ }) {
519
+ const { fitView } = useReactFlow();
520
+ const initialized = useStore(
521
+ (state) => visibleIds.every((id) => {
522
+ const node = state.nodeLookup.get(id);
523
+ return node?.measured.width !== void 0 && node.measured.height !== void 0 && node.internals.handleBounds !== void 0;
524
+ })
525
+ );
526
+ const fittedKey = useRef(null);
527
+ useEffect(() => {
528
+ if (!initialized || visibleIds.length === 0) return;
529
+ if (fittedKey.current === visibleKey) return;
530
+ const duration = reducedMotion || fittedKey.current === null ? 0 : CAMERA_MS;
531
+ const frame = requestAnimationFrame(() => {
532
+ fittedKey.current = visibleKey;
533
+ void fitView({
534
+ nodes: visibleIds.map((id) => ({ id })),
535
+ padding,
536
+ maxZoom,
537
+ minZoom: MIN_ZOOM,
538
+ duration
539
+ });
540
+ });
541
+ return () => cancelAnimationFrame(frame);
542
+ }, [
543
+ initialized,
544
+ visibleIds,
545
+ visibleKey,
546
+ padding,
547
+ maxZoom,
548
+ reducedMotion,
549
+ fitView
550
+ ]);
551
+ return null;
552
+ }
553
+ function PreviewFlow({
554
+ model,
555
+ visibleNodeIds,
556
+ freshNodeIds,
557
+ running,
558
+ maxZoom,
559
+ fitPadding,
560
+ ariaLabel
561
+ }) {
562
+ const reducedMotion = usePrefersReducedMotion();
563
+ const order = useMemo(() => previewRevealOrder(model.nodes), [model]);
564
+ const visible = useMemo(() => {
565
+ const known = new Set(order);
566
+ if (!visibleNodeIds) return known;
567
+ return new Set([...visibleNodeIds].filter((id) => known.has(id)));
568
+ }, [order, visibleNodeIds]);
569
+ const visibleKey = idsKey(visible);
570
+ const visibleIds = useMemo(
571
+ () => order.filter((id) => visible.has(id)),
572
+ [order, visible]
573
+ );
574
+ const [track, setTrack] = useState(() => ({
575
+ key: visibleKey,
576
+ visible,
577
+ newest: newestNodeId(/* @__PURE__ */ new Set(), visible, order, null)
578
+ }));
579
+ if (track.key !== visibleKey) {
580
+ setTrack({
581
+ key: visibleKey,
582
+ visible,
583
+ newest: newestNodeId(track.visible, visible, order, track.newest)
584
+ });
585
+ }
586
+ const newest = track.key === visibleKey ? track.newest : null;
587
+ const [measured, setMeasured] = useState(
588
+ () => /* @__PURE__ */ new Map()
589
+ );
590
+ const onNodesChange = useCallback(
591
+ (changes) => {
592
+ setMeasured((current) => applyMeasurements(current, changes));
593
+ },
594
+ []
595
+ );
596
+ const nodes = useMemo(
597
+ () => model.nodes.filter((node) => visible.has(node.id)).map((node) => ({
598
+ ...node,
599
+ measured: measured.get(node.id),
600
+ data: {
601
+ card: node.data.card,
602
+ fresh: freshNodeIds.has(node.id),
603
+ pulse: running && node.id === newest
604
+ }
605
+ })),
606
+ [model, visible, measured, freshNodeIds, running, newest]
607
+ );
608
+ const accents = useMemo(
609
+ () => new Map(model.nodes.map((node) => [node.id, node.data.card.accent])),
610
+ [model]
611
+ );
612
+ const edges = useMemo(
613
+ () => edgesBetweenVisible(model.edges, visible).map((edge) => ({
614
+ ...edge,
615
+ type: "preview",
616
+ markerEnd: {
617
+ type: MarkerType.ArrowClosed,
618
+ width: 18,
619
+ height: 18,
620
+ color: "var(--color-edge-default)"
621
+ },
622
+ data: {
623
+ accent: accents.get(edge.source) ?? "var(--color-edge-default)",
624
+ running,
625
+ reducedMotion
626
+ }
627
+ })),
628
+ [model, visible, accents, running, reducedMotion]
629
+ );
630
+ return /* @__PURE__ */ jsxs(
631
+ ReactFlow,
632
+ {
633
+ "aria-label": ariaLabel,
634
+ nodes,
635
+ onNodesChange,
636
+ edges,
637
+ nodeTypes: NODE_TYPES,
638
+ edgeTypes: EDGE_TYPES,
639
+ nodesDraggable: false,
640
+ nodesConnectable: false,
641
+ nodesFocusable: false,
642
+ edgesFocusable: false,
643
+ elementsSelectable: false,
644
+ zoomOnDoubleClick: false,
645
+ deleteKeyCode: null,
646
+ selectionKeyCode: null,
647
+ multiSelectionKeyCode: null,
648
+ minZoom: MIN_ZOOM,
649
+ maxZoom,
650
+ fitView: true,
651
+ fitViewOptions: { padding: fitPadding, maxZoom },
652
+ proOptions: { hideAttribution: true },
653
+ style: { background: "var(--color-canvas-bg)" },
654
+ children: [
655
+ /* @__PURE__ */ jsx(Background, { gap: 24, color: "var(--color-canvas-grid)" }),
656
+ /* @__PURE__ */ jsx(
657
+ CameraFollow,
658
+ {
659
+ visibleIds,
660
+ visibleKey,
661
+ padding: fitPadding,
662
+ maxZoom,
663
+ reducedMotion
664
+ }
665
+ )
666
+ ]
667
+ }
668
+ );
669
+ }
670
+ function WorkflowPreviewCanvasComponent({
671
+ definition,
672
+ visibleNodeIds,
673
+ freshNodeIds,
674
+ running = false,
675
+ resolveNodeMeta,
676
+ maxZoom = DEFAULT_MAX_ZOOM,
677
+ fitPadding = DEFAULT_PADDING,
678
+ emptyState = null,
679
+ ariaLabel,
680
+ className,
681
+ style
682
+ }) {
683
+ const model = useMemo(
684
+ () => previewCanvasFromDefinition(definition, resolveNodeMeta),
685
+ [definition, resolveNodeMeta]
686
+ );
687
+ const visibleKey = visibleNodeIds ? idsKey(visibleNodeIds) : null;
688
+ const freshKey = idsKey(freshNodeIds ?? []);
689
+ const stableVisible = useMemo(
690
+ () => visibleKey === null ? null : idsFromKey(visibleKey),
691
+ [visibleKey]
692
+ );
693
+ const stableFresh = useMemo(() => idsFromKey(freshKey), [freshKey]);
694
+ return /* @__PURE__ */ jsxs(
695
+ "div",
696
+ {
697
+ className: cn(PREVIEW_CLASS.root, "relative h-full w-full", className),
698
+ style,
699
+ "data-testid": "workflow-preview-canvas",
700
+ "data-running": running ? "true" : "false",
701
+ children: [
702
+ /* @__PURE__ */ jsx("style", { children: PREVIEW_STYLES }),
703
+ model ? /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(
704
+ PreviewFlow,
705
+ {
706
+ model,
707
+ visibleNodeIds: stableVisible,
708
+ freshNodeIds: stableFresh,
709
+ running,
710
+ maxZoom,
711
+ fitPadding,
712
+ ariaLabel
713
+ }
714
+ ) }) : emptyState
715
+ ]
716
+ }
717
+ );
718
+ }
719
+ var WorkflowPreviewCanvas = memo(WorkflowPreviewCanvasComponent);
720
+
721
+ export { CAMERA_MS as WORKFLOW_PREVIEW_CAMERA_MS, EDGE_DRAW_MS as WORKFLOW_PREVIEW_EDGE_DRAW_MS, NODE_ENTER_MS as WORKFLOW_PREVIEW_NODE_ENTER_MS, WorkflowPreviewCanvas, edgesBetweenVisible, fallbackNodeIcon, humanizeNodeType, newestNodeId, previewCanvasFromDefinition, previewCardModel, previewRevealOrder };