@dilipod/ui 0.4.14 → 0.4.16

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
@@ -10,7 +10,7 @@ import { clsx } from 'clsx';
10
10
  import { twMerge } from 'tailwind-merge';
11
11
  import * as SheetPrimitive from '@radix-ui/react-dialog';
12
12
  import * as react_star 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, WebhooksLogo, Copy, CloudArrowUp, CloudArrowDown, ArrowsClockwise, DownloadSimple, ClockCounterClockwise, FileImage, FilePdf, FileDoc, Question, Warning, Trash, Robot, Globe, GitBranch, Package, Timer } 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, WebhooksLogo, Copy, CloudArrowUp, CloudArrowDown, ArrowsClockwise, DownloadSimple, ClockCounterClockwise, ArrowsLeftRight, Minus, Pencil, FileImage, FilePdf, FileDoc, Question, Warning, Trash, Robot, Globe, GitBranch, Package, Timer } from '@phosphor-icons/react';
14
14
  import 'react-dom';
15
15
  import * as SwitchPrimitive from '@radix-ui/react-switch';
16
16
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
@@ -53,6 +53,7 @@ __export(workflow_flow_exports, {
53
53
  });
54
54
  function getNodeTypeLabel(type) {
55
55
  const labels = {
56
+ // n8n types
56
57
  "n8n-nodes-base.webhook": "Webhook",
57
58
  "n8n-nodes-base.scheduleTrigger": "Schedule",
58
59
  "n8n-nodes-base.if": "Condition",
@@ -62,7 +63,22 @@ function getNodeTypeLabel(type) {
62
63
  "n8n-nodes-base.respondToWebhook": "Response",
63
64
  "@n8n/n8n-nodes-langchain.agent": "AI Agent",
64
65
  "@n8n/n8n-nodes-langchain.lmChatOpenAi": "OpenAI",
65
- "@n8n/n8n-nodes-langchain.lmChatAnthropic": "Anthropic"
66
+ "@n8n/n8n-nodes-langchain.lmChatAnthropic": "Anthropic",
67
+ // Sim Studio types
68
+ "starter": "Webhook",
69
+ "webhook": "Webhook",
70
+ "agent": "AI Agent",
71
+ "llm": "LLM",
72
+ "openai": "OpenAI",
73
+ "anthropic": "Anthropic",
74
+ "api": "API Request",
75
+ "http_request": "HTTP Request",
76
+ "condition": "Condition",
77
+ "code": "Code",
78
+ "response": "Response",
79
+ "function": "Function",
80
+ "evaluator": "Evaluator",
81
+ "router": "Router"
66
82
  };
67
83
  return labels[type] || type.split(".").pop()?.replace(/([A-Z])/g, " $1").trim() || type;
68
84
  }
@@ -74,10 +90,98 @@ function CustomNode({ data }) {
74
90
  /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, className: "!bg-slate-300 !w-1.5 !h-1.5 !border-0" })
75
91
  ] });
76
92
  }
