@arbidocs/blocks 0.4.72 → 0.4.74

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 (109) hide show
  1. package/dist/agents/index.cjs +401 -262
  2. package/dist/agents/index.js +7 -4
  3. package/dist/agents/tasks/TaskActivityTimeline.d.ts +1 -1
  4. package/dist/agents/tasks/WorkflowThreadStrip.d.ts +58 -0
  5. package/dist/agents/tasks/index.d.ts +1 -0
  6. package/dist/agents/workflows/WorkflowRunner.d.ts +86 -0
  7. package/dist/agents/workflows/WorkflowStepBar.d.ts +23 -0
  8. package/dist/agents/workflows/index.d.ts +7 -1
  9. package/dist/agents/workflows/planSteps.d.ts +28 -0
  10. package/dist/agents/workflows/run.d.ts +98 -0
  11. package/dist/agents/workflows/skillMarkdown.d.ts +15 -0
  12. package/dist/agents/workflows/types.d.ts +111 -0
  13. package/dist/agents/workflows/useWorkflowPreview.d.ts +14 -0
  14. package/dist/agents/workflows/useWorkflowView.d.ts +54 -0
  15. package/dist/agents/workflows/workflow.d.ts +3 -1
  16. package/dist/chat/index.cjs +124 -124
  17. package/dist/chat/index.js +3 -3
  18. package/dist/{chunk-DH7C2UJO.cjs → chunk-5LP54CWR.cjs} +299 -526
  19. package/dist/chunk-5LP54CWR.cjs.map +1 -0
  20. package/dist/{chunk-3SFFTSOI.cjs → chunk-7CVF3DK2.cjs} +5 -5
  21. package/dist/{chunk-3SFFTSOI.cjs.map → chunk-7CVF3DK2.cjs.map} +1 -1
  22. package/dist/chunk-7D737FLE.cjs +43 -0
  23. package/dist/chunk-7D737FLE.cjs.map +1 -0
  24. package/dist/{chunk-V3L5DONY.cjs → chunk-B4SRYJLM.cjs} +31 -2
  25. package/dist/chunk-B4SRYJLM.cjs.map +1 -0
  26. package/dist/{chunk-LFGJAZ5H.js → chunk-DDF5GPIJ.js} +6 -36
  27. package/dist/chunk-DDF5GPIJ.js.map +1 -0
  28. package/dist/chunk-DETZZBG2.cjs +91 -0
  29. package/dist/chunk-DETZZBG2.cjs.map +1 -0
  30. package/dist/chunk-EVUEIBNU.js +89 -0
  31. package/dist/chunk-EVUEIBNU.js.map +1 -0
  32. package/dist/{chunk-G5QTGKDJ.cjs → chunk-HITAN7JF.cjs} +8 -8
  33. package/dist/{chunk-G5QTGKDJ.cjs.map → chunk-HITAN7JF.cjs.map} +1 -1
  34. package/dist/{chunk-3VQYJBOI.js → chunk-HOXKR26S.js} +31 -4
  35. package/dist/chunk-HOXKR26S.js.map +1 -0
  36. package/dist/{chunk-KJ3QEKOJ.cjs → chunk-IPGKTUF7.cjs} +7 -33
  37. package/dist/chunk-IPGKTUF7.cjs.map +1 -0
  38. package/dist/{chunk-IMC2Z32C.js → chunk-KOSGX3UK.js} +269 -485
  39. package/dist/chunk-KOSGX3UK.js.map +1 -0
  40. package/dist/{chunk-KOXUBCLL.js → chunk-MK6NJQGF.js} +4 -4
  41. package/dist/{chunk-KOXUBCLL.js.map → chunk-MK6NJQGF.js.map} +1 -1
  42. package/dist/{chunk-THO7DZNN.cjs → chunk-MUMH3Z6K.cjs} +20 -15
  43. package/dist/chunk-MUMH3Z6K.cjs.map +1 -0
  44. package/dist/{chunk-5HOAOKZL.js → chunk-OYMWV45N.js} +21 -16
  45. package/dist/chunk-OYMWV45N.js.map +1 -0
  46. package/dist/chunk-P2RBE3FD.cjs +1448 -0
  47. package/dist/chunk-P2RBE3FD.cjs.map +1 -0
  48. package/dist/{chunk-G5DP27DU.js → chunk-UIWLDSTF.js} +3 -29
  49. package/dist/chunk-UIWLDSTF.js.map +1 -0
  50. package/dist/{chunk-VRPRAU3J.js → chunk-WJEKRSPH.js} +3 -3
  51. package/dist/{chunk-VRPRAU3J.js.map → chunk-WJEKRSPH.js.map} +1 -1
  52. package/dist/chunk-XD2M6LIG.js +41 -0
  53. package/dist/chunk-XD2M6LIG.js.map +1 -0
  54. package/dist/chunk-YWYVIQ7T.js +1402 -0
  55. package/dist/chunk-YWYVIQ7T.js.map +1 -0
  56. package/dist/{chunk-WBDK4PIA.cjs → chunk-Z5VUZ6YD.cjs} +6 -37
  57. package/dist/chunk-Z5VUZ6YD.cjs.map +1 -0
  58. package/dist/connect/docx.cjs +2 -2
  59. package/dist/connect/docx.js +1 -1
  60. package/dist/connect/index.cjs +379 -27
  61. package/dist/connect/index.cjs.map +1 -1
  62. package/dist/connect/index.d.ts +1 -0
  63. package/dist/connect/index.js +362 -11
  64. package/dist/connect/index.js.map +1 -1
  65. package/dist/connection/ConnectedWorkflowBuilder.d.ts +44 -0
  66. package/dist/connection/ConnectedWorkflowRunner.d.ts +31 -0
  67. package/dist/connection/useWorkflowThread.d.ts +38 -0
  68. package/dist/facts/index.cjs +1 -1
  69. package/dist/facts/index.js +1 -1
  70. package/dist/files/index.cjs +9 -83
  71. package/dist/files/index.cjs.map +1 -1
  72. package/dist/files/index.js +6 -83
  73. package/dist/files/index.js.map +1 -1
  74. package/dist/home/index.cjs +22 -21
  75. package/dist/home/index.cjs.map +1 -1
  76. package/dist/home/index.js +4 -3
  77. package/dist/home/index.js.map +1 -1
  78. package/dist/index.cjs +579 -440
  79. package/dist/index.cjs.map +1 -1
  80. package/dist/index.js +11 -8
  81. package/dist/index.js.map +1 -1
  82. package/dist/shell/index.cjs +1 -1
  83. package/dist/shell/index.js +1 -1
  84. package/dist/studio/index.cjs +7 -7
  85. package/dist/studio/index.js +5 -5
  86. package/dist/workflow-canvas/StepInspector.d.ts +57 -0
  87. package/dist/workflow-canvas/TriggerInspector.d.ts +46 -0
  88. package/dist/workflow-canvas/WorkflowBuilder.d.ts +55 -0
  89. package/dist/workflow-canvas/WorkflowCanvas.d.ts +69 -0
  90. package/dist/workflow-canvas/edit.d.ts +54 -0
  91. package/dist/workflow-canvas/index.cjs +1719 -0
  92. package/dist/workflow-canvas/index.cjs.map +1 -0
  93. package/dist/workflow-canvas/index.d.ts +13 -0
  94. package/dist/workflow-canvas/index.js +1689 -0
  95. package/dist/workflow-canvas/index.js.map +1 -0
  96. package/dist/workflow-canvas/trigger.d.ts +51 -0
  97. package/dist/workflow-canvas/viewport.d.ts +47 -0
  98. package/package.json +25 -14
  99. package/src/workflow-canvas/canvas.css +113 -0
  100. package/dist/chunk-3VQYJBOI.js.map +0 -1
  101. package/dist/chunk-5HOAOKZL.js.map +0 -1
  102. package/dist/chunk-DH7C2UJO.cjs.map +0 -1
  103. package/dist/chunk-G5DP27DU.js.map +0 -1
  104. package/dist/chunk-IMC2Z32C.js.map +0 -1
  105. package/dist/chunk-KJ3QEKOJ.cjs.map +0 -1
  106. package/dist/chunk-LFGJAZ5H.js.map +0 -1
  107. package/dist/chunk-THO7DZNN.cjs.map +0 -1
  108. package/dist/chunk-V3L5DONY.cjs.map +0 -1
  109. package/dist/chunk-WBDK4PIA.cjs.map +0 -1
