@dilipod/ui 0.4.7 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,14 +1,16 @@
1
1
  "use client";
2
2
  import * as React50 from 'react';
3
- import { useState, useRef } from 'react';
3
+ import { lazy, useMemo, useState, useRef, Suspense } from 'react';
4
+ import { MarkerType, useNodesState, useEdgesState, ReactFlow, Background, Handle, Position } from '@xyflow/react';
5
+ import '@xyflow/react/dist/style.css';
6
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
7
  import { Slot, Slottable, createSlot } from '@radix-ui/react-slot';
5
8
  import { cva } from 'class-variance-authority';
6
9
  import { clsx } from 'clsx';
7
10
  import { twMerge } from 'tailwind-merge';
8
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
11
  import * as SheetPrimitive from '@radix-ui/react-dialog';
10
12
  import * as react_star from '@phosphor-icons/react';
11
- import { X, CaretDown, Circle, CaretLeft, DotsThree, CaretRight, Check, House, Info, WarningCircle, Play, Download, Folder, ArrowSquareOut, CircleNotch, File, FileVideo, Lightning, Plus, CheckCircle, FileImage, FilePdf, FileDoc, Question, Warning, PencilSimple, Trash } from '@phosphor-icons/react';
13
+ import { X, CaretDown, Circle, CaretLeft, DotsThree, CaretRight, Check, House, Info, WarningCircle, Play, Download, Folder, ArrowSquareOut, CircleNotch, File, FileVideo, Lightning, Plus, CheckCircle, PaperPlaneTilt, CaretUp, Eye, TreeStructure, Code, PencilSimple, Copy, CloudArrowUp, CloudArrowDown, FileImage, FilePdf, FileDoc, Question, Warning, Trash, Robot, Globe, GitBranch, WebhooksLogo, Package, Timer } from '@phosphor-icons/react';
12
14
  import 'react-dom';
13
15
  import * as SwitchPrimitive from '@radix-ui/react-switch';
14
16
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
@@ -26,6 +28,9 @@ var __defProp = Object.defineProperty;
26
28
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
27
29
  var __getOwnPropNames = Object.getOwnPropertyNames;
28
30
  var __hasOwnProp = Object.prototype.hasOwnProperty;
31
+ var __esm = (fn, res) => function __init() {
32
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
33
+ };
29
34
  var __export = (target, all) => {
30
35
  for (var name in all)
31
36
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -40,6 +45,181 @@ var __copyProps = (to, from, except, desc) => {
40
45
  };
41
46
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget);
42
47
 
48
+ // src/components/workflow-flow.tsx
49
+ var workflow_flow_exports = {};
50
+ __export(workflow_flow_exports, {
51
+ WorkflowFlow: () => WorkflowFlow,
52
+ default: () => workflow_flow_default
53
+ });
54
+ function getNodeTypeLabel(type) {
55
+ const labels = {
56
+ "n8n-nodes-base.webhook": "Webhook",
57
+ "n8n-nodes-base.scheduleTrigger": "Schedule",
58
+ "n8n-nodes-base.if": "Condition",
59
+ "n8n-nodes-base.httpRequest": "HTTP Request",
60
+ "n8n-nodes-base.set": "Set Data",
61
+ "n8n-nodes-base.code": "Code",
62
+ "n8n-nodes-base.respondToWebhook": "Response",
63
+ "@n8n/n8n-nodes-langchain.agent": "AI Agent",
64
+ "@n8n/n8n-nodes-langchain.lmChatOpenAi": "OpenAI",
65
+ "@n8n/n8n-nodes-langchain.lmChatAnthropic": "Anthropic"
66
+ };
67
+ return labels[type] || type.split(".").pop()?.replace(/([A-Z])/g, " $1").trim() || type;
68
+ }
69
+ function CustomNode({ data }) {
70
+ return /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 rounded bg-white border border-slate-200 shadow-sm min-w-[110px] text-center", children: [
71
+ /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left, className: "!bg-slate-300 !w-1.5 !h-1.5 !border-0" }),
72
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-slate-700 truncate max-w-[130px]", children: data.label }),
73
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] text-slate-400 truncate max-w-[130px]", children: getNodeTypeLabel(data.type) }),
74
+ /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, className: "!bg-slate-300 !w-1.5 !h-1.5 !border-0" })
75
+ ] });
76
+ }
77
+ function WorkflowFlow({ workflow, height = 350, className = "" }) {
78
+ const { initialNodes, initialEdges } = useMemo(() => {
79
+ const n8nNodes = workflow.nodes || [];
80
+ const connections = workflow.connections || {};
81
+ const nodeIdMap = new Map(n8nNodes.map((n) => [n.name, n.id || n.name]));
82
+ const forwardEdges = /* @__PURE__ */ new Map();
83
+ const backwardEdges = /* @__PURE__ */ new Map();
84
+ const allEdgeData = [];
85
+ Object.entries(connections).forEach(([fromNodeName, outputTypes]) => {
86
+ Object.entries(outputTypes).forEach(([outputType, outputs]) => {
87
+ if (!Array.isArray(outputs)) return;
88
+ outputs.forEach((outputArray, oi) => {
89
+ if (!Array.isArray(outputArray)) return;
90
+ outputArray.forEach((conn, ci) => {
91
+ if (!forwardEdges.has(fromNodeName)) forwardEdges.set(fromNodeName, []);
92
+ forwardEdges.get(fromNodeName).push(conn.node);
93
+ if (!backwardEdges.has(conn.node)) backwardEdges.set(conn.node, []);
94
+ backwardEdges.get(conn.node).push(fromNodeName);
95
+ allEdgeData.push({ from: fromNodeName, to: conn.node, outputType, oi, ci });
96
+ });
97
+ });
98
+ });
99
+ });
100
+ const triggerNodes = n8nNodes.filter(
101
+ (n) => n.type.includes("webhook") || n.type.includes("Trigger") || n.type.includes("schedule")
102
+ );
103
+ const roots = triggerNodes.length > 0 ? triggerNodes : n8nNodes.filter((n) => !backwardEdges.has(n.name) || backwardEdges.get(n.name).length === 0);
104
+ const levels = /* @__PURE__ */ new Map();
105
+ const queue = [];
106
+ roots.forEach((r) => {
107
+ levels.set(r.name, 0);
108
+ queue.push(r.name);
109
+ });
110
+ const visited = /* @__PURE__ */ new Set();
111
+ while (queue.length > 0) {
112
+ const name = queue.shift();
113
+ if (visited.has(name)) continue;
114
+ visited.add(name);
115
+ const children = forwardEdges.get(name) || [];
116
+ const myLevel = levels.get(name) || 0;
117
+ children.forEach((child) => {
118
+ const childLevel = levels.get(child);
119
+ if (childLevel === void 0 || myLevel + 1 > childLevel) {
120
+ levels.set(child, myLevel + 1);
121
+ }
122
+ if (!visited.has(child)) {
123
+ queue.push(child);
124
+ }
125
+ });
126
+ }
127
+ n8nNodes.forEach((node) => {
128
+ if (!levels.has(node.name)) {
129
+ const targets = forwardEdges.get(node.name) || [];
130
+ if (targets.length > 0) {
131
+ const targetLevels = targets.map((t) => levels.get(t) ?? 0);
132
+ const targetLevel = Math.min(...targetLevels);
133
+ levels.set(node.name, Math.max(0, targetLevel - 1));
134
+ } else {
135
+ const maxLevel = Math.max(0, ...Array.from(levels.values()));
136
+ levels.set(node.name, maxLevel);
137
+ }
138
+ }
139
+ });
140
+ const nodesByLevel = /* @__PURE__ */ new Map();
141
+ levels.forEach((level, name) => {
142
+ if (!nodesByLevel.has(level)) nodesByLevel.set(level, []);
143
+ nodesByLevel.get(level).push(name);
144
+ });
145
+ const sortedLevels = Array.from(nodesByLevel.keys()).sort((a, b) => a - b);
146
+ sortedLevels.forEach((level, levelIdx) => {
147
+ if (levelIdx === 0) return;
148
+ const nodesInLevel = nodesByLevel.get(level);
149
+ const prevLevel = sortedLevels[levelIdx - 1];
150
+ const prevNodes = nodesByLevel.get(prevLevel) || [];
151
+ const prevPositions = new Map(prevNodes.map((n, i) => [n, i]));
152
+ nodesInLevel.sort((a, b) => {
153
+ const parentsA = backwardEdges.get(a) || [];
154
+ const parentsB = backwardEdges.get(b) || [];
155
+ const avgA = parentsA.length > 0 ? parentsA.reduce((sum, p) => sum + (prevPositions.get(p) ?? 0), 0) / parentsA.length : 0;
156
+ const avgB = parentsB.length > 0 ? parentsB.reduce((sum, p) => sum + (prevPositions.get(p) ?? 0), 0) / parentsB.length : 0;
157
+ return avgA - avgB;
158
+ });
159
+ });
160
+ const positions = /* @__PURE__ */ new Map();
161
+ const xGap = 170;
162
+ const yGap = 70;
163
+ sortedLevels.forEach((level) => {
164
+ const nodesInLevel = nodesByLevel.get(level);
165
+ const totalHeight = (nodesInLevel.length - 1) * yGap;
166
+ const startY = -totalHeight / 2;
167
+ nodesInLevel.forEach((name, i) => {
168
+ positions.set(name, { x: level * xGap, y: startY + i * yGap });
169
+ });
170
+ });
171
+ const nodes2 = n8nNodes.map((node) => ({
172
+ id: node.id || node.name,
173
+ type: "custom",
174
+ position: positions.get(node.name) || { x: 0, y: 0 },
175
+ data: { label: node.name, type: node.type }
176
+ }));
177
+ const edges2 = allEdgeData.map(({ from, to, oi, ci }) => {
178
+ const fromId = nodeIdMap.get(from);
179
+ const toId = nodeIdMap.get(to);
180
+ return {
181
+ id: `${fromId}-${toId}-${oi}-${ci}`,
182
+ source: fromId,
183
+ target: toId,
184
+ type: "smoothstep",
185
+ pathOptions: { borderRadius: 20 },
186
+ style: { stroke: "#94a3b8", strokeWidth: 1.5 },
187
+ markerEnd: { type: MarkerType.ArrowClosed, color: "#94a3b8", width: 14, height: 14 }
188
+ };
189
+ });
190
+ return { initialNodes: nodes2, initialEdges: edges2 };
191
+ }, [workflow]);
192
+ const [nodes, , onNodesChange] = useNodesState(initialNodes);
193
+ const [edges, , onEdgesChange] = useEdgesState(initialEdges);
194
+ return /* @__PURE__ */ jsx("div", { style: { height }, className: `bg-slate-50 rounded-lg border border-slate-200 overflow-hidden ${className}`, children: /* @__PURE__ */ jsx(
195
+ ReactFlow,
196
+ {
197
+ nodes,
198
+ edges,
199
+ onNodesChange,
200
+ onEdgesChange,
201
+ nodeTypes,
202
+ fitView: true,
203
+ fitViewOptions: { padding: 0.1, minZoom: 0.8 },
204
+ minZoom: 0.5,
205
+ maxZoom: 2,
206
+ proOptions: { hideAttribution: true },
207
+ defaultEdgeOptions: {
208
+ type: "smoothstep"
209
+ },
210
+ children: /* @__PURE__ */ jsx(Background, { color: "#e2e8f0", gap: 24, size: 1 })
211
+ }
212
+ ) });
213
+ }
214
+ var nodeTypes, workflow_flow_default;
215
+ var init_workflow_flow = __esm({
216
+ "src/components/workflow-flow.tsx"() {
217
+ "use client";
218
+ nodeTypes = { custom: CustomNode };
219
+ workflow_flow_default = WorkflowFlow;
220
+ }
221
+ });
222
+
43
223
  // src/index.ts