77
- function WorkflowFlow({ workflow, height = 350, className = "" }) {
93
+ function WorkflowFlow({ workflow, height = 350, className = "", platform = "n8n" }) {
78
94
  const { initialNodes, initialEdges } = useMemo(() => {
79
- const n8nNodes = workflow.nodes || [];
80
- const connections = workflow.connections || {};
95
+ if (platform === "sim") {
96
+ const simWorkflow = workflow;
97
+ const blocks = simWorkflow.blocks || {};
98
+ const simEdges = simWorkflow.edges || [];
99
+ const blockList = Object.values(blocks);
100
+ if (blockList.length === 0) {
101
+ return { initialNodes: [], initialEdges: [] };
102
+ }
103
+ const forwardEdges2 = /* @__PURE__ */ new Map();
104
+ const backwardEdges2 = /* @__PURE__ */ new Map();
105
+ simEdges.forEach((edge) => {
106
+ const from = edge.source;
107
+ const to = edge.target;
108
+ if (from && to) {
109
+ if (!forwardEdges2.has(from)) forwardEdges2.set(from, []);
110
+ forwardEdges2.get(from).push(to);
111
+ if (!backwardEdges2.has(to)) backwardEdges2.set(to, []);
112
+ backwardEdges2.get(to).push(from);
113
+ }
114
+ });
115
+ const triggerBlocks = blockList.filter(
116
+ (b) => b.type === "starter" || b.type === "webhook" || b.type === "api"
117
+ );
118
+ const roots2 = triggerBlocks.length > 0 ? triggerBlocks : blockList.filter((b) => !backwardEdges2.has(b.id) || backwardEdges2.get(b.id).length === 0);
119
+ const levels2 = /* @__PURE__ */ new Map();
120
+ const queue2 = [];
121
+ roots2.forEach((r) => {
122
+ levels2.set(r.id, 0);
123
+ queue2.push(r.id);
124
+ });
125
+ const visited2 = /* @__PURE__ */ new Set();
126
+ while (queue2.length > 0) {
127
+ const id = queue2.shift();
128
+ if (visited2.has(id)) continue;
129
+ visited2.add(id);
130
+ const children = forwardEdges2.get(id) || [];
131
+ const myLevel = levels2.get(id) || 0;
132
+ children.forEach((child) => {
133
+ const childLevel = levels2.get(child);
134
+ if (childLevel === void 0 || myLevel + 1 > childLevel) {
135
+ levels2.set(child, myLevel + 1);
136
+ }
137
+ if (!visited2.has(child)) {
138
+ queue2.push(child);
139
+ }
140
+ });
141
+ }
142
+ blockList.forEach((block) => {
143
+ if (!levels2.has(block.id)) {
144
+ const maxLevel = Math.max(0, ...Array.from(levels2.values()));
145
+ levels2.set(block.id, maxLevel + 1);
146
+ }
147
+ });
148
+ const nodesByLevel2 = /* @__PURE__ */ new Map();
149
+ levels2.forEach((level, id) => {
150
+ if (!nodesByLevel2.has(level)) nodesByLevel2.set(level, []);
151
+ nodesByLevel2.get(level).push(id);
152
+ });
153
+ const xGap2 = 170;
154
+ const yGap2 = 70;
155
+ const positions2 = /* @__PURE__ */ new Map();
156
+ const sortedLevels2 = Array.from(nodesByLevel2.keys()).sort((a, b) => a - b);
157
+ sortedLevels2.forEach((level) => {
158
+ const nodesInLevel = nodesByLevel2.get(level);
159
+ const totalHeight = (nodesInLevel.length - 1) * yGap2;
160
+ const startY = -totalHeight / 2;
161
+ nodesInLevel.forEach((id, i) => {
162
+ positions2.set(id, { x: level * xGap2, y: startY + i * yGap2 });
163
+ });
164
+ });
165
+ const nodes3 = blockList.map((block) => ({
166
+ id: block.id,
167
+ type: "custom",
168
+ position: positions2.get(block.id) || { x: 0, y: 0 },
169
+ data: { label: block.name || block.type, type: block.type }
170
+ }));
171
+ const edges3 = simEdges.map((edge, idx) => ({
172
+ id: edge.id || `edge-${idx}`,
173
+ source: edge.source || "",
174
+ target: edge.target || "",
175
+ type: "smoothstep",
176
+ pathOptions: { borderRadius: 20 },
177
+ style: { stroke: "#94a3b8", strokeWidth: 1.5 },
178
+ markerEnd: { type: MarkerType.ArrowClosed, color: "#94a3b8", width: 14, height: 14 }
179
+ })).filter((e) => e.source && e.target);
180
+ return { initialNodes: nodes3, initialEdges: edges3 };
181
+ }
182
+ const n8nWorkflow = workflow;
183
+ const n8nNodes = n8nWorkflow.nodes || [];
184
+ const connections = n8nWorkflow.connections || {};
81
185
  const nodeIdMap = new Map(n8nNodes.map((n) => [n.name, n.id || n.name]));
82
186
  const forwardEdges = /* @__PURE__ */ new Map();
83
187
  const backwardEdges = /* @__PURE__ */ new Map();
@@ -4748,6 +4852,55 @@ function defaultFormatDistance(date, options) {
4748
4852
  else result = `${diffDays} day${diffDays > 1 ? "s" : ""}`;
4749
4853
  return options?.addSuffix ? `${result} ago` : result;
4750
4854
  }
4855
+ function computeWorkflowDiff(stateA, stateB) {
4856
+ if (!stateA || !stateB) {
4857
+ return {
4858
+ blocksAdded: [],
4859
+ blocksRemoved: [],
4860
+ blocksModified: [],
4861
+ edgesAdded: 0,
4862
+ edgesRemoved: 0,
4863
+ summary: "Unable to compare - missing workflow data"
4864
+ };
4865
+ }
4866
+ const blocksA = stateA.blocks || {};
4867
+ const blocksB = stateB.blocks || {};
4868
+ const edgesA = stateA.edges || [];
4869
+ const edgesB = stateB.edges || [];
4870
+ const blockIdsA = new Set(Object.keys(blocksA));
4871
+ const blockIdsB = new Set(Object.keys(blocksB));
4872
+ const blocksAdded = [...blockIdsB].filter((id) => !blockIdsA.has(id));
4873
+ const blocksRemoved = [...blockIdsA].filter((id) => !blockIdsB.has(id));
4874
+ const blocksModified = [];
4875
+ for (const id of blockIdsA) {
4876
+ if (blockIdsB.has(id)) {
4877
+ const blockA = blocksA[id];
4878
+ const blockB = blocksB[id];
4879
+ if (JSON.stringify(blockA) !== JSON.stringify(blockB)) {
4880
+ blocksModified.push(id);
4881
+ }
4882
+ }
4883
+ }
4884
+ const edgeSignature = (e) => `${e.source || e.from}->${e.target || e.to}`;
4885
+ const edgeSigsA = new Set(edgesA.map(edgeSignature));
4886
+ const edgeSigsB = new Set(edgesB.map(edgeSignature));
4887
+ const edgesAdded = [...edgeSigsB].filter((sig) => !edgeSigsA.has(sig)).length;
4888
+ const edgesRemoved = [...edgeSigsA].filter((sig) => !edgeSigsB.has(sig)).length;
4889
+ const changes = [];
4890
+ if (blocksAdded.length > 0) changes.push(`+${blocksAdded.length} block${blocksAdded.length > 1 ? "s" : ""}`);
4891
+ if (blocksRemoved.length > 0) changes.push(`-${blocksRemoved.length} block${blocksRemoved.length > 1 ? "s" : ""}`);
4892
+ if (blocksModified.length > 0) changes.push(`~${blocksModified.length} modified`);
4893
+ if (edgesAdded > 0) changes.push(`+${edgesAdded} edge${edgesAdded > 1 ? "s" : ""}`);
4894
+ if (edgesRemoved > 0) changes.push(`-${edgesRemoved} edge${edgesRemoved > 1 ? "s" : ""}`);
4895
+ return {
4896
+ blocksAdded,
4897
+ blocksRemoved,
4898
+ blocksModified,
4899
+ edgesAdded,
4900
+ edgesRemoved,
4901
+ summary: changes.length > 0 ? changes.join(", ") : "No changes detected"
4902
+ };
4903
+ }
4751
4904
  function N8nWorkflowSummary({ workflow, showFlow = false }) {
4752
4905
  const nodes = workflow.nodes || [];
4753
4906
  const triggerNode = nodes.find(
@@ -4969,6 +5122,12 @@ function WorkflowViewer({
4969
5122
  const [pushingToSim, setPushingToSim] = useState(false);
4970
5123
  const [pullingFromSim, setPullingFromSim] = useState(false);
4971
5124
  const [switchingPlatform, setSwitchingPlatform] = useState(false);
5125
+ const [diffMode, setDiffMode] = useState(false);
5126
+ const [selectedBackupA, setSelectedBackupA] = useState(null);
5127
+ const [selectedBackupB, setSelectedBackupB] = useState(null);
5128
+ const [backupStateA, setBackupStateA] = useState(null);
5129
+ const [backupStateB, setBackupStateB] = useState(null);
5130
+ const [loadingDiff, setLoadingDiff] = useState(false);
4972
5131
  const [localPlatform, setLocalPlatform] = useState(platform);
4973
5132
  const [localIsActive, setLocalIsActive] = useState(isActive ?? true);
4974
5133
  const hasUnsavedChanges = localPlatform !== platform || localIsActive !== (isActive ?? true);
@@ -5171,6 +5330,42 @@ function WorkflowViewer({
5171
5330
  setViewMode("backups");
5172
5331
  loadBackups();
5173
5332
  }
5333
+ async function loadBackupForDiff(backupId, slot) {
5334
+ if (!workflowDefinitionId || !apiHandlers?.getBackupState) {
5335
+ return;
5336
+ }
5337
+ setLoadingDiff(true);
5338
+ try {
5339
+ const result = await apiHandlers.getBackupState(workflowDefinitionId, backupId);
5340
+ if (result.success && result.backup) {
5341
+ if (slot === "A") {
5342
+ setBackupStateA(result.backup.state);
5343
+ setSelectedBackupA(backupId);
5344
+ } else {
5345
+ setBackupStateB(result.backup.state);
5346
+ setSelectedBackupB(backupId);
5347
+ }
5348
+ }
5349
+ } catch {
5350
+ console.error("Failed to load backup for diff");
5351
+ } finally {
5352
+ setLoadingDiff(false);
5353
+ }
5354
+ }
5355
+ function startDiffMode() {
5356
+ setDiffMode(true);
5357
+ setSelectedBackupA(null);
5358
+ setSelectedBackupB(null);
5359
+ setBackupStateA(null);
5360
+ setBackupStateB(null);
5361
+ }
5362
+ function exitDiffMode() {
5363
+ setDiffMode(false);
5364
+ setSelectedBackupA(null);
5365
+ setSelectedBackupB(null);
5366
+ setBackupStateA(null);
5367
+ setBackupStateB(null);
5368
+ }
5174
5369
  function openInSimStudio() {
5175
5370
  if (simStudioUrl && simWorkflowId) {
5176
5371
  window.open(`${simStudioUrl}/w/${simWorkflowId}`, "_blank");
@@ -5427,7 +5622,7 @@ function WorkflowViewer({
5427
5622
  ]
5428
5623
  }
5429
5624
  ),
5430
- platform === "n8n" && /* @__PURE__ */ jsxs(
5625
+ /* @__PURE__ */ jsxs(
5431
5626
  "button",
5432
5627
  {
5433
5628
  onClick: () => setViewMode("flow"),
@@ -5551,14 +5746,15 @@ function WorkflowViewer({
5551
5746
  children: pushingToSim ? "Pushing..." : "Push to Sim"
5552
5747
  }
5553
5748
  ),
5554
- simWorkflowId && apiHandlers?.pullFromSim && /* @__PURE__ */ jsx(
5749
+ apiHandlers?.pullFromSim && /* @__PURE__ */ jsx(
5555
5750
  Button,
5556
5751
  {
5557
5752
  onClick: pullFromSim,
5558
- disabled: pullingFromSim,
5753
+ disabled: pullingFromSim || !simWorkflowId,
5559
5754
  variant: "outline",
5560
5755
  size: "sm",
5561
5756
  icon: /* @__PURE__ */ jsx(CloudArrowDown, { size: 16 }),
5757
+ title: !simWorkflowId ? "No Sim workflow linked yet. Push to Sim first." : void 0,
5562
5758
  children: pullingFromSim ? "Pulling..." : "Pull from Sim"
5563
5759
  }
5564
5760
  ),
@@ -5624,43 +5820,196 @@ function WorkflowViewer({
5624
5820
  )
5625
5821
  ] }),
5626
5822
  /* @__PURE__ */ jsx("pre", { className: "p-4 bg-[var(--black)] text-gray-100 text-xs overflow-auto max-h-[500px] font-mono", children: JSON.stringify(workflow, null, 2) })
5627
- ] }) : viewMode === "flow" ? platform === "n8n" && /* @__PURE__ */ jsx(Suspense, { fallback: loadingComponent || DefaultLoading, children: /* @__PURE__ */ jsx(WorkflowFlow2, { workflow, height: 380 }) }) : viewMode === "backups" ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
5823
+ ] }) : viewMode === "flow" ? /* @__PURE__ */ jsx(Suspense, { fallback: loadingComponent || DefaultLoading, children: /* @__PURE__ */ jsx(WorkflowFlow2, { workflow, height: 380, platform }) }) : viewMode === "backups" ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
5628
5824
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
5629
- /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium", children: "Backup History" }),
5630
- /* @__PURE__ */ jsx(
5631
- Button,
5632
- {
5633
- onClick: () => setViewMode("summary"),
5634
- variant: "outline",
5635
- size: "sm",
5636
- children: "Back to Summary"
5637
- }
5638
- )
5825
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium", children: diffMode ? "Compare Versions" : "Backup History" }),
5826
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5827
+ !diffMode && backups.length >= 2 && apiHandlers?.getBackupState && /* @__PURE__ */ jsx(
5828
+ Button,
5829
+ {
5830
+ onClick: startDiffMode,
5831
+ variant: "outline",
5832
+ size: "sm",
5833
+ icon: /* @__PURE__ */ jsx(ArrowsLeftRight, { size: 14 }),
5834
+ children: "Compare"
5835
+ }
5836
+ ),
5837
+ diffMode && /* @__PURE__ */ jsx(
5838
+ Button,
5839
+ {
5840
+ onClick: exitDiffMode,
5841
+ variant: "outline",
5842
+ size: "sm",
5843
+ icon: /* @__PURE__ */ jsx(X, { size: 14 }),
5844
+ children: "Cancel"
5845
+ }
5846
+ ),
5847
+ /* @__PURE__ */ jsx(
5848
+ Button,
5849
+ {
5850
+ onClick: () => {
5851
+ exitDiffMode();
5852
+ setViewMode("summary");
5853
+ },
5854
+ variant: "outline",
5855
+ size: "sm",
5856
+ children: "Back to Summary"
5857
+ }
5858
+ )
5859
+ ] })
5639
5860
  ] }),
