@marktoflow/gui 2.0.0-alpha.1 → 2.0.0-alpha.13

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 (172) hide show
  1. package/README.md +49 -3
  2. package/dist/client/assets/index-CM44OayM.js +704 -0
  3. package/dist/client/assets/index-CM44OayM.js.map +1 -0
  4. package/dist/client/assets/index-Dru63gi6.css +1 -0
  5. package/dist/client/index.html +2 -2
  6. package/dist/server/index.js +93 -33
  7. package/dist/server/index.js.map +1 -1
  8. package/dist/server/routes/ai.js +38 -1
  9. package/dist/server/routes/ai.js.map +1 -1
  10. package/dist/server/routes/execute.js +23 -22
  11. package/dist/server/routes/execute.js.map +1 -1
  12. package/dist/server/routes/executions.js +125 -0
  13. package/dist/server/routes/executions.js.map +1 -0
  14. package/dist/server/{server/routes → routes}/tools.js +406 -0
  15. package/dist/server/{server/routes → routes}/tools.js.map +1 -1
  16. package/dist/server/routes/workflows.js +41 -5
  17. package/dist/server/routes/workflows.js.map +1 -1
  18. package/dist/server/services/AIService.js +55 -202
  19. package/dist/server/services/AIService.js.map +1 -1
  20. package/dist/server/services/FileWatcher.js +0 -2
  21. package/dist/server/services/FileWatcher.js.map +1 -1
  22. package/dist/server/services/WorkflowService.js +199 -16
  23. package/dist/server/services/WorkflowService.js.map +1 -1
  24. package/dist/server/services/agents/codex-provider.js +270 -0
  25. package/dist/server/services/agents/codex-provider.js.map +1 -0
  26. package/dist/server/{server/services → services}/agents/prompts.js +27 -0
  27. package/dist/server/services/agents/prompts.js.map +1 -0
  28. package/dist/server/{server/services → services}/agents/registry.js +20 -0
  29. package/dist/server/services/agents/registry.js.map +1 -0
  30. package/dist/server/websocket/index.js +12 -0
  31. package/dist/server/websocket/index.js.map +1 -1
  32. package/package.json +19 -7
  33. package/scripts/flatten-dist.js +69 -0
  34. package/.turbo/turbo-build.log +0 -26
  35. package/.turbo/turbo-test.log +0 -22
  36. package/dist/client/assets/index-DwTI8opO.js +0 -608
  37. package/dist/client/assets/index-DwTI8opO.js.map +0 -1
  38. package/dist/client/assets/index-RoEdL6gO.css +0 -1
  39. package/dist/server/index.d.ts +0 -3
  40. package/dist/server/index.d.ts.map +0 -1
  41. package/dist/server/server/index.js +0 -95
  42. package/dist/server/server/index.js.map +0 -1
  43. package/dist/server/server/routes/ai.js +0 -87
  44. package/dist/server/server/routes/ai.js.map +0 -1
  45. package/dist/server/server/routes/execute.js +0 -63
  46. package/dist/server/server/routes/execute.js.map +0 -1
  47. package/dist/server/server/routes/workflows.js +0 -99
  48. package/dist/server/server/routes/workflows.js.map +0 -1
  49. package/dist/server/server/services/AIService.js +0 -69
  50. package/dist/server/server/services/AIService.js.map +0 -1
  51. package/dist/server/server/services/FileWatcher.js +0 -60
  52. package/dist/server/server/services/FileWatcher.js.map +0 -1
  53. package/dist/server/server/services/WorkflowService.js +0 -363
  54. package/dist/server/server/services/WorkflowService.js.map +0 -1
  55. package/dist/server/server/services/agents/prompts.js.map +0 -1
  56. package/dist/server/server/services/agents/registry.js.map +0 -1
  57. package/dist/server/server/websocket/index.js +0 -85
  58. package/dist/server/server/websocket/index.js.map +0 -1
  59. package/dist/server/services/AIService.d.ts +0 -30
  60. package/dist/server/services/AIService.d.ts.map +0 -1
  61. package/dist/server/services/FileWatcher.d.ts +0 -10
  62. package/dist/server/services/FileWatcher.d.ts.map +0 -1
  63. package/dist/server/services/WorkflowService.d.ts +0 -54
  64. package/dist/server/services/WorkflowService.d.ts.map +0 -1
  65. package/dist/server/websocket/index.d.ts +0 -10
  66. package/dist/server/websocket/index.d.ts.map +0 -1
  67. package/playwright.config.ts +0 -27
  68. package/postcss.config.js +0 -6
  69. package/src/client/App.tsx +0 -520
  70. package/src/client/components/Canvas/Canvas.tsx +0 -405
  71. package/src/client/components/Canvas/ExecutionOverlay.tsx +0 -847
  72. package/src/client/components/Canvas/NodeContextMenu.tsx +0 -188
  73. package/src/client/components/Canvas/OutputNode.tsx +0 -111
  74. package/src/client/components/Canvas/StepNode.tsx +0 -106
  75. package/src/client/components/Canvas/SubWorkflowNode.tsx +0 -141
  76. package/src/client/components/Canvas/Toolbar.tsx +0 -189
  77. package/src/client/components/Canvas/TriggerNode.tsx +0 -128
  78. package/src/client/components/Editor/InputsEditor.tsx +0 -458
  79. package/src/client/components/Editor/NewStepWizard.tsx +0 -344
  80. package/src/client/components/Editor/StepEditor.tsx +0 -532
  81. package/src/client/components/Editor/YamlEditor.tsx +0 -160
  82. package/src/client/components/Panels/PropertiesPanel.tsx +0 -589
  83. package/src/client/components/Prompt/ChangePreview.tsx +0 -281
  84. package/src/client/components/Prompt/PromptHistoryPanel.tsx +0 -209
  85. package/src/client/components/Prompt/PromptInput.tsx +0 -108
  86. package/src/client/components/Sidebar/Sidebar.tsx +0 -343
  87. package/src/client/components/common/Breadcrumb.tsx +0 -40
  88. package/src/client/components/common/Button.tsx +0 -68
  89. package/src/client/components/common/ContextMenu.tsx +0 -202
  90. package/src/client/components/common/KeyboardShortcuts.tsx +0 -143
  91. package/src/client/components/common/Modal.tsx +0 -93
  92. package/src/client/components/common/Tabs.tsx +0 -57
  93. package/src/client/components/common/ThemeToggle.tsx +0 -63
  94. package/src/client/components/index.ts +0 -32
  95. package/src/client/hooks/index.ts +0 -4
  96. package/src/client/hooks/useAIPrompt.ts +0 -108
  97. package/src/client/hooks/useCanvas.ts +0 -247
  98. package/src/client/hooks/useWebSocket.ts +0 -164
  99. package/src/client/hooks/useWorkflow.ts +0 -138
  100. package/src/client/main.tsx +0 -10
  101. package/src/client/stores/canvasStore.ts +0 -348
  102. package/src/client/stores/editorStore.ts +0 -133
  103. package/src/client/stores/executionStore.ts +0 -440
  104. package/src/client/stores/index.ts +0 -4
  105. package/src/client/stores/layoutStore.ts +0 -103
  106. package/src/client/stores/navigationStore.ts +0 -49
  107. package/src/client/stores/promptStore.ts +0 -113
  108. package/src/client/stores/themeStore.ts +0 -75
  109. package/src/client/stores/workflowStore.ts +0 -177
  110. package/src/client/styles/globals.css +0 -346
  111. package/src/client/utils/cn.ts +0 -9
  112. package/src/client/utils/index.ts +0 -4
  113. package/src/client/utils/serviceIcons.tsx +0 -64
  114. package/src/client/utils/stepValidation.ts +0 -155
  115. package/src/client/utils/workflowToGraph.ts +0 -299
  116. package/src/server/index.ts +0 -114
  117. package/src/server/routes/ai.ts +0 -91
  118. package/src/server/routes/execute.ts +0 -71
  119. package/src/server/routes/tools.ts +0 -564
  120. package/src/server/routes/workflows.ts +0 -106
  121. package/src/server/services/AIService.ts +0 -105
  122. package/src/server/services/FileWatcher.ts +0 -69
  123. package/src/server/services/WorkflowService.ts +0 -441
  124. package/src/server/services/agents/claude-code-provider.ts +0 -320
  125. package/src/server/services/agents/claude-provider.ts +0 -248
  126. package/src/server/services/agents/copilot-provider.ts +0 -311
  127. package/src/server/services/agents/demo-provider.ts +0 -184
  128. package/src/server/services/agents/index.ts +0 -31
  129. package/src/server/services/agents/ollama-provider.ts +0 -267
  130. package/src/server/services/agents/prompts.ts +0 -482
  131. package/src/server/services/agents/registry.ts +0 -289
  132. package/src/server/services/agents/types.ts +0 -146
  133. package/src/server/websocket/index.ts +0 -104
  134. package/src/shared/constants.ts +0 -180
  135. package/src/shared/types.ts +0 -179
  136. package/tailwind.config.ts +0 -73
  137. package/tests/e2e/app.spec.ts +0 -90
  138. package/tests/e2e/canvas.spec.ts +0 -128
  139. package/tests/e2e/workflow.spec.ts +0 -185
  140. package/tests/integration/api.test.ts +0 -250
  141. package/tests/integration/testApp.ts +0 -31
  142. package/tests/setup.ts +0 -37
  143. package/tests/unit/canvasStore.test.ts +0 -502
  144. package/tests/unit/components.test.tsx +0 -151
  145. package/tests/unit/executionStore.test.ts +0 -527
  146. package/tests/unit/layoutStore.test.ts +0 -194
  147. package/tests/unit/navigationStore.test.ts +0 -152
  148. package/tests/unit/stepValidation.test.ts +0 -226
  149. package/tests/unit/themeStore.test.ts +0 -141
  150. package/tests/unit/workflowToGraph.test.ts +0 -289
  151. package/tsconfig.json +0 -29
  152. package/tsconfig.server.json +0 -28
  153. package/vite.config.ts +0 -31
  154. package/vitest.config.ts +0 -26
  155. /package/dist/server/{server/services → services}/agents/claude-code-provider.js +0 -0
  156. /package/dist/server/{server/services → services}/agents/claude-code-provider.js.map +0 -0
  157. /package/dist/server/{server/services → services}/agents/claude-provider.js +0 -0
  158. /package/dist/server/{server/services → services}/agents/claude-provider.js.map +0 -0
  159. /package/dist/server/{server/services → services}/agents/copilot-provider.js +0 -0
  160. /package/dist/server/{server/services → services}/agents/copilot-provider.js.map +0 -0
  161. /package/dist/server/{server/services → services}/agents/demo-provider.js +0 -0
  162. /package/dist/server/{server/services → services}/agents/demo-provider.js.map +0 -0
  163. /package/dist/server/{server/services → services}/agents/index.js +0 -0
  164. /package/dist/server/{server/services → services}/agents/index.js.map +0 -0
  165. /package/dist/server/{server/services → services}/agents/ollama-provider.js +0 -0
  166. /package/dist/server/{server/services → services}/agents/ollama-provider.js.map +0 -0
  167. /package/dist/server/{server/services → services}/agents/types.js +0 -0
  168. /package/dist/server/{server/services → services}/agents/types.js.map +0 -0
  169. /package/dist/{server/shared → shared}/constants.js +0 -0
  170. /package/dist/{server/shared → shared}/constants.js.map +0 -0
  171. /package/dist/{server/shared → shared}/types.js +0 -0
  172. /package/dist/{server/shared → shared}/types.js.map +0 -0
