@arbidocs/blocks 0.4.71 → 0.4.73

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