@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.
- package/README.md +48 -180
- 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/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,227 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Plus,
|
|
4
|
-
Play,
|
|
5
|
-
Pause,
|
|
6
|
-
Layout,
|
|
7
|
-
ZoomIn,
|
|
8
|
-
ZoomOut,
|
|
9
|
-
Maximize,
|
|
10
|
-
Save,
|
|
11
|
-
Undo,
|
|
12
|
-
Redo,
|
|
13
|
-
Copy,
|
|
14
|
-
Trash2,
|
|
15
|
-
Bot,
|
|
16
|
-
ChevronDown,
|
|
17
|
-
} from 'lucide-react';
|
|
18
|
-
import { useCanvas } from '../../hooks/useCanvas';
|
|
19
|
-
import { useEditorStore } from '../../stores/editorStore';
|
|
20
|
-
import { useReactFlow } from '@xyflow/react';
|
|
21
|
-
import { getModKey } from '../../utils/platform';
|
|
22
|
-
import { useAgentStore } from '../../stores/agentStore';
|
|
23
|
-
import { ProviderSwitcher } from '../Settings/ProviderSwitcher';
|
|
24
|
-
|
|
25
|
-
interface ToolbarProps {
|
|
26
|
-
onAddStep: () => void;
|
|
27
|
-
onExecute?: () => void;
|
|
28
|
-
onSave?: () => void;
|
|
29
|
-
isExecuting?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function Toolbar({
|
|
33
|
-
onAddStep,
|
|
34
|
-
onExecute,
|
|
35
|
-
onSave,
|
|
36
|
-
isExecuting = false,
|
|
37
|
-
}: ToolbarProps) {
|
|
38
|
-
const { autoLayout, fitView, selectedNodes, deleteSelected, duplicateSelected } =
|
|
39
|
-
useCanvas();
|
|
40
|
-
const { undo, redo, undoStack, redoStack } = useEditorStore();
|
|
41
|
-
const { zoomIn, zoomOut } = useReactFlow();
|
|
42
|
-
const modKey = getModKey();
|
|
43
|
-
const { providers, activeProviderId, loadProviders } = useAgentStore();
|
|
44
|
-
const [showProviderSwitcher, setShowProviderSwitcher] = useState(false);
|
|
45
|
-
|
|
46
|
-
const canUndo = undoStack.length > 0;
|
|
47
|
-
const canRedo = redoStack.length > 0;
|
|
48
|
-
const hasSelection = selectedNodes.length > 0;
|
|
49
|
-
|
|
50
|
-
// Load providers on mount
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
loadProviders();
|
|
53
|
-
}, [loadProviders]);
|
|
54
|
-
|
|
55
|
-
const activeProvider = providers.find((p) => p.id === activeProviderId);
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div className="absolute top-4 left-1/2 -translate-x-1/2 z-10 flex items-center gap-1 px-2 py-1.5 bg-panel-bg/95 backdrop-blur border border-node-border rounded-lg shadow-lg">
|
|
59
|
-
{/* Add Step */}
|
|
60
|
-
<ToolbarButton
|
|
61
|
-
icon={<Plus className="w-4 h-4" />}
|
|
62
|
-
label="Add Step"
|
|
63
|
-
onClick={onAddStep}
|
|
64
|
-
shortcut="N"
|
|
65
|
-
/>
|
|
66
|
-
|
|
67
|
-
<ToolbarDivider />
|
|
68
|
-
|
|
69
|
-
{/* Undo/Redo */}
|
|
70
|
-
<ToolbarButton
|
|
71
|
-
icon={<Undo className="w-4 h-4" />}
|
|
72
|
-
label="Undo"
|
|
73
|
-
onClick={() => undo()}
|
|
74
|
-
disabled={!canUndo}
|
|
75
|
-
shortcut={`${modKey}Z`}
|
|
76
|
-
/>
|
|
77
|
-
<ToolbarButton
|
|
78
|
-
icon={<Redo className="w-4 h-4" />}
|
|
79
|
-
label="Redo"
|
|
80
|
-
onClick={() => redo()}
|
|
81
|
-
disabled={!canRedo}
|
|
82
|
-
shortcut={`${modKey}⇧Z`}
|
|
83
|
-
/>
|
|
84
|
-
|
|
85
|
-
<ToolbarDivider />
|
|
86
|
-
|
|
87
|
-
{/* Selection actions */}
|
|
88
|
-
<ToolbarButton
|
|
89
|
-
icon={<Copy className="w-4 h-4" />}
|
|
90
|
-
label="Duplicate"
|
|
91
|
-
onClick={duplicateSelected}
|
|
92
|
-
disabled={!hasSelection}
|
|
93
|
-
shortcut={`${modKey}D`}
|
|
94
|
-
/>
|
|
95
|
-
<ToolbarButton
|
|
96
|
-
icon={<Trash2 className="w-4 h-4" />}
|
|
97
|
-
label="Delete"
|
|
98
|
-
onClick={deleteSelected}
|
|
99
|
-
disabled={!hasSelection}
|
|
100
|
-
shortcut="⌫"
|
|
101
|
-
/>
|
|
102
|
-
|
|
103
|
-
<ToolbarDivider />
|
|
104
|
-
|
|
105
|
-
{/* Layout & Zoom */}
|
|
106
|
-
<ToolbarButton
|
|
107
|
-
icon={<Layout className="w-4 h-4" />}
|
|
108
|
-
label="Auto Layout"
|
|
109
|
-
onClick={autoLayout}
|
|
110
|
-
shortcut={`${modKey}L`}
|
|
111
|
-
/>
|
|
112
|
-
<ToolbarButton
|
|
113
|
-
icon={<ZoomIn className="w-4 h-4" />}
|
|
114
|
-
label="Zoom In"
|
|
115
|
-
onClick={() => zoomIn()}
|
|
116
|
-
shortcut={`${modKey}+`}
|
|
117
|
-
/>
|
|
118
|
-
<ToolbarButton
|
|
119
|
-
icon={<ZoomOut className="w-4 h-4" />}
|
|
120
|
-
label="Zoom Out"
|
|
121
|
-
onClick={() => zoomOut()}
|
|
122
|
-
shortcut={`${modKey}-`}
|
|
123
|
-
/>
|
|
124
|
-
<ToolbarButton
|
|
125
|
-
icon={<Maximize className="w-4 h-4" />}
|
|
126
|
-
label="Fit View"
|
|
127
|
-
onClick={fitView}
|
|
128
|
-
shortcut={`${modKey}0`}
|
|
129
|
-
/>
|
|
130
|
-
|
|
131
|
-
<ToolbarDivider />
|
|
132
|
-
|
|
133
|
-
{/* AI Provider */}
|
|
134
|
-
<button
|
|
135
|
-
onClick={() => setShowProviderSwitcher(true)}
|
|
136
|
-
className="flex items-center gap-1.5 px-2 py-1.5 rounded text-sm text-gray-300 hover:text-white hover:bg-white/10 transition-colors"
|
|
137
|
-
title="Select AI Provider"
|
|
138
|
-
>
|
|
139
|
-
<Bot className="w-4 h-4" />
|
|
140
|
-
<span className="hidden sm:inline text-xs">
|
|
141
|
-
{activeProvider?.name || 'No Provider'}
|
|
142
|
-
</span>
|
|
143
|
-
<ChevronDown className="w-3 h-3" />
|
|
144
|
-
</button>
|
|
145
|
-
|
|
146
|
-
{/* Execute */}
|
|
147
|
-
{onExecute && (
|
|
148
|
-
<>
|
|
149
|
-
<ToolbarDivider />
|
|
150
|
-
<ToolbarButton
|
|
151
|
-
icon={
|
|
152
|
-
isExecuting ? (
|
|
153
|
-
<Pause className="w-4 h-4" />
|
|
154
|
-
) : (
|
|
155
|
-
<Play className="w-4 h-4" />
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
label={isExecuting ? 'Stop' : 'Execute'}
|
|
159
|
-
onClick={onExecute}
|
|
160
|
-
variant={isExecuting ? 'destructive' : 'primary'}
|
|
161
|
-
shortcut={`${modKey}⏎`}
|
|
162
|
-
/>
|
|
163
|
-
</>
|
|
164
|
-
)}
|
|
165
|
-
|
|
166
|
-
{/* Save */}
|
|
167
|
-
{onSave && (
|
|
168
|
-
<ToolbarButton
|
|
169
|
-
icon={<Save className="w-4 h-4" />}
|
|
170
|
-
label="Save"
|
|
171
|
-
onClick={onSave}
|
|
172
|
-
shortcut={`${modKey}S`}
|
|
173
|
-
/>
|
|
174
|
-
)}
|
|
175
|
-
|
|
176
|
-
{/* Provider Switcher Modal */}
|
|
177
|
-
<ProviderSwitcher
|
|
178
|
-
open={showProviderSwitcher}
|
|
179
|
-
onOpenChange={setShowProviderSwitcher}
|
|
180
|
-
/>
|
|
181
|
-
</div>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
interface ToolbarButtonProps {
|
|
186
|
-
icon: React.ReactNode;
|
|
187
|
-
label: string;
|
|
188
|
-
onClick: () => void;
|
|
189
|
-
disabled?: boolean;
|
|
190
|
-
shortcut?: string;
|
|
191
|
-
variant?: 'default' | 'primary' | 'destructive';
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function ToolbarButton({
|
|
195
|
-
icon,
|
|
196
|
-
label,
|
|
197
|
-
onClick,
|
|
198
|
-
disabled,
|
|
199
|
-
shortcut,
|
|
200
|
-
variant = 'default',
|
|
201
|
-
}: ToolbarButtonProps) {
|
|
202
|
-
const variantClasses = {
|
|
203
|
-
default: 'text-gray-300 hover:text-white hover:bg-white/10',
|
|
204
|
-
primary: 'text-primary hover:text-primary-light hover:bg-primary/10',
|
|
205
|
-
destructive: 'text-error hover:text-error hover:bg-error/10',
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
return (
|
|
209
|
-
<button
|
|
210
|
-
onClick={onClick}
|
|
211
|
-
disabled={disabled}
|
|
212
|
-
className={`relative group p-2 rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed ${variantClasses[variant]}`}
|
|
213
|
-
title={`${label}${shortcut ? ` (${shortcut})` : ''}`}
|
|
214
|
-
>
|
|
215
|
-
{icon}
|
|
216
|
-
{/* Tooltip */}
|
|
217
|
-
<div className="absolute top-full left-1/2 -translate-x-1/2 mt-2 px-2 py-1 bg-black/90 rounded text-xs text-white whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
|
|
218
|
-
{label}
|
|
219
|
-
{shortcut && <span className="ml-2 text-gray-400">{shortcut}</span>}
|
|
220
|
-
</div>
|
|
221
|
-
</button>
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function ToolbarDivider() {
|
|
226
|
-
return <div className="w-px h-6 bg-node-border mx-1" />;
|
|
227
|
-
}
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
import { Handle, Position, type Node, type NodeProps } from '@xyflow/react';
|
|
3
|
-
import {
|
|
4
|
-
ArrowRight,
|
|
5
|
-
Filter,
|
|
6
|
-
Minimize2,
|
|
7
|
-
CheckCircle,
|
|
8
|
-
XCircle,
|
|
9
|
-
Clock,
|
|
10
|
-
} from 'lucide-react';
|
|
11
|
-
|
|
12
|
-
export interface TransformNodeData extends Record<string, unknown> {
|
|
13
|
-
id: string;
|
|
14
|
-
name?: string;
|
|
15
|
-
transformType: 'map' | 'filter' | 'reduce';
|
|
16
|
-
items: string;
|
|
17
|
-
itemVariable?: string;
|
|
18
|
-
expression?: string;
|
|
19
|
-
condition?: string;
|
|
20
|
-
accumulatorVariable?: string;
|
|
21
|
-
initialValue?: unknown;
|
|
22
|
-
status?: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
23
|
-
inputCount?: number;
|
|
24
|
-
outputCount?: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type TransformNodeType = Node<TransformNodeData, 'map' | 'filter' | 'reduce'>;
|
|
28
|
-
|
|
29
|
-
function TransformNodeComponent({ data, selected }: NodeProps<TransformNodeType>) {
|
|
30
|
-
const transformConfig: Record<
|
|
31
|
-
NonNullable<TransformNodeData['transformType']>,
|
|
32
|
-
{ icon: typeof ArrowRight; label: string; color: string }
|
|
33
|
-
> = {
|
|
34
|
-
map: { icon: ArrowRight, label: 'Map', color: '#14b8a6' },
|
|
35
|
-
filter: { icon: Filter, label: 'Filter', color: '#10b981' },
|
|
36
|
-
reduce: { icon: Minimize2, label: 'Reduce', color: '#06b6d4' },
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const statusConfig: Record<
|
|
40
|
-
NonNullable<TransformNodeData['status']>,
|
|
41
|
-
{ icon: typeof Clock; color: string; bgColor: string; animate?: boolean }
|
|
42
|
-
> = {
|
|
43
|
-
pending: { icon: Clock, color: 'text-gray-400', bgColor: 'bg-gray-400/10' },
|
|
44
|
-
running: {
|
|
45
|
-
icon: transformConfig[data.transformType].icon,
|
|
46
|
-
color: 'text-teal-400',
|
|
47
|
-
bgColor: 'bg-teal-400/10',
|
|
48
|
-
animate: true,
|
|
49
|
-
},
|
|
50
|
-
completed: {
|
|
51
|
-
icon: CheckCircle,
|
|
52
|
-
color: 'text-success',
|
|
53
|
-
bgColor: 'bg-success/10',
|
|
54
|
-
},
|
|
55
|
-
failed: { icon: XCircle, color: 'text-error', bgColor: 'bg-error/10' },
|
|
56
|
-
skipped: {
|
|
57
|
-
icon: XCircle,
|
|
58
|
-
color: 'text-gray-500',
|
|
59
|
-
bgColor: 'bg-gray-500/10',
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const status = data.status || 'pending';
|
|
64
|
-
const statusCfg = statusConfig[status];
|
|
65
|
-
const StatusIcon = statusCfg.icon;
|
|
66
|
-
|
|
67
|
-
const transformCfg = transformConfig[data.transformType];
|
|
68
|
-
const TransformIcon = transformCfg.icon;
|
|
69
|
-
|
|
70
|
-
const displayExpression =
|
|
71
|
-
data.transformType === 'filter'
|
|
72
|
-
? data.condition
|
|
73
|
-
: data.transformType === 'reduce'
|
|
74
|
-
? `${data.accumulatorVariable || 'acc'}: ${data.expression || 'Not set'}`
|
|
75
|
-
: data.expression;
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div
|
|
79
|
-
className={`control-flow-node transform-node p-0 ${selected ? 'selected' : ''} ${status === 'running' ? 'running' : ''}`}
|
|
80
|
-
style={{
|
|
81
|
-
background: 'linear-gradient(135deg, #14b8a6 0%, #06b6d4 100%)',
|
|
82
|
-
}}
|
|
83
|
-
>
|
|
84
|
-
{/* Input handle */}
|
|
85
|
-
<Handle
|
|
86
|
-
type="target"
|
|
87
|
-
position={Position.Top}
|
|
88
|
-
className="!w-3 !h-3 !bg-primary !border-2 !border-node-bg"
|
|
89
|
-
/>
|
|
90
|
-
|
|
91
|
-
{/* Node header */}
|
|
92
|
-
<div className="flex items-center gap-3 p-3 border-b border-white/20">
|
|
93
|
-
<div className="w-8 h-8 rounded-lg bg-white/20 flex items-center justify-center">
|
|
94
|
-
<TransformIcon className="w-5 h-5 text-white" />
|
|
95
|
-
</div>
|
|
96
|
-
<div className="flex-1 min-w-0">
|
|
97
|
-
<div className="text-sm font-medium text-white">
|
|
98
|
-
{data.name || transformCfg.label}
|
|
99
|
-
</div>
|
|
100
|
-
<div className="text-xs text-white/70">Transform</div>
|
|
101
|
-
</div>
|
|
102
|
-
<div
|
|
103
|
-
className={`w-6 h-6 rounded-full ${statusCfg.bgColor} flex items-center justify-center`}
|
|
104
|
-
>
|
|
105
|
-
<StatusIcon
|
|
106
|
-
className={`w-4 h-4 ${statusCfg.color} ${statusCfg.animate ? 'animate-pulse' : ''}`}
|
|
107
|
-
/>
|
|
108
|
-
</div>
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
{/* Node body */}
|
|
112
|
-
<div className="p-3 bg-white/10">
|
|
113
|
-
{/* Items source */}
|
|
114
|
-
<div className="text-xs text-white/90 mb-2">
|
|
115
|
-
<span className="text-white/60">Items:</span>{' '}
|
|
116
|
-
<span className="font-mono">{data.items || 'Not set'}</span>
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
{/* Variable */}
|
|
120
|
-
<div className="text-xs text-white/90 mb-3">
|
|
121
|
-
<span className="text-white/60">Variable:</span>{' '}
|
|
122
|
-
<span className="font-mono">{data.itemVariable || 'item'}</span>
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
{/* Expression/Condition */}
|
|
126
|
-
<div className="mb-3 p-2 bg-white/5 rounded">
|
|
127
|
-
<div className="text-xs text-white/60 mb-1">
|
|
128
|
-
{data.transformType === 'filter'
|
|
129
|
-
? 'Condition'
|
|
130
|
-
: data.transformType === 'reduce'
|
|
131
|
-
? 'Reducer'
|
|
132
|
-
: 'Expression'}
|
|
133
|
-
</div>
|
|
134
|
-
<div className="text-xs text-white font-mono break-all">
|
|
135
|
-
{displayExpression || 'Not set'}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
{/* Initial value for reduce */}
|
|
140
|
-
{data.transformType === 'reduce' && data.initialValue !== undefined && (
|
|
141
|
-
<div className="text-xs text-white/90 mb-3">
|
|
142
|
-
<span className="text-white/60">Initial:</span>{' '}
|
|
143
|
-
<span className="font-mono">{JSON.stringify(data.initialValue)}</span>
|
|
144
|
-
</div>
|
|
145
|
-
)}
|
|
146
|
-
|
|
147
|
-
{/* Input/Output count */}
|
|
148
|
-
{(data.inputCount !== undefined || data.outputCount !== undefined) && (
|
|
149
|
-
<div className="mt-3 p-2 bg-white/5 rounded flex items-center justify-between">
|
|
150
|
-
<div className="text-xs">
|
|
151
|
-
<span className="text-white/60">In:</span>{' '}
|
|
152
|
-
<span className="text-white font-medium">{data.inputCount ?? '?'}</span>
|
|
153
|
-
</div>
|
|
154
|
-
<div className="text-white/40">→</div>
|
|
155
|
-
<div className="text-xs">
|
|
156
|
-
<span className="text-white/60">Out:</span>{' '}
|
|
157
|
-
<span className="text-white font-medium">{data.outputCount ?? '?'}</span>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
)}
|
|
161
|
-
|
|
162
|
-
{/* Transform type indicator */}
|
|
163
|
-
<div className="mt-3 text-xs text-white/50 flex items-center gap-2">
|
|
164
|
-
<span>ℹ️</span>
|
|
165
|
-
<span>
|
|
166
|
-
{data.transformType === 'map'
|
|
167
|
-
? 'Transforms each item'
|
|
168
|
-
: data.transformType === 'filter'
|
|
169
|
-
? 'Selects matching items'
|
|
170
|
-
: 'Aggregates to single value'}
|
|
171
|
-
</span>
|
|
172
|
-
</div>
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
|
-
{/* Output handle */}
|
|
176
|
-
<Handle
|
|
177
|
-
type="source"
|
|
178
|
-
position={Position.Bottom}
|
|
179
|
-
className="!w-3 !h-3 !bg-primary !border-2 !border-node-bg"
|
|
180
|
-
/>
|
|
181
|
-
|
|
182
|
-
{/* Loop-back handle for iteration visualization */}
|
|
183
|
-
<Handle
|
|
184
|
-
id="loop-back"
|
|
185
|
-
type="source"
|
|
186
|
-
position={Position.Left}
|
|
187
|
-
className="!w-3 !h-3 !bg-teal-500 !border-2 !border-node-bg"
|
|
188
|
-
style={{ top: '50%', left: '-6px' }}
|
|
189
|
-
/>
|
|
190
|
-
</div>
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export const TransformNode = memo(TransformNodeComponent);
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
import { Handle, Position, type Node, type NodeProps } from '@xyflow/react';
|
|
3
|
-
import { Webhook, FolderOpen, Play, Zap, Calendar } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
export interface TriggerNodeData extends Record<string, unknown> {
|
|
6
|
-
id: string;
|
|
7
|
-
name?: string;
|
|
8
|
-
type: 'manual' | 'schedule' | 'webhook' | 'file' | 'event';
|
|
9
|
-
// Schedule trigger
|
|
10
|
-
cron?: string;
|
|
11
|
-
// Webhook trigger
|
|
12
|
-
path?: string;
|
|
13
|
-
method?: string;
|
|
14
|
-
// File watcher trigger
|
|
15
|
-
pattern?: string;
|
|
16
|
-
// Event trigger
|
|
17
|
-
events?: string[];
|
|
18
|
-
// Status
|
|
19
|
-
active?: boolean;
|
|
20
|
-
lastTriggered?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type TriggerNodeType = Node<TriggerNodeData, 'trigger'>;
|
|
24
|
-
|
|
25
|
-
const triggerConfig = {
|
|
26
|
-
manual: {
|
|
27
|
-
icon: Play,
|
|
28
|
-
color: 'text-primary',
|
|
29
|
-
bgColor: 'bg-primary/10',
|
|
30
|
-
borderColor: 'border-primary',
|
|
31
|
-
label: 'Manual Trigger',
|
|
32
|
-
},
|
|
33
|
-
schedule: {
|
|
34
|
-
icon: Calendar,
|
|
35
|
-
color: 'text-info',
|
|
36
|
-
bgColor: 'bg-info/10',
|
|
37
|
-
borderColor: 'border-info',
|
|
38
|
-
label: 'Schedule',
|
|
39
|
-
},
|
|
40
|
-
webhook: {
|
|
41
|
-
icon: Webhook,
|
|
42
|
-
color: 'text-success',
|
|
43
|
-
bgColor: 'bg-success/10',
|
|
44
|
-
borderColor: 'border-success',
|
|
45
|
-
label: 'Webhook',
|
|
46
|
-
},
|
|
47
|
-
file: {
|
|
48
|
-
icon: FolderOpen,
|
|
49
|
-
color: 'text-warning',
|
|
50
|
-
bgColor: 'bg-warning/10',
|
|
51
|
-
borderColor: 'border-warning',
|
|
52
|
-
label: 'File Watcher',
|
|
53
|
-
},
|
|
54
|
-
event: {
|
|
55
|
-
icon: Zap,
|
|
56
|
-
color: 'text-purple-400',
|
|
57
|
-
bgColor: 'bg-purple-400/10',
|
|
58
|
-
borderColor: 'border-purple-400',
|
|
59
|
-
label: 'Event',
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
function TriggerNodeComponent({ data, selected }: NodeProps<TriggerNodeType>) {
|
|
64
|
-
const config = triggerConfig[data.type] || triggerConfig.manual;
|
|
65
|
-
const Icon = config.icon;
|
|
66
|
-
|
|
67
|
-
const getSubtitle = () => {
|
|
68
|
-
switch (data.type) {
|
|
69
|
-
case 'schedule':
|
|
70
|
-
return data.cron || 'No schedule set';
|
|
71
|
-
case 'webhook':
|
|
72
|
-
return `${data.method || 'POST'} ${data.path || '/webhook'}`;
|
|
73
|
-
case 'file':
|
|
74
|
-
return data.pattern || '**/*';
|
|
75
|
-
case 'event':
|
|
76
|
-
return data.events?.join(', ') || 'No events';
|
|
77
|
-
default:
|
|
78
|
-
return 'Click to run';
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<div
|
|
84
|
-
className={`min-w-[180px] rounded-lg border-2 ${config.borderColor} ${config.bgColor} ${
|
|
85
|
-
selected ? 'ring-2 ring-primary ring-offset-2 ring-offset-canvas-bg' : ''
|
|
86
|
-
} transition-all duration-200`}
|
|
87
|
-
>
|
|
88
|
-
{/* Header */}
|
|
89
|
-
<div className="px-3 py-2 flex items-center gap-2">
|
|
90
|
-
<div className={`w-8 h-8 rounded-full ${config.bgColor} flex items-center justify-center`}>
|
|
91
|
-
<Icon className={`w-4 h-4 ${config.color}`} />
|
|
92
|
-
</div>
|
|
93
|
-
<div className="flex-1 min-w-0">
|
|
94
|
-
<div className="text-xs font-medium text-gray-400 uppercase tracking-wider">
|
|
95
|
-
{config.label}
|
|
96
|
-
</div>
|
|
97
|
-
<div className="text-sm font-medium text-white truncate">
|
|
98
|
-
{data.name || 'Trigger'}
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
{data.active !== false && (
|
|
102
|
-
<div className="w-2 h-2 rounded-full bg-success animate-pulse" title="Active" />
|
|
103
|
-
)}
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
{/* Details */}
|
|
107
|
-
<div className="px-3 py-2 border-t border-white/10">
|
|
108
|
-
<div className="text-xs text-gray-400 font-mono truncate" title={getSubtitle()}>
|
|
109
|
-
{getSubtitle()}
|
|
110
|
-
</div>
|
|
111
|
-
{data.lastTriggered && (
|
|
112
|
-
<div className="text-xs text-gray-500 mt-1">
|
|
113
|
-
Last: {new Date(data.lastTriggered).toLocaleString()}
|
|
114
|
-
</div>
|
|
115
|
-
)}
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
{/* Output handle */}
|
|
119
|
-
<Handle
|
|
120
|
-
type="source"
|
|
121
|
-
position={Position.Bottom}
|
|
122
|
-
className="!w-3 !h-3 !bg-primary !border-2 !border-canvas-bg"
|
|
123
|
-
/>
|
|
124
|
-
</div>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export const TriggerNode = memo(TriggerNodeComponent);
|