@marktoflow/gui 2.0.0-alpha.4 → 2.0.1

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 (194) hide show
  1. package/README.md +48 -170
  2. package/client.log +0 -0
  3. package/dist/client/assets/index-DQeR1ew6.css +1 -0
  4. package/dist/client/assets/index-LbIVPHbD.js +833 -0
  5. package/dist/client/assets/index-LbIVPHbD.js.map +1 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/client/marktoflow-logo.png +0 -0
  8. package/dist/server/{server/index.js → index.js} +53 -6
  9. package/dist/server/index.js.map +1 -0
  10. package/dist/server/routes/admin.js +95 -0
  11. package/dist/server/routes/admin.js.map +1 -0
  12. package/dist/server/{server/routes → routes}/ai.js +2 -2
  13. package/dist/server/{server/routes → routes}/ai.js.map +1 -1
  14. package/dist/server/routes/collaboration.js +104 -0
  15. package/dist/server/routes/collaboration.js.map +1 -0
  16. package/dist/server/routes/execute.js +230 -0
  17. package/dist/server/routes/execute.js.map +1 -0
  18. package/dist/server/routes/executions.js +125 -0
  19. package/dist/server/routes/executions.js.map +1 -0
  20. package/dist/server/routes/form.js +160 -0
  21. package/dist/server/routes/form.js.map +1 -0
  22. package/dist/server/routes/settings.js +90 -0
  23. package/dist/server/routes/settings.js.map +1 -0
  24. package/dist/server/routes/templates.js +106 -0
  25. package/dist/server/routes/templates.js.map +1 -0
  26. package/dist/server/routes/versions.js +101 -0
  27. package/dist/server/routes/versions.js.map +1 -0
  28. package/dist/server/{server/routes → routes}/workflows.js +37 -1
  29. package/dist/server/routes/workflows.js.map +1 -0
  30. package/dist/server/services/AIService.js +152 -0
  31. package/dist/server/services/AIService.js.map +1 -0
  32. package/dist/server/services/ExecutionManager.js +571 -0
  33. package/dist/server/services/ExecutionManager.js.map +1 -0
  34. package/dist/server/services/VersionService.js +65 -0
  35. package/dist/server/services/VersionService.js.map +1 -0
  36. package/dist/server/{server/services → services}/WorkflowService.js +166 -17
  37. package/dist/server/services/WorkflowService.js.map +1 -0
  38. package/dist/server/{server/services → services}/agents/copilot-provider.js +32 -0
  39. package/dist/server/services/agents/copilot-provider.js.map +1 -0
  40. package/dist/server/{server/websocket → websocket}/index.js +54 -0
  41. package/dist/server/websocket/index.js.map +1 -0
  42. package/dist/{server/shared → shared}/constants.js +9 -0
  43. package/dist/shared/constants.js.map +1 -0
  44. package/dist/shared/settings.js +51 -0
  45. package/dist/shared/settings.js.map +1 -0
  46. package/package.json +32 -14
  47. package/public/marktoflow-logo.png +0 -0
  48. package/scripts/flatten-dist.js +69 -0
  49. package/server.log +0 -0
  50. package/tests/integration/fixtures/test-workflow.md +6 -0
  51. package/.turbo/turbo-build.log +0 -26
  52. package/dist/client/assets/index-C90Y_aBX.js +0 -678
  53. package/dist/client/assets/index-C90Y_aBX.js.map +0 -1
  54. package/dist/client/assets/index-CRWeQ3NN.css +0 -1
  55. package/dist/server/server/index.js.map +0 -1
  56. package/dist/server/server/routes/execute.js +0 -63
  57. package/dist/server/server/routes/execute.js.map +0 -1
  58. package/dist/server/server/routes/workflows.js.map +0 -1
  59. package/dist/server/server/services/AIService.js +0 -69
  60. package/dist/server/server/services/AIService.js.map +0 -1
  61. package/dist/server/server/services/WorkflowService.js.map +0 -1
  62. package/dist/server/server/services/agents/copilot-provider.js.map +0 -1
  63. package/dist/server/server/websocket/index.js.map +0 -1
  64. package/dist/server/shared/constants.js.map +0 -1
  65. package/playwright.config.ts +0 -27
  66. package/postcss.config.js +0 -6
  67. package/src/client/App.tsx +0 -520
  68. package/src/client/components/Canvas/Canvas.tsx +0 -423
  69. package/src/client/components/Canvas/ExecutionOverlay.tsx +0 -847
  70. package/src/client/components/Canvas/ForEachNode.tsx +0 -128
  71. package/src/client/components/Canvas/IfElseNode.tsx +0 -126
  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/ParallelNode.tsx +0 -140
  75. package/src/client/components/Canvas/StepNode.tsx +0 -106
  76. package/src/client/components/Canvas/SubWorkflowNode.tsx +0 -141
  77. package/src/client/components/Canvas/SwitchNode.tsx +0 -164
  78. package/src/client/components/Canvas/Toolbar.tsx +0 -189
  79. package/src/client/components/Canvas/TransformNode.tsx +0 -185
  80. package/src/client/components/Canvas/TriggerNode.tsx +0 -128
  81. package/src/client/components/Canvas/TryCatchNode.tsx +0 -164
  82. package/src/client/components/Canvas/WhileNode.tsx +0 -129
  83. package/src/client/components/Canvas/index.ts +0 -24
  84. package/src/client/components/Editor/InputsEditor.tsx +0 -458
  85. package/src/client/components/Editor/NewStepWizard.tsx +0 -344
  86. package/src/client/components/Editor/StepEditor.tsx +0 -532
  87. package/src/client/components/Editor/YamlEditor.tsx +0 -160
  88. package/src/client/components/Panels/PropertiesPanel.tsx +0 -589
  89. package/src/client/components/Prompt/ChangePreview.tsx +0 -281
  90. package/src/client/components/Prompt/PromptHistoryPanel.tsx +0 -209
  91. package/src/client/components/Prompt/PromptInput.tsx +0 -108
  92. package/src/client/components/Sidebar/Sidebar.tsx +0 -343
  93. package/src/client/components/common/Breadcrumb.tsx +0 -40
  94. package/src/client/components/common/Button.tsx +0 -68
  95. package/src/client/components/common/ContextMenu.tsx +0 -202
  96. package/src/client/components/common/KeyboardShortcuts.tsx +0 -143
  97. package/src/client/components/common/Modal.tsx +0 -93
  98. package/src/client/components/common/Tabs.tsx +0 -57
  99. package/src/client/components/common/ThemeToggle.tsx +0 -63
  100. package/src/client/components/index.ts +0 -32
  101. package/src/client/hooks/index.ts +0 -4
  102. package/src/client/hooks/useAIPrompt.ts +0 -108
  103. package/src/client/hooks/useCanvas.ts +0 -247
  104. package/src/client/hooks/useWebSocket.ts +0 -164
  105. package/src/client/hooks/useWorkflow.ts +0 -138
  106. package/src/client/main.tsx +0 -10
  107. package/src/client/stores/canvasStore.ts +0 -348
  108. package/src/client/stores/editorStore.ts +0 -133
  109. package/src/client/stores/executionStore.ts +0 -440
  110. package/src/client/stores/index.ts +0 -4
  111. package/src/client/stores/layoutStore.ts +0 -103
  112. package/src/client/stores/navigationStore.ts +0 -49
  113. package/src/client/stores/promptStore.ts +0 -113
  114. package/src/client/stores/themeStore.ts +0 -75
  115. package/src/client/stores/workflowStore.ts +0 -177
  116. package/src/client/styles/globals.css +0 -346
  117. package/src/client/utils/cn.ts +0 -9
  118. package/src/client/utils/index.ts +0 -4
  119. package/src/client/utils/serviceIcons.tsx +0 -97
  120. package/src/client/utils/stepValidation.ts +0 -155
  121. package/src/client/utils/workflowToGraph.ts +0 -299
  122. package/src/server/index.ts +0 -114
  123. package/src/server/routes/ai.ts +0 -91
  124. package/src/server/routes/execute.ts +0 -71
  125. package/src/server/routes/tools.ts +0 -970
  126. package/src/server/routes/workflows.ts +0 -106
  127. package/src/server/services/AIService.ts +0 -105
  128. package/src/server/services/FileWatcher.ts +0 -69
  129. package/src/server/services/WorkflowService.ts +0 -441
  130. package/src/server/services/agents/claude-code-provider.ts +0 -320
  131. package/src/server/services/agents/claude-provider.ts +0 -248
  132. package/src/server/services/agents/codex-provider.ts +0 -398
  133. package/src/server/services/agents/copilot-provider.ts +0 -311
  134. package/src/server/services/agents/demo-provider.ts +0 -184
  135. package/src/server/services/agents/index.ts +0 -31
  136. package/src/server/services/agents/ollama-provider.ts +0 -267
  137. package/src/server/services/agents/prompts.ts +0 -509
  138. package/src/server/services/agents/registry.ts +0 -310
  139. package/src/server/services/agents/types.ts +0 -146
  140. package/src/server/websocket/index.ts +0 -104
  141. package/src/shared/constants.ts +0 -180
  142. package/src/shared/types.ts +0 -179
  143. package/tailwind.config.ts +0 -73
  144. package/tests/e2e/app.spec.ts +0 -90
  145. package/tests/e2e/canvas.spec.ts +0 -128
  146. package/tests/e2e/workflow.spec.ts +0 -185
  147. package/tests/integration/api.test.ts +0 -452
  148. package/tests/integration/testApp.ts +0 -31
  149. package/tests/setup.ts +0 -72
  150. package/tests/unit/ForEachNode.test.tsx +0 -218
  151. package/tests/unit/IfElseNode.test.tsx +0 -188
  152. package/tests/unit/ParallelNode.test.tsx +0 -264
  153. package/tests/unit/SwitchNode.test.tsx +0 -252
  154. package/tests/unit/TransformNode.test.tsx +0 -386
  155. package/tests/unit/TryCatchNode.test.tsx +0 -243
  156. package/tests/unit/WhileNode.test.tsx +0 -226
  157. package/tests/unit/canvasStore.test.ts +0 -502
  158. package/tests/unit/codexProvider.test.ts +0 -399
  159. package/tests/unit/components.test.tsx +0 -151
  160. package/tests/unit/executionStore.test.ts +0 -527
  161. package/tests/unit/layoutStore.test.ts +0 -194
  162. package/tests/unit/navigationStore.test.ts +0 -152
  163. package/tests/unit/serviceIcons.test.ts +0 -197
  164. package/tests/unit/stepValidation.test.ts +0 -226
  165. package/tests/unit/themeStore.test.ts +0 -141
  166. package/tests/unit/workflowToGraph.test.ts +0 -289
  167. package/tsconfig.json +0 -29
  168. package/tsconfig.server.json +0 -28
  169. package/vite.config.ts +0 -31
  170. package/vitest.config.ts +0 -26
  171. /package/dist/server/{server/routes → routes}/tools.js +0 -0
  172. /package/dist/server/{server/routes → routes}/tools.js.map +0 -0
  173. /package/dist/server/{server/services → services}/FileWatcher.js +0 -0
  174. /package/dist/server/{server/services → services}/FileWatcher.js.map +0 -0
  175. /package/dist/server/{server/services → services}/agents/claude-code-provider.js +0 -0
  176. /package/dist/server/{server/services → services}/agents/claude-code-provider.js.map +0 -0
  177. /package/dist/server/{server/services → services}/agents/claude-provider.js +0 -0
  178. /package/dist/server/{server/services → services}/agents/claude-provider.js.map +0 -0
  179. /package/dist/server/{server/services → services}/agents/codex-provider.js +0 -0
  180. /package/dist/server/{server/services → services}/agents/codex-provider.js.map +0 -0
  181. /package/dist/server/{server/services → services}/agents/demo-provider.js +0 -0
  182. /package/dist/server/{server/services → services}/agents/demo-provider.js.map +0 -0
  183. /package/dist/server/{server/services → services}/agents/index.js +0 -0
  184. /package/dist/server/{server/services → services}/agents/index.js.map +0 -0
  185. /package/dist/server/{server/services → services}/agents/ollama-provider.js +0 -0
  186. /package/dist/server/{server/services → services}/agents/ollama-provider.js.map +0 -0
  187. /package/dist/server/{server/services → services}/agents/prompts.js +0 -0
  188. /package/dist/server/{server/services → services}/agents/prompts.js.map +0 -0
  189. /package/dist/server/{server/services → services}/agents/registry.js +0 -0
  190. /package/dist/server/{server/services → services}/agents/registry.js.map +0 -0
  191. /package/dist/server/{server/services → services}/agents/types.js +0 -0
  192. /package/dist/server/{server/services → services}/agents/types.js.map +0 -0
  193. /package/dist/{server/shared → shared}/types.js +0 -0
  194. /package/dist/{server/shared → shared}/types.js.map +0 -0
