@marktoflow/gui 2.0.0-alpha.5 → 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.
- package/README.md +48 -180
- package/client.log +0 -0
- package/dist/client/assets/index-DQeR1ew6.css +1 -0
- package/dist/client/assets/index-LbIVPHbD.js +833 -0
- package/dist/client/assets/index-LbIVPHbD.js.map +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/marktoflow-logo.png +0 -0
- package/dist/server/index.js +31 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/admin.js +95 -0
- package/dist/server/routes/admin.js.map +1 -0
- package/dist/server/routes/ai.js +2 -2
- package/dist/server/routes/ai.js.map +1 -1
- package/dist/server/routes/collaboration.js +104 -0
- package/dist/server/routes/collaboration.js.map +1 -0
- package/dist/server/routes/execute.js +181 -14
- package/dist/server/routes/execute.js.map +1 -1
- package/dist/server/routes/form.js +160 -0
- package/dist/server/routes/form.js.map +1 -0
- package/dist/server/routes/settings.js +90 -0
- package/dist/server/routes/settings.js.map +1 -0
- package/dist/server/routes/templates.js +106 -0
- package/dist/server/routes/templates.js.map +1 -0
- package/dist/server/routes/versions.js +101 -0
- package/dist/server/routes/versions.js.map +1 -0
- package/dist/server/services/AIService.js +85 -2
- package/dist/server/services/AIService.js.map +1 -1
- package/dist/server/services/ExecutionManager.js +571 -0
- package/dist/server/services/ExecutionManager.js.map +1 -0
- package/dist/server/services/VersionService.js +65 -0
- package/dist/server/services/VersionService.js.map +1 -0
- package/dist/server/services/WorkflowService.js +8 -2
- package/dist/server/services/WorkflowService.js.map +1 -1
- package/dist/server/services/agents/copilot-provider.js +32 -0
- package/dist/server/services/agents/copilot-provider.js.map +1 -1
- package/dist/server/websocket/index.js +42 -0
- package/dist/server/websocket/index.js.map +1 -1
- package/dist/shared/constants.js +9 -0
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/settings.js +51 -0
- package/dist/shared/settings.js.map +1 -0
- package/package.json +14 -10
- package/public/marktoflow-logo.png +0 -0
- package/server.log +0 -0
- package/tests/integration/fixtures/test-workflow.md +6 -0
- package/.turbo/turbo-build.log +0 -42
- package/dist/client/assets/index-CM44OayM.js +0 -704
- package/dist/client/assets/index-CM44OayM.js.map +0 -1
- package/dist/client/assets/index-Dru63gi6.css +0 -1
- package/marktoflow-gui-2.0.0-alpha.5.tgz +0 -0
- package/playwright.config.ts +0 -27
- package/postcss.config.js +0 -6
- package/src/client/App.tsx +0 -520
- package/src/client/components/Canvas/Canvas.tsx +0 -425
- package/src/client/components/Canvas/ExecutionOverlay.tsx +0 -935
- package/src/client/components/Canvas/ForEachNode.tsx +0 -152
- package/src/client/components/Canvas/IfElseNode.tsx +0 -141
- package/src/client/components/Canvas/NodeContextMenu.tsx +0 -192
- package/src/client/components/Canvas/OutputNode.tsx +0 -111
- package/src/client/components/Canvas/ParallelNode.tsx +0 -157
- package/src/client/components/Canvas/StepNode.tsx +0 -106
- package/src/client/components/Canvas/SubWorkflowNode.tsx +0 -141
- package/src/client/components/Canvas/SwitchNode.tsx +0 -185
- package/src/client/components/Canvas/Toolbar.tsx +0 -227
- package/src/client/components/Canvas/TransformNode.tsx +0 -194
- package/src/client/components/Canvas/TriggerNode.tsx +0 -128
- package/src/client/components/Canvas/TryCatchNode.tsx +0 -164
- package/src/client/components/Canvas/WhileNode.tsx +0 -161
- package/src/client/components/Canvas/index.ts +0 -24
- package/src/client/components/Debug/VariableInspector.tsx +0 -148
- package/src/client/components/Editor/InputsEditor.tsx +0 -458
- package/src/client/components/Editor/NewStepWizard.tsx +0 -344
- package/src/client/components/Editor/StepEditor.tsx +0 -532
- package/src/client/components/Editor/YamlEditor.tsx +0 -160
- package/src/client/components/Panels/PropertiesPanel.tsx +0 -589
- package/src/client/components/Prompt/ChangePreview.tsx +0 -281
- package/src/client/components/Prompt/PromptHistoryPanel.tsx +0 -209
- package/src/client/components/Prompt/PromptInput.tsx +0 -110
- package/src/client/components/Settings/ProviderSwitcher.tsx +0 -228
- package/src/client/components/Sidebar/ImportDialog.tsx +0 -257
- package/src/client/components/Sidebar/Sidebar.tsx +0 -362
- package/src/client/components/common/Breadcrumb.tsx +0 -40
- package/src/client/components/common/Button.tsx +0 -68
- package/src/client/components/common/ContextMenu.tsx +0 -202
- package/src/client/components/common/KeyboardShortcuts.tsx +0 -149
- package/src/client/components/common/Modal.tsx +0 -93
- package/src/client/components/common/Tabs.tsx +0 -57
- package/src/client/components/common/ThemeToggle.tsx +0 -63
- package/src/client/components/index.ts +0 -32
- package/src/client/hooks/index.ts +0 -4
- package/src/client/hooks/useAIPrompt.ts +0 -108
- package/src/client/hooks/useCanvas.ts +0 -247
- package/src/client/hooks/useWebSocket.ts +0 -164
- package/src/client/hooks/useWorkflow.ts +0 -138
- package/src/client/main.tsx +0 -10
- package/src/client/stores/agentStore.ts +0 -109
- package/src/client/stores/canvasStore.ts +0 -348
- package/src/client/stores/editorStore.ts +0 -133
- package/src/client/stores/executionStore.ts +0 -502
- package/src/client/stores/index.ts +0 -4
- package/src/client/stores/layoutStore.ts +0 -103
- package/src/client/stores/navigationStore.ts +0 -49
- package/src/client/stores/promptStore.ts +0 -113
- package/src/client/stores/themeStore.ts +0 -75
- package/src/client/stores/workflowStore.ts +0 -185
- package/src/client/styles/globals.css +0 -452
- package/src/client/utils/cn.ts +0 -9
- package/src/client/utils/index.ts +0 -4
- package/src/client/utils/platform.ts +0 -46
- package/src/client/utils/serviceIcons.tsx +0 -97
- package/src/client/utils/stepValidation.ts +0 -155
- package/src/client/utils/workflowToGraph.ts +0 -523
- package/src/server/index.ts +0 -137
- package/src/server/routes/ai.ts +0 -91
- package/src/server/routes/execute.ts +0 -71
- package/src/server/routes/executions.ts +0 -136
- package/src/server/routes/tools.ts +0 -970
- package/src/server/routes/workflows.ts +0 -147
- package/src/server/services/AIService.ts +0 -105
- package/src/server/services/FileWatcher.ts +0 -69
- package/src/server/services/WorkflowService.ts +0 -601
- package/src/server/services/agents/claude-code-provider.ts +0 -320
- package/src/server/services/agents/claude-provider.ts +0 -248
- package/src/server/services/agents/codex-provider.ts +0 -398
- package/src/server/services/agents/copilot-provider.ts +0 -311
- package/src/server/services/agents/demo-provider.ts +0 -184
- package/src/server/services/agents/index.ts +0 -31
- package/src/server/services/agents/ollama-provider.ts +0 -267
- package/src/server/services/agents/prompts.ts +0 -509
- package/src/server/services/agents/registry.ts +0 -310
- package/src/server/services/agents/types.ts +0 -146
- package/src/server/websocket/index.ts +0 -117
- package/src/shared/constants.ts +0 -180
- package/src/shared/types.ts +0 -179
- package/tailwind.config.ts +0 -73
- package/tests/e2e/app.spec.ts +0 -90
- package/tests/e2e/canvas.spec.ts +0 -128
- package/tests/e2e/workflow.spec.ts +0 -185
- package/tests/integration/api.test.ts +0 -452
- package/tests/integration/testApp.ts +0 -31
- package/tests/setup.ts +0 -72
- package/tests/unit/ForEachNode.test.tsx +0 -308
- package/tests/unit/IfElseNode.test.tsx +0 -235
- package/tests/unit/ParallelNode.test.tsx +0 -344
- package/tests/unit/SwitchNode.test.tsx +0 -327
- package/tests/unit/TransformNode.test.tsx +0 -386
- package/tests/unit/TryCatchNode.test.tsx +0 -243
- package/tests/unit/WhileNode.test.tsx +0 -230
- package/tests/unit/agentStore.test.ts +0 -218
- package/tests/unit/canvasStore.test.ts +0 -502
- package/tests/unit/codexProvider.test.ts +0 -399
- package/tests/unit/components.test.tsx +0 -151
- package/tests/unit/executionStore.test.ts +0 -567
- package/tests/unit/layoutStore.test.ts +0 -194
- package/tests/unit/navigationStore.test.ts +0 -152
- package/tests/unit/platform.test.ts +0 -118
- package/tests/unit/serviceIcons.test.ts +0 -197
- package/tests/unit/stepValidation.test.ts +0 -226
- package/tests/unit/themeStore.test.ts +0 -141
- package/tests/unit/workflowToGraph.test.ts +0 -311
- package/tsconfig.json +0 -29
- package/tsconfig.server.json +0 -28
- package/vite.config.ts +0 -31
- package/vitest.config.ts +0 -26
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
import { Handle, Position, type Node, type NodeProps } from '@xyflow/react';
|
|
3
|
-
import { Repeat, CheckCircle, XCircle, Clock, LogOut } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
export interface ForEachNodeData extends Record<string, unknown> {
|
|
6
|
-
id: string;
|
|
7
|
-
name?: string;
|
|
8
|
-
items: string;
|
|
9
|
-
itemVariable?: string;
|
|
10
|
-
status?: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
11
|
-
currentIteration?: number;
|
|
12
|
-
totalIterations?: number;
|
|
13
|
-
earlyExit?: boolean;
|
|
14
|
-
exitReason?: 'break' | 'error';
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type ForEachNodeType = Node<ForEachNodeData, 'for_each'>;
|
|
18
|
-
|
|
19
|
-
function ForEachNodeComponent({ data, selected }: NodeProps<ForEachNodeType>) {
|
|
20
|
-
const statusConfig: Record<
|
|
21
|
-
NonNullable<ForEachNodeData['status']>,
|
|
22
|
-
{ icon: typeof Clock; color: string; bgColor: string; animate?: boolean }
|
|
23
|
-
> = {
|
|
24
|
-
pending: { icon: Clock, color: 'text-gray-400', bgColor: 'bg-gray-400/10' },
|
|
25
|
-
running: {
|
|
26
|
-
icon: Repeat,
|
|
27
|
-
color: 'text-orange-400',
|
|
28
|
-
bgColor: 'bg-orange-400/10',
|
|
29
|
-
animate: true,
|
|
30
|
-
},
|
|
31
|
-
completed: {
|
|
32
|
-
icon: CheckCircle,
|
|
33
|
-
color: 'text-success',
|
|
34
|
-
bgColor: 'bg-success/10',
|
|
35
|
-
},
|
|
36
|
-
failed: { icon: XCircle, color: 'text-error', bgColor: 'bg-error/10' },
|
|
37
|
-
skipped: {
|
|
38
|
-
icon: XCircle,
|
|
39
|
-
color: 'text-gray-500',
|
|
40
|
-
bgColor: 'bg-gray-500/10',
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const status = data.status || 'pending';
|
|
45
|
-
const config = statusConfig[status];
|
|
46
|
-
const StatusIcon = config.icon;
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<div
|
|
50
|
-
className={`control-flow-node for-each-node p-0 ${selected ? 'selected' : ''} ${status === 'running' ? 'running' : ''} ${status === 'completed' ? 'completed' : ''} ${status === 'failed' ? 'failed' : ''}`}
|
|
51
|
-
style={{
|
|
52
|
-
background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
|
|
53
|
-
}}
|
|
54
|
-
>
|
|
55
|
-
{/* Input handle */}
|
|
56
|
-
<Handle
|
|
57
|
-
type="target"
|
|
58
|
-
position={Position.Top}
|
|
59
|
-
className="!w-3 !h-3 !bg-primary !border-2 !border-node-bg"
|
|
60
|
-
/>
|
|
61
|
-
|
|
62
|
-
{/* Node header */}
|
|
63
|
-
<div className="flex items-center gap-3 p-3 border-b border-white/20">
|
|
64
|
-
<div className="w-8 h-8 rounded-lg bg-white/20 flex items-center justify-center">
|
|
65
|
-
<Repeat className="w-5 h-5 text-white" />
|
|
66
|
-
</div>
|
|
67
|
-
<div className="flex-1 min-w-0">
|
|
68
|
-
<div className="text-sm font-medium text-white">
|
|
69
|
-
{data.name || 'For Each'}
|
|
70
|
-
</div>
|
|
71
|
-
<div className="text-xs text-white/70">Loop</div>
|
|
72
|
-
</div>
|
|
73
|
-
<div
|
|
74
|
-
className={`w-6 h-6 rounded-full ${config.bgColor} flex items-center justify-center`}
|
|
75
|
-
>
|
|
76
|
-
<StatusIcon
|
|
77
|
-
className={`w-4 h-4 ${config.color} ${config.animate ? 'animate-pulse' : ''}`}
|
|
78
|
-
/>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
|
|
82
|
-
{/* Node body */}
|
|
83
|
-
<div className="p-3 bg-white/10">
|
|
84
|
-
<div className="text-xs text-white/90 mb-2">
|
|
85
|
-
<span className="text-white/60">Items:</span>{' '}
|
|
86
|
-
<span className="font-mono">{data.items || 'Not set'}</span>
|
|
87
|
-
</div>
|
|
88
|
-
<div className="text-xs text-white/90 mb-3">
|
|
89
|
-
<span className="text-white/60">Variable:</span>{' '}
|
|
90
|
-
<span className="font-mono">{data.itemVariable || 'item'}</span>
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
{/* Early exit indicator */}
|
|
94
|
-
{data.earlyExit && (
|
|
95
|
-
<div className="mb-3 p-2 bg-orange-500/20 border border-orange-500/30 rounded flex items-center gap-2">
|
|
96
|
-
<LogOut className="w-4 h-4 text-orange-200" />
|
|
97
|
-
<span className="text-xs text-orange-200 font-medium">
|
|
98
|
-
{data.exitReason === 'break' ? 'Loop exited early (break)' : 'Loop stopped on error'}
|
|
99
|
-
</span>
|
|
100
|
-
</div>
|
|
101
|
-
)}
|
|
102
|
-
|
|
103
|
-
{/* Iteration progress */}
|
|
104
|
-
{data.totalIterations !== undefined && (
|
|
105
|
-
<div className="mt-2 p-2 bg-white/5 rounded">
|
|
106
|
-
<div className="flex items-center justify-between mb-1">
|
|
107
|
-
<span className="text-xs text-white/70">Progress</span>
|
|
108
|
-
<span className="text-xs text-white font-medium">
|
|
109
|
-
{data.currentIteration || 0} / {data.totalIterations}
|
|
110
|
-
{data.earlyExit && (
|
|
111
|
-
<span className="ml-1 text-orange-300 text-[10px]">(stopped)</span>
|
|
112
|
-
)}
|
|
113
|
-
</span>
|
|
114
|
-
</div>
|
|
115
|
-
<div className="w-full bg-white/10 rounded-full h-1.5">
|
|
116
|
-
<div
|
|
117
|
-
className={`h-1.5 rounded-full transition-all ${data.earlyExit ? 'bg-orange-400' : 'bg-pink-400'}`}
|
|
118
|
-
style={{
|
|
119
|
-
width: `${((data.currentIteration || 0) / data.totalIterations) * 100}%`,
|
|
120
|
-
}}
|
|
121
|
-
/>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
)}
|
|
125
|
-
|
|
126
|
-
{/* Loop metadata */}
|
|
127
|
-
<div className="mt-3 text-xs text-white/50 flex items-center gap-2">
|
|
128
|
-
<span>ℹ️</span>
|
|
129
|
-
<span>Access: loop.index, loop.first, loop.last</span>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
{/* Output handle */}
|
|
134
|
-
<Handle
|
|
135
|
-
type="source"
|
|
136
|
-
position={Position.Bottom}
|
|
137
|
-
className="!w-3 !h-3 !bg-primary !border-2 !border-node-bg"
|
|
138
|
-
/>
|
|
139
|
-
|
|
140
|
-
{/* Loop-back handle (left side for iteration feedback) */}
|
|
141
|
-
<Handle
|
|
142
|
-
id="loop-back"
|
|
143
|
-
type="source"
|
|
144
|
-
position={Position.Left}
|
|
145
|
-
className="!w-3 !h-3 !bg-purple-500 !border-2 !border-node-bg"
|
|
146
|
-
style={{ top: '50%', left: '-6px' }}
|
|
147
|
-
/>
|
|
148
|
-
</div>
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export const ForEachNode = memo(ForEachNodeComponent);
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
import { Handle, Position, type Node, type NodeProps } from '@xyflow/react';
|
|
3
|
-
import { GitBranch, CheckCircle, XCircle, Clock } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
export interface IfElseNodeData extends Record<string, unknown> {
|
|
6
|
-
id: string;
|
|
7
|
-
name?: string;
|
|
8
|
-
condition: string;
|
|
9
|
-
status?: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
10
|
-
activeBranch?: 'then' | 'else' | null;
|
|
11
|
-
skippedBranch?: 'then' | 'else' | null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type IfElseNodeType = Node<IfElseNodeData, 'if'>;
|
|
15
|
-
|
|
16
|
-
function IfElseNodeComponent({ data, selected }: NodeProps<IfElseNodeType>) {
|
|
17
|
-
const statusConfig: Record<
|
|
18
|
-
NonNullable<IfElseNodeData['status']>,
|
|
19
|
-
{ icon: typeof Clock; color: string; bgColor: string; animate?: boolean }
|
|
20
|
-
> = {
|
|
21
|
-
pending: { icon: Clock, color: 'text-gray-400', bgColor: 'bg-gray-400/10' },
|
|
22
|
-
running: {
|
|
23
|
-
icon: GitBranch,
|
|
24
|
-
color: 'text-blue-400',
|
|
25
|
-
bgColor: 'bg-blue-400/10',
|
|
26
|
-
animate: true,
|
|
27
|
-
},
|
|
28
|
-
completed: {
|
|
29
|
-
icon: CheckCircle,
|
|
30
|
-
color: 'text-success',
|
|
31
|
-
bgColor: 'bg-success/10',
|
|
32
|
-
},
|
|
33
|
-
failed: { icon: XCircle, color: 'text-error', bgColor: 'bg-error/10' },
|
|
34
|
-
skipped: {
|
|
35
|
-
icon: XCircle,
|
|
36
|
-
color: 'text-gray-500',
|
|
37
|
-
bgColor: 'bg-gray-500/10',
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const status = data.status || 'pending';
|
|
42
|
-
const config = statusConfig[status];
|
|
43
|
-
const StatusIcon = config.icon;
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<div
|
|
47
|
-
className={`control-flow-node if-else-node p-0 ${selected ? 'selected' : ''} ${status === 'running' ? 'running' : ''} ${status === 'completed' ? 'completed' : ''} ${status === 'failed' ? 'failed' : ''}`}
|
|
48
|
-
style={{
|
|
49
|
-
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
50
|
-
}}
|
|
51
|
-
>
|
|
52
|
-
{/* Input handle */}
|
|
53
|
-
<Handle
|
|
54
|
-
type="target"
|
|
55
|
-
position={Position.Top}
|
|
56
|
-
className="!w-3 !h-3 !bg-primary !border-2 !border-node-bg"
|
|
57
|
-
/>
|
|
58
|
-
|
|
59
|
-
{/* Node header */}
|
|
60
|
-
<div className="flex items-center gap-3 p-3 border-b border-white/20">
|
|
61
|
-
<div className="w-8 h-8 rounded-lg bg-white/20 flex items-center justify-center">
|
|
62
|
-
<GitBranch className="w-5 h-5 text-white" />
|
|
63
|
-
</div>
|
|
64
|
-
<div className="flex-1 min-w-0">
|
|
65
|
-
<div className="text-sm font-medium text-white">
|
|
66
|
-
{data.name || 'If/Else'}
|
|
67
|
-
</div>
|
|
68
|
-
<div className="text-xs text-white/70">Conditional</div>
|
|
69
|
-
</div>
|
|
70
|
-
<div
|
|
71
|
-
className={`w-6 h-6 rounded-full ${config.bgColor} flex items-center justify-center`}
|
|
72
|
-
>
|
|
73
|
-
<StatusIcon
|
|
74
|
-
className={`w-4 h-4 ${config.color} ${config.animate ? 'animate-pulse' : ''}`}
|
|
75
|
-
/>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
{/* Node body */}
|
|
80
|
-
<div className="p-3 bg-white/10">
|
|
81
|
-
<div className="text-xs text-white/90 font-mono mb-3">
|
|
82
|
-
{data.condition || 'No condition set'}
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
{/* Branch outputs */}
|
|
86
|
-
<div className="grid grid-cols-2 gap-2">
|
|
87
|
-
<div
|
|
88
|
-
className={`text-center p-2 rounded text-xs font-medium transition-colors relative ${
|
|
89
|
-
data.activeBranch === 'then'
|
|
90
|
-
? 'bg-green-500/30 text-green-200 ring-1 ring-green-400/50'
|
|
91
|
-
: data.skippedBranch === 'then'
|
|
92
|
-
? 'bg-gray-500/20 text-gray-400'
|
|
93
|
-
: 'bg-white/5 text-white/60'
|
|
94
|
-
}`}
|
|
95
|
-
>
|
|
96
|
-
✓ Then
|
|
97
|
-
{data.skippedBranch === 'then' && (
|
|
98
|
-
<span className="absolute -top-1 -right-1 text-[8px] px-1 py-0.5 rounded bg-gray-500/50 text-gray-300">
|
|
99
|
-
SKIP
|
|
100
|
-
</span>
|
|
101
|
-
)}
|
|
102
|
-
</div>
|
|
103
|
-
<div
|
|
104
|
-
className={`text-center p-2 rounded text-xs font-medium transition-colors relative ${
|
|
105
|
-
data.activeBranch === 'else'
|
|
106
|
-
? 'bg-red-500/30 text-red-200 ring-1 ring-red-400/50'
|
|
107
|
-
: data.skippedBranch === 'else'
|
|
108
|
-
? 'bg-gray-500/20 text-gray-400'
|
|
109
|
-
: 'bg-white/5 text-white/60'
|
|
110
|
-
}`}
|
|
111
|
-
>
|
|
112
|
-
✗ Else
|
|
113
|
-
{data.skippedBranch === 'else' && (
|
|
114
|
-
<span className="absolute -top-1 -right-1 text-[8px] px-1 py-0.5 rounded bg-gray-500/50 text-gray-300">
|
|
115
|
-
SKIP
|
|
116
|
-
</span>
|
|
117
|
-
)}
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
|
|
122
|
-
{/* Output handles */}
|
|
123
|
-
<Handle
|
|
124
|
-
type="source"
|
|
125
|
-
position={Position.Bottom}
|
|
126
|
-
id="then"
|
|
127
|
-
style={{ left: '33%' }}
|
|
128
|
-
className="!w-3 !h-3 !bg-green-500 !border-2 !border-node-bg"
|
|
129
|
-
/>
|
|
130
|
-
<Handle
|
|
131
|
-
type="source"
|
|
132
|
-
position={Position.Bottom}
|
|
133
|
-
id="else"
|
|
134
|
-
style={{ left: '67%' }}
|
|
135
|
-
className="!w-3 !h-3 !bg-red-500 !border-2 !border-node-bg"
|
|
136
|
-
/>
|
|
137
|
-
</div>
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export const IfElseNode = memo(IfElseNodeComponent);
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ContextMenu,
|
|
3
|
-
ContextMenuContent,
|
|
4
|
-
ContextMenuItem,
|
|
5
|
-
ContextMenuSeparator,
|
|
6
|
-
ContextMenuSub,
|
|
7
|
-
ContextMenuSubContent,
|
|
8
|
-
ContextMenuSubTrigger,
|
|
9
|
-
ContextMenuShortcut,
|
|
10
|
-
} from '../common/ContextMenu';
|
|
11
|
-
import {
|
|
12
|
-
Edit,
|
|
13
|
-
Code,
|
|
14
|
-
FileText,
|
|
15
|
-
Copy,
|
|
16
|
-
Trash2,
|
|
17
|
-
Plus,
|
|
18
|
-
FolderOpen,
|
|
19
|
-
Play,
|
|
20
|
-
AlertTriangle,
|
|
21
|
-
} from 'lucide-react';
|
|
22
|
-
import type { Node } from '@xyflow/react';
|
|
23
|
-
import { getModKey } from '../../utils/platform';
|
|
24
|
-
|
|
25
|
-
interface NodeContextMenuProps {
|
|
26
|
-
children: React.ReactNode;
|
|
27
|
-
node: Node;
|
|
28
|
-
onEdit: () => void;
|
|
29
|
-
onViewYaml: () => void;
|
|
30
|
-
onViewDocs: () => void;
|
|
31
|
-
onDuplicate: () => void;
|
|
32
|
-
onDelete: () => void;
|
|
33
|
-
onAddStepBefore: () => void;
|
|
34
|
-
onAddStepAfter: () => void;
|
|
35
|
-
onConvertToSubworkflow: () => void;
|
|
36
|
-
onExecuteFrom: () => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function NodeContextMenu({
|
|
40
|
-
children,
|
|
41
|
-
node,
|
|
42
|
-
onEdit,
|
|
43
|
-
onViewYaml,
|
|
44
|
-
onViewDocs,
|
|
45
|
-
onDuplicate,
|
|
46
|
-
onDelete,
|
|
47
|
-
onAddStepBefore,
|
|
48
|
-
onAddStepAfter,
|
|
49
|
-
onConvertToSubworkflow,
|
|
50
|
-
onExecuteFrom,
|
|
51
|
-
}: NodeContextMenuProps) {
|
|
52
|
-
const isSubworkflow = node.type === 'subworkflow';
|
|
53
|
-
const hasError = node.data?.status === 'failed';
|
|
54
|
-
const modKey = getModKey();
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<ContextMenu>
|
|
58
|
-
{children}
|
|
59
|
-
<ContextMenuContent>
|
|
60
|
-
<ContextMenuItem onClick={onEdit}>
|
|
61
|
-
<Edit className="w-4 h-4 mr-2" />
|
|
62
|
-
Edit Step
|
|
63
|
-
<ContextMenuShortcut>E</ContextMenuShortcut>
|
|
64
|
-
</ContextMenuItem>
|
|
65
|
-
|
|
66
|
-
<ContextMenuItem onClick={onViewYaml}>
|
|
67
|
-
<Code className="w-4 h-4 mr-2" />
|
|
68
|
-
View YAML
|
|
69
|
-
<ContextMenuShortcut>Y</ContextMenuShortcut>
|
|
70
|
-
</ContextMenuItem>
|
|
71
|
-
|
|
72
|
-
<ContextMenuItem onClick={onViewDocs}>
|
|
73
|
-
<FileText className="w-4 h-4 mr-2" />
|
|
74
|
-
View Documentation
|
|
75
|
-
</ContextMenuItem>
|
|
76
|
-
|
|
77
|
-
<ContextMenuSeparator />
|
|
78
|
-
|
|
79
|
-
<ContextMenuItem onClick={onDuplicate}>
|
|
80
|
-
<Copy className="w-4 h-4 mr-2" />
|
|
81
|
-
Duplicate
|
|
82
|
-
<ContextMenuShortcut>{modKey}D</ContextMenuShortcut>
|
|
83
|
-
</ContextMenuItem>
|
|
84
|
-
|
|
85
|
-
<ContextMenuSub>
|
|
86
|
-
<ContextMenuSubTrigger>
|
|
87
|
-
<Plus className="w-4 h-4 mr-2" />
|
|
88
|
-
Add Step
|
|
89
|
-
</ContextMenuSubTrigger>
|
|
90
|
-
<ContextMenuSubContent>
|
|
91
|
-
<ContextMenuItem onClick={onAddStepBefore}>
|
|
92
|
-
Before this step
|
|
93
|
-
</ContextMenuItem>
|
|
94
|
-
<ContextMenuItem onClick={onAddStepAfter}>
|
|
95
|
-
After this step
|
|
96
|
-
</ContextMenuItem>
|
|
97
|
-
</ContextMenuSubContent>
|
|
98
|
-
</ContextMenuSub>
|
|
99
|
-
|
|
100
|
-
{!isSubworkflow && (
|
|
101
|
-
<ContextMenuItem onClick={onConvertToSubworkflow}>
|
|
102
|
-
<FolderOpen className="w-4 h-4 mr-2" />
|
|
103
|
-
Convert to Sub-workflow
|
|
104
|
-
</ContextMenuItem>
|
|
105
|
-
)}
|
|
106
|
-
|
|
107
|
-
<ContextMenuSeparator />
|
|
108
|
-
|
|
109
|
-
<ContextMenuItem onClick={onExecuteFrom}>
|
|
110
|
-
<Play className="w-4 h-4 mr-2" />
|
|
111
|
-
Execute from here
|
|
112
|
-
</ContextMenuItem>
|
|
113
|
-
|
|
114
|
-
{hasError && (
|
|
115
|
-
<ContextMenuItem className="text-error">
|
|
116
|
-
<AlertTriangle className="w-4 h-4 mr-2" />
|
|
117
|
-
View Error Details
|
|
118
|
-
</ContextMenuItem>
|
|
119
|
-
)}
|
|
120
|
-
|
|
121
|
-
<ContextMenuSeparator />
|
|
122
|
-
|
|
123
|
-
<ContextMenuItem destructive onClick={onDelete}>
|
|
124
|
-
<Trash2 className="w-4 h-4 mr-2" />
|
|
125
|
-
Delete
|
|
126
|
-
<ContextMenuShortcut>⌫</ContextMenuShortcut>
|
|
127
|
-
</ContextMenuItem>
|
|
128
|
-
</ContextMenuContent>
|
|
129
|
-
</ContextMenu>
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Canvas context menu (right-click on empty space)
|
|
134
|
-
interface CanvasContextMenuProps {
|
|
135
|
-
children: React.ReactNode;
|
|
136
|
-
onAddStep: () => void;
|
|
137
|
-
onAddSubworkflow: () => void;
|
|
138
|
-
onPaste: () => void;
|
|
139
|
-
onAutoLayout: () => void;
|
|
140
|
-
onFitView: () => void;
|
|
141
|
-
canPaste: boolean;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function CanvasContextMenu({
|
|
145
|
-
children,
|
|
146
|
-
onAddStep,
|
|
147
|
-
onAddSubworkflow,
|
|
148
|
-
onPaste,
|
|
149
|
-
onAutoLayout,
|
|
150
|
-
onFitView,
|
|
151
|
-
canPaste,
|
|
152
|
-
}: CanvasContextMenuProps) {
|
|
153
|
-
const modKey = getModKey();
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<ContextMenu>
|
|
157
|
-
{children}
|
|
158
|
-
<ContextMenuContent>
|
|
159
|
-
<ContextMenuItem onClick={onAddStep}>
|
|
160
|
-
<Plus className="w-4 h-4 mr-2" />
|
|
161
|
-
Add Step
|
|
162
|
-
<ContextMenuShortcut>N</ContextMenuShortcut>
|
|
163
|
-
</ContextMenuItem>
|
|
164
|
-
|
|
165
|
-
<ContextMenuItem onClick={onAddSubworkflow}>
|
|
166
|
-
<FolderOpen className="w-4 h-4 mr-2" />
|
|
167
|
-
Add Sub-workflow
|
|
168
|
-
</ContextMenuItem>
|
|
169
|
-
|
|
170
|
-
<ContextMenuSeparator />
|
|
171
|
-
|
|
172
|
-
<ContextMenuItem onClick={onPaste} disabled={!canPaste}>
|
|
173
|
-
<Copy className="w-4 h-4 mr-2" />
|
|
174
|
-
Paste
|
|
175
|
-
<ContextMenuShortcut>{modKey}V</ContextMenuShortcut>
|
|
176
|
-
</ContextMenuItem>
|
|
177
|
-
|
|
178
|
-
<ContextMenuSeparator />
|
|
179
|
-
|
|
180
|
-
<ContextMenuItem onClick={onAutoLayout}>
|
|
181
|
-
Auto-layout
|
|
182
|
-
<ContextMenuShortcut>{modKey}L</ContextMenuShortcut>
|
|
183
|
-
</ContextMenuItem>
|
|
184
|
-
|
|
185
|
-
<ContextMenuItem onClick={onFitView}>
|
|
186
|
-
Fit to View
|
|
187
|
-
<ContextMenuShortcut>{modKey}0</ContextMenuShortcut>
|
|
188
|
-
</ContextMenuItem>
|
|
189
|
-
</ContextMenuContent>
|
|
190
|
-
</ContextMenu>
|
|
191
|
-
);
|
|
192
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
import { Handle, Position, type Node, type NodeProps } from '@xyflow/react';
|
|
3
|
-
import { Flag, CheckCircle, XCircle, Clock } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
export interface OutputNodeData extends Record<string, unknown> {
|
|
6
|
-
id: string;
|
|
7
|
-
name?: string;
|
|
8
|
-
description?: string;
|
|
9
|
-
variables?: string[];
|
|
10
|
-
status?: 'pending' | 'completed' | 'failed';
|
|
11
|
-
result?: unknown;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type OutputNodeType = Node<OutputNodeData, 'output'>;
|
|
15
|
-
|
|
16
|
-
function OutputNodeComponent({ data, selected }: NodeProps<OutputNodeType>) {
|
|
17
|
-
const statusConfig = {
|
|
18
|
-
pending: {
|
|
19
|
-
icon: Clock,
|
|
20
|
-
color: 'text-gray-400',
|
|
21
|
-
borderColor: 'border-gray-500',
|
|
22
|
-
bgColor: 'bg-gray-500/10',
|
|
23
|
-
},
|
|
24
|
-
completed: {
|
|
25
|
-
icon: CheckCircle,
|
|
26
|
-
color: 'text-success',
|
|
27
|
-
borderColor: 'border-success',
|
|
28
|
-
bgColor: 'bg-success/10',
|
|
29
|
-
},
|
|
30
|
-
failed: {
|
|
31
|
-
icon: XCircle,
|
|
32
|
-
color: 'text-error',
|
|
33
|
-
borderColor: 'border-error',
|
|
34
|
-
bgColor: 'bg-error/10',
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const status = data.status || 'pending';
|
|
39
|
-
const config = statusConfig[status];
|
|
40
|
-
const StatusIcon = config.icon;
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<div
|
|
44
|
-
className={`min-w-[160px] rounded-lg border-2 ${config.borderColor} ${config.bgColor} ${
|
|
45
|
-
selected ? 'ring-2 ring-primary ring-offset-2 ring-offset-canvas-bg' : ''
|
|
46
|
-
} transition-all duration-200`}
|
|
47
|
-
>
|
|
48
|
-
{/* Input handle */}
|
|
49
|
-
<Handle
|
|
50
|
-
type="target"
|
|
51
|
-
position={Position.Top}
|
|
52
|
-
className="!w-3 !h-3 !bg-primary !border-2 !border-canvas-bg"
|
|
53
|
-
/>
|
|
54
|
-
|
|
55
|
-
{/* Header */}
|
|
56
|
-
<div className="px-3 py-2 flex items-center gap-2">
|
|
57
|
-
<div className={`w-8 h-8 rounded-full ${config.bgColor} flex items-center justify-center`}>
|
|
58
|
-
<Flag className={`w-4 h-4 ${config.color}`} />
|
|
59
|
-
</div>
|
|
60
|
-
<div className="flex-1 min-w-0">
|
|
61
|
-
<div className="text-xs font-medium text-gray-400 uppercase tracking-wider">
|
|
62
|
-
Output
|
|
63
|
-
</div>
|
|
64
|
-
<div className="text-sm font-medium text-white truncate">
|
|
65
|
-
{data.name || 'Workflow End'}
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
<StatusIcon className={`w-4 h-4 ${config.color}`} />
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
{/* Variables */}
|
|
72
|
-
{data.variables && data.variables.length > 0 && (
|
|
73
|
-
<div className="px-3 py-2 border-t border-white/10">
|
|
74
|
-
<div className="text-xs text-gray-500 mb-1">Output Variables</div>
|
|
75
|
-
<div className="flex flex-wrap gap-1">
|
|
76
|
-
{data.variables.map((variable) => (
|
|
77
|
-
<code
|
|
78
|
-
key={variable}
|
|
79
|
-
className="px-1.5 py-0.5 bg-white/5 text-primary text-xs rounded"
|
|
80
|
-
>
|
|
81
|
-
{variable}
|
|
82
|
-
</code>
|
|
83
|
-
))}
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
)}
|
|
87
|
-
|
|
88
|
-
{/* Description */}
|
|
89
|
-
{data.description && (
|
|
90
|
-
<div className="px-3 py-2 border-t border-white/10">
|
|
91
|
-
<div className="text-xs text-gray-400">{data.description}</div>
|
|
92
|
-
</div>
|
|
93
|
-
)}
|
|
94
|
-
|
|
95
|
-
{/* Result preview */}
|
|
96
|
-
{status === 'completed' && data.result !== undefined && (
|
|
97
|
-
<div className="px-3 py-2 border-t border-white/10">
|
|
98
|
-
<div className="text-xs text-gray-500 mb-1">Result</div>
|
|
99
|
-
<pre className="text-xs text-success font-mono bg-black/20 rounded p-1.5 overflow-x-auto max-h-20">
|
|
100
|
-
{typeof data.result === 'string'
|
|
101
|
-
? data.result.slice(0, 100)
|
|
102
|
-
: JSON.stringify(data.result, null, 2).slice(0, 100)}
|
|
103
|
-
{(typeof data.result === 'string' ? data.result : JSON.stringify(data.result)).length > 100 && '...'}
|
|
104
|
-
</pre>
|
|
105
|
-
</div>
|
|
106
|
-
)}
|
|
107
|
-
</div>
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export const OutputNode = memo(OutputNodeComponent);
|