5640
- loadingBackups ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-muted-foreground", children: "Loading backups..." }) : backups.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "py-8 text-center text-muted-foreground", children: [
5861
+ diffMode && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
5862
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
5863
+ /* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/50 rounded border border-border", children: [
5864
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "From (older)" }),
5865
+ /* @__PURE__ */ jsxs(
5866
+ Select,
5867
+ {
5868
+ value: selectedBackupA || "",
5869
+ onChange: (e) => loadBackupForDiff(e.target.value, "A"),
5870
+ disabled: loadingDiff,
5871
+ className: "w-full",
5872
+ children: [
5873
+ /* @__PURE__ */ jsx("option", { value: "", children: "Select version..." }),
5874
+ backups.map((b) => /* @__PURE__ */ jsxs("option", { value: b.id, disabled: b.id === selectedBackupB, children: [
5875
+ "v",
5876
+ b.version,
5877
+ " - ",
5878
+ b.versionLabel || b.workflowName
5879
+ ] }, b.id))
5880
+ ]
5881
+ }
5882
+ )
5883
+ ] }),
5884
+ /* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/50 rounded border border-border", children: [
5885
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "To (newer)" }),
5886
+ /* @__PURE__ */ jsxs(
5887
+ Select,
5888
+ {
5889
+ value: selectedBackupB || "",
5890
+ onChange: (e) => loadBackupForDiff(e.target.value, "B"),
5891
+ disabled: loadingDiff,
5892
+ className: "w-full",
5893
+ children: [
5894
+ /* @__PURE__ */ jsx("option", { value: "", children: "Select version..." }),
5895
+ backups.map((b) => /* @__PURE__ */ jsxs("option", { value: b.id, disabled: b.id === selectedBackupA, children: [
5896
+ "v",
5897
+ b.version,
5898
+ " - ",
5899
+ b.versionLabel || b.workflowName
5900
+ ] }, b.id))
5901
+ ]
5902
+ }
5903
+ )
5904
+ ] })
5905
+ ] }),
5906
+ loadingDiff ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-muted-foreground", children: "Loading versions..." }) : backupStateA && backupStateB ? (() => {
5907
+ const diff = computeWorkflowDiff(backupStateA, backupStateB);
5908
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
5909
+ /* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/30 rounded border border-border", children: [
5910
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-2", children: "Changes Summary" }),
5911
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: diff.summary })
5912
+ ] }),
5913
+ diff.blocksAdded.length > 0 && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-green-500/10 rounded border border-green-500/30", children: [
5914
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-green-600 dark:text-green-400 mb-2", children: [
5915
+ /* @__PURE__ */ jsx(Plus, { size: 14 }),
5916
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
5917
+ "Blocks Added (",
5918
+ diff.blocksAdded.length,
5919
+ ")"
5920
+ ] })
5921
+ ] }),
5922
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: diff.blocksAdded.map((id) => {
5923
+ const block = backupStateB?.blocks?.[id];
5924
+ return /* @__PURE__ */ jsx(Badge, { variant: "success", size: "sm", children: block?.name || id }, id);
5925
+ }) })
5926
+ ] }),
5927
+ diff.blocksRemoved.length > 0 && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-red-500/10 rounded border border-red-500/30", children: [
5928
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-red-600 dark:text-red-400 mb-2", children: [
5929
+ /* @__PURE__ */ jsx(Minus, { size: 14 }),
5930
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
5931
+ "Blocks Removed (",
5932
+ diff.blocksRemoved.length,
5933
+ ")"
5934
+ ] })
5935
+ ] }),
5936
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: diff.blocksRemoved.map((id) => {
5937
+ const block = backupStateA?.blocks?.[id];
5938
+ return /* @__PURE__ */ jsx(Badge, { variant: "error", size: "sm", children: block?.name || id }, id);
5939
+ }) })
5940
+ ] }),
5941
+ diff.blocksModified.length > 0 && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-amber-500/10 rounded border border-amber-500/30", children: [
5942
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-amber-600 dark:text-amber-400 mb-2", children: [
5943
+ /* @__PURE__ */ jsx(Pencil, { size: 14 }),
5944
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
5945
+ "Blocks Modified (",
5946
+ diff.blocksModified.length,
5947
+ ")"
5948
+ ] })
5949
+ ] }),
5950
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: diff.blocksModified.map((id) => {
5951
+ const block = backupStateB?.blocks?.[id];
5952
+ return /* @__PURE__ */ jsx(Badge, { variant: "warning", size: "sm", children: block?.name || id }, id);
5953
+ }) })
5954
+ ] }),
5955
+ (diff.edgesAdded > 0 || diff.edgesRemoved > 0) && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/30 rounded border border-border", children: [
5956
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Connection Changes" }),
5957
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 text-sm", children: [
5958
+ diff.edgesAdded > 0 && /* @__PURE__ */ jsxs("span", { className: "text-green-600 dark:text-green-400", children: [
5959
+ "+",
5960
+ diff.edgesAdded,
5961
+ " added"
5962
+ ] }),
5963
+ diff.edgesRemoved > 0 && /* @__PURE__ */ jsxs("span", { className: "text-red-600 dark:text-red-400", children: [
5964
+ "-",
5965
+ diff.edgesRemoved,
5966
+ " removed"
5967
+ ] })
5968
+ ] })
5969
+ ] })
5970
+ ] });
5971
+ })() : selectedBackupA || selectedBackupB ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-muted-foreground text-sm", children: "Select both versions to compare" }) : null
5972
+ ] }),
5973
+ !diffMode && /* @__PURE__ */ jsx(Fragment, { children: loadingBackups ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-muted-foreground", children: "Loading backups..." }) : backups.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "py-8 text-center text-muted-foreground", children: [
5641
5974
  /* @__PURE__ */ jsx(ClockCounterClockwise, { size: 32, className: "mx-auto mb-2 opacity-50" }),
5642
5975
  /* @__PURE__ */ jsx("p", { children: "No backups yet" }),
5643
5976
  /* @__PURE__ */ jsx("p", { className: "text-xs mt-1", children: 'Click "Export from Sim" to create a backup' })
5644
- ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: backups.map((backup) => /* @__PURE__ */ jsx(
5977
+ ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: backups.map((backup, index) => /* @__PURE__ */ jsxs(
5645
5978
  "div",
5646
5979
  {
5647
5980
  className: "flex items-center justify-between p-3 bg-muted/50 rounded border border-border",
5648
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
5649
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center w-8 h-8 rounded bg-primary/10 text-primary text-sm font-semibold", children: [
5650
- "v",
5651
- backup.version
5652
- ] }),
5653
- /* @__PURE__ */ jsxs("div", { children: [
5654
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: backup.versionLabel || backup.workflowName }),
5655
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
5656
- formatDistance(new Date(backup.exportedAt), { addSuffix: true }),
5657
- backup.isDeployed && /* @__PURE__ */ jsx(Badge, { variant: "success", size: "sm", className: "ml-2", children: "Deployed" })
5981
+ children: [
5982
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
5983
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center w-8 h-8 rounded bg-primary/10 text-primary text-sm font-semibold", children: [
5984
+ "v",
5985
+ backup.version
5986
+ ] }),
5987
+ /* @__PURE__ */ jsxs("div", { children: [
5988
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: backup.versionLabel || backup.workflowName }),
5989
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
5990
+ formatDistance(new Date(backup.exportedAt), { addSuffix: true }),
5991
+ backup.isDeployed && /* @__PURE__ */ jsx(Badge, { variant: "success", size: "sm", className: "ml-2", children: "Deployed" })
5992
+ ] })
5658
5993
  ] })
5659
- ] })
5660
- ] })
5994
+ ] }),
5995
+ index < backups.length - 1 && apiHandlers?.getBackupState && /* @__PURE__ */ jsx(
5996
+ Button,
5997
+ {
5998
+ variant: "ghost",
5999
+ size: "sm",
6000
+ onClick: () => {
6001
+ startDiffMode();
6002
+ loadBackupForDiff(backups[index + 1].id, "A");
6003
+ loadBackupForDiff(backup.id, "B");
6004
+ },
6005
+ icon: /* @__PURE__ */ jsx(ArrowsLeftRight, { size: 14 }),
6006
+ children: "Diff"
6007
+ }
6008
+ )
6009
+ ]
5661
6010
  },
5662
6011
  backup.id
5663
- )) })
6012
+ )) }) })
5664
6013
  ] }) : platform === "n8n" ? /* @__PURE__ */ jsx(N8nWorkflowSummary, { workflow, showFlow: false }) : /* @__PURE__ */ jsx(SimWorkflowSummary, { workflow }) })
5665
6014
  ] })
5666
6015
  ] });