44
224
  var index_exports = {};
45
225
  __export(index_exports, {
@@ -47,6 +227,7 @@ __export(index_exports, {
47
227
  AccordionContent: () => AccordionContent,
48
228
  AccordionItem: () => AccordionItem,
49
229
  AccordionTrigger: () => AccordionTrigger,
230
+ ActivityTimeline: () => ActivityTimeline,
50
231
  Alert: () => Alert,
51
232
  AlertDialog: () => AlertDialog,
52
233
  AlertDialogAction: () => AlertDialogAction,
@@ -195,6 +376,8 @@ __export(index_exports, {
195
376
  TooltipTrigger: () => TooltipTrigger,
196
377
  UsageBar: () => UsageBar,
197
378
  UsageChart: () => UsageChart,
379
+ WorkflowFlow: () => WorkflowFlow,
380
+ WorkflowViewer: () => WorkflowViewer,
198
381
  alertVariants: () => alertVariants,
199
382
  badgeVariants: () => badgeVariants,
200
383
  buttonVariants: () => buttonVariants,
@@ -1241,7 +1424,7 @@ var NODES = [
1241
1424
  ];
1242
1425
  var Primitive = NODES.reduce((primitive, node) => {
1243
1426
  const Slot2 = createSlot(`Primitive.${node}`);
1244
- const Node = React50.forwardRef((props, forwardedRef) => {
1427
+ const Node2 = React50.forwardRef((props, forwardedRef) => {
1245
1428
  const { asChild, ...primitiveProps } = props;
1246
1429
  const Comp = asChild ? Slot2 : node;
1247
1430
  if (typeof window !== "undefined") {
@@ -1249,8 +1432,8 @@ var Primitive = NODES.reduce((primitive, node) => {
1249
1432
  }
1250
1433
  return /* @__PURE__ */ jsx(Comp, { ...primitiveProps, ref: forwardedRef });
1251
1434
  });
1252
- Node.displayName = `Primitive.${node}`;
1253
- return { ...primitive, [node]: Node };
1435
+ Node2.displayName = `Primitive.${node}`;
1436
+ return { ...primitive, [node]: Node2 };
1254
1437
  }, {});
1255
1438
  var NAME = "Label";
1256
1439
  var Label = React50.forwardRef((props, forwardedRef) => {
@@ -1566,7 +1749,7 @@ var NODES2 = [
1566
1749
  ];
1567
1750
  var Primitive2 = NODES2.reduce((primitive, node) => {
1568
1751
  const Slot2 = createSlot(`Primitive.${node}`);
1569
- const Node = React50.forwardRef((props, forwardedRef) => {
1752
+ const Node2 = React50.forwardRef((props, forwardedRef) => {
1570
1753
  const { asChild, ...primitiveProps } = props;
1571
1754
  const Comp = asChild ? Slot2 : node;
1572
1755
  if (typeof window !== "undefined") {
@@ -1574,8 +1757,8 @@ var Primitive2 = NODES2.reduce((primitive, node) => {
1574
1757
  }
1575
1758
  return /* @__PURE__ */ jsx(Comp, { ...primitiveProps, ref: forwardedRef });
1576
1759
  });
1577
- Node.displayName = `Primitive.${node}`;
1578
- return { ...primitive, [node]: Node };
1760
+ Node2.displayName = `Primitive.${node}`;
1761
+ return { ...primitive, [node]: Node2 };
1579
1762
  }, {});
1580
1763
  var NAME2 = "Separator";
1581
1764
  var DEFAULT_ORIENTATION = "horizontal";
@@ -4375,10 +4558,834 @@ function ImpactMetricsForm({
4375
4558
  ] })
4376
4559
  ] }) });
4377
4560
  }
4561
+ function defaultFormatDate(date) {
4562
+ const d = typeof date === "string" ? new Date(date) : date;
4563
+ const now = /* @__PURE__ */ new Date();
4564
+ const diffMs = now.getTime() - d.getTime();
4565
+ const diffMins = Math.floor(diffMs / 6e4);
4566
+ const diffHours = Math.floor(diffMs / 36e5);
4567
+ const diffDays = Math.floor(diffMs / 864e5);
4568
+ if (diffMins < 1) return "just now";
4569
+ if (diffMins < 60) return `${diffMins}m ago`;
4570
+ if (diffHours < 24) return `${diffHours}h ago`;
4571
+ if (diffDays < 7) return `${diffDays}d ago`;
4572
+ return d.toLocaleDateString();
4573
+ }
4574
+ function ActivityTimeline({
4575
+ activities,
4576
+ loading = false,
4577
+ activityLabels = {},
4578
+ collapsedCount = 3,
4579
+ showNoteInput = true,
4580
+ notePlaceholder = "Add a note...",
4581
+ onAddNote,
4582
+ submitting = false,
4583
+ formatDate = defaultFormatDate,
4584
+ loadingComponent,
4585
+ emptyMessage = "No activity yet",
4586
+ className = ""
4587
+ }) {
4588
+ const [newNote, setNewNote] = useState("");
4589
+ const [expanded, setExpanded] = useState(false);
4590
+ const [isSubmitting, setIsSubmitting] = useState(false);
4591
+ const handleAddNote = async () => {
4592
+ if (!newNote.trim() || !onAddNote) return;
4593
+ setIsSubmitting(true);
4594
+ try {
4595
+ await onAddNote(newNote.trim());
4596
+ setNewNote("");
4597
+ } finally {
4598
+ setIsSubmitting(false);
4599
+ }
4600
+ };
4601
+ const handleKeyDown = (e) => {
4602
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
4603
+ handleAddNote();
4604
+ }
4605
+ };
4606
+ const visibleActivities = expanded ? activities : activities.slice(0, collapsedCount);
4607
+ const hasMore = activities.length > collapsedCount;
4608
+ const isCurrentlySubmitting = submitting || isSubmitting;
4609
+ const DefaultLoading = /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ jsx(CircleNotch, { className: "w-5 h-5 animate-spin text-muted-foreground" }) });
4610
+ return /* @__PURE__ */ jsxs("div", { className: `space-y-3 ${className}`, children: [
4611
+ showNoteInput && onAddNote && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4612
+ /* @__PURE__ */ jsx(
4613
+ Textarea,
4614
+ {
4615
+ value: newNote,
4616
+ onChange: (e) => setNewNote(e.target.value),
4617
+ onKeyDown: handleKeyDown,
4618
+ placeholder: notePlaceholder,
4619
+ rows: 1,
4620
+ className: "resize-none min-h-[36px] py-2"
4621
+ }
4622
+ ),
4623
+ /* @__PURE__ */ jsx(
4624
+ Button,
4625
+ {
4626
+ onClick: handleAddNote,
4627
+ disabled: isCurrentlySubmitting || !newNote.trim(),
4628
+ size: "sm",
4629
+ className: "flex-shrink-0 h-9",
4630
+ children: isCurrentlySubmitting ? /* @__PURE__ */ jsx(CircleNotch, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ jsx(PaperPlaneTilt, { className: "w-4 h-4", weight: "bold" })
4631
+ }
4632
+ )
4633
+ ] }),
4634
+ loading ? loadingComponent || DefaultLoading : activities.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center py-2", children: emptyMessage }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
4635
+ visibleActivities.map((activity) => {
4636
+ const label = activityLabels[activity.activity_type] || "";
4637
+ return /* @__PURE__ */ jsxs("div", { className: "text-sm border-l-2 border-gray-200 pl-3 py-1", children: [
4638
+ /* @__PURE__ */ jsxs("p", { className: "text-gray-700", children: [
4639
+ label && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
4640
+ label,
4641
+ " "
4642
+ ] }),
4643
+ activity.content
4644
+ ] }),
4645
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-0.5", children: [
4646
+ activity.user,
4647
+ " \xB7 ",
4648
+ formatDate(activity.created_at)
4649
+ ] })
4650
+ ] }, activity.id);
4651
+ }),
4652
+ hasMore && /* @__PURE__ */ jsx(
4653
+ "button",
4654
+ {
4655
+ onClick: () => setExpanded(!expanded),
4656
+ className: "flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors",
4657
+ children: expanded ? /* @__PURE__ */ jsxs(Fragment, { children: [
4658
+ /* @__PURE__ */ jsx(CaretUp, { className: "w-3 h-3" }),
4659
+ "Show less"
4660
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4661
+ /* @__PURE__ */ jsx(CaretDown, { className: "w-3 h-3" }),
4662
+ "Show ",
4663
+ activities.length - collapsedCount,
4664
+ " more"
4665
+ ] })
4666
+ }
4667
+ )
4668
+ ] })
4669
+ ] });
4670
+ }
4671
+
4672
+ // src/index.ts
4673
+ init_workflow_flow();
4674
+ var WorkflowFlow2 = lazy(() => Promise.resolve().then(() => (init_workflow_flow(), workflow_flow_exports)).then((m) => ({ default: m.WorkflowFlow })));
4675
+ var BLANK_N8N_WORKFLOW = {
4676
+ name: "New Workflow",
4677
+ nodes: [
4678
+ {
4679
+ id: "webhook-trigger",
4680
+ name: "Webhook",
4681
+ type: "n8n-nodes-base.webhook",
4682
+ parameters: {
4683
+ httpMethod: "POST",
4684
+ path: "my-webhook"
4685
+ }
4686
+ }
4687
+ ],
4688
+ connections: {},
4689
+ settings: { executionOrder: "v1" }
4690
+ };
4691
+ var BLANK_SIM_WORKFLOW = {
4692
+ name: "New Workflow",
4693
+ description: "A new Sim workflow",
4694
+ trigger: { type: "webhook_input" },
4695
+ nodes: [
4696
+ { id: "input", type: "webhook_input" },
4697
+ { id: "output", type: "output" }
4698
+ ],
4699
+ edges: [{ from: "input", to: "output" }]
4700
+ };
4701
+ function getNodeIcon(type) {
4702
+ if (type.includes("webhook")) return /* @__PURE__ */ jsx(WebhooksLogo, { size: 16, weight: "fill" });
4703
+ if (type.includes("Trigger") || type.includes("schedule")) return /* @__PURE__ */ jsx(Timer, { size: 16, weight: "fill" });
4704
+ if (type.includes("if") || type.includes("switch")) return /* @__PURE__ */ jsx(GitBranch, { size: 16, weight: "fill" });
4705
+ if (type.includes("httpRequest")) return /* @__PURE__ */ jsx(Globe, { size: 16, weight: "fill" });
4706
+ if (type.includes("langchain") || type.includes("openai") || type.includes("anthropic")) return /* @__PURE__ */ jsx(Robot, { size: 16, weight: "fill" });
4707
+ if (type.includes("code")) return /* @__PURE__ */ jsx(Code, { size: 16, weight: "fill" });
4708
+ if (type.includes("respondToWebhook")) return /* @__PURE__ */ jsx(CheckCircle, { size: 16, weight: "fill" });
4709
+ if (type.includes("set")) return /* @__PURE__ */ jsx(Package, { size: 16, weight: "fill" });
4710
+ return /* @__PURE__ */ jsx(Package, { size: 16 });
4711
+ }
4712
+ function getNodeTypeLabel2(type) {
4713
+ const typeMap = {
4714
+ "n8n-nodes-base.webhook": "Webhook Trigger",
4715
+ "n8n-nodes-base.scheduleTrigger": "Schedule Trigger",
4716
+ "n8n-nodes-base.if": "Condition",
4717
+ "n8n-nodes-base.httpRequest": "HTTP Request",
4718
+ "n8n-nodes-base.set": "Set Data",
4719
+ "n8n-nodes-base.code": "Code",
4720
+ "n8n-nodes-base.respondToWebhook": "Webhook Response",
4721
+ "@n8n/n8n-nodes-langchain.agent": "AI Agent",
4722
+ "@n8n/n8n-nodes-langchain.lmChatOpenAi": "OpenAI Model",
4723
+ "@n8n/n8n-nodes-langchain.lmChatAnthropic": "Anthropic Model"
4724
+ };
4725
+ return typeMap[type] || type.split(".").pop() || type;
4726
+ }
4727
+ function getSimNodeTypeLabel(type) {
4728
+ const typeMap = {
4729
+ "webhook_input": "Webhook Input",
4730
+ "llm": "AI/LLM",
4731
+ "code": "Code",
4732
+ "http_request": "HTTP Request",
4733
+ "condition": "Condition",
4734
+ "output": "Output"
4735
+ };
4736
+ return typeMap[type] || type;
4737
+ }
4738
+ function defaultFormatDistance(date, options) {
4739
+ const now = /* @__PURE__ */ new Date();
4740
+ const diffMs = now.getTime() - date.getTime();
4741
+ const diffMins = Math.floor(diffMs / 6e4);
4742
+ const diffHours = Math.floor(diffMs / 36e5);
4743
+ const diffDays = Math.floor(diffMs / 864e5);
4744
+ let result = "";
4745
+ if (diffMins < 1) result = "less than a minute";
4746
+ else if (diffMins < 60) result = `${diffMins} minute${diffMins > 1 ? "s" : ""}`;
4747
+ else if (diffHours < 24) result = `${diffHours} hour${diffHours > 1 ? "s" : ""}`;
4748
+ else result = `${diffDays} day${diffDays > 1 ? "s" : ""}`;
4749
+ return options?.addSuffix ? `${result} ago` : result;
4750
+ }
4751
+ function N8nWorkflowSummary({ workflow, showFlow = false }) {
4752
+ const nodes = workflow.nodes || [];
4753
+ const triggerNode = nodes.find(
4754
+ (n) => n.type.includes("webhook") || n.type.includes("Trigger")
4755
+ );
4756
+ const aiNodes = nodes.filter(
4757
+ (n) => n.type.includes("langchain") || n.type.includes("openai") || n.type.includes("anthropic")
4758
+ );
4759
+ const httpNodes = nodes.filter(
4760
+ (n) => n.type === "n8n-nodes-base.httpRequest"
4761
+ );
4762
+ const conditionNodes = nodes.filter(
4763
+ (n) => n.type === "n8n-nodes-base.if"
4764
+ );
4765
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4766
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-4 gap-3", children: [
4767
+ /* @__PURE__ */ jsxs("div", { className: "bg-muted/50 rounded-lg p-3", children: [
4768
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground mb-1", children: [
4769
+ triggerNode && getNodeIcon(triggerNode.type),
4770
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Trigger" })
4771
+ ] }),
4772
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold truncate", children: triggerNode ? getNodeTypeLabel2(triggerNode.type) : "None" })
4773
+ ] }),
4774
+ /* @__PURE__ */ jsxs("div", { className: "bg-muted/50 rounded-lg p-3", children: [
4775
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground mb-1", children: [
4776
+ /* @__PURE__ */ jsx(TreeStructure, { size: 16 }),
4777
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Steps" })
4778
+ ] }),
4779
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold", children: [
4780
+ nodes.length,
4781
+ " nodes"
4782
+ ] })
4783
+ ] }),
4784
+ aiNodes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-purple-50 rounded-lg p-3", children: [
4785
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-purple-600 mb-1", children: [
4786
+ /* @__PURE__ */ jsx(Robot, { size: 16, weight: "fill" }),
4787
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "AI" })
4788
+ ] }),
4789
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-purple-700", children: [
4790
+ aiNodes.length,
4791
+ " ",
4792
+ aiNodes.length === 1 ? "node" : "nodes"
4793
+ ] })
4794
+ ] }),
4795
+ httpNodes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-blue-50 rounded-lg p-3", children: [
4796
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-blue-600 mb-1", children: [
4797
+ /* @__PURE__ */ jsx(Globe, { size: 16, weight: "fill" }),
4798
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "APIs" })
4799
+ ] }),
4800
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-blue-700", children: [
4801
+ httpNodes.length,
4802
+ " ",
4803
+ httpNodes.length === 1 ? "request" : "requests"
4804
+ ] })
4805
+ ] }),
4806
+ conditionNodes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-yellow-50 rounded-lg p-3", children: [
4807
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-yellow-600 mb-1", children: [
4808
+ /* @__PURE__ */ jsx(GitBranch, { size: 16, weight: "fill" }),
4809
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Logic" })
4810
+ ] }),
4811
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-yellow-700", children: [
4812
+ conditionNodes.length,
4813
+ " ",
4814
+ conditionNodes.length === 1 ? "condition" : "conditions"
4815
+ ] })
4816
+ ] })
4817
+ ] }),
4818
+ showFlow && /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "h-[320px] bg-slate-50 rounded-lg border border-slate-200 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-slate-400 text-sm", children: "Loading..." }) }), children: /* @__PURE__ */ jsx(WorkflowFlow2, { workflow, height: 320 }) }),
4819
+ !showFlow && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
4820
+ /* @__PURE__ */ jsx("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: "Workflow Nodes" }),
4821
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: nodes.map((node) => /* @__PURE__ */ jsx(
4822
+ "span",
4823
+ {
4824
+ className: "px-2 py-1 rounded bg-slate-100 text-slate-600 text-xs",
4825
+ children: node.name
4826
+ },
4827
+ node.id
4828
+ )) })
4829
+ ] })
4830
+ ] });
4831
+ }
4832
+ function SimWorkflowSummary({ workflow }) {
4833
+ const nodes = workflow.nodes || [];
4834
+ const aiNodes = nodes.filter((n) => n.type === "llm");
4835
+ const httpNodes = nodes.filter((n) => n.type === "http_request");
4836
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4837
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-4 gap-3", children: [
4838
+ /* @__PURE__ */ jsxs("div", { className: "bg-muted/50 rounded-lg p-3", children: [
4839
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground mb-1", children: [
4840
+ /* @__PURE__ */ jsx(WebhooksLogo, { size: 16, weight: "fill" }),
4841
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Trigger" })
4842
+ ] }),
4843
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: workflow.trigger?.type ? getSimNodeTypeLabel(workflow.trigger.type) : "Manual" })
4844
+ ] }),
4845
+ /* @__PURE__ */ jsxs("div", { className: "bg-muted/50 rounded-lg p-3", children: [
4846
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground mb-1", children: [
4847
+ /* @__PURE__ */ jsx(TreeStructure, { size: 16 }),
4848
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Steps" })
4849
+ ] }),
4850
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold", children: [
4851
+ nodes.length,
4852
+ " nodes"
4853
+ ] })
4854
+ ] }),
4855
+ aiNodes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-purple-50 rounded-lg p-3", children: [
4856
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-purple-600 mb-1", children: [
4857
+ /* @__PURE__ */ jsx(Robot, { size: 16, weight: "fill" }),
4858
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "AI" })
4859
+ ] }),
4860
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-purple-700", children: [
4861
+ aiNodes.length,
4862
+ " LLM ",
4863
+ aiNodes.length === 1 ? "node" : "nodes"
4864
+ ] })
4865
+ ] }),
4866
+ httpNodes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-blue-50 rounded-lg p-3", children: [
4867
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-blue-600 mb-1", children: [
4868
+ /* @__PURE__ */ jsx(Globe, { size: 16, weight: "fill" }),
4869
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "APIs" })
4870
+ ] }),
4871
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-blue-700", children: [
4872
+ httpNodes.length,
4873
+ " ",
4874
+ httpNodes.length === 1 ? "request" : "requests"
4875
+ ] })
4876
+ ] })
4877
+ ] }),
4878
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
4879
+ /* @__PURE__ */ jsx("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: "Workflow Nodes" }),
4880
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: nodes.map((node) => /* @__PURE__ */ jsxs(
4881
+ "div",
4882
+ {
4883
+ className: "flex items-center gap-1.5 px-2.5 py-1.5 rounded-md border text-xs bg-gray-100 text-gray-700 border-gray-300",
4884
+ children: [
4885
+ /* @__PURE__ */ jsx(Package, { size: 14 }),
4886
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: getSimNodeTypeLabel(node.type) })
4887
+ ]
4888
+ },
4889
+ node.id
4890
+ )) })
4891
+ ] })
4892
+ ] });
4893
+ }
4894
+ function WorkflowViewer({
4895
+ workflow,
4896
+ platform,
4897
+ title = "Workflow",
4898
+ webhookUrl,
4899
+ workflowId,
4900
+ workflowDefinitionId,
4901
+ workerId,
4902
+ workerName,
4903
+ internalWorkerType,
4904
+ lastSynced,
4905
+ isActive,
4906
+ syncError,
4907
+ className = "",
4908
+ editable = false,
4909
+ allowCreate = false,
4910
+ allowPlatformChange = false,
4911
+ allowStatusChange = false,
4912
+ onWorkflowUpdate,
4913
+ onWorkflowCreated,
4914
+ onPlatformChange,
4915
+ onStatusChange,
4916
+ apiHandlers,
4917
+ loadingComponent,
4918
+ formatDistance = defaultFormatDistance
4919
+ }) {
4920
+ const [viewMode, setViewMode] = useState("summary");
4921
+ const [editedJson, setEditedJson] = useState("");
4922
+ const [jsonError, setJsonError] = useState(null);
4923
+ const [message, setMessage] = useState(null);
4924
+ const [saving, setSaving] = useState(false);
4925
+ const [syncing, setSyncing] = useState(false);
4926
+ const [pulling, setPulling] = useState(false);
4927
+ const [creating, setCreating] = useState(false);
4928
+ const [selectedTemplate, setSelectedTemplate] = useState("blank");
4929
+ const [localPlatform, setLocalPlatform] = useState(platform);
4930
+ const [localIsActive, setLocalIsActive] = useState(isActive ?? true);
4931
+ const hasUnsavedChanges = localPlatform !== platform || localIsActive !== (isActive ?? true);
4932
+ function copyToClipboard(text, label = "Copied") {
4933
+ navigator.clipboard.writeText(text);
4934
+ setMessage({ type: "success", text: label });
4935
+ setTimeout(() => setMessage(null), 2e3);
4936
+ }
4937
+ function handleLocalPlatformChange(newPlatform) {
4938
+ setLocalPlatform(newPlatform);
4939
+ }
4940
+ function handleLocalStatusChange(newStatus) {
4941
+ setLocalIsActive(newStatus);
4942
+ }
4943
+ function cancelChanges() {
4944
+ setLocalPlatform(platform);
4945
+ setLocalIsActive(isActive ?? true);
4946
+ setMessage(null);
4947
+ }
4948
+ async function saveSettings() {
4949
+ if (!workflowDefinitionId || !apiHandlers?.saveSettings) {
4950
+ setMessage({ type: "error", text: "Cannot save - no workflow definition or API handler." });
4951
+ return;
4952
+ }
4953
+ setSaving(true);
4954
+ setMessage(null);
4955
+ try {
4956
+ const updateData = {};
4957
+ if (localPlatform !== platform) {
4958
+ updateData.platform = localPlatform;
4959
+ }
4960
+ if (localIsActive !== (isActive ?? true)) {
4961
+ updateData.is_active = localIsActive;
4962
+ }
4963
+ if (Object.keys(updateData).length === 0) {
4964
+ setMessage({ type: "success", text: "No changes to save" });
4965
+ setSaving(false);
4966
+ return;
4967
+ }
4968
+ const result = await apiHandlers.saveSettings(workflowDefinitionId, updateData);
4969
+ if (result.success) {
4970
+ setMessage({ type: "success", text: "Settings saved successfully" });
4971
+ onPlatformChange?.(localPlatform);
4972
+ onStatusChange?.(localIsActive);
4973
+ } else {
4974
+ setMessage({ type: "error", text: result.error || "Failed to save settings" });
4975
+ }
4976
+ } catch {
4977
+ setMessage({ type: "error", text: "Failed to save settings" });
4978
+ } finally {
4979
+ setSaving(false);
4980
+ }
4981
+ }
4982
+ function startEditing() {
4983
+ setEditedJson(JSON.stringify(workflow, null, 2));
4984
+ setJsonError(null);
4985
+ setViewMode("edit");
4986
+ }
4987
+ function cancelEditing() {
4988
+ setEditedJson("");
4989
+ setJsonError(null);
4990
+ setViewMode("summary");
4991
+ }
4992
+ function validateJson(json) {
4993
+ try {
4994
+ const parsed = JSON.parse(json);
4995
+ if (platform === "n8n") {
4996
+ if (!parsed.nodes || !Array.isArray(parsed.nodes)) {
4997
+ throw new Error("n8n workflow must have a nodes array");
4998
+ }
4999
+ } else {
5000
+ if (!parsed.nodes || !Array.isArray(parsed.nodes)) {
5001
+ throw new Error("Sim workflow must have a nodes array");
5002
+ }
5003
+ }
5004
+ return parsed;
5005
+ } catch {
5006
+ return null;
5007
+ }
5008
+ }
5009
+ async function saveWorkflow() {
5010
+ const parsed = validateJson(editedJson);
5011
+ if (!parsed) {
5012
+ setJsonError("Invalid JSON. Please check the syntax.");
5013
+ return;
5014
+ }
5015
+ if (!workflowDefinitionId || !apiHandlers?.saveWorkflow) {
5016
+ setJsonError("Cannot save - no workflow definition or API handler.");
5017
+ return;
5018
+ }
5019
+ setSaving(true);
5020
+ setJsonError(null);
5021
+ try {
5022
+ const result = await apiHandlers.saveWorkflow(workflowDefinitionId, parsed, platform);
5023
+ if (result.success) {
5024
+ setMessage({ type: "success", text: "Workflow saved to database" });
5025
+ setViewMode("summary");
5026
+ onWorkflowUpdate?.(parsed);
5027
+ } else {
5028
+ setJsonError(result.error || "Failed to save workflow");
5029
+ }
5030
+ } catch {
5031
+ setJsonError("Failed to save workflow");
5032
+ } finally {
5033
+ setSaving(false);
5034
+ }
5035
+ }
5036
+ async function pushToN8n() {
5037
+ if (!workerId || !apiHandlers?.pushToN8n) {
5038
+ setMessage({ type: "error", text: "Cannot sync - no worker ID or API handler" });
5039
+ return;
5040
+ }
5041
+ setSyncing(true);
5042
+ setMessage(null);
5043
+ try {
5044
+ const result = await apiHandlers.pushToN8n(workerId);
5045
+ if (result.success) {
5046
+ setMessage({ type: "success", text: "Pushed to n8n successfully" });
5047
+ } else {
5048
+ setMessage({ type: "error", text: result.error || "Failed to push to n8n" });
5049
+ }
5050
+ } catch {
5051
+ setMessage({ type: "error", text: "Failed to push to n8n" });
5052
+ } finally {
5053
+ setSyncing(false);
5054
+ }
5055
+ }
5056
+ async function pullFromN8n() {
5057
+ if (!workflowId || !workflowDefinitionId || !apiHandlers?.pullFromN8n) {
5058
+ setMessage({ type: "error", text: "Cannot pull - no n8n workflow ID or API handler" });
5059
+ return;
5060
+ }
5061
+ setPulling(true);
5062
+ setMessage(null);
5063
+ try {
5064
+ const result = await apiHandlers.pullFromN8n(workflowDefinitionId);
5065
+ if (result.success) {
5066
+ if (result.descriptionSync?.needsUpdate) {
5067
+ setMessage({
5068
+ type: "success",
5069
+ text: `Pulled from n8n. Note: ${result.descriptionSync.reason || "Worker description may need updating."}`
5070
+ });
5071
+ } else {
5072
+ setMessage({ type: "success", text: "Pulled from n8n successfully" });
5073
+ }
5074
+ } else {
5075
+ setMessage({ type: "error", text: result.error || "Failed to pull from n8n" });
5076
+ }
5077
+ } catch {
5078
+ setMessage({ type: "error", text: "Failed to pull from n8n" });
5079
+ } finally {
5080
+ setPulling(false);
5081
+ }
5082
+ }
5083
+ function startCreating() {
5084
+ if (internalWorkerType) {
5085
+ setSelectedTemplate(internalWorkerType);
5086
+ } else {
5087
+ setSelectedTemplate("blank");
5088
+ }
5089
+ const blank = platform === "n8n" ? BLANK_N8N_WORKFLOW : BLANK_SIM_WORKFLOW;
5090
+ setEditedJson(JSON.stringify(blank, null, 2));
5091
+ setViewMode("create");
5092
+ }
5093
+ async function createWorkflow() {
5094
+ const parsed = validateJson(editedJson);
5095
+ if (!parsed) {
5096
+ setJsonError("Invalid JSON. Please check the syntax.");
5097
+ return;
5098
+ }
5099
+ if (!workerId || !apiHandlers?.createWorkflow) {
5100
+ setJsonError("Cannot create - no worker ID or API handler.");
5101
+ return;
5102
+ }
5103
+ setCreating(true);
5104
+ setJsonError(null);
5105
+ try {
5106
+ const workflowName = parsed.name || `${workerName || "Worker"} Workflow`;
5107
+ const result = await apiHandlers.createWorkflow({
5108
+ agent_id: workerId,
5109
+ name: workflowName,
5110
+ platform,
5111
+ n8n_workflow: platform === "n8n" ? parsed : null,
5112
+ sim_workflow: platform === "sim" ? parsed : null,
5113
+ is_active: true,
5114
+ is_global: false
5115
+ });
5116
+ if (result.success && result.workflow) {
5117
+ setMessage({ type: "success", text: "Workflow created successfully" });
5118
+ onWorkflowCreated?.(result.workflow.id);
5119
+ } else {
5120
+ setJsonError(result.error || "Failed to create workflow");
5121
+ }
5122
+ } catch {
5123
+ setJsonError("Failed to create workflow");
5124
+ } finally {
5125
+ setCreating(false);
5126
+ }
5127
+ }
5128
+ async function loadTemplate() {
5129
+ if (!workerId || !workerName || !apiHandlers?.loadTemplate) {
5130
+ setJsonError("Worker information or API handler required to generate template.");
5131
+ return;
5132
+ }
5133
+ if (selectedTemplate === "blank" || selectedTemplate === "custom") {
5134
+ return;
5135
+ }
5136
+ setCreating(true);
5137
+ setJsonError(null);
5138
+ try {
5139
+ const result = await apiHandlers.loadTemplate(selectedTemplate, workerId);
5140
+ if (result.success && result.workflow) {
5141
+ setEditedJson(JSON.stringify(result.workflow, null, 2));
5142
+ setMessage({ type: "success", text: "Template loaded" });
5143
+ } else {
5144
+ setJsonError(result.error || "Failed to load template");
5145
+ }
5146
+ } catch {
5147
+ setJsonError("Failed to load template");
5148
+ } finally {
5149
+ setCreating(false);
5150
+ }
5151
+ }
5152
+ const DefaultLoading = /* @__PURE__ */ jsx("div", { className: "h-[320px] bg-slate-50 rounded-lg border border-slate-200 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-slate-400 text-sm", children: "Loading..." }) });
5153
+ if (!workflow) {
5154
+ return /* @__PURE__ */ jsxs(Card, { className, children: [
5155
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
5156
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: title }),
5157
+ allowCreate && viewMode !== "create" && /* @__PURE__ */ jsx(Button, { onClick: startCreating, variant: "primary", size: "sm", children: "Create Workflow" })
5158
+ ] }) }),
5159
+ /* @__PURE__ */ jsx(CardContent, { children: viewMode === "create" ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
5160
+ message && /* @__PURE__ */ jsx(Alert, { variant: message.type === "success" ? "success" : "error", children: message.text }),
5161
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
5162
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Start from template:" }),
5163
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
5164
+ /* @__PURE__ */ jsx(
5165
+ Button,
5166
+ {
5167
+ onClick: () => {
5168
+ setSelectedTemplate("blank");
5169
+ const blank = platform === "n8n" ? BLANK_N8N_WORKFLOW : BLANK_SIM_WORKFLOW;
5170
+ setEditedJson(JSON.stringify(blank, null, 2));
5171
+ },
5172
+ variant: selectedTemplate === "blank" ? "primary" : "outline",
5173
+ size: "sm",
5174
+ children: "Blank"
5175
+ }
5176
+ ),
5177
+ internalWorkerType && apiHandlers?.loadTemplate && /* @__PURE__ */ jsxs(
5178
+ Button,
5179
+ {
5180
+ onClick: () => {
5181
+ setSelectedTemplate(internalWorkerType);
5182
+ loadTemplate();
5183
+ },
5184
+ variant: selectedTemplate === internalWorkerType ? "primary" : "outline",
5185
+ size: "sm",
5186
+ children: [
5187
+ internalWorkerType.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
5188
+ " Template"
5189
+ ]
5190
+ }
5191
+ )
5192
+ ] })
5193
+ ] }),
5194
+ jsonError && /* @__PURE__ */ jsx(Alert, { variant: "error", children: jsonError }),
5195
+ /* @__PURE__ */ jsx(
5196
+ Textarea,
5197
+ {
5198
+ value: editedJson,
5199
+ onChange: (e) => {
5200
+ setEditedJson(e.target.value);
5201
+ setJsonError(null);
5202
+ },
5203
+ className: "h-72 font-mono text-xs",
5204
+ placeholder: "Paste or edit workflow JSON here..."
5205
+ }
5206
+ ),
5207
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
5208
+ /* @__PURE__ */ jsx(Button, { onClick: () => setViewMode("summary"), variant: "outline", size: "sm", children: "Cancel" }),
5209
+ /* @__PURE__ */ jsx(
5210
+ Button,
5211
+ {
5212
+ onClick: createWorkflow,
5213
+ disabled: creating || !editedJson.trim(),
5214
+ variant: "primary",
5215
+ size: "sm",
5216
+ children: creating ? "Creating..." : "Create Workflow"
5217
+ }
5218
+ )
5219
+ ] }),
5220
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: 'Create a workflow definition. After creating, use "Push to n8n" to deploy it.' })
5221
+ ] }) : /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
5222
+ "No workflow defined yet.",
5223
+ allowCreate && ' Click "Create Workflow" to add one.'
5224
+ ] }) })
5225
+ ] });
5226
+ }
5227
+ return /* @__PURE__ */ jsxs(Card, { className, children: [
5228
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
5229
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5230
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: title }),
5231
+ allowStatusChange && workflowDefinitionId ? /* @__PURE__ */ jsx(
5232
+ Badge,
5233
+ {
5234
+ variant: localIsActive ? "success" : "default",
5235
+ size: "sm",
5236
+ children: localIsActive ? "Active" : "Inactive"
5237
+ }
5238
+ ) : isActive !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: isActive ? "success" : "default", size: "sm", children: isActive ? "Active" : "Inactive" }),
5239
+ hasUnsavedChanges && /* @__PURE__ */ jsx(Badge, { variant: "warning", size: "sm", children: "Unsaved" }),
5240
+ syncError && /* @__PURE__ */ jsx(Badge, { variant: "error", size: "sm", children: "Sync Error" })
5241
+ ] }),
5242
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: viewMode === "edit" ? /* @__PURE__ */ jsxs(Fragment, { children: [
5243
+ /* @__PURE__ */ jsx(Button, { onClick: cancelEditing, variant: "outline", size: "sm", children: "Cancel" }),
5244
+ /* @__PURE__ */ jsx(Button, { onClick: saveWorkflow, disabled: saving, variant: "primary", size: "sm", children: saving ? "Saving..." : "Save JSON" })
5245
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5246
+ /* @__PURE__ */ jsxs("div", { className: "flex border border-border rounded-md overflow-hidden mr-2", children: [
5247
+ /* @__PURE__ */ jsxs(
5248
+ "button",
5249
+ {
5250
+ onClick: () => setViewMode("summary"),
5251
+ className: `px-3 py-1.5 text-xs font-medium flex items-center gap-1.5 transition-colors ${viewMode === "summary" ? "bg-primary text-white" : "bg-background hover:bg-muted text-muted-foreground"}`,
5252
+ children: [
5253
+ /* @__PURE__ */ jsx(Eye, { size: 14 }),
5254
+ "Summary"
5255
+ ]
5256
+ }
5257
+ ),
5258
+ platform === "n8n" && /* @__PURE__ */ jsxs(
5259
+ "button",
5260
+ {
5261
+ onClick: () => setViewMode("flow"),
5262
+ className: `px-3 py-1.5 text-xs font-medium flex items-center gap-1.5 transition-colors border-l border-border ${viewMode === "flow" ? "bg-primary text-white" : "bg-background hover:bg-muted text-muted-foreground"}`,
5263
+ children: [
5264
+ /* @__PURE__ */ jsx(TreeStructure, { size: 14 }),
5265
+ "Diagram"
5266
+ ]
5267
+ }
5268
+ ),
5269
+ /* @__PURE__ */ jsxs(
5270
+ "button",
5271
+ {
5272
+ onClick: () => setViewMode("json"),
5273
+ className: `px-3 py-1.5 text-xs font-medium flex items-center gap-1.5 transition-colors border-l border-border ${viewMode === "json" ? "bg-primary text-white" : "bg-background hover:bg-muted text-muted-foreground"}`,
5274
+ children: [
5275
+ /* @__PURE__ */ jsx(Code, { size: 14 }),
5276
+ "JSON"
5277
+ ]
5278
+ }
5279
+ )
5280
+ ] }),
5281
+ editable && apiHandlers?.saveWorkflow && /* @__PURE__ */ jsx(Button, { onClick: startEditing, variant: "outline", size: "sm", icon: /* @__PURE__ */ jsx(PencilSimple, { size: 14 }), children: "Edit" })
5282
+ ] }) })
5283
+ ] }) }),
5284
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
5285
+ message && /* @__PURE__ */ jsx(Alert, { variant: message.type === "success" ? "success" : "error", children: message.text }),
5286
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4 text-xs text-muted-foreground", children: [
5287
+ workflowId && /* @__PURE__ */ jsx("span", { className: "font-mono bg-muted px-2 py-1 rounded", children: workflowId }),
5288
+ lastSynced && /* @__PURE__ */ jsxs("span", { children: [
5289
+ "Synced ",
5290
+ formatDistance(new Date(lastSynced), { addSuffix: true })
5291
+ ] }),
5292
+ webhookUrl && /* @__PURE__ */ jsxs(
5293
+ "button",
5294
+ {
5295
+ onClick: () => copyToClipboard(webhookUrl, "Webhook URL copied"),
5296
+ className: "flex items-center gap-1 hover:text-foreground",
5297
+ children: [
5298
+ /* @__PURE__ */ jsx(Copy, { size: 12 }),
5299
+ "Copy webhook"
5300
+ ]
5301
+ }
5302
+ )
5303
+ ] }),
5304
+ (allowPlatformChange || allowStatusChange) && workflowDefinitionId && hasUnsavedChanges && apiHandlers?.saveSettings && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-amber-50 border border-amber-200 rounded-lg space-y-3", children: [
5305
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
5306
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-amber-800", children: "Unsaved changes" }),
5307
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
5308
+ /* @__PURE__ */ jsx(Button, { onClick: cancelChanges, variant: "outline", size: "sm", children: "Cancel" }),
5309
+ /* @__PURE__ */ jsx(Button, { onClick: saveSettings, disabled: saving, variant: "primary", size: "sm", children: saving ? "Saving..." : "Save" })
5310
+ ] })
5311
+ ] }),
5312
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4 text-sm", children: [
5313
+ allowPlatformChange && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5314
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Platform:" }),
5315
+ /* @__PURE__ */ jsxs(
5316
+ Select,
5317
+ {
5318
+ value: localPlatform,
5319
+ onChange: (e) => handleLocalPlatformChange(e.target.value),
5320
+ className: "h-8 text-sm w-28",
5321
+ children: [
5322
+ /* @__PURE__ */ jsx("option", { value: "n8n", children: "n8n" }),
5323
+ /* @__PURE__ */ jsx("option", { value: "sim", children: "Sim Studio" })
5324
+ ]
5325
+ }
5326
+ )
5327
+ ] }),
5328
+ allowStatusChange && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5329
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Status:" }),
5330
+ /* @__PURE__ */ jsxs(
5331
+ Select,
5332
+ {
5333
+ value: localIsActive ? "active" : "inactive",
5334
+ onChange: (e) => handleLocalStatusChange(e.target.value === "active"),
5335
+ className: "h-8 text-sm w-28",
5336
+ children: [
5337
+ /* @__PURE__ */ jsx("option", { value: "active", children: "Active" }),
5338
+ /* @__PURE__ */ jsx("option", { value: "inactive", children: "Inactive" })
5339
+ ]
5340
+ }
5341
+ )
5342
+ ] })
5343
+ ] })
5344
+ ] }),
5345
+ syncError && /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
5346
+ /* @__PURE__ */ jsx("strong", { children: "Sync Error:" }),
5347
+ " ",
5348
+ syncError
5349
+ ] }),
5350
+ editable && workerId && platform === "n8n" && viewMode !== "edit" && apiHandlers?.pushToN8n && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
5351
+ /* @__PURE__ */ jsx(Button, { onClick: pushToN8n, disabled: syncing, variant: "primary", size: "sm", icon: /* @__PURE__ */ jsx(CloudArrowUp, { size: 16 }), children: syncing ? "Pushing..." : "Push to n8n" }),
5352
+ workflowId && apiHandlers?.pullFromN8n && /* @__PURE__ */ jsx(Button, { onClick: pullFromN8n, disabled: pulling, variant: "outline", size: "sm", icon: /* @__PURE__ */ jsx(CloudArrowDown, { size: 16 }), children: pulling ? "Pulling..." : "Pull from n8n" })
5353
+ ] }),
5354
+ /* @__PURE__ */ jsx("div", { className: "pt-2", children: viewMode === "edit" ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
5355
+ jsonError && /* @__PURE__ */ jsx(Alert, { variant: "error", children: jsonError }),
5356
+ /* @__PURE__ */ jsx(
5357
+ Textarea,
5358
+ {
5359
+ value: editedJson,
5360
+ onChange: (e) => {
5361
+ setEditedJson(e.target.value);
5362
+ setJsonError(null);
5363
+ },
5364
+ className: "h-[400px] font-mono text-xs"
5365
+ }
5366
+ ),
5367
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: 'Edit the workflow JSON above. Changes will be saved to the database. Use "Push to n8n" to sync.' })
5368
+ ] }) : viewMode === "json" ? /* @__PURE__ */ jsxs("div", { className: "relative", children: [
5369
+ /* @__PURE__ */ jsx(
5370
+ Button,
5371
+ {
5372
+ onClick: () => copyToClipboard(JSON.stringify(workflow, null, 2), "JSON copied"),
5373
+ variant: "outline",
5374
+ size: "sm",
5375
+ className: "absolute top-2 right-2 z-10",
5376
+ icon: /* @__PURE__ */ jsx(Copy, { size: 14 }),
5377
+ children: "Copy"
5378
+ }
5379
+ ),
5380
+ /* @__PURE__ */ jsx("pre", { className: "p-4 bg-slate-900 text-slate-100 rounded-lg text-xs overflow-auto max-h-[500px] font-mono", children: JSON.stringify(workflow, null, 2) })
5381
+ ] }) : viewMode === "flow" ? platform === "n8n" && /* @__PURE__ */ jsx(Suspense, { fallback: loadingComponent || DefaultLoading, children: /* @__PURE__ */ jsx(WorkflowFlow2, { workflow, height: 380 }) }) : platform === "n8n" ? /* @__PURE__ */ jsx(N8nWorkflowSummary, { workflow, showFlow: false }) : /* @__PURE__ */ jsx(SimWorkflowSummary, { workflow }) })
5382
+ ] })
5383
+ ] });
5384
+ }
4378
5385
 