@@ -0,0 +1,1719 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var chunkMUMH3Z6K_cjs = require('../chunk-MUMH3Z6K.cjs');
5
+ var chunk5LP54CWR_cjs = require('../chunk-5LP54CWR.cjs');
6
+ require('../chunk-YJQVLVBJ.cjs');
7
+ require('../chunk-ZW6FPIFC.cjs');
8
+ var chunkP2RBE3FD_cjs = require('../chunk-P2RBE3FD.cjs');
9
+ require('../chunk-7RGLTF76.cjs');
10
+ require('../chunk-3AFWGEDJ.cjs');
11
+ require('../chunk-VFUWWH52.cjs');
12
+ require('../chunk-DETZZBG2.cjs');
13
+ require('../chunk-IA7RTPT3.cjs');
14
+ require('../chunk-2EI7NCUA.cjs');
15
+ require('../chunk-Z5VUZ6YD.cjs');
16
+ require('../chunk-7D737FLE.cjs');
17
+ require('../chunk-OB5F4577.cjs');
18
+ require('../chunk-B4SRYJLM.cjs');
19
+ require('../chunk-7UTB4PHM.cjs');
20
+ require('../chunk-FX3K6LNZ.cjs');
21
+ require('../chunk-PPCATBOY.cjs');
22
+ require('../chunk-C54XEN5T.cjs');
23
+ require('../chunk-KDG6FER6.cjs');
24
+ require('../chunk-ULWRNGGE.cjs');
25
+ require('../chunk-MHVNT4J3.cjs');
26
+ require('../chunk-DES3K4SD.cjs');
27
+ var react = require('react');
28
+ var react$2 = require('@arbidocs/react');
29
+ var ui = require('@arbidocs/react/ui');
30
+ var lucideReact = require('lucide-react');
31
+ var jsxRuntime = require('react/jsx-runtime');
32
+ var react$1 = require('@xyflow/react');
33
+
34
+ // src/workflow-canvas/edit.ts
35
+ function newSteppedWorkflow(id, title) {
36
+ return {
37
+ id,
38
+ title,
39
+ description: "",
40
+ inputs: [],
41
+ landing: "chat",
42
+ schedulable: false,
43
+ run: { kind: "steps", steps: [] }
44
+ };
45
+ }
46
+ function withSteps(workflow, steps, layout) {
47
+ const run = workflow.run.kind === "steps" ? workflow.run : { kind: "steps", steps: [] };
48
+ return {
49
+ ...workflow,
50
+ run: { ...run, steps: chunkP2RBE3FD_cjs.orderSteps(steps), layout: layout ?? run.layout }
51
+ };
52
+ }
53
+ var layoutOf = (workflow) => workflow.run.kind === "steps" && workflow.run.layout || {};
54
+ var explicit = (steps) => steps.map((s) => ({ ...s, after: chunkP2RBE3FD_cjs.stepDependencies(steps, s.id) }));
55
+ function stepIdFor(steps, title) {
56
+ const base = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/^[^a-z]+/, "") || "step";
57
+ const taken = new Set(steps.map((s) => s.id));
58
+ if (!taken.has(base)) return base;
59
+ let n = 2;
60
+ while (taken.has(`${base}-${n}`)) n++;
61
+ return `${base}-${n}`;
62
+ }
63
+ var DEFAULT_TITLES = {
64
+ form: "Details",
65
+ upload: "Upload files",
66
+ agent: "Agent step",
67
+ review: "Review"
68
+ };
69
+ function blankStep(kind, id, title) {
70
+ switch (kind) {
71
+ case "form":
72
+ return { id, kind, title, inputs: [] };
73
+ case "upload":
74
+ return { id, kind, title };
75
+ case "agent":
76
+ return { id, kind, title };
77
+ case "review":
78
+ return { id, kind, title, instructions: "" };
79
+ }
80
+ }
81
+ function addStep(workflow, kind, options = {}) {
82
+ const steps = explicit(chunkP2RBE3FD_cjs.workflowSteps(workflow));
83
+ const title = options.title ?? DEFAULT_TITLES[kind];
84
+ const id = stepIdFor(steps, title);
85
+ const step = { ...blankStep(kind, id, title), after: options.after ? [options.after] : [] };
86
+ const layout = { ...layoutOf(workflow) };
87
+ if (options.position) layout[id] = options.position;
88
+ return { workflow: withSteps(workflow, [...steps, step], layout), id };
89
+ }
90
+ function removeStep(workflow, id) {
91
+ const steps = explicit(chunkP2RBE3FD_cjs.workflowSteps(workflow));
92
+ const removed = steps.find((s) => s.id === id);
93
+ if (!removed) return workflow;
94
+ const bridged = steps.filter((s) => s.id !== id).map(
95
+ (s) => s.after?.includes(id) ? {
96
+ ...s,
97
+ after: [.../* @__PURE__ */ new Set([...s.after.filter((d) => d !== id), ...removed.after ?? []])]
98
+ } : s
99
+ );
100
+ const { [id]: _gone, ...layout } = layoutOf(workflow);
101
+ return withSteps(workflow, bridged, layout);
102
+ }
103
+ function connectSteps(workflow, from, to) {
104
+ const steps = explicit(chunkP2RBE3FD_cjs.workflowSteps(workflow));
105
+ if (from === to || !steps.some((s) => s.id === from) || chunkP2RBE3FD_cjs.dependsOn(steps, from, to)) {
106
+ return workflow;
107
+ }
108
+ return withSteps(
109
+ workflow,
110
+ steps.map(
111
+ (s) => s.id === to && !s.after?.includes(from) ? { ...s, after: [...s.after ?? [], from] } : s
112
+ )
113
+ );
114
+ }
115
+ var wouldCycle = (workflow, from, to) => from === to || chunkP2RBE3FD_cjs.dependsOn(chunkP2RBE3FD_cjs.workflowSteps(workflow), from, to);
116
+ function disconnectSteps(workflow, from, to) {
117
+ const steps = explicit(chunkP2RBE3FD_cjs.workflowSteps(workflow));
118
+ return withSteps(
119
+ workflow,
120
+ steps.map((s) => s.id === to ? { ...s, after: (s.after ?? []).filter((d) => d !== from) } : s)
121
+ );
122
+ }
123
+ function updateStep(workflow, id, patch) {
124
+ const steps = explicit(chunkP2RBE3FD_cjs.workflowSteps(workflow));
125
+ return withSteps(
126
+ workflow,
127
+ steps.map(
128
+ (s) => s.id === id ? { ...s, ...patch, id: s.id, kind: s.kind } : s
129
+ )
130
+ );
131
+ }
132
+ var renameIn = (text, from, to) => text.replace(
133
+ /\{\{([^}]*)\}\}/g,
134
+ (_, inner) => `{{${inner.split("||").map((part) => part.trim() === from ? part.replace(from, to) : part).join("||")}}}`
135
+ );
136
+ function renameInput(workflow, from, to) {
137
+ if (from === to) return workflow;
138
+ const steps = chunkP2RBE3FD_cjs.workflowSteps(workflow).map((s) => {
139
+ if (s.kind === "agent")
140
+ return {
141
+ ...s,
142
+ with: s.with ? Object.fromEntries(Object.entries(s.with).map(([k, v]) => [k, renameIn(v, from, to)])) : s.with,
143
+ run: s.run?.kind === "prompt" ? { ...s.run, promptTemplate: renameIn(s.run.promptTemplate, from, to) } : s.run
144
+ };
145
+ if (s.kind === "review" && s.instructions)
146
+ return { ...s, instructions: renameIn(s.instructions, from, to) };
147
+ return s;
148
+ });
149
+ return withSteps(workflow, steps);
150
+ }
151
+ function moveStep(workflow, id, position) {
152
+ return withSteps(workflow, chunkP2RBE3FD_cjs.workflowSteps(workflow), { ...layoutOf(workflow), [id]: position });
153
+ }
154
+ var COLUMN = 300;
155
+ var ROW = 140;
156
+ function autoLayout(steps) {
157
+ const layout = {};
158
+ chunkP2RBE3FD_cjs.stepLevels(steps).forEach(
159
+ (level, x) => level.forEach((s, y) => {
160
+ layout[s.id] = { x: x * COLUMN, y: (y - (level.length - 1) / 2) * ROW };
161
+ })
162
+ );
163
+ return layout;
164
+ }
165
+ function layoutWithDefaults(workflow) {
166
+ const auto = autoLayout(chunkP2RBE3FD_cjs.workflowSteps(workflow));
167
+ return { ...auto, ...layoutOf(workflow) };
168
+ }
169
+ var DEFAULT_STEP_INSPECTOR_LABELS = {
170
+ workflow: "Workflow",
171
+ title: "Title",
172
+ description: "Description",
173
+ waitsFor: "Waits for",
174
+ startsWithRun: "Starts with the run",
175
+ fields: "Fields",
176
+ addField: "Add field",
177
+ fieldLabel: "Label",
178
+ fieldType: "Type",
179
+ required: "Required",
180
+ runs: "Runs",
181
+ chooseRun: "Choose a skill or write an instruction\u2026",
182
+ ownPrompt: "Its own instruction",
183
+ prompt: "Instruction",
184
+ promptHint: "Use {{field}} for an input and {{steps.<id>}} for an earlier step\u2019s answer.",
185
+ inputFor: "Input",
186
+ inputHint: "Type a value, or use a field: {fields}",
187
+ inputHintNoFields: "Type a value, or add a form field to fill it from.",
188
+ thread: "Where it runs",
189
+ threadMain: "In the run\u2019s thread (sees the conversation so far)",
190
+ threadBranch: "On a branch (delivers only its result to the thread)",
191
+ output: "Shows",
192
+ outputAnswer: "The answer",
193
+ outputArtifact: "A drafted document",
194
+ uploadRequired: "Files are required",
195
+ hint: "Hint",
196
+ instructions: "What to review",
197
+ instructionsHint: "Shown on the review task; {{field}} and {{steps.<id>}} work here.",
198
+ deleteStep: "Delete step"
199
+ };
200
+ var FIELD_TYPES = ["text", "longtext", "boolean", "number", "date", "scope"];
201
+ var selectClass = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
202
+ function Field({ label, children, hint }) {
203
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
204
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: label }),
205
+ children,
206
+ hint && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: hint })
207
+ ] });
208
+ }
209
+ var fieldName = (label, taken) => {
210
+ const base = label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").replace(/^[^a-z]+/, "") || "field";
211
+ let name = base;
212
+ let n = 2;
213
+ while (taken.includes(name)) name = `${base}_${n++}`;
214
+ return name;
215
+ };
216
+ function StepInspector({
217
+ workflow,
218
+ stepId,
219
+ onChange,
220
+ onSelect,
221
+ workflows = [],
222
+ labels,
223
+ testId = "step-inspector"
224
+ }) {
225
+ const l = { ...DEFAULT_STEP_INSPECTOR_LABELS, ...labels };
226
+ const steps = chunkP2RBE3FD_cjs.workflowSteps(workflow);
227
+ const step = steps.find((s) => s.id === stepId);
228
+ const run = workflow.run.kind === "steps" ? workflow.run : void 0;
229
+ if (!step || !run) {
230
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", "data-testid": `${testId}-workflow`, children: [
231
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: l.workflow }),
232
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.title, children: /* @__PURE__ */ jsxRuntime.jsx(
233
+ ui.Input,
234
+ {
235
+ value: workflow.title,
236
+ onChange: (e) => onChange({ ...workflow, title: e.target.value }),
237
+ "data-testid": `${testId}-workflow-title`
238
+ }
239
+ ) }),
240
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.description, children: /* @__PURE__ */ jsxRuntime.jsx(
241
+ ui.Textarea,
242
+ {
243
+ value: workflow.description,
244
+ rows: 2,
245
+ onChange: (e) => onChange({ ...workflow, description: e.target.value })
246
+ }
247
+ ) })
248
+ ] });
249
+ }
250
+ const patch = (p) => onChange(updateStep(workflow, step.id, p));
251
+ const deps = chunkP2RBE3FD_cjs.stepDependencies(steps, step.id);
252
+ const titleOf = (id) => steps.find((s) => s.id === id)?.title ?? id;
253
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", "data-testid": `${testId}-step`, children: [
254
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
255
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-semibold", children: step.title }),
256
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "shrink-0 rounded bg-muted px-1.5 py-0.5 text-[11px] text-muted-foreground", children: step.id })
257
+ ] }),
258
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.title, children: /* @__PURE__ */ jsxRuntime.jsx(
259
+ ui.Input,
260
+ {
261
+ value: step.title,
262
+ onChange: (e) => patch({ title: e.target.value }),
263
+ "data-testid": `${testId}-title`
264
+ }
265
+ ) }),
266
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.description, children: /* @__PURE__ */ jsxRuntime.jsx(
267
+ ui.Textarea,
268
+ {
269
+ value: step.description ?? "",
270
+ rows: 2,
271
+ onChange: (e) => patch({ description: e.target.value || void 0 })
272
+ }
273
+ ) }),
274
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.waitsFor, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-1.5", "data-testid": `${testId}-waits-for`, children: [
275
+ deps.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: l.startsWithRun }),
276
+ deps.map((d) => /* @__PURE__ */ jsxRuntime.jsxs(
277
+ "span",
278
+ {
279
+ className: "inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-xs",
280
+ children: [
281
+ titleOf(d),
282
+ /* @__PURE__ */ jsxRuntime.jsx(
283
+ "button",
284
+ {
285
+ type: "button",
286
+ "aria-label": `${titleOf(d)} \xD7`,
287
+ onClick: () => onChange(disconnectSteps(workflow, d, step.id)),
288
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3" })
289
+ }
290
+ )
291
+ ]
292
+ },
293
+ d
294
+ ))
295
+ ] }) }),
296
+ step.kind === "form" && /* @__PURE__ */ jsxRuntime.jsx(
297
+ FormFields,
298
+ {
299
+ fields: step.inputs,
300
+ taken: steps.flatMap((s) => s.kind === "form" && s.id !== step.id ? s.inputs : []).map((f) => f.name),
301
+ onChange: (inputs) => {
302
+ const renamed = step.inputs.flatMap(
303
+ (f, i) => inputs[i] && inputs[i].name !== f.name ? [[f.name, inputs[i].name]] : []
304
+ );
305
+ onChange(
306
+ renamed.reduce(
307
+ (w, [from, to]) => renameInput(w, from, to),
308
+ updateStep(workflow, step.id, { inputs })
309
+ )
310
+ );
311
+ },
312
+ l,
313
+ testId
314
+ }
315
+ ),
316
+ step.kind === "upload" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
317
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
318
+ /* @__PURE__ */ jsxRuntime.jsx(
319
+ ui.Checkbox,
320
+ {
321
+ checked: !!step.required,
322
+ onCheckedChange: (v) => patch({ required: v === true || void 0 })
323
+ }
324
+ ),
325
+ l.uploadRequired
326
+ ] }),
327
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.hint, children: /* @__PURE__ */ jsxRuntime.jsx(
328
+ ui.Input,
329
+ {
330
+ value: step.hint ?? "",
331
+ onChange: (e) => patch({ hint: e.target.value || void 0 })
332
+ }
333
+ ) })
334
+ ] }),
335
+ step.kind === "agent" && /* @__PURE__ */ jsxRuntime.jsx(
336
+ AgentSettings,
337
+ {
338
+ step,
339
+ workflows,
340
+ fields: chunkP2RBE3FD_cjs.runFields(workflow).map((f) => f.name),
341
+ patch,
342
+ l,
343
+ testId
344
+ }
345
+ ),
346
+ step.kind === "review" && /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.instructions, hint: l.instructionsHint, children: /* @__PURE__ */ jsxRuntime.jsx(
347
+ ui.Textarea,
348
+ {
349
+ value: step.instructions ?? "",
350
+ rows: 4,
351
+ onChange: (e) => patch({ instructions: e.target.value || void 0 }),
352
+ "data-testid": `${testId}-instructions`
353
+ }
354
+ ) }),
355
+ /* @__PURE__ */ jsxRuntime.jsxs(
356
+ ui.Button,
357
+ {
358
+ variant: "ghost",
359
+ size: "sm",
360
+ className: "text-destructive",
361
+ onClick: () => {
362
+ onChange(removeStep(workflow, step.id));
363
+ onSelect?.(null);
364
+ },
365
+ "data-testid": `${testId}-delete`,
366
+ children: [
367
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "size-4" }),
368
+ " ",
369
+ l.deleteStep
370
+ ]
371
+ }
372
+ )
373
+ ] });
374
+ }
375
+ function FormFields({
376
+ fields,
377
+ taken,
378
+ onChange,
379
+ l,
380
+ testId
381
+ }) {
382
+ const set = (i, p) => onChange(fields.map((f, j) => j === i ? { ...f, ...p } : f));
383
+ return /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.fields, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", "data-testid": `${testId}-fields`, children: [
384
+ fields.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 rounded-md border border-border p-2", children: [
385
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
386
+ /* @__PURE__ */ jsxRuntime.jsx(
387
+ ui.Input,
388
+ {
389
+ value: f.label,
390
+ "aria-label": l.fieldLabel,
391
+ onChange: (e) => set(i, {
392
+ label: e.target.value,
393
+ name: fieldName(e.target.value, [
394
+ ...taken,
395
+ ...fields.filter((_, j) => j !== i).map((x) => x.name)
396
+ ])
397
+ }),
398
+ className: "h-8"
399
+ }
400
+ ),
401
+ /* @__PURE__ */ jsxRuntime.jsx(
402
+ "button",
403
+ {
404
+ type: "button",
405
+ "aria-label": `${f.label} \xD7`,
406
+ onClick: () => onChange(fields.filter((_, j) => j !== i)),
407
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4 text-muted-foreground" })
408
+ }
409
+ )
410
+ ] }),
411
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
412
+ /* @__PURE__ */ jsxRuntime.jsx(
413
+ "select",
414
+ {
415
+ "aria-label": l.fieldType,
416
+ className: ui.cn(selectClass, "h-8"),
417
+ value: f.type,
418
+ onChange: (e) => set(i, { type: e.target.value }),
419
+ children: FIELD_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t, children: t }, t))
420
+ }
421
+ ),
422
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex shrink-0 items-center gap-1.5 text-xs", children: [
423
+ /* @__PURE__ */ jsxRuntime.jsx(
424
+ ui.Checkbox,
425
+ {
426
+ checked: !!f.required,
427
+ onCheckedChange: (v) => set(i, { required: v === true || void 0 })
428
+ }
429
+ ),
430
+ l.required
431
+ ] }),
432
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "shrink-0 text-[11px] text-muted-foreground", children: `{{${f.name}}}` })
433
+ ] })
434
+ ] }, i)),
435
+ /* @__PURE__ */ jsxRuntime.jsxs(
436
+ ui.Button,
437
+ {
438
+ variant: "outline",
439
+ size: "sm",
440
+ onClick: () => onChange([
441
+ ...fields,
442
+ {
443
+ name: fieldName("Field", [...taken, ...fields.map((f) => f.name)]),
444
+ type: "text",
445
+ label: "Field"
446
+ }
447
+ ]),
448
+ "data-testid": `${testId}-add-field`,
449
+ children: [
450
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "size-4" }),
451
+ " ",
452
+ l.addField
453
+ ]
454
+ }
455
+ )
456
+ ] }) });
457
+ }
458
+ var OWN_PROMPT = "__prompt";
459
+ function prefillSkillInputs(target, fields, current = {}) {
460
+ const out = {};
461
+ for (const f of target?.inputs ?? []) {
462
+ const typed = current[f.name];
463
+ if (typed) out[f.name] = typed;
464
+ else if (fields.includes(f.name)) out[f.name] = `{{${f.name}}}`;
465
+ }
466
+ return out;
467
+ }
468
+ function AgentSettings({
469
+ step,
470
+ workflows,
471
+ fields,
472
+ patch,
473
+ l,
474
+ testId
475
+ }) {
476
+ const target = step.workflow ? workflows.find((w) => w.id === step.workflow) : void 0;
477
+ const value = step.workflow ?? (step.run ? OWN_PROMPT : "");
478
+ const codes = fields.map((f) => `{{${f}}}`).join(", ");
479
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
480
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.runs, children: /* @__PURE__ */ jsxRuntime.jsxs(
481
+ "select",
482
+ {
483
+ className: selectClass,
484
+ value,
485
+ onChange: (e) => {
486
+ const id = e.target.value;
487
+ if (id === OWN_PROMPT)
488
+ patch({
489
+ workflow: void 0,
490
+ with: void 0,
491
+ run: { kind: "prompt", promptTemplate: "" }
492
+ });
493
+ else if (id)
494
+ patch({
495
+ workflow: id,
496
+ run: void 0,
497
+ with: prefillSkillInputs(
498
+ workflows.find((w) => w.id === id),
499
+ fields,
500
+ step.with
501
+ )
502
+ });
503
+ },
504
+ "data-testid": `${testId}-runs`,
505
+ children: [
506
+ !value && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: l.chooseRun }),
507
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: OWN_PROMPT, children: l.ownPrompt }),
508
+ workflows.map((w) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: w.id, children: w.title }, w.id)),
509
+ step.workflow && !target && /* @__PURE__ */ jsxRuntime.jsx("option", { value: step.workflow, children: step.workflow })
510
+ ]
511
+ }
512
+ ) }),
513
+ step.workflow ? (target?.inputs ?? []).map((f) => /* @__PURE__ */ jsxRuntime.jsx(
514
+ Field,
515
+ {
516
+ label: `${l.inputFor}: ${f.label}`,
517
+ hint: step.with?.[f.name] ? void 0 : codes ? l.inputHint.replace("{fields}", codes) : l.inputHintNoFields,
518
+ children: /* @__PURE__ */ jsxRuntime.jsx(
519
+ ui.Input,
520
+ {
521
+ value: step.with?.[f.name] ?? "",
522
+ onChange: (e) => patch({ with: { ...step.with, [f.name]: e.target.value } }),
523
+ "data-testid": `${testId}-with-${f.name}`
524
+ }
525
+ )
526
+ },
527
+ f.name
528
+ )) : step.run?.kind === "prompt" ? /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.prompt, hint: l.promptHint, children: /* @__PURE__ */ jsxRuntime.jsx(
529
+ ui.Textarea,
530
+ {
531
+ value: step.run.promptTemplate,
532
+ rows: 5,
533
+ onChange: (e) => patch({ run: { kind: "prompt", promptTemplate: e.target.value } }),
534
+ "data-testid": `${testId}-prompt`
535
+ }
536
+ ) }) : null,
537
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.thread, children: /* @__PURE__ */ jsxRuntime.jsxs(
538
+ "select",
539
+ {
540
+ className: selectClass,
541
+ value: step.branch ? "branch" : "main",
542
+ onChange: (e) => patch({ branch: e.target.value === "branch" || void 0 }),
543
+ "data-testid": `${testId}-thread`,
544
+ children: [
545
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "main", children: l.threadMain }),
546
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "branch", children: l.threadBranch })
547
+ ]
548
+ }
549
+ ) }),
550
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: l.output, children: /* @__PURE__ */ jsxRuntime.jsxs(
551
+ "select",
552
+ {
553
+ className: selectClass,
554
+ value: step.output ?? "answer",
555
+ onChange: (e) => patch({ output: e.target.value === "artifact" ? "artifact" : void 0 }),
556
+ children: [
557
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "answer", children: l.outputAnswer }),
558
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "artifact", children: l.outputArtifact })
559
+ ]
560
+ }
561
+ ) })
562
+ ] });
563
+ }
564
+ var DEFAULT_TRIGGER_INSPECTOR_LABELS = {
565
+ heading: "Start",
566
+ start: "Starts",
567
+ starts: {
568
+ manual: "When someone runs it",
569
+ once: "Once, at a time",
570
+ repeat: "On a schedule",
571
+ event: "When something happens",
572
+ task: "When a task is done"
573
+ },
574
+ runAt: "When",
575
+ event: "Event",
576
+ eventPlaceholder: "Pick an event",
577
+ afterTask: "After this task",
578
+ afterTaskPlaceholder: "Pick a task",
579
+ agent: "Runs as",
580
+ access: "Access when nobody is there",
581
+ accessAsk: "Ask me each run",
582
+ accessStanding: "Keep access"
583
+ };
584
+ function triggerSummary(trigger, options = {}) {
585
+ const l = { ...DEFAULT_TRIGGER_INSPECTOR_LABELS, ...options.labels };
586
+ switch (trigger.start) {
587
+ case "once":
588
+ return trigger.runAt ? new Date(trigger.runAt).toLocaleString() : l.starts.once;
589
+ case "repeat":
590
+ return trigger.rrule ? chunk5LP54CWR_cjs.describeRRule(trigger.rrule) : l.starts.repeat;
591
+ case "event":
592
+ return trigger.eventKind && options.eventLabel?.(trigger.eventKind) || trigger.eventKind || l.starts.event;
593
+ case "task":
594
+ return trigger.afterTaskId && options.taskLabel?.(trigger.afterTaskId) || l.starts.task;
595
+ default:
596
+ return l.starts.manual;
597
+ }
598
+ }
599
+ var selectClass2 = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
600
+ var STARTS = ["manual", "once", "repeat", "event", "task"];
601
+ function TriggerInspector({
602
+ trigger,
603
+ onChange,
604
+ agents = [],
605
+ eventKinds = [],
606
+ tasks = [],
607
+ labels,
608
+ testId = "trigger-inspector"
609
+ }) {
610
+ const l = { ...DEFAULT_TRIGGER_INSPECTOR_LABELS, ...labels };
611
+ const set = (patch) => onChange({ ...trigger, ...patch });
612
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", "data-testid": testId, children: [
613
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: l.heading }),
614
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
615
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: l.start }),
616
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-1.5", role: "radiogroup", "aria-label": l.start, children: STARTS.map((start) => /* @__PURE__ */ jsxRuntime.jsx(
617
+ "button",
618
+ {
619
+ type: "button",
620
+ role: "radio",
621
+ "aria-checked": trigger.start === start,
622
+ onClick: () => set({ start }),
623
+ className: ui.cn(
624
+ "rounded-md border px-3 py-2 text-left text-sm transition-colors",
625
+ trigger.start === start ? "border-primary bg-primary/10 text-primary" : "border-border hover:bg-muted/50"
626
+ ),
627
+ "data-testid": `${testId}-start-${start}`,
628
+ children: l.starts[start]
629
+ },
630
+ start
631
+ )) })
632
+ ] }),
633
+ trigger.start === "once" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
634
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: l.runAt }),
635
+ /* @__PURE__ */ jsxRuntime.jsx(
636
+ ui.Input,
637
+ {
638
+ type: "datetime-local",
639
+ value: trigger.runAt,
640
+ onChange: (e) => set({ runAt: e.target.value }),
641
+ "data-testid": `${testId}-run-at`
642
+ }
643
+ )
644
+ ] }),
645
+ trigger.start === "repeat" && /* @__PURE__ */ jsxRuntime.jsx(chunk5LP54CWR_cjs.RecurrenceBuilder, { rrule: trigger.rrule, onChange: (rrule) => set({ rrule }) }),
646
+ trigger.start === "event" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
647
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: l.event }),
648
+ /* @__PURE__ */ jsxRuntime.jsxs(
649
+ "select",
650
+ {
651
+ className: selectClass2,
652
+ value: trigger.eventKind,
653
+ onChange: (e) => set({ eventKind: e.target.value }),
654
+ "data-testid": `${testId}-event`,
655
+ children: [
656
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: l.eventPlaceholder }),
657
+ eventKinds.map((k) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: k.value, children: k.label }, k.value))
658
+ ]
659
+ }
660
+ )
661
+ ] }),
662
+ trigger.start === "task" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
663
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: l.afterTask }),
664
+ /* @__PURE__ */ jsxRuntime.jsxs(
665
+ "select",
666
+ {
667
+ className: selectClass2,
668
+ value: trigger.afterTaskId,
669
+ onChange: (e) => set({ afterTaskId: e.target.value }),
670
+ "data-testid": `${testId}-after-task`,
671
+ children: [
672
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: l.afterTaskPlaceholder }),
673
+ tasks.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t.value, children: t.label }, t.value))
674
+ ]
675
+ }
676
+ )
677
+ ] }),
678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
679
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: l.agent }),
680
+ /* @__PURE__ */ jsxRuntime.jsx(
681
+ chunk5LP54CWR_cjs.AgentSelect,
682
+ {
683
+ agents,
684
+ value: trigger.agentId,
685
+ onValueChange: (agentId) => set({ agentId }),
686
+ hideLabel: true,
687
+ testId: `${testId}-agent`
688
+ }
689
+ )
690
+ ] }),
691
+ trigger.start !== "manual" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
692
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: l.access }),
693
+ /* @__PURE__ */ jsxRuntime.jsxs(
694
+ "select",
695
+ {
696
+ className: selectClass2,
697
+ value: trigger.access,
698
+ onChange: (e) => set({ access: e.target.value === "standing" ? "standing" : "ask_each_run" }),
699
+ "data-testid": `${testId}-access`,
700
+ children: [
701
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "ask_each_run", children: l.accessAsk }),
702
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "standing", children: l.accessStanding })
703
+ ]
704
+ }
705
+ )
706
+ ] })
707
+ ] });
708
+ }
709
+
710
+ // src/workflow-canvas/viewport.ts
711
+ var READABLE_ZOOM = 0.7;
712
+ var DEFAULT_W = 256;
713
+ var DEFAULT_H = 80;
714
+ var MARGIN = 48;
715
+ var sizeOf = (n) => ({
716
+ w: n.measured?.width ?? DEFAULT_W,
717
+ h: n.measured?.height ?? DEFAULT_H
718
+ });
719
+ function readableViewport(nodes, fittedZoom, frame, {
720
+ anchor,
721
+ centre = false,
722
+ reserveRight = 0
723
+ } = {}) {
724
+ if (fittedZoom >= READABLE_ZOOM || !frame.height || nodes.length === 0) return null;
725
+ const z = READABLE_ZOOM;
726
+ const at = nodes.find((n) => n.id === anchor) ?? nodes.reduce((a, b) => b.position.x < a.position.x ? b : a);
727
+ const { w, h } = sizeOf(at);
728
+ const y = frame.height / 2 - (at.position.y + h / 2) * z;
729
+ if (centre && at.id === anchor) {
730
+ const visible = Math.max(0, frame.width - reserveRight);
731
+ return { x: visible / 2 - (at.position.x + w / 2) * z, y, zoom: z };
732
+ }
733
+ return { x: MARGIN - at.position.x * z, y, zoom: z };
734
+ }
735
+ function revealCenter(node, view, frame, reserveRight = 0) {
736
+ const { w, h } = sizeOf(node);
737
+ const { x, y, zoom } = view;
738
+ const left = node.position.x * zoom + x;
739
+ const top = node.position.y * zoom + y;
740
+ const right = frame.width - reserveRight;
741
+ if (left >= 16 && top >= 16 && left + w * zoom <= right - 16 && top + h * zoom <= frame.height - 16)
742
+ return null;
743
+ return {
744
+ x: node.position.x + w / 2 + reserveRight / 2 / zoom,
745
+ y: node.position.y + h / 2
746
+ };
747
+ }
748
+ var DEFAULT_WORKFLOW_CANVAS_LABELS = {
749
+ form: "Form",
750
+ upload: "Upload",
751
+ agent: "AI step",
752
+ review: "Approval",
753
+ branch: "Branch",
754
+ kindHints: {
755
+ form: "Ask for details",
756
+ upload: "Collect files",
757
+ agent: "Run a skill or an instruction",
758
+ review: "A person approves"
759
+ },
760
+ addNext: "Add the next step",
761
+ runs: "Runs \u201C{name}\u201D",
762
+ chooseRun: "Choose a skill or write an instruction",
763
+ fields: "{count} fields",
764
+ noFields: "No fields yet",
765
+ approvedBy: "A person approves",
766
+ emptyTitle: "Start your workflow",
767
+ emptyHint: "Add the first step: what should happen when it starts?",
768
+ fitView: "Fit view"
769
+ };
770
+ var TRIGGER_NODE = "__trigger";
771
+ var STEP_KIND_ICONS = {
772
+ form: lucideReact.FileInput,
773
+ upload: lucideReact.Upload,
774
+ agent: lucideReact.Bot,
775
+ review: lucideReact.ClipboardCheck
776
+ };
777
+ var KINDS = ["form", "upload", "agent", "review"];
778
+ function AddNext({
779
+ from,
780
+ onAdd,
781
+ l,
782
+ testId
783
+ }) {
784
+ const [open, setOpen] = react.useState(false);
785
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Popover, { open, onOpenChange: setOpen, children: [
786
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
787
+ "button",
788
+ {
789
+ type: "button",
790
+ className: "wf-add nodrag nopan flex size-7 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm hover:text-foreground",
791
+ "aria-label": l.addNext,
792
+ "data-testid": `${testId}-add-after-${from === TRIGGER_NODE ? "trigger" : from}`,
793
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "size-4" })
794
+ }
795
+ ) }),
796
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.PopoverContent, { side: "right", align: "start", className: "w-60 p-1.5", children: [
797
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-2 pb-1 pt-0.5 text-xs font-medium text-muted-foreground", children: l.addNext }),
798
+ KINDS.map((kind) => {
799
+ const Icon = STEP_KIND_ICONS[kind];
800
+ return /* @__PURE__ */ jsxRuntime.jsxs(
801
+ "button",
802
+ {
803
+ type: "button",
804
+ onClick: (e) => {
805
+ e.stopPropagation();
806
+ setOpen(false);
807
+ onAdd(from, kind);
808
+ },
809
+ className: ui.cn(
810
+ `wf-kind-${kind}`,
811
+ "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-muted"
812
+ ),
813
+ "data-testid": `${testId}-add-after-${from === TRIGGER_NODE ? "trigger" : from}-${kind}`,
814
+ children: [
815
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "wf-badge flex size-7 shrink-0 items-center justify-center rounded-md", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }) }),
816
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0", children: [
817
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-sm font-medium", children: l[kind] }),
818
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-xs text-muted-foreground", children: l.kindHints[kind] })
819
+ ] })
820
+ ]
821
+ },
822
+ kind
823
+ );
824
+ })
825
+ ] })
826
+ ] });
827
+ }
828
+ function PhaseMark({ phase }) {
829
+ if (phase === "running") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "wf-kind-text size-4 animate-spin" });
830
+ if (phase === "done") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "wf-kind-text size-4" });
831
+ if (phase === "failed") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "size-4 text-destructive" });
832
+ if (phase === "skipped") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MinusCircle, { className: "size-4 text-muted-foreground" });
833
+ return null;
834
+ }
835
+ function StepNode({ data }) {
836
+ const { step, phase, summary, icon, l, onAdd, testId } = data;
837
+ const KindIcon = STEP_KIND_ICONS[step.kind];
838
+ return /* @__PURE__ */ jsxRuntime.jsxs(
839
+ "div",
840
+ {
841
+ className: ui.cn(
842
+ "wf-node w-64 py-3 pl-4 pr-3 text-left",
843
+ `wf-kind-${step.kind}`,
844
+ phase && `wf-phase-${phase}`
845
+ ),
846
+ "data-testid": `${testId}-node-${step.id}`,
847
+ "data-phase": phase,
848
+ children: [
849
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "target", position: react$1.Position.Left }),
850
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2.5", children: [
851
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "wf-badge flex size-8 shrink-0 items-center justify-center rounded-lg [&_svg]:size-4", children: icon ?? /* @__PURE__ */ jsxRuntime.jsx(KindIcon, {}) }),
852
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
853
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
854
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "wf-kind-text text-[10px] font-semibold uppercase tracking-wider", children: l[step.kind] }),
855
+ /* @__PURE__ */ jsxRuntime.jsx(PhaseMark, { phase })
856
+ ] }),
857
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-semibold leading-snug", children: step.title }),
858
+ summary && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 line-clamp-2 text-xs leading-snug text-muted-foreground", children: summary }),
859
+ step.kind === "agent" && step.branch && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-1.5 inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground", children: [
860
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GitBranch, { className: "size-3" }),
861
+ " ",
862
+ l.branch
863
+ ] })
864
+ ] })
865
+ ] }),
866
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "source", position: react$1.Position.Right }),
867
+ onAdd && /* @__PURE__ */ jsxRuntime.jsx(AddNext, { from: step.id, onAdd, l, testId })
868
+ ]
869
+ }
870
+ );
871
+ }
872
+ function TriggerNode({ data }) {
873
+ return /* @__PURE__ */ jsxRuntime.jsxs(
874
+ "div",
875
+ {
876
+ className: "wf-start wf-kind-start relative w-60 px-4 py-3 text-left",
877
+ "data-testid": `${data.testId}-node-trigger`,
878
+ children: [
879
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
880
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "wf-badge flex size-8 shrink-0 items-center justify-center rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Zap, { className: "size-4" }) }),
881
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
882
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "wf-kind-text text-[10px] font-semibold uppercase tracking-wider", children: data.title }),
883
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-semibold", children: data.summary })
884
+ ] })
885
+ ] }),
886
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "source", position: react$1.Position.Right }),
887
+ data.onAdd && /* @__PURE__ */ jsxRuntime.jsx(AddNext, { from: TRIGGER_NODE, onAdd: data.onAdd, l: data.l, testId: data.testId })
888
+ ]
889
+ }
890
+ );
891
+ }
892
+ var NODE_TYPES = { step: StepNode, trigger: TriggerNode };
893
+ var firstWords = (text, n = 14) => {
894
+ const words = text.trim().split(/\s+/);
895
+ return words.length > n ? `${words.slice(0, n).join(" ")}\u2026` : text.trim();
896
+ };
897
+ function summaryOf(step, l, workflowTitle) {
898
+ if (step.description) return step.description;
899
+ switch (step.kind) {
900
+ case "agent":
901
+ if (step.workflow)
902
+ return l.runs.replace("{name}", workflowTitle?.(step.workflow) ?? step.workflow);
903
+ if (step.run?.kind === "operation") return void 0;
904
+ return step.run?.kind === "prompt" && step.run.promptTemplate.trim() ? firstWords(step.run.promptTemplate) : l.chooseRun;
905
+ case "form":
906
+ return step.inputs.length ? step.inputs.length === 1 ? step.inputs[0].label : l.fields.replace("{count}", String(step.inputs.length)) : l.noFields;
907
+ case "upload":
908
+ return step.hint;
909
+ case "review":
910
+ return step.instructions ? firstWords(step.instructions) : l.approvedBy;
911
+ }
912
+ }
913
+ function WorkflowCanvas({
914
+ workflow,
915
+ onChange,
916
+ selected = null,
917
+ onSelect,
918
+ focus,
919
+ onAddAfter,
920
+ phases,
921
+ workflowTitle,
922
+ trigger,
923
+ renderIcon,
924
+ labels,
925
+ className,
926
+ reserveRight = 0,
927
+ testId = "workflow-canvas"
928
+ }) {
929
+ const l = react.useMemo(() => ({ ...DEFAULT_WORKFLOW_CANVAS_LABELS, ...labels }), [labels]);
930
+ const steps = chunkP2RBE3FD_cjs.workflowSteps(workflow);
931
+ const editable = !!onChange;
932
+ const built = react.useMemo(() => {
933
+ const layout = layoutWithDefaults(workflow);
934
+ const all = chunkP2RBE3FD_cjs.workflowSteps(workflow);
935
+ const roots = all.filter((s) => chunkP2RBE3FD_cjs.stepDependencies(all, s.id).length === 0);
936
+ const triggerNode = trigger ? [
937
+ {
938
+ id: TRIGGER_NODE,
939
+ type: "trigger",
940
+ deletable: false,
941
+ selected: selected === TRIGGER_NODE,
942
+ position: layout[TRIGGER_NODE] ?? {
943
+ x: Math.min(0, ...Object.values(layout).map((p) => p.x)) - 320,
944
+ y: roots.length ? roots.reduce((sum, s) => sum + (layout[s.id]?.y ?? 0), 0) / roots.length : 0
945
+ },
946
+ data: { title: trigger.title, summary: trigger.summary, l, onAdd: onAddAfter, testId }
947
+ }
948
+ ] : [];
949
+ return [
950
+ ...triggerNode,
951
+ ...all.map((step) => ({
952
+ id: step.id,
953
+ type: "step",
954
+ position: layout[step.id] ?? { x: 0, y: 0 },
955
+ selected: step.id === selected,
956
+ data: {
957
+ step,
958
+ phase: phases?.[step.id],
959
+ summary: summaryOf(step, l, workflowTitle),
960
+ icon: step.icon && renderIcon ? renderIcon(step.icon) : void 0,
961
+ l,
962
+ onAdd: onAddAfter,
963
+ testId
964
+ }
965
+ }))
966
+ ];
967
+ }, [workflow, selected, phases, workflowTitle, trigger, renderIcon, l, onAddAfter, testId]);
968
+ const [nodes, setNodes] = react.useState(built);
969
+ react.useEffect(() => setNodes(built), [built]);
970
+ const edges = react.useMemo(
971
+ () => steps.flatMap((s) => {
972
+ const deps = chunkP2RBE3FD_cjs.stepDependencies(steps, s.id);
973
+ const from = deps.length ? deps : trigger ? [TRIGGER_NODE] : [];
974
+ return from.map((d) => ({
975
+ id: `${d}->${s.id}`,
976
+ source: d,
977
+ target: s.id,
978
+ type: "smoothstep",
979
+ deletable: d !== TRIGGER_NODE,
980
+ markerEnd: { type: react$1.MarkerType.ArrowClosed, width: 16, height: 16 },
981
+ animated: phases?.[s.id] === "running"
982
+ }));
983
+ }),
984
+ [steps, phases, trigger]
985
+ );
986
+ const onNodesChange = react.useCallback(
987
+ (changes) => {
988
+ setNodes((ns) => react$1.applyNodeChanges(changes, ns));
989
+ if (!onChange) return;
990
+ let next = workflow;
991
+ for (const c of changes) {
992
+ if (c.type === "position" && c.dragging === false && c.position && c.id !== TRIGGER_NODE) {
993
+ next = moveStep(next, c.id, c.position);
994
+ }
995
+ }
996
+ if (next !== workflow) onChange(next);
997
+ },
998
+ [workflow, onChange]
999
+ );
1000
+ const onConnect = react.useCallback(
1001
+ (c) => {
1002
+ if (!onChange || !c.source || !c.target) return;
1003
+ if (c.source === TRIGGER_NODE) onChange(updateStep(workflow, c.target, { after: [] }));
1004
+ else onChange(connectSteps(workflow, c.source, c.target));
1005
+ },
1006
+ [workflow, onChange]
1007
+ );
1008
+ const frame = react.useRef(null);
1009
+ const flowRef = react.useRef(null);
1010
+ const hasTrigger = !!trigger;
1011
+ const selectedRef = react.useRef(selected);
1012
+ selectedRef.current = selected;
1013
+ const fit = react.useCallback(
1014
+ async (flow, anchorOnSelection) => {
1015
+ await flow.fitView({
1016
+ padding: { top: "48px", bottom: "48px", left: "48px", right: `${reserveRight + 48}px` },
1017
+ maxZoom: 1
1018
+ });
1019
+ const box = frame.current;
1020
+ const picked = anchorOnSelection ? selectedRef.current : null;
1021
+ const view = readableViewport(
1022
+ flow.getNodes(),
1023
+ flow.getZoom(),
1024
+ { width: box?.clientWidth ?? 0, height: box?.clientHeight ?? 0 },
1025
+ {
1026
+ anchor: picked ?? (hasTrigger ? TRIGGER_NODE : null),
1027
+ centre: !!picked,
1028
+ reserveRight
1029
+ }
1030
+ );
1031
+ if (view) await flow.setViewport(view);
1032
+ },
1033
+ [reserveRight, hasTrigger]
1034
+ );
1035
+ const onInit = react.useCallback(
1036
+ async (flow) => {
1037
+ flowRef.current = flow;
1038
+ await fit(flow, false);
1039
+ },
1040
+ [fit]
1041
+ );
1042
+ const target = focus ?? selected;
1043
+ const revealed = react.useRef(null);
1044
+ react.useEffect(() => {
1045
+ const flow = flowRef.current;
1046
+ const box = frame.current;
1047
+ const node = target ? flow?.getNode(target) : void 0;
1048
+ const key = `${target}:${reserveRight}`;
1049
+ if (!target) revealed.current = null;
1050
+ if (!flow || !box || !node || revealed.current === key) return;
1051
+ revealed.current = key;
1052
+ const view = flow.getViewport();
1053
+ const centre = revealCenter(
1054
+ node,
1055
+ view,
1056
+ { width: box.clientWidth, height: box.clientHeight },
1057
+ reserveRight
1058
+ );
1059
+ if (centre) void flow.setCenter(centre.x, centre.y, { zoom: view.zoom, duration: 250 });
1060
+ }, [target, nodes, reserveRight]);
1061
+ const onDelete = react.useCallback(
1062
+ ({ nodes: gone, edges: cut }) => {
1063
+ if (!onChange) return;
1064
+ const removed = new Set(gone.map((n) => n.id));
1065
+ let next = gone.reduce((w, n) => removeStep(w, n.id), workflow);
1066
+ for (const e of cut) {
1067
+ if (!removed.has(e.source) && !removed.has(e.target)) {
1068
+ next = disconnectSteps(next, e.source, e.target);
1069
+ }
1070
+ }
1071
+ onChange(next);
1072
+ },
1073
+ [workflow, onChange]
1074
+ );
1075
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1076
+ "div",
1077
+ {
1078
+ className: ui.cn(
1079
+ "relative h-[max(560px,calc(100dvh-200px))] w-full overflow-hidden rounded-xl border border-border bg-muted/30",
1080
+ className
1081
+ ),
1082
+ ref: frame,
1083
+ "data-testid": testId,
1084
+ children: [
1085
+ /* @__PURE__ */ jsxRuntime.jsxs(
1086
+ react$1.ReactFlow,
1087
+ {
1088
+ nodes,
1089
+ edges,
1090
+ nodeTypes: NODE_TYPES,
1091
+ onNodesChange,
1092
+ onConnect,
1093
+ onDelete,
1094
+ isValidConnection: (c) => !!c.source && !!c.target && !wouldCycle(workflow, c.source, c.target),
1095
+ onNodeClick: (_, node) => onSelect?.(node.id),
1096
+ onPaneClick: () => onSelect?.(null),
1097
+ nodesDraggable: editable,
1098
+ nodesConnectable: editable,
1099
+ elementsSelectable: true,
1100
+ deleteKeyCode: editable ? ["Backspace", "Delete"] : null,
1101
+ onInit,
1102
+ minZoom: 0.3,
1103
+ proOptions: { hideAttribution: true },
1104
+ children: [
1105
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.Background, { variant: react$1.BackgroundVariant.Dots, gap: 18, size: 1.5 }),
1106
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.Controls, { showInteractive: false, showFitView: false, position: "bottom-left", children: /* @__PURE__ */ jsxRuntime.jsx(
1107
+ react$1.ControlButton,
1108
+ {
1109
+ className: "react-flow__controls-fitview",
1110
+ onClick: () => flowRef.current && void fit(flowRef.current, true),
1111
+ title: l.fitView,
1112
+ "aria-label": l.fitView,
1113
+ "data-testid": `${testId}-fit`,
1114
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize, {})
1115
+ }
1116
+ ) }),
1117
+ steps.length > 3 && /* @__PURE__ */ jsxRuntime.jsx(
1118
+ react$1.MiniMap,
1119
+ {
1120
+ pannable: true,
1121
+ zoomable: true,
1122
+ position: "bottom-right",
1123
+ className: "wf-minimap !rounded-lg !border !border-border",
1124
+ style: { width: 168, height: 104 },
1125
+ nodeBorderRadius: 8,
1126
+ nodeStrokeWidth: 0
1127
+ }
1128
+ )
1129
+ ]
1130
+ }
1131
+ ),
1132
+ steps.length === 0 && onAddAfter && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-16 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
1133
+ "div",
1134
+ {
1135
+ className: "pointer-events-auto rounded-xl border border-dashed border-border bg-card/90 p-5 text-center shadow-sm backdrop-blur",
1136
+ "data-testid": `${testId}-empty`,
1137
+ children: [
1138
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: l.emptyTitle }),
1139
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: l.emptyHint }),
1140
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 grid grid-cols-2 gap-2", children: KINDS.map((kind) => {
1141
+ const Icon = STEP_KIND_ICONS[kind];
1142
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1143
+ "button",
1144
+ {
1145
+ type: "button",
1146
+ onClick: () => onAddAfter(TRIGGER_NODE, kind),
1147
+ className: ui.cn(
1148
+ `wf-kind-${kind}`,
1149
+ "flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-left text-sm hover:bg-muted"
1150
+ ),
1151
+ "data-testid": `${testId}-empty-${kind}`,
1152
+ children: [
1153
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "wf-badge flex size-6 items-center justify-center rounded-md", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-3.5" }) }),
1154
+ l[kind]
1155
+ ]
1156
+ },
1157
+ kind
1158
+ );
1159
+ }) })
1160
+ ]
1161
+ }
1162
+ ) })
1163
+ ]
1164
+ }
1165
+ );
1166
+ }
1167
+ var DEFAULT_WORKFLOW_BUILDER_LABELS = {
1168
+ addStep: "Add step",
1169
+ tidy: "Tidy",
1170
+ settings: "Workflow settings",
1171
+ closePanel: "Close panel",
1172
+ design: "Design",
1173
+ preview: "Preview",
1174
+ start: "Start",
1175
+ previewAnswer: "Preview: the agent would be asked \u2014\n\n{prompt}",
1176
+ problems: {
1177
+ duplicate: "\u201C{step}\u201D appears twice.",
1178
+ unknown: "\u201C{step}\u201D waits for a step that does not exist.",
1179
+ cycle: "\u201C{step}\u201D waits for itself (a loop).",
1180
+ order: "\u201C{step}\u201D is listed before a step it waits for."
1181
+ }
1182
+ };
1183
+ var PANEL_WIDTH = 344;
1184
+ var KINDS2 = ["form", "upload", "agent", "review"];
1185
+ function WorkflowBuilder({
1186
+ value,
1187
+ onChange,
1188
+ workflows = [],
1189
+ agents = [],
1190
+ eventKinds = [],
1191
+ tasks = [],
1192
+ compose,
1193
+ renderIcon,
1194
+ actions,
1195
+ labels,
1196
+ canvasLabels,
1197
+ inspectorLabels,
1198
+ triggerLabels,
1199
+ testId = "workflow-builder",
1200
+ className
1201
+ }) {
1202
+ const l = { ...DEFAULT_WORKFLOW_BUILDER_LABELS, ...labels };
1203
+ const [selected, setSelectedState] = react.useState(null);
1204
+ const [panelOpen, setPanelOpen] = react.useState(false);
1205
+ const setSelected = (id) => {
1206
+ setSelectedState(id);
1207
+ if (id) setPanelOpen(true);
1208
+ };
1209
+ const [mode, setMode] = react.useState("design");
1210
+ const [adding, setAdding] = react.useState(false);
1211
+ const { workflow, trigger } = value;
1212
+ const steps = chunkP2RBE3FD_cjs.workflowSteps(workflow);
1213
+ const problems = chunkP2RBE3FD_cjs.validateWorkflowGraph(steps);
1214
+ const titles = react.useMemo(() => new Map(workflows.map((w) => [w.id, w.title])), [workflows]);
1215
+ const setWorkflow = (next) => onChange({ ...value, workflow: next });
1216
+ const addAfter = (from, kind) => {
1217
+ const layout = layoutWithDefaults(workflow);
1218
+ const origin = from && from !== TRIGGER_NODE ? layout[from] : void 0;
1219
+ const siblings = steps.filter(
1220
+ (s) => from && from !== TRIGGER_NODE ? s.after?.includes(from) : s.after?.length === 0
1221
+ ).length;
1222
+ const { workflow: next, id } = addStep(workflow, kind, {
1223
+ after: from && from !== TRIGGER_NODE ? from : void 0,
1224
+ position: origin ? { x: origin.x + 320, y: origin.y + siblings * 150 } : {
1225
+ x: Math.max(0, ...Object.values(layout).map((p) => p.x)) + (steps.length ? 320 : 0),
1226
+ y: siblings * 150
1227
+ }
1228
+ });
1229
+ setWorkflow(next);
1230
+ setSelected(id);
1231
+ };
1232
+ const add = (kind) => addAfter(
1233
+ selected && selected !== TRIGGER_NODE ? selected : steps.at(-1)?.id ?? TRIGGER_NODE,
1234
+ kind
1235
+ );
1236
+ const summary = triggerSummary(trigger, {
1237
+ labels: triggerLabels,
1238
+ eventLabel: (k) => eventKinds.find((e) => e.value === k)?.label,
1239
+ taskLabel: (id) => tasks.find((t) => t.value === id)?.label
1240
+ });
1241
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cn("space-y-3", className), "data-testid": testId, children: [
1242
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
1243
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex rounded-md border border-border p-0.5", children: [
1244
+ /* @__PURE__ */ jsxRuntime.jsxs(
1245
+ ui.Button,
1246
+ {
1247
+ variant: mode === "design" ? "secondary" : "ghost",
1248
+ size: "sm",
1249
+ onClick: () => setMode("design"),
1250
+ "data-testid": `${testId}-mode-design`,
1251
+ children: [
1252
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PencilRuler, { className: "size-4" }),
1253
+ " ",
1254
+ l.design
1255
+ ]
1256
+ }
1257
+ ),
1258
+ /* @__PURE__ */ jsxRuntime.jsxs(
1259
+ ui.Button,
1260
+ {
1261
+ variant: mode === "preview" ? "secondary" : "ghost",
1262
+ size: "sm",
1263
+ onClick: () => setMode("preview"),
1264
+ disabled: problems.length > 0 || steps.length === 0,
1265
+ "data-testid": `${testId}-mode-preview`,
1266
+ children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "size-4" }),
1268
+ " ",
1269
+ l.preview
1270
+ ]
1271
+ }
1272
+ )
1273
+ ] }),
1274
+ mode === "design" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1275
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Popover, { open: adding, onOpenChange: setAdding, children: [
1276
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "sm", "data-testid": `${testId}-add`, children: [
1277
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "size-4" }),
1278
+ " ",
1279
+ l.addStep
1280
+ ] }) }),
1281
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PopoverContent, { align: "start", className: "w-64 p-1.5", children: KINDS2.map((kind) => {
1282
+ const Icon = STEP_KIND_ICONS[kind];
1283
+ const cls = { ...DEFAULT_WORKFLOW_CANVAS_LABELS, ...canvasLabels };
1284
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1285
+ "button",
1286
+ {
1287
+ type: "button",
1288
+ onClick: () => {
1289
+ setAdding(false);
1290
+ add(kind);
1291
+ },
1292
+ className: ui.cn(
1293
+ `wf-kind-${kind}`,
1294
+ "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-left hover:bg-muted"
1295
+ ),
1296
+ "data-testid": `${testId}-add-${kind}`,
1297
+ children: [
1298
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "wf-badge flex size-7 shrink-0 items-center justify-center rounded-md", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }) }),
1299
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0", children: [
1300
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-sm font-medium", children: cls[kind] }),
1301
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-xs text-muted-foreground", children: cls.kindHints[kind] })
1302
+ ] })
1303
+ ]
1304
+ },
1305
+ kind
1306
+ );
1307
+ }) })
1308
+ ] }),
1309
+ /* @__PURE__ */ jsxRuntime.jsxs(
1310
+ ui.Button,
1311
+ {
1312
+ variant: "ghost",
1313
+ size: "sm",
1314
+ onClick: () => workflow.run.kind === "steps" && setWorkflow({ ...workflow, run: { ...workflow.run, layout: autoLayout(steps) } }),
1315
+ "data-testid": `${testId}-tidy`,
1316
+ children: [
1317
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { className: "size-4" }),
1318
+ " ",
1319
+ l.tidy
1320
+ ]
1321
+ }
1322
+ )
1323
+ ] }),
1324
+ actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: actions })
1325
+ ] }),
1326
+ problems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1 text-sm text-destructive", "data-testid": `${testId}-problems`, children: problems.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: l.problems[p.code].replace(
1327
+ "{step}",
1328
+ steps.find((s) => s.id === p.step)?.title ?? p.step
1329
+ ) }, i)) }),
1330
+ mode === "design" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1331
+ /* @__PURE__ */ jsxRuntime.jsx(
1332
+ WorkflowCanvas,
1333
+ {
1334
+ workflow,
1335
+ onChange: setWorkflow,
1336
+ selected,
1337
+ onSelect: setSelected,
1338
+ focus: selected ?? (panelOpen ? TRIGGER_NODE : null),
1339
+ onAddAfter: addAfter,
1340
+ trigger: { title: l.start, summary },
1341
+ workflowTitle: (id) => titles.get(id),
1342
+ renderIcon,
1343
+ labels: canvasLabels,
1344
+ reserveRight: panelOpen ? PANEL_WIDTH : 0,
1345
+ testId: `${testId}-canvas`
1346
+ }
1347
+ ),
1348
+ panelOpen ? /* @__PURE__ */ jsxRuntime.jsxs(
1349
+ "div",
1350
+ {
1351
+ className: "absolute bottom-3 right-3 top-3 z-10 w-80 max-w-[calc(100%-1.5rem)] overflow-y-auto rounded-xl border border-border bg-card/95 p-4 pt-10 shadow-lg backdrop-blur",
1352
+ "data-testid": `${testId}-panel`,
1353
+ children: [
1354
+ /* @__PURE__ */ jsxRuntime.jsx(
1355
+ ui.Button,
1356
+ {
1357
+ size: "icon",
1358
+ variant: "ghost",
1359
+ className: "absolute right-2 top-2 size-7",
1360
+ onClick: () => setPanelOpen(false),
1361
+ "aria-label": l.closePanel,
1362
+ "data-testid": `${testId}-panel-close`,
1363
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4" })
1364
+ }
1365
+ ),
1366
+ selected === TRIGGER_NODE ? /* @__PURE__ */ jsxRuntime.jsx(
1367
+ TriggerInspector,
1368
+ {
1369
+ trigger,
1370
+ onChange: (next) => onChange({ ...value, trigger: next }),
1371
+ agents,
1372
+ eventKinds,
1373
+ tasks,
1374
+ labels: triggerLabels,
1375
+ testId: `${testId}-trigger`
1376
+ }
1377
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1378
+ StepInspector,
1379
+ {
1380
+ workflow,
1381
+ stepId: selected,
1382
+ onChange: setWorkflow,
1383
+ onSelect: setSelected,
1384
+ workflows,
1385
+ labels: inspectorLabels,
1386
+ testId: `${testId}-inspector`
1387
+ }
1388
+ )
1389
+ ]
1390
+ }
1391
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
1392
+ ui.Button,
1393
+ {
1394
+ size: "sm",
1395
+ variant: "outline",
1396
+ className: "absolute right-3 top-3 z-10 bg-card shadow-sm",
1397
+ onClick: () => setPanelOpen(true),
1398
+ "data-testid": `${testId}-panel-open`,
1399
+ children: [
1400
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, { className: "size-4" }),
1401
+ " ",
1402
+ l.settings
1403
+ ]
1404
+ }
1405
+ )
1406
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1407
+ WorkflowPreview,
1408
+ {
1409
+ workflow,
1410
+ workflows,
1411
+ compose,
1412
+ answer: l.previewAnswer,
1413
+ renderIcon,
1414
+ testId: `${testId}-preview`
1415
+ },
1416
+ JSON.stringify(workflow)
1417
+ )
1418
+ ] });
1419
+ }
1420
+ function WorkflowPreview({
1421
+ workflow,
1422
+ workflows,
1423
+ compose,
1424
+ answer,
1425
+ renderIcon,
1426
+ testId
1427
+ }) {
1428
+ const run = chunk5LP54CWR_cjs.useWorkflowPreview(workflow, {
1429
+ resolveWorkflow: (id) => workflows.find((w) => w.id === id),
1430
+ compose,
1431
+ answer
1432
+ });
1433
+ return /* @__PURE__ */ jsxRuntime.jsx(
1434
+ chunkP2RBE3FD_cjs.WorkflowRunner,
1435
+ {
1436
+ workflow,
1437
+ run,
1438
+ renderIcon,
1439
+ testIdPrefix: testId,
1440
+ renderAgentOutput: (step, state) => state?.text ? /* @__PURE__ */ jsxRuntime.jsx(
1441
+ "pre",
1442
+ {
1443
+ className: "whitespace-pre-wrap rounded-md bg-muted/40 p-3 text-xs text-muted-foreground",
1444
+ "data-testid": `${testId}-${step.id}-answer`,
1445
+ children: state.text
1446
+ }
1447
+ ) : null
1448
+ }
1449
+ );
1450
+ }
1451
+
1452
+ // src/workflow-canvas/trigger.ts
1453
+ var DEFAULT_WORKFLOW_TRIGGER = {
1454
+ start: "manual",
1455
+ runAt: "",
1456
+ rrule: "",
1457
+ timezone: typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
1458
+ eventKind: "",
1459
+ afterTaskId: "",
1460
+ agentId: "",
1461
+ access: "ask_each_run"
1462
+ };
1463
+ function triggerProblem(trigger) {
1464
+ if (trigger.start === "once" && !trigger.runAt) return "runAt";
1465
+ if (trigger.start === "repeat" && !trigger.rrule.trim()) return "rrule";
1466
+ if (trigger.start === "event" && !trigger.eventKind.trim()) return "event";
1467
+ if (trigger.start === "task" && !trigger.afterTaskId) return "task";
1468
+ return null;
1469
+ }
1470
+ function triggerFields(trigger, clearOthers) {
1471
+ const none = clearOthers ? null : void 0;
1472
+ return {
1473
+ timezone: trigger.timezone,
1474
+ rrule: trigger.start === "repeat" ? trigger.rrule.trim() : none,
1475
+ run_at: trigger.start === "once" ? chunk5LP54CWR_cjs.localInputToIso(trigger.runAt) : none,
1476
+ event_kind: trigger.start === "event" ? trigger.eventKind.trim() : none,
1477
+ after_conversation_ext_id: trigger.start === "task" ? trigger.afterTaskId : none,
1478
+ on_demand: trigger.start === "manual"
1479
+ };
1480
+ }
1481
+ var authMode = (trigger) => trigger.start === "manual" ? "run_now_temp" : trigger.access === "standing" ? "scheduled_permanent" : "scheduled_require_auth";
1482
+ var withoutUndefined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
1483
+ function plan(value, options) {
1484
+ return {
1485
+ goal: value.workflow.description,
1486
+ criteria: [],
1487
+ max_passes: 3,
1488
+ documents: [],
1489
+ steps: chunkP2RBE3FD_cjs.toPlanSteps(value.workflow, options)
1490
+ };
1491
+ }
1492
+ function builderToWorkflowRequest(value, workspaceId, options = {}) {
1493
+ return withoutUndefined({
1494
+ workspace_ext_id: workspaceId,
1495
+ name: value.workflow.title,
1496
+ description: value.workflow.description,
1497
+ active: true,
1498
+ shared: true,
1499
+ ...triggerFields(value.trigger, false),
1500
+ prompt: "",
1501
+ skill_ref: null,
1502
+ skill_inputs: null,
1503
+ agent_ext_id: value.trigger.agentId || null,
1504
+ auth_mode: authMode(value.trigger),
1505
+ plan: plan(value, options)
1506
+ });
1507
+ }
1508
+ function builderToWorkflowUpdate(value, options = {}) {
1509
+ return withoutUndefined({
1510
+ name: value.workflow.title,
1511
+ description: value.workflow.description,
1512
+ ...triggerFields(value.trigger, true),
1513
+ // The steps are what runs (a classic automation's single prompt or skill becomes its one step).
1514
+ prompt: "",
1515
+ skill_ref: null,
1516
+ skill_inputs: null,
1517
+ agent_ext_id: value.trigger.agentId || null,
1518
+ auth_mode: authMode(value.trigger),
1519
+ plan: plan(value, options)
1520
+ });
1521
+ }
1522
+ function workflowRecordToBuilder(record) {
1523
+ const planned = record.plan?.steps ?? [];
1524
+ const steps = planned.length ? chunkP2RBE3FD_cjs.fromPlanSteps(planned) : record.prompt || record.skill_ref ? [
1525
+ {
1526
+ id: "work",
1527
+ kind: "agent",
1528
+ title: record.name,
1529
+ ...record.skill_ref ? { workflow: record.skill_ref } : { run: { kind: "prompt", promptTemplate: record.prompt } }
1530
+ }
1531
+ ] : [];
1532
+ const when = chunk5LP54CWR_cjs.workflowWhen(record);
1533
+ return {
1534
+ workflow: {
1535
+ id: record.external_id,
1536
+ title: record.name,
1537
+ description: record.description ?? "",
1538
+ inputs: [],
1539
+ landing: "chat",
1540
+ schedulable: false,
1541
+ run: { kind: "steps", steps, layout: chunkP2RBE3FD_cjs.layoutFromPlanSteps(planned ?? []) }
1542
+ },
1543
+ trigger: {
1544
+ ...DEFAULT_WORKFLOW_TRIGGER,
1545
+ start: when === "now" ? "manual" : when,
1546
+ runAt: chunk5LP54CWR_cjs.isoToLocalInput(record.run_at),
1547
+ rrule: record.rrule ?? "",
1548
+ timezone: record.timezone || DEFAULT_WORKFLOW_TRIGGER.timezone,
1549
+ eventKind: record.event_kind ?? "",
1550
+ afterTaskId: record.after_conversation_ext_id ?? "",
1551
+ agentId: record.agent_ext_id ?? "",
1552
+ access: record.auth_mode === "scheduled_permanent" ? "standing" : "ask_each_run"
1553
+ }
1554
+ };
1555
+ }
1556
+ var DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS = {
1557
+ save: "Save",
1558
+ saving: "Saving\u2026",
1559
+ runNow: "Run now",
1560
+ saved: "Saved.",
1561
+ needTrigger: "Finish setting when it starts."
1562
+ };
1563
+ var DEFAULT_WORKFLOW_EVENT_KINDS = [
1564
+ { value: "conversation_done", label: "A task is marked done" },
1565
+ { value: "conversation_ready", label: "Work is ready for me" },
1566
+ { value: "mention", label: "Someone mentions me" },
1567
+ { value: "workflow_completed", label: "Another workflow finishes" },
1568
+ { value: "workflow_failed", label: "Another workflow fails" }
1569
+ ];
1570
+ function ConnectedWorkflowBuilder({
1571
+ workflowId,
1572
+ initial,
1573
+ workflows: hostWorkflows = [],
1574
+ eventKinds = DEFAULT_WORKFLOW_EVENT_KINDS,
1575
+ compose,
1576
+ onSaved,
1577
+ onRun,
1578
+ actions,
1579
+ connectedLabels,
1580
+ testId = "workflow-builder",
1581
+ ...builderProps
1582
+ }) {
1583
+ const l = { ...DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS, ...connectedLabels };
1584
+ const { workspaceId } = chunkMUMH3Z6K_cjs.useConnection();
1585
+ const saved = react$2.useWorkflows();
1586
+ const skills = react$2.useSkills();
1587
+ const agentsQuery = react$2.useAgents();
1588
+ const conversations = react$2.useConversations(workspaceId ?? void 0);
1589
+ const create = react$2.useCreateWorkflow();
1590
+ const update = react$2.useUpdateWorkflow();
1591
+ const run = react$2.useRunWorkflow();
1592
+ const [id, setId] = react.useState(workflowId);
1593
+ const [value, setValue] = react.useState(initial);
1594
+ const [message, setMessage] = react.useState(null);
1595
+ const record = saved.data?.find((w) => w.external_id === id);
1596
+ const [loaded, setLoaded] = react.useState(void 0);
1597
+ react.useEffect(() => {
1598
+ if (record && loaded !== record.external_id) {
1599
+ setValue(workflowRecordToBuilder(record));
1600
+ setLoaded(record.external_id);
1601
+ }
1602
+ }, [record, loaded]);
1603
+ const workflows = react.useMemo(
1604
+ () => [
1605
+ ...hostWorkflows,
1606
+ ...(skills.data ?? []).map(
1607
+ (s) => chunkP2RBE3FD_cjs.skillToWorkflow({
1608
+ name: s.name,
1609
+ slug: s.slug,
1610
+ description: s.description,
1611
+ arg_hint: s.arg_hint
1612
+ })
1613
+ )
1614
+ ],
1615
+ [hostWorkflows, skills.data]
1616
+ );
1617
+ const agents = (agentsQuery.data ?? []).map((a) => ({
1618
+ id: a.external_id,
1619
+ label: a.given_name || a.email
1620
+ }));
1621
+ const tasks = (conversations.data ?? []).filter((c) => c.status && c.status !== "done").map((c) => ({ value: c.external_id, label: c.title || c.external_id }));
1622
+ const options = { resolveWorkflow: (wid) => workflows.find((w) => w.id === wid), compose };
1623
+ const problem = triggerProblem(value.trigger) ?? (chunkP2RBE3FD_cjs.validateWorkflowGraph(chunkP2RBE3FD_cjs.workflowSteps(value.workflow)).length ? "graph" : null);
1624
+ const busy = create.isPending || update.isPending || run.isPending;
1625
+ const save = async () => {
1626
+ if (!workspaceId) return void 0;
1627
+ setMessage(null);
1628
+ const result = id ? await update.mutateAsync({ workflowId: id, patch: builderToWorkflowUpdate(value, options) }) : await create.mutateAsync(builderToWorkflowRequest(value, workspaceId, options));
1629
+ setId(result.external_id);
1630
+ setLoaded(result.external_id);
1631
+ setMessage(l.saved);
1632
+ onSaved?.(result.external_id);
1633
+ return result.external_id;
1634
+ };
1635
+ return /* @__PURE__ */ jsxRuntime.jsx(
1636
+ WorkflowBuilder,
1637
+ {
1638
+ ...builderProps,
1639
+ value,
1640
+ onChange: setValue,
1641
+ workflows,
1642
+ agents,
1643
+ eventKinds,
1644
+ tasks,
1645
+ compose,
1646
+ testId,
1647
+ actions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1648
+ actions,
1649
+ message && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: message }),
1650
+ problem && problem !== "graph" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive", children: l.needTrigger }),
1651
+ /* @__PURE__ */ jsxRuntime.jsxs(
1652
+ ui.Button,
1653
+ {
1654
+ variant: "outline",
1655
+ size: "sm",
1656
+ disabled: !!problem || busy || !workspaceId,
1657
+ onClick: async () => {
1658
+ const saved2 = await save();
1659
+ if (!saved2) return;
1660
+ const started = await run.mutateAsync(saved2);
1661
+ onRun?.(started.conversation_ext_id ?? void 0);
1662
+ },
1663
+ "data-testid": `${testId}-run`,
1664
+ children: [
1665
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "size-4" }),
1666
+ " ",
1667
+ l.runNow
1668
+ ]
1669
+ }
1670
+ ),
1671
+ /* @__PURE__ */ jsxRuntime.jsxs(
1672
+ ui.Button,
1673
+ {
1674
+ size: "sm",
1675
+ disabled: !!problem || busy || !workspaceId,
1676
+ onClick: () => void save(),
1677
+ "data-testid": `${testId}-save`,
1678
+ children: [
1679
+ create.isPending || update.isPending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { className: "size-4" }),
1680
+ create.isPending || update.isPending ? l.saving : l.save
1681
+ ]
1682
+ }
1683
+ )
1684
+ ] })
1685
+ }
1686
+ );
1687
+ }
1688
+
1689
+ exports.ConnectedWorkflowBuilder = ConnectedWorkflowBuilder;
1690
+ exports.DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS = DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS;
1691
+ exports.DEFAULT_STEP_INSPECTOR_LABELS = DEFAULT_STEP_INSPECTOR_LABELS;
1692
+ exports.DEFAULT_TRIGGER_INSPECTOR_LABELS = DEFAULT_TRIGGER_INSPECTOR_LABELS;
1693
+ exports.DEFAULT_WORKFLOW_BUILDER_LABELS = DEFAULT_WORKFLOW_BUILDER_LABELS;
1694
+ exports.DEFAULT_WORKFLOW_CANVAS_LABELS = DEFAULT_WORKFLOW_CANVAS_LABELS;
1695
+ exports.DEFAULT_WORKFLOW_EVENT_KINDS = DEFAULT_WORKFLOW_EVENT_KINDS;
1696
+ exports.DEFAULT_WORKFLOW_TRIGGER = DEFAULT_WORKFLOW_TRIGGER;
1697
+ exports.StepInspector = StepInspector;
1698
+ exports.TRIGGER_NODE = TRIGGER_NODE;
1699
+ exports.TriggerInspector = TriggerInspector;
1700
+ exports.WorkflowBuilder = WorkflowBuilder;
1701
+ exports.WorkflowCanvas = WorkflowCanvas;
1702
+ exports.addStep = addStep;
1703
+ exports.autoLayout = autoLayout;
1704
+ exports.builderToWorkflowRequest = builderToWorkflowRequest;
1705
+ exports.builderToWorkflowUpdate = builderToWorkflowUpdate;
1706
+ exports.connectSteps = connectSteps;
1707
+ exports.disconnectSteps = disconnectSteps;
1708
+ exports.layoutWithDefaults = layoutWithDefaults;
1709
+ exports.moveStep = moveStep;
1710
+ exports.newSteppedWorkflow = newSteppedWorkflow;
1711
+ exports.removeStep = removeStep;
1712
+ exports.stepIdFor = stepIdFor;
1713
+ exports.triggerProblem = triggerProblem;
1714
+ exports.triggerSummary = triggerSummary;
1715
+ exports.updateStep = updateStep;
1716
+ exports.workflowRecordToBuilder = workflowRecordToBuilder;
1717
+ exports.wouldCycle = wouldCycle;
1718
+ //# sourceMappingURL=index.cjs.map
1719
+ //# sourceMappingURL=index.cjs.map