@@ -1,405 +0,0 @@
1
- import { useCallback, useState, useRef, type DragEvent } from 'react';
2
- import {
3
- ReactFlow,
4
- Background,
5
- Controls,
6
- MiniMap,
7
- BackgroundVariant,
8
- useReactFlow,
9
- type NodeMouseHandler,
10
- type Node,
11
- } from '@xyflow/react';
12
- import { Edit, Copy, Trash2, Code, Play } from 'lucide-react';
13
- import { useCanvasStore } from '../../stores/canvasStore';
14
- import { useWorkflowStore } from '../../stores/workflowStore';
15
- import { StepNode } from './StepNode';
16
- import { SubWorkflowNode } from './SubWorkflowNode';
17
- import { TriggerNode } from './TriggerNode';
18
- import { OutputNode } from './OutputNode';
19
- import { StepEditor } from '../Editor/StepEditor';
20
- import { YamlViewer } from '../Editor/YamlEditor';
21
- import { Modal } from '../common/Modal';
22
- import {
23
- ContextMenu,
24
- ContextMenuContent,
25
- ContextMenuItem,
26
- ContextMenuSeparator,
27
- ContextMenuShortcut,
28
- ContextMenuTrigger,
29
- } from '../common/ContextMenu';
30
- import { useCanvas } from '../../hooks/useCanvas';
31
- import { type ToolDefinition } from '../Sidebar/Sidebar';
32
- import type { WorkflowStep } from '@shared/types';
33
-
34
- // Custom node types
35
- const nodeTypes = {
36
- step: StepNode,
37
- subworkflow: SubWorkflowNode,
38
- trigger: TriggerNode,
39
- output: OutputNode,
40
- };
41
-
42
- export function Canvas() {
43
- const { nodes, edges, onNodesChange, onEdgesChange, onConnect, setNodes } =
44
- useCanvasStore();
45
- const { autoLayout, deleteSelected, duplicateSelected } = useCanvas();
46
- const currentWorkflow = useWorkflowStore((s) => s.currentWorkflow);
47
- const { screenToFlowPosition } = useReactFlow();
48
-
49
- // Editor state
50
- const [editingStep, setEditingStep] = useState<WorkflowStep | null>(null);
51
- const [isEditorOpen, setIsEditorOpen] = useState(false);
52
- const [yamlViewStep, setYamlViewStep] = useState<WorkflowStep | null>(null);
53
- const [isYamlViewOpen, setIsYamlViewOpen] = useState(false);
54
-
55
- // Context menu state
56
- const [contextMenuNode, setContextMenuNode] = useState<Node | null>(null);
57
- const contextMenuRef = useRef<HTMLDivElement>(null);
58
-
59
- // Handle node double-click to open editor or drill down
60
- const onNodeDoubleClick: NodeMouseHandler = useCallback(
61
- (event, node) => {
62
- event.preventDefault();
63
-
64
- // Sub-workflow nodes handle their own double-click via the drill-down button
65
- // Don't open editor for special node types
66
- if (node.type === 'subworkflow' || node.type === 'trigger' || node.type === 'output') {
67
- return;
68
- }
69
-
70
- const step = currentWorkflow?.steps.find((s) => s.id === node.data.id);
71
- if (step) {
72
- setEditingStep(step);
73
- setIsEditorOpen(true);
74
- }
75
- },
76
- [currentWorkflow]
77
- );
78
-
79
- // Get the currently selected step node
80
- const getSelectedStep = useCallback((): WorkflowStep | null => {
81
- if (!currentWorkflow) return null;
82
- const selectedNode = nodes.find((n) => n.selected && n.type === 'step');
83
- if (!selectedNode) return null;
84
- return currentWorkflow.steps.find((s) => s.id === selectedNode.data.id) || null;
85
- }, [currentWorkflow, nodes]);
86
-
87
- // Get undo/redo and copy/paste functions from canvas store
88
- const { undo, redo, canUndo, canRedo, copySelected, paste, canPaste } = useCanvasStore();
89
-
90
- // Handle keyboard shortcuts
91
- const onKeyDown = useCallback(
92
- (event: React.KeyboardEvent) => {
93
- const isMeta = event.metaKey || event.ctrlKey;
94
-
95
- // Delete selected nodes
96
- if (event.key === 'Backspace' || event.key === 'Delete') {
97
- deleteSelected();
98
- }
99
- // Duplicate selected nodes
100
- if (isMeta && event.key === 'd') {
101
- event.preventDefault();
102
- duplicateSelected();
103
- }
104
- // Auto-layout
105
- if (isMeta && event.key === 'l') {
106
- event.preventDefault();
107
- autoLayout();
108
- }
109
- // Undo (Cmd/Ctrl + Z)
110
- if (isMeta && event.key === 'z' && !event.shiftKey) {
111
- event.preventDefault();
112
- if (canUndo()) {
113
- undo();
114
- }
115
- }
116
- // Redo (Cmd/Ctrl + Shift + Z or Cmd/Ctrl + Y)
117
- if ((isMeta && event.shiftKey && event.key === 'z') || (isMeta && event.key === 'y')) {
118
- event.preventDefault();
119
- if (canRedo()) {
120
- redo();
121
- }
122
- }
123
- // Copy (Cmd/Ctrl + C)
124
- if (isMeta && event.key === 'c') {
125
- event.preventDefault();
126
- copySelected();
127
- }
128
- // Paste (Cmd/Ctrl + V)
129
- if (isMeta && event.key === 'v') {
130
- event.preventDefault();
131
- if (canPaste()) {
132
- paste();
133
- }
134
- }
135
- // Edit selected step (E key without modifiers)
136
- if (event.key === 'e' && !isMeta && !event.shiftKey && !event.altKey) {
137
- event.preventDefault();
138
- const step = getSelectedStep();
139
- if (step) {
140
- setEditingStep(step);
141
- setIsEditorOpen(true);
142
- }
143
- }
144
- // View YAML (Y key without modifiers)
145
- if (event.key === 'y' && !isMeta && !event.shiftKey && !event.altKey) {
146
- event.preventDefault();
147
- const step = getSelectedStep();
148
- if (step) {
149
- setYamlViewStep(step);
150
- setIsYamlViewOpen(true);
151
- }
152
- }
153
- },
154
- [deleteSelected, duplicateSelected, autoLayout, getSelectedStep, undo, redo, canUndo, canRedo, copySelected, paste, canPaste]
155
- );
156
-
157
- // Handle step save
158
- const handleStepSave = useCallback(
159
- (updatedStep: WorkflowStep) => {
160
- // TODO: Update workflow through store
161
- console.log('Saving step:', updatedStep);
162
- setIsEditorOpen(false);
163
- setEditingStep(null);
164
- },
165
- []
166
- );
167
-
168
- // Context menu handlers
169
- const handleContextEdit = useCallback(() => {
170
- if (!contextMenuNode || !currentWorkflow) return;
171
- const step = currentWorkflow.steps.find((s) => s.id === contextMenuNode.data.id);
172
- if (step) {
173
- setEditingStep(step);
174
- setIsEditorOpen(true);
175
- }
176
- setContextMenuNode(null);
177
- }, [contextMenuNode, currentWorkflow]);
178
-
179
- const handleContextViewYaml = useCallback(() => {
180
- if (!contextMenuNode || !currentWorkflow) return;
181
- const step = currentWorkflow.steps.find((s) => s.id === contextMenuNode.data.id);
182
- if (step) {
183
- setYamlViewStep(step);
184
- setIsYamlViewOpen(true);
185
- }
186
- setContextMenuNode(null);
187
- }, [contextMenuNode, currentWorkflow]);
188
-
189
- const handleContextDuplicate = useCallback(() => {
190
- if (contextMenuNode) {
191
- // Select the node first, then duplicate
192
- duplicateSelected();
193
- }
194
- setContextMenuNode(null);
195
- }, [contextMenuNode, duplicateSelected]);
196
-
197
- const handleContextDelete = useCallback(() => {
198
- if (contextMenuNode) {
199
- deleteSelected();
200
- }
201
- setContextMenuNode(null);
202
- }, [contextMenuNode, deleteSelected]);
203
-
204
- const handleContextExecute = useCallback(() => {
205
- if (contextMenuNode) {
206
- console.log('Execute step:', contextMenuNode.data.id);
207
- // TODO: Implement single step execution
208
- }
209
- setContextMenuNode(null);
210
- }, [contextMenuNode]);
211
-
212
- // Handle right-click on node
213
- const onNodeContextMenu = useCallback(
214
- (event: React.MouseEvent, node: Node) => {
215
- event.preventDefault();
216
- // Only show context menu for step nodes
217
- if (node.type === 'step') {
218
- setContextMenuNode(node);
219
- }
220
- },
221
- []
222
- );
223
-
224
- // Handle drag over for drop target
225
- const onDragOver = useCallback((event: DragEvent) => {
226
- event.preventDefault();
227
- event.dataTransfer.dropEffect = 'copy';
228
- }, []);
229
-
230
- // Handle drop from tools palette
231
- const onDrop = useCallback(
232
- (event: DragEvent) => {
233
- event.preventDefault();
234
-
235
- const toolData = event.dataTransfer.getData('application/marktoflow-tool');
236
- if (!toolData) return;
237
-
238
- try {
239
- const tool: ToolDefinition = JSON.parse(toolData);
240
-
241
- // Get the position where the node was dropped
242
- const position = screenToFlowPosition({
243
- x: event.clientX,
244
- y: event.clientY,
245
- });
246
-
247
- // Create a new node
248
- const newId = tool.id + '-' + Date.now().toString(36);
249
- const newNode: Node = {
250
- id: newId,
251
- type: 'step',
252
- position,
253
- data: {
254
- id: newId,
255
- name: tool.name + ' Action',
256
- action: tool.id + '.' + (tool.actions?.[0] || 'action'),
257
- status: 'pending',
258
- },
259
- };
260
-
261
- // Add the node to the canvas
262
- setNodes([...nodes, newNode]);
263
- } catch (e) {
264
- console.error('Failed to parse dropped tool data:', e);
265
- }
266
- },
267
- [nodes, setNodes, screenToFlowPosition]
268
- );
269
-
270
- // Get available variables for the editing step
271
- const getAvailableVariables = useCallback((): string[] => {
272
- if (!currentWorkflow || !editingStep) return [];
273
-
274
- const variables: string[] = [];
275
-
276
- // Add input variables
277
- if (currentWorkflow.inputs) {
278
- for (const key of Object.keys(currentWorkflow.inputs)) {
279
- variables.push(`inputs.${key}`);
280
- }
281
- }
282
-
283
- // Add output variables from steps before the editing step
284
- const stepIndex = currentWorkflow.steps.findIndex(
285
- (s) => s.id === editingStep.id
286
- );
287
- for (let i = 0; i < stepIndex; i++) {
288
- const step = currentWorkflow.steps[i];
289
- if (step.outputVariable) {
290
- variables.push(step.outputVariable);
291
- }
292
- }
293
-
294
- return variables;
295
- }, [currentWorkflow, editingStep]);
296
-
297
- return (
298
- <ContextMenu>
299
- <ContextMenuTrigger asChild>
300
- <div
301
- ref={contextMenuRef}
302
- className="w-full h-full"
303
- onKeyDown={onKeyDown}
304
- onDragOver={onDragOver}
305
- onDrop={onDrop}
306
- tabIndex={0}
307
- >
308
- <ReactFlow
309
- nodes={nodes}
310
- edges={edges}
311
- onNodesChange={onNodesChange}
312
- onEdgesChange={onEdgesChange}
313
- onConnect={onConnect}
314
- onNodeDoubleClick={onNodeDoubleClick}
315
- onNodeContextMenu={onNodeContextMenu}
316
- nodeTypes={nodeTypes}
317
- fitView
318
- snapToGrid
319
- snapGrid={[16, 16]}
320
- defaultEdgeOptions={{
321
- type: 'smoothstep',
322
- animated: true,
323
- style: { stroke: '#ff6d5a', strokeWidth: 2 },
324
- }}
325
- proOptions={{ hideAttribution: true }}
326
- >
327
- <Background
328
- variant={BackgroundVariant.Dots}
329
- gap={24}
330
- size={1}
331
- color="#3d3d5c"
332
- />
333
- <Controls />
334
- <MiniMap
335
- nodeColor={(node) => {
336
- switch (node.data?.status) {
337
- case 'running':
338
- return '#f0ad4e';
339
- case 'completed':
340
- return '#5cb85c';
341
- case 'failed':
342
- return '#d9534f';
343
- default:
344
- return '#2d2d4a';
345
- }
346
- }}
347
- maskColor="rgba(26, 26, 46, 0.8)"
348
- />
349
- </ReactFlow>
350
- </div>
351
- </ContextMenuTrigger>
352
-
353
- {/* Node Context Menu */}
354
- <ContextMenuContent>
355
- <ContextMenuItem onClick={handleContextEdit}>
356
- <Edit className="w-4 h-4 mr-2" />
357
- Edit Step
358
- <ContextMenuShortcut>E</ContextMenuShortcut>
359
- </ContextMenuItem>
360
- <ContextMenuItem onClick={handleContextViewYaml}>
361
- <Code className="w-4 h-4 mr-2" />
362
- View YAML
363
- <ContextMenuShortcut>Y</ContextMenuShortcut>
364
- </ContextMenuItem>
365
- <ContextMenuSeparator />
366
- <ContextMenuItem onClick={handleContextExecute}>
367
- <Play className="w-4 h-4 mr-2" />
368
- Execute Step
369
- </ContextMenuItem>
370
- <ContextMenuSeparator />
371
- <ContextMenuItem onClick={handleContextDuplicate}>
372
- <Copy className="w-4 h-4 mr-2" />
373
- Duplicate
374
- <ContextMenuShortcut>⌘D</ContextMenuShortcut>
375
- </ContextMenuItem>
376
- <ContextMenuItem onClick={handleContextDelete} destructive>
377
- <Trash2 className="w-4 h-4 mr-2" />
378
- Delete
379
- <ContextMenuShortcut>⌫</ContextMenuShortcut>
380
- </ContextMenuItem>
381
- </ContextMenuContent>
382
-
383
- {/* Step Editor Modal */}
384
- <StepEditor
385
- open={isEditorOpen}
386
- onOpenChange={setIsEditorOpen}
387
- step={editingStep}
388
- onSave={handleStepSave}
389
- availableVariables={getAvailableVariables()}
390
- />
391
-
392
- {/* YAML Viewer Modal */}
393
- <Modal
394
- open={isYamlViewOpen}
395
- onOpenChange={setIsYamlViewOpen}
396
- title={`YAML: ${yamlViewStep?.name || yamlViewStep?.id}`}
397
- size="lg"
398
- >
399
- <div className="p-4">
400
- <YamlViewer value={yamlViewStep} />
401
- </div>
402
- </Modal>
403
- </ContextMenu>
404
- );
405
- }