@marktoflow/gui 2.0.0-alpha.5 → 2.0.2

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