@@ -1,847 +0,0 @@
1
- import { useState } from 'react';
2
- import {
3
- Play,
4
- Pause,
5
- SkipForward,
6
- Square,
7
- CheckCircle,
8
- XCircle,
9
- Loader2,
10
- ChevronDown,
11
- ChevronRight,
12
- Copy,
13
- Check,
14
- Bug,
15
- Circle,
16
- ArrowRight,
17
- ArrowDown,
18
- ArrowUp,
19
- Trash2,
20
- Plus,
21
- X,
22
- } from 'lucide-react';
23
- import { Button } from '../common/Button';
24
- import type { StepStatus, WorkflowStatus } from '@shared/types';
25
- import type { DebugState } from '../../stores/executionStore';
26
-
27
- interface ExecutionStep {
28
- stepId: string;
29
- stepName: string;
30
- status: StepStatus;
31
- duration?: number;
32
- error?: string;
33
- output?: unknown;
34
- outputVariable?: string;
35
- }
36
-
37
- interface ExecutionOverlayProps {
38
- isExecuting: boolean;
39
- isPaused: boolean;
40
- workflowStatus: WorkflowStatus;
41
- currentStepId: string | null;
42
- steps: ExecutionStep[];
43
- logs: string[];
44
- onPause: () => void;
45
- onResume: () => void;
46
- onStop: () => void;
47
- onStepOver: () => void;
48
- onClose: () => void;
49
- // Debug props
50
- debug?: DebugState;
51
- onToggleDebugMode?: () => void;
52
- onToggleBreakpoint?: (stepId: string) => void;
53
- onStepInto?: () => void;
54
- onStepOut?: () => void;
55
- onClearBreakpoints?: () => void;
56
- onAddWatchExpression?: (expression: string) => void;
57
- onRemoveWatchExpression?: (expression: string) => void;
58
- }
59
-
60
- export function ExecutionOverlay({
61
- isExecuting,
62
- isPaused,
63
- workflowStatus,
64
- currentStepId,
65
- steps,
66
- logs,
67
- onPause,
68
- onResume,
69
- onStop,
70
- onStepOver,
71
- onClose,
72
- // Debug props
73
- debug,
74
- onToggleDebugMode,
75
- onToggleBreakpoint,
76
- onStepInto,
77
- onStepOut,
78
- onClearBreakpoints,
79
- onAddWatchExpression,
80
- onRemoveWatchExpression,
81
- }: ExecutionOverlayProps) {
82
- const [activeTab, setActiveTab] = useState<'steps' | 'variables' | 'logs' | 'debug'>('steps');
83
- const isDebugEnabled = debug?.enabled ?? false;
84
-
85
- const completedSteps = steps.filter((s) => s.status === 'completed').length;
86
- const failedSteps = steps.filter((s) => s.status === 'failed').length;
87
- const progress = steps.length > 0 ? (completedSteps / steps.length) * 100 : 0;
88
-
89
- if (!isExecuting && workflowStatus === 'pending') {
90
- return null;
91
- }
92
-
93
- return (
94
- <div className="absolute bottom-20 left-4 right-4 z-20 bg-panel-bg border border-node-border rounded-lg shadow-xl max-h-[400px] flex flex-col">
95
- {/* Header */}
96
- <div className="flex items-center justify-between px-4 py-3 border-b border-node-border">
97
- <div className="flex items-center gap-3">
98
- <StatusIcon status={workflowStatus} />
99
- <div>
100
- <div className="text-sm font-medium text-white">
101
- {getStatusText(workflowStatus)}
102
- </div>
103
- <div className="text-xs text-gray-400">
104
- {completedSteps}/{steps.length} steps completed
105
- {failedSteps > 0 && ` • ${failedSteps} failed`}
106
- </div>
107
- </div>
108
- </div>
109
-
110
- {/* Controls */}
111
- <div className="flex items-center gap-2">
112
- {/* Debug mode toggle */}
113
- {onToggleDebugMode && (
114
- <Button
115
- variant={isDebugEnabled ? 'primary' : 'ghost'}
116
- size="sm"
117
- onClick={onToggleDebugMode}
118
- icon={<Bug className="w-4 h-4" />}
119
- title={isDebugEnabled ? 'Disable debug mode' : 'Enable debug mode'}
120
- >
121
- Debug
122
- </Button>
123
- )}
124
-
125
- {isExecuting && (
126
- <>
127
- {isPaused ? (
128
- <Button
129
- variant="secondary"
130
- size="sm"
131
- onClick={onResume}
132
- icon={<Play className="w-4 h-4" />}
133
- >
134
- Resume
135
- </Button>
136
- ) : (
137
- <Button
138
- variant="secondary"
139
- size="sm"
140
- onClick={onPause}
141
- icon={<Pause className="w-4 h-4" />}
142
- >
143
- Pause
144
- </Button>
145
- )}
146
-
147
- {/* Debug stepping controls */}
148
- {isDebugEnabled && isPaused && (
149
- <>
150
- <Button
151
- variant="secondary"
152
- size="sm"
153
- onClick={onStepOver}
154
- icon={<ArrowRight className="w-4 h-4" />}
155
- title="Step Over (F10)"
156
- >
157
- Over
158
- </Button>
159
- {onStepInto && (
160
- <Button
161
- variant="secondary"
162
- size="sm"
163
- onClick={onStepInto}
164
- icon={<ArrowDown className="w-4 h-4" />}
165
- title="Step Into (F11)"
166
- >
167
- Into
168
- </Button>
169
- )}
170
- {onStepOut && (
171
- <Button
172
- variant="secondary"
173
- size="sm"
174
- onClick={onStepOut}
175
- icon={<ArrowUp className="w-4 h-4" />}
176
- title="Step Out (Shift+F11)"
177
- >
178
- Out
179
- </Button>
180
- )}
181
- </>
182
- )}
183
-
184
- {/* Regular step control when not in debug mode */}
185
- {!isDebugEnabled && (
186
- <Button
187
- variant="secondary"
188
- size="sm"
189
- onClick={onStepOver}
190
- icon={<SkipForward className="w-4 h-4" />}
191
- disabled={!isPaused}
192
- >
193
- Step
194
- </Button>
195
- )}
196
-
197
- <Button
198
- variant="destructive"
199
- size="sm"
200
- onClick={onStop}
201
- icon={<Square className="w-4 h-4" />}
202
- >
203
- Stop
204
- </Button>
205
- </>
206
- )}
207
- {!isExecuting && (
208
- <Button variant="secondary" size="sm" onClick={onClose}>
209
- Close
210
- </Button>
211
- )}
212
- </div>
213
- </div>
214
-
215
- {/* Progress bar */}
216
- <div className="h-1 bg-node-bg">
217
- <div
218
- className={`h-full transition-all duration-300 ${
219
- workflowStatus === 'failed'
220
- ? 'bg-error'
221
- : workflowStatus === 'completed'
222
- ? 'bg-success'
223
- : 'bg-primary'
224
- }`}
225
- style={{ width: `${progress}%` }}
226
- />
227
- </div>
228
-
229
- {/* Tabs */}
230
- <div className="flex border-b border-node-border">
231
- <button
232
- onClick={() => setActiveTab('steps')}
233
- className={`px-4 py-2 text-sm font-medium transition-colors ${
234
- activeTab === 'steps'
235
- ? 'text-primary border-b-2 border-primary -mb-px'
236
- : 'text-gray-400 hover:text-white'
237
- }`}
238
- >
239
- Steps
240
- </button>
241
- <button
242
- onClick={() => setActiveTab('variables')}
243
- className={`px-4 py-2 text-sm font-medium transition-colors ${
244
- activeTab === 'variables'
245
- ? 'text-primary border-b-2 border-primary -mb-px'
246
- : 'text-gray-400 hover:text-white'
247
- }`}
248
- >
249
- Variables
250
- </button>
251
- <button
252
- onClick={() => setActiveTab('logs')}
253
- className={`px-4 py-2 text-sm font-medium transition-colors ${
254
- activeTab === 'logs'
255
- ? 'text-primary border-b-2 border-primary -mb-px'
256
- : 'text-gray-400 hover:text-white'
257
- }`}
258
- >
259
- Logs
260
- </button>
261
- {isDebugEnabled && (
262
- <button
263
- onClick={() => setActiveTab('debug')}
264
- className={`px-4 py-2 text-sm font-medium transition-colors flex items-center gap-1 ${
265
- activeTab === 'debug'
266
- ? 'text-primary border-b-2 border-primary -mb-px'
267
- : 'text-gray-400 hover:text-white'
268
- }`}
269
- >
270
- <Bug className="w-3 h-3" />
271
- Debug
272
- </button>
273
- )}
274
- </div>
275
-
276
- {/* Content */}
277
- <div className="flex-1 overflow-y-auto p-4">
278
- {activeTab === 'steps' && (
279
- <StepsList
280
- steps={steps}
281
- currentStepId={currentStepId}
282
- debugEnabled={isDebugEnabled}
283
- breakpoints={debug?.breakpoints}
284
- onToggleBreakpoint={onToggleBreakpoint}
285
- />
286
- )}
287
- {activeTab === 'variables' && (
288
- <VariableInspector steps={steps} />
289
- )}
290
- {activeTab === 'logs' && (
291
- <LogsViewer logs={logs} />
292
- )}
293
- {activeTab === 'debug' && isDebugEnabled && (
294
- <DebugPanel
295
- debug={debug!}
296
- onClearBreakpoints={onClearBreakpoints}
297
- onAddWatchExpression={onAddWatchExpression}
298
- onRemoveWatchExpression={onRemoveWatchExpression}
299
- />
300
- )}
301
- </div>
302
- </div>
303
- );
304
- }
305
-
306
- function StepsList({
307
- steps,
308
- currentStepId,
309
- debugEnabled,
310
- breakpoints,
311
- onToggleBreakpoint,
312
- }: {
313
- steps: ExecutionStep[];
314
- currentStepId: string | null;
315
- debugEnabled?: boolean;
316
- breakpoints?: Set<string>;
317
- onToggleBreakpoint?: (stepId: string) => void;
318
- }) {
319
- return (
320
- <div className="space-y-2">
321
- {steps.map((step) => {
322
- const hasBreakpoint = breakpoints?.has(step.stepId);
323
-
324
- return (
325
- <div
326
- key={step.stepId}
327
- className={`flex items-center gap-3 p-3 rounded-lg border ${
328
- step.stepId === currentStepId
329
- ? 'bg-primary/10 border-primary'
330
- : hasBreakpoint
331
- ? 'bg-error/10 border-error/50'
332
- : 'bg-node-bg border-node-border'
333
- }`}
334
- >
335
- {/* Breakpoint indicator/toggle */}
336
- {debugEnabled && onToggleBreakpoint && (
337
- <button
338
- onClick={() => onToggleBreakpoint(step.stepId)}
339
- className={`w-4 h-4 rounded-full flex items-center justify-center transition-colors ${
340
- hasBreakpoint
341
- ? 'bg-error'
342
- : 'bg-transparent border border-gray-500 hover:border-error hover:bg-error/20'
343
- }`}
344
- title={hasBreakpoint ? 'Remove breakpoint' : 'Add breakpoint'}
345
- >
346
- {hasBreakpoint && <Circle className="w-2 h-2 fill-current text-white" />}
347
- </button>
348
- )}
349
-
350
- <StepStatusIcon status={step.status} />
351
- <div className="flex-1 min-w-0">
352
- <div className="text-sm font-medium text-white truncate">
353
- {step.stepName || step.stepId}
354
- </div>
355
- {step.error && (
356
- <div className="text-xs text-error mt-1 truncate">{step.error}</div>
357
- )}
358
- </div>
359
- {step.duration !== undefined && (
360
- <div className="text-xs text-gray-400">{step.duration}ms</div>
361
- )}
362
- </div>
363
- );
364
- })}
365
- </div>
366
- );
367
- }
368
-
369
- function LogsViewer({ logs }: { logs: string[] }) {
370
- return (
371
- <div className="font-mono text-xs space-y-1">
372
- {logs.length === 0 ? (
373
- <div className="text-gray-500">No logs yet...</div>
374
- ) : (
375
- logs.map((log, index) => (
376
- <div key={index} className="text-gray-300">
377
- {log}
378
- </div>
379
- ))
380
- )}
381
- </div>
382
- );
383
- }
384
-
385
- function VariableInspector({ steps }: { steps: ExecutionStep[] }) {
386
- const [expandedSteps, setExpandedSteps] = useState<Set<string>>(new Set());
387
- const [copiedKey, setCopiedKey] = useState<string | null>(null);
388
-
389
- // Filter steps that have output data
390
- const stepsWithOutput = steps.filter(
391
- (step) => step.output !== undefined && step.outputVariable
392
- );
393
-
394
- const toggleStep = (stepId: string) => {
395
- setExpandedSteps((prev) => {
396
- const next = new Set(prev);
397
- if (next.has(stepId)) {
398
- next.delete(stepId);
399
- } else {
400
- next.add(stepId);
401
- }
402
- return next;
403
- });
404
- };
405
-
406
- const copyValue = async (key: string, value: unknown) => {
407
- try {
408
- const text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
409
- await navigator.clipboard.writeText(text);
410
- setCopiedKey(key);
411
- setTimeout(() => setCopiedKey(null), 2000);
412
- } catch (error) {
413
- console.error('Failed to copy:', error);
414
- }
415
- };
416
-
417
- if (stepsWithOutput.length === 0) {
418
- return (
419
- <div className="text-center py-8 text-gray-500 text-sm">
420
- No variables available yet.
421
- <br />
422
- <span className="text-xs">Variables will appear as steps complete.</span>
423
- </div>
424
- );
425
- }
426
-
427
- return (
428
- <div className="space-y-2">
429
- {stepsWithOutput.map((step) => {
430
- const isExpanded = expandedSteps.has(step.stepId);
431
- return (
432
- <div
433
- key={step.stepId}
434
- className="border border-node-border rounded-lg overflow-hidden"
435
- >
436
- {/* Variable Header */}
437
- <button
438
- onClick={() => toggleStep(step.stepId)}
439
- className="w-full flex items-center gap-2 px-3 py-2 bg-node-bg hover:bg-white/5 transition-colors"
440
- >
441
- {isExpanded ? (
442
- <ChevronDown className="w-4 h-4 text-gray-400" />
443
- ) : (
444
- <ChevronRight className="w-4 h-4 text-gray-400" />
445
- )}
446
- <code className="text-sm text-primary font-mono">
447
- {step.outputVariable}
448
- </code>
449
- <span className="text-xs text-gray-500 ml-auto">
450
- {getTypeLabel(step.output)}
451
- </span>
452
- </button>
453
-
454
- {/* Variable Value */}
455
- {isExpanded && (
456
- <div className="p-3 bg-panel-bg border-t border-node-border">
457
- <div className="flex items-start gap-2">
458
- <div className="flex-1 overflow-x-auto">
459
- <ValueRenderer
460
- value={step.output}
461
- onCopy={(key, val) => copyValue(key, val)}
462
- copiedKey={copiedKey}
463
- path={step.outputVariable || ''}
464
- />
465
- </div>
466
- <button
467
- onClick={() => copyValue(step.outputVariable || '', step.output)}
468
- className="p-1.5 hover:bg-white/10 rounded transition-colors"
469
- title="Copy entire value"
470
- >
471
- {copiedKey === step.outputVariable ? (
472
- <Check className="w-4 h-4 text-success" />
473
- ) : (
474
- <Copy className="w-4 h-4 text-gray-400" />
475
- )}
476
- </button>
477
- </div>
478
- </div>
479
- )}
480
- </div>
481
- );
482
- })}
483
- </div>
484
- );
485
- }
486
-
487
- function ValueRenderer({
488
- value,
489
- onCopy,
490
- copiedKey,
491
- path,
492
- depth = 0,
493
- }: {
494
- value: unknown;
495
- onCopy: (key: string, value: unknown) => void;
496
- copiedKey: string | null;
497
- path: string;
498
- depth?: number;
499
- }) {
500
- const [expanded, setExpanded] = useState(depth < 2);
501
-
502
- if (value === null) {
503
- return <span className="text-gray-500 font-mono text-xs">null</span>;
504
- }
505
-
506
- if (value === undefined) {
507
- return <span className="text-gray-500 font-mono text-xs">undefined</span>;
508
- }
509
-
510
- if (typeof value === 'boolean') {
511
- return (
512
- <span className={`font-mono text-xs ${value ? 'text-success' : 'text-error'}`}>
513
- {String(value)}
514
- </span>
515
- );
516
- }
517
-
518
- if (typeof value === 'number') {
519
- return <span className="text-warning font-mono text-xs">{value}</span>;
520
- }
521
-
522
- if (typeof value === 'string') {
523
- // Truncate long strings
524
- const displayValue = value.length > 200 ? value.substring(0, 200) + '...' : value;
525
- return (
526
- <span className="text-success font-mono text-xs">
527
- &quot;{displayValue}&quot;
528
- </span>
529
- );
530
- }
531
-
532
- if (Array.isArray(value)) {
533
- if (value.length === 0) {
534
- return <span className="text-gray-400 font-mono text-xs">[]</span>;
535
- }
536
-
537
- return (
538
- <div className="space-y-1">
539
- <button
540
- onClick={() => setExpanded(!expanded)}
541
- className="flex items-center gap-1 text-gray-400 hover:text-white transition-colors"
542
- >
543
- {expanded ? (
544
- <ChevronDown className="w-3 h-3" />
545
- ) : (
546
- <ChevronRight className="w-3 h-3" />
547
- )}
548
- <span className="text-xs font-mono">Array({value.length})</span>
549
- </button>
550
- {expanded && (
551
- <div className="ml-4 pl-2 border-l border-node-border space-y-1">
552
- {value.slice(0, 20).map((item, index) => (
553
- <div key={index} className="flex items-start gap-2">
554
- <span className="text-gray-500 font-mono text-xs">[{index}]:</span>
555
- <ValueRenderer
556
- value={item}
557
- onCopy={onCopy}
558
- copiedKey={copiedKey}
559
- path={`${path}[${index}]`}
560
- depth={depth + 1}
561
- />
562
- </div>
563
- ))}
564
- {value.length > 20 && (
565
- <div className="text-gray-500 text-xs">
566
- ... and {value.length - 20} more items
567
- </div>
568
- )}
569
- </div>
570
- )}
571
- </div>
572
- );
573
- }
574
-
575
- if (typeof value === 'object') {
576
- const entries = Object.entries(value);
577
- if (entries.length === 0) {
578
- return <span className="text-gray-400 font-mono text-xs">{'{}'}</span>;
579
- }
580
-
581
- return (
582
- <div className="space-y-1">
583
- <button
584
- onClick={() => setExpanded(!expanded)}
585
- className="flex items-center gap-1 text-gray-400 hover:text-white transition-colors"
586
- >
587
- {expanded ? (
588
- <ChevronDown className="w-3 h-3" />
589
- ) : (
590
- <ChevronRight className="w-3 h-3" />
591
- )}
592
- <span className="text-xs font-mono">Object({entries.length} keys)</span>
593
- </button>
594
- {expanded && (
595
- <div className="ml-4 pl-2 border-l border-node-border space-y-1">
596
- {entries.slice(0, 30).map(([key, val]) => (
597
- <div key={key} className="flex items-start gap-2">
598
- <span className="text-primary font-mono text-xs">{key}:</span>
599
- <ValueRenderer
600
- value={val}
601
- onCopy={onCopy}
602
- copiedKey={copiedKey}
603
- path={`${path}.${key}`}
604
- depth={depth + 1}
605
- />
606
- </div>
607
- ))}
608
- {entries.length > 30 && (
609
- <div className="text-gray-500 text-xs">
610
- ... and {entries.length - 30} more keys
611
- </div>
612
- )}
613
- </div>
614
- )}
615
- </div>
616
- );
617
- }
618
-
619
- return <span className="text-gray-400 font-mono text-xs">{String(value)}</span>;
620
- }
621
-
622
- function getTypeLabel(value: unknown): string {
623
- if (value === null) return 'null';
624
- if (value === undefined) return 'undefined';
625
- if (Array.isArray(value)) return `array[${value.length}]`;
626
- if (typeof value === 'object') return `object`;
627
- return typeof value;
628
- }
629
-
630
- function StatusIcon({ status }: { status: WorkflowStatus }) {
631
- switch (status) {
632
- case 'running':
633
- return <Loader2 className="w-5 h-5 text-primary animate-spin" />;
634
- case 'completed':
635
- return <CheckCircle className="w-5 h-5 text-success" />;
636
- case 'failed':
637
- return <XCircle className="w-5 h-5 text-error" />;
638
- case 'cancelled':
639
- return <Square className="w-5 h-5 text-gray-400" />;
640
- default:
641
- return <div className="w-5 h-5 rounded-full bg-gray-500" />;
642
- }
643
- }
644
-
645
- function StepStatusIcon({ status }: { status: StepStatus }) {
646
- switch (status) {
647
- case 'running':
648
- return <Loader2 className="w-4 h-4 text-warning animate-spin" />;
649
- case 'completed':
650
- return <CheckCircle className="w-4 h-4 text-success" />;
651
- case 'failed':
652
- return <XCircle className="w-4 h-4 text-error" />;
653
- case 'skipped':
654
- return <SkipForward className="w-4 h-4 text-gray-400" />;
655
- default:
656
- return <div className="w-4 h-4 rounded-full border-2 border-gray-500" />;
657
- }
658
- }
659
-
660
- function getStatusText(status: WorkflowStatus): string {
661
- switch (status) {
662
- case 'pending':
663
- return 'Pending';
664
- case 'running':
665
- return 'Executing Workflow...';
666
- case 'completed':
667
- return 'Workflow Completed';
668
- case 'failed':
669
- return 'Workflow Failed';
670
- case 'cancelled':
671
- return 'Workflow Cancelled';
672
- default:
673
- return 'Unknown';
674
- }
675
- }
676
-
677
- // Debug Panel Component
678
- function DebugPanel({
679
- debug,
680
- onClearBreakpoints,
681
- onAddWatchExpression,
682
- onRemoveWatchExpression,
683
- }: {
684
- debug: DebugState;
685
- onClearBreakpoints?: () => void;
686
- onAddWatchExpression?: (expression: string) => void;
687
- onRemoveWatchExpression?: (expression: string) => void;
688
- }) {
689
- const [newWatchExpr, setNewWatchExpr] = useState('');
690
-
691
- const handleAddWatch = () => {
692
- if (newWatchExpr.trim() && onAddWatchExpression) {
693
- onAddWatchExpression(newWatchExpr.trim());
694
- setNewWatchExpr('');
695
- }
696
- };
697
-
698
- return (
699
- <div className="space-y-4">
700
- {/* Breakpoints Section */}
701
- <div className="space-y-2">
702
- <div className="flex items-center justify-between">
703
- <h4 className="text-sm font-medium text-white flex items-center gap-2">
704
- <Circle className="w-3 h-3 text-error" />
705
- Breakpoints ({debug.breakpoints.size})
706
- </h4>
707
- {debug.breakpoints.size > 0 && onClearBreakpoints && (
708
- <button
709
- onClick={onClearBreakpoints}
710
- className="text-xs text-gray-400 hover:text-white flex items-center gap-1"
711
- >
712
- <Trash2 className="w-3 h-3" />
713
- Clear all
714
- </button>
715
- )}
716
- </div>
717
- <div className="bg-node-bg rounded-lg p-3">
718
- {debug.breakpoints.size === 0 ? (
719
- <div className="text-xs text-gray-500">
720
- No breakpoints set. Click the dot next to a step to add one.
721
- </div>
722
- ) : (
723
- <div className="space-y-1">
724
- {Array.from(debug.breakpoints).map((stepId) => (
725
- <div
726
- key={stepId}
727
- className="flex items-center gap-2 text-xs text-gray-300"
728
- >
729
- <Circle className="w-2 h-2 fill-current text-error" />
730
- <span className="font-mono">{stepId}</span>
731
- </div>
732
- ))}
733
- </div>
734
- )}
735
- </div>
736
- </div>
737
-
738
- {/* Call Stack Section */}
739
- <div className="space-y-2">
740
- <h4 className="text-sm font-medium text-white">Call Stack</h4>
741
- <div className="bg-node-bg rounded-lg p-3">
742
- {debug.callStack.length === 0 ? (
743
- <div className="text-xs text-gray-500">No active call stack</div>
744
- ) : (
745
- <div className="space-y-1">
746
- {debug.callStack.map((frame, index) => (
747
- <div
748
- key={index}
749
- className={`flex items-center gap-2 text-xs ${
750
- index === 0 ? 'text-primary' : 'text-gray-400'
751
- }`}
752
- >
753
- <ArrowRight className="w-3 h-3" />
754
- <span className="font-mono">{frame}</span>
755
- </div>
756
- ))}
757
- </div>
758
- )}
759
- </div>
760
- </div>
761
-
762
- {/* Watch Expressions Section */}
763
- <div className="space-y-2">
764
- <h4 className="text-sm font-medium text-white">Watch Expressions</h4>
765
- <div className="bg-node-bg rounded-lg p-3 space-y-2">
766
- {/* Add new watch expression */}
767
- {onAddWatchExpression && (
768
- <div className="flex items-center gap-2">
769
- <input
770
- type="text"
771
- value={newWatchExpr}
772
- onChange={(e) => setNewWatchExpr(e.target.value)}
773
- onKeyDown={(e) => e.key === 'Enter' && handleAddWatch()}
774
- placeholder="Add expression..."
775
- className="flex-1 bg-transparent border border-node-border rounded px-2 py-1 text-xs text-white placeholder-gray-500 focus:outline-none focus:border-primary"
776
- />
777
- <button
778
- onClick={handleAddWatch}
779
- disabled={!newWatchExpr.trim()}
780
- className="p-1 text-gray-400 hover:text-white disabled:opacity-50"
781
- >
782
- <Plus className="w-4 h-4" />
783
- </button>
784
- </div>
785
- )}
786
-
787
- {/* Watch list */}
788
- {debug.watchExpressions.length === 0 ? (
789
- <div className="text-xs text-gray-500">
790
- No watch expressions. Add an expression to monitor its value.
791
- </div>
792
- ) : (
793
- <div className="space-y-1">
794
- {debug.watchExpressions.map((expr) => (
795
- <div
796
- key={expr}
797
- className="flex items-center justify-between gap-2 text-xs group"
798
- >
799
- <div className="flex items-center gap-2 min-w-0">
800
- <span className="text-primary font-mono truncate">{expr}</span>
801
- <span className="text-gray-500">=</span>
802
- <span className="text-gray-300 font-mono truncate">
803
- (not evaluated)
804
- </span>
805
- </div>
806
- {onRemoveWatchExpression && (
807
- <button
808
- onClick={() => onRemoveWatchExpression(expr)}
809
- className="p-1 text-gray-500 hover:text-error opacity-0 group-hover:opacity-100 transition-opacity"
810
- >
811
- <X className="w-3 h-3" />
812
- </button>
813
- )}
814
- </div>
815
- ))}
816
- </div>
817
- )}
818
- </div>
819
- </div>
820
-
821
- {/* Debug State Info */}
822
- <div className="space-y-2">
823
- <h4 className="text-sm font-medium text-white">Debug State</h4>
824
- <div className="bg-node-bg rounded-lg p-3 text-xs space-y-1">
825
- <div className="flex items-center justify-between">
826
- <span className="text-gray-400">Current Step:</span>
827
- <span className="text-white font-mono">
828
- {debug.currentStepId || '(none)'}
829
- </span>
830
- </div>
831
- <div className="flex items-center justify-between">
832
- <span className="text-gray-400">Paused at Breakpoint:</span>
833
- <span className={debug.pausedAtBreakpoint ? 'text-error' : 'text-gray-500'}>
834
- {debug.pausedAtBreakpoint ? 'Yes' : 'No'}
835
- </span>
836
- </div>
837
- <div className="flex items-center justify-between">
838
- <span className="text-gray-400">Step Over Pending:</span>
839
- <span className={debug.stepOverPending ? 'text-warning' : 'text-gray-500'}>
840
- {debug.stepOverPending ? 'Yes' : 'No'}
841
- </span>
842
- </div>
843
- </div>
844
- </div>
845
- </div>
846
- );
847
- }