4379
5386
  // src/index.ts
4380
5387
  __reExport(index_exports, icons_exports);
4381
5388
 
4382
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarFallback, AvatarImage, Badge, BreadcrumbLink, Breadcrumbs, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CodeBlock, ConfirmDialog, DateRangePicker, DateRangeSelect, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Divider, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorState, FilePreview, FormField, IconBox, ImpactMetricsForm, Input, Label2 as Label, LabeledSwitch, Logo, Metric, MetricCard, MetricLabel, MetricSubtext, MetricValue, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupCard, RadioGroupItem, RadioGroupOption, ScenariosManager, Select, Separator2 as Separator, SettingsNav, SettingsNavLink, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SimplePagination, SimpleTooltip, Skeleton, SkeletonCard, SkeletonText, Stat, StepDots, StepProgress, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsListUnderline, TabsTrigger, TabsTriggerUnderline, Tag, Textarea, Toast, ToastAction, ToastClose, ToastDescription, ToastIcon, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, UsageBar, UsageChart, alertVariants, badgeVariants, buttonVariants, cn, getDateRangeFromPreset, iconBoxVariants, metricCardVariants, navigationMenuTriggerStyle, progressVariants, statVariants, tagVariants, toast, usageBarVariants, useToast, valueVariants };
5389
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActivityTimeline, Alert, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarFallback, AvatarImage, Badge, BreadcrumbLink, Breadcrumbs, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CodeBlock, ConfirmDialog, DateRangePicker, DateRangeSelect, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Divider, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorState, FilePreview, FormField, IconBox, ImpactMetricsForm, Input, Label2 as Label, LabeledSwitch, Logo, Metric, MetricCard, MetricLabel, MetricSubtext, MetricValue, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupCard, RadioGroupItem, RadioGroupOption, ScenariosManager, Select, Separator2 as Separator, SettingsNav, SettingsNavLink, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SimplePagination, SimpleTooltip, Skeleton, SkeletonCard, SkeletonText, Stat, StepDots, StepProgress, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsListUnderline, TabsTrigger, TabsTriggerUnderline, Tag, Textarea, Toast, ToastAction, ToastClose, ToastDescription, ToastIcon, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, UsageBar, UsageChart, WorkflowFlow, WorkflowViewer, alertVariants, badgeVariants, buttonVariants, cn, getDateRangeFromPreset, iconBoxVariants, metricCardVariants, navigationMenuTriggerStyle, progressVariants, statVariants, tagVariants, toast, usageBarVariants, useToast, valueVariants };
4383
5390
  //# sourceMappingURL=index.mjs.map
4384
5391
  //# sourceMappingURL=index.mjs.map