@motiadev/workbench 0.14.0-beta.165-285707 → 0.15.0-beta.165
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/dist/index.d.ts +189 -10
- package/dist/index.html +1 -1
- package/dist/index.js +1065 -7
- package/dist/middleware.d.ts +66 -8
- package/dist/middleware.js +694 -86
- package/dist/motia-plugin/__tests__/generator.test.ts +129 -0
- package/dist/motia-plugin/__tests__/resolver.test.ts +82 -0
- package/dist/motia-plugin/__tests__/validator.test.ts +71 -0
- package/dist/motia-plugin/{generator.js → generator.ts} +37 -35
- package/dist/motia-plugin/hmr.ts +123 -0
- package/dist/motia-plugin/index.ts +183 -0
- package/dist/motia-plugin/{resolver.d.ts → resolver.ts} +38 -5
- package/dist/motia-plugin/types.ts +198 -0
- package/dist/motia-plugin/{utils.d.ts → utils.ts} +17 -4
- package/dist/motia-plugin/validator.ts +197 -0
- package/dist/src/App.tsx +41 -0
- package/dist/src/components/NotFoundPage.tsx +11 -0
- package/dist/src/components/bottom-panel.tsx +39 -0
- package/dist/src/components/flow/base-edge.tsx +61 -0
- package/dist/src/components/flow/flow-loader.tsx +3 -0
- package/dist/src/components/flow/flow-page.tsx +75 -0
- package/dist/src/components/flow/flow-tab-menu-item.tsx +52 -0
- package/dist/src/components/flow/flow-view.tsx +66 -0
- package/dist/src/components/flow/hooks/use-get-flow-state.tsx +171 -0
- package/dist/src/components/flow/hooks/use-save-workflow-config.ts +25 -0
- package/dist/src/components/flow/node-organizer.tsx +103 -0
- package/dist/src/components/flow/nodes/api-flow-node.tsx +6 -0
- package/dist/src/components/flow/nodes/cron-flow-node.tsx +6 -0
- package/dist/src/components/flow/nodes/event-flow-node.tsx +6 -0
- package/dist/src/components/flow/nodes/noop-flow-node.tsx +6 -0
- package/dist/src/components/header/deploy-button.tsx +110 -0
- package/dist/src/components/header/header.tsx +39 -0
- package/dist/src/components/root-motia.tsx +10 -0
- package/dist/src/components/top-panel.tsx +40 -0
- package/dist/src/components/tutorial/engine/tutorial-engine.ts +26 -0
- package/dist/src/components/tutorial/engine/tutorial-types.ts +26 -0
- package/dist/src/components/tutorial/engine/workbench-xpath.ts +53 -0
- package/dist/src/components/tutorial/hooks/tutorial-utils.ts +26 -0
- package/dist/src/components/tutorial/hooks/use-tutorial-engine.ts +213 -0
- package/dist/src/components/tutorial/hooks/use-tutorial.ts +14 -0
- package/dist/src/components/tutorial/tutorial-button.tsx +46 -0
- package/dist/src/components/tutorial/tutorial-step.tsx +82 -0
- package/dist/src/components/tutorial/tutorial.tsx +59 -0
- package/dist/src/components/ui/json-editor.tsx +68 -0
- package/dist/src/components/ui/table.tsx +75 -0
- package/dist/src/components/ui/theme-toggle.tsx +54 -0
- package/dist/src/components/ui/tooltip.tsx +26 -0
- package/dist/src/hooks/use-debounced.ts +22 -0
- package/dist/src/hooks/use-fetch-flows.ts +33 -0
- package/dist/src/hooks/use-mobile.ts +19 -0
- package/dist/src/hooks/use-update-handle-positions.ts +42 -0
- package/dist/src/index.css +5 -5
- package/dist/src/lib/__tests__/utils.test.ts +110 -0
- package/dist/src/lib/motia-analytics.ts +140 -0
- package/dist/src/lib/plugins.tsx +132 -0
- package/dist/src/lib/utils.ts +37 -0
- package/dist/src/main.tsx +30 -0
- package/dist/src/project-view-mode.tsx +32 -0
- package/dist/src/publicComponents/api-node.tsx +26 -0
- package/dist/src/publicComponents/base-node/base-handle.tsx +50 -0
- package/dist/src/publicComponents/base-node/base-node.tsx +114 -0
- package/dist/src/publicComponents/base-node/code-display.tsx +119 -0
- package/dist/src/publicComponents/base-node/emits.tsx +17 -0
- package/dist/src/publicComponents/base-node/feature-card.tsx +32 -0
- package/dist/src/publicComponents/base-node/language-indicator.tsx +131 -0
- package/dist/src/publicComponents/base-node/node-header.tsx +49 -0
- package/dist/src/publicComponents/base-node/node-sidebar.tsx +41 -0
- package/dist/src/publicComponents/base-node/subscribe.tsx +13 -0
- package/dist/src/publicComponents/cron-node.tsx +24 -0
- package/dist/src/publicComponents/event-node.tsx +20 -0
- package/dist/src/publicComponents/node-props.tsx +15 -0
- package/dist/src/publicComponents/noop-node.tsx +19 -0
- package/dist/src/setupTests.ts +1 -0
- package/dist/src/stores/use-app-tabs-store.ts +49 -0
- package/dist/src/stores/use-flow-store.ts +31 -0
- package/dist/src/stores/use-global-store.ts +24 -0
- package/dist/src/stores/use-motia-config-store.ts +36 -0
- package/dist/src/stores/use-tabs-store.ts +34 -0
- package/dist/src/system-view-mode.tsx +28 -0
- package/dist/src/types/endpoint.ts +12 -0
- package/dist/src/types/file.ts +7 -0
- package/dist/src/types/flow.ts +103 -0
- package/eslint.config.cjs +22 -0
- package/jest.config.cjs +68 -0
- package/package.json +53 -51
- package/dist/motia-plugin/__tests__/generator.test.d.ts +0 -1
- package/dist/motia-plugin/__tests__/generator.test.js +0 -97
- package/dist/motia-plugin/__tests__/resolver.test.d.ts +0 -1
- package/dist/motia-plugin/__tests__/resolver.test.js +0 -64
- package/dist/motia-plugin/__tests__/validator.test.d.ts +0 -1
- package/dist/motia-plugin/__tests__/validator.test.js +0 -59
- package/dist/motia-plugin/generator.d.ts +0 -78
- package/dist/motia-plugin/hmr.d.ts +0 -22
- package/dist/motia-plugin/hmr.js +0 -100
- package/dist/motia-plugin/index.d.ts +0 -3
- package/dist/motia-plugin/index.js +0 -153
- package/dist/motia-plugin/resolver.js +0 -92
- package/dist/motia-plugin/types.d.ts +0 -169
- package/dist/motia-plugin/types.js +0 -36
- package/dist/motia-plugin/utils.js +0 -75
- package/dist/motia-plugin/validator.d.ts +0 -19
- package/dist/motia-plugin/validator.js +0 -163
- package/dist/src/App.d.ts +0 -2
- package/dist/src/App.js +0 -35
- package/dist/src/components/NotFoundPage.d.ts +0 -1
- package/dist/src/components/NotFoundPage.js +0 -3
- package/dist/src/components/bottom-panel.d.ts +0 -1
- package/dist/src/components/bottom-panel.js +0 -15
- package/dist/src/components/flow/base-edge.d.ts +0 -3
- package/dist/src/components/flow/base-edge.js +0 -39
- package/dist/src/components/flow/flow-loader.d.ts +0 -1
- package/dist/src/components/flow/flow-loader.js +0 -4
- package/dist/src/components/flow/flow-page.d.ts +0 -1
- package/dist/src/components/flow/flow-page.js +0 -25
- package/dist/src/components/flow/flow-tab-menu-item.d.ts +0 -1
- package/dist/src/components/flow/flow-tab-menu-item.js +0 -18
- package/dist/src/components/flow/flow-view.d.ts +0 -12
- package/dist/src/components/flow/flow-view.js +0 -22
- package/dist/src/components/flow/hooks/use-get-flow-state.d.ts +0 -10
- package/dist/src/components/flow/hooks/use-get-flow-state.js +0 -133
- package/dist/src/components/flow/hooks/use-save-workflow-config.d.ts +0 -2
- package/dist/src/components/flow/hooks/use-save-workflow-config.js +0 -22
- package/dist/src/components/flow/node-organizer.d.ts +0 -10
- package/dist/src/components/flow/node-organizer.js +0 -82
- package/dist/src/components/flow/nodes/api-flow-node.d.ts +0 -2
- package/dist/src/components/flow/nodes/api-flow-node.js +0 -5
- package/dist/src/components/flow/nodes/cron-flow-node.d.ts +0 -2
- package/dist/src/components/flow/nodes/cron-flow-node.js +0 -5
- package/dist/src/components/flow/nodes/event-flow-node.d.ts +0 -2
- package/dist/src/components/flow/nodes/event-flow-node.js +0 -5
- package/dist/src/components/flow/nodes/noop-flow-node.d.ts +0 -2
- package/dist/src/components/flow/nodes/noop-flow-node.js +0 -5
- package/dist/src/components/header/deploy-button.d.ts +0 -1
- package/dist/src/components/header/deploy-button.js +0 -28
- package/dist/src/components/header/header.d.ts +0 -2
- package/dist/src/components/header/header.js +0 -23
- package/dist/src/components/root-motia.d.ts +0 -2
- package/dist/src/components/root-motia.js +0 -7
- package/dist/src/components/top-panel.d.ts +0 -1
- package/dist/src/components/top-panel.js +0 -15
- package/dist/src/components/tutorial/engine/tutorial-engine.d.ts +0 -12
- package/dist/src/components/tutorial/engine/tutorial-engine.js +0 -36
- package/dist/src/components/tutorial/engine/tutorial-types.d.ts +0 -22
- package/dist/src/components/tutorial/engine/tutorial-types.js +0 -1
- package/dist/src/components/tutorial/engine/workbench-xpath.d.ts +0 -45
- package/dist/src/components/tutorial/engine/workbench-xpath.js +0 -45
- package/dist/src/components/tutorial/hooks/tutorial-utils.d.ts +0 -1
- package/dist/src/components/tutorial/hooks/tutorial-utils.js +0 -17
- package/dist/src/components/tutorial/hooks/use-tutorial-engine.d.ts +0 -15
- package/dist/src/components/tutorial/hooks/use-tutorial-engine.js +0 -183
- package/dist/src/components/tutorial/hooks/use-tutorial.d.ts +0 -5
- package/dist/src/components/tutorial/hooks/use-tutorial.js +0 -10
- package/dist/src/components/tutorial/tutorial-button.d.ts +0 -2
- package/dist/src/components/tutorial/tutorial-button.js +0 -21
- package/dist/src/components/tutorial/tutorial-step.d.ts +0 -14
- package/dist/src/components/tutorial/tutorial-step.js +0 -19
- package/dist/src/components/tutorial/tutorial.d.ts +0 -2
- package/dist/src/components/tutorial/tutorial.js +0 -32
- package/dist/src/components/ui/json-editor.d.ts +0 -12
- package/dist/src/components/ui/json-editor.js +0 -35
- package/dist/src/components/ui/table.d.ts +0 -10
- package/dist/src/components/ui/table.js +0 -20
- package/dist/src/components/ui/theme-toggle.d.ts +0 -2
- package/dist/src/components/ui/theme-toggle.js +0 -19
- package/dist/src/components/ui/tooltip.d.ts +0 -6
- package/dist/src/components/ui/tooltip.js +0 -3
- package/dist/src/hooks/use-debounced.d.ts +0 -1
- package/dist/src/hooks/use-debounced.js +0 -18
- package/dist/src/hooks/use-fetch-flows.d.ts +0 -1
- package/dist/src/hooks/use-fetch-flows.js +0 -26
- package/dist/src/hooks/use-mobile.d.ts +0 -1
- package/dist/src/hooks/use-mobile.js +0 -15
- package/dist/src/hooks/use-update-handle-positions.d.ts +0 -10
- package/dist/src/hooks/use-update-handle-positions.js +0 -35
- package/dist/src/lib/__tests__/utils.test.d.ts +0 -1
- package/dist/src/lib/__tests__/utils.test.js +0 -94
- package/dist/src/lib/motia-analytics.d.ts +0 -38
- package/dist/src/lib/motia-analytics.js +0 -132
- package/dist/src/lib/plugins.d.ts +0 -2
- package/dist/src/lib/plugins.js +0 -105
- package/dist/src/lib/utils.d.ts +0 -7
- package/dist/src/lib/utils.js +0 -34
- package/dist/src/main.d.ts +0 -2
- package/dist/src/main.js +0 -17
- package/dist/src/project-view-mode.d.ts +0 -1
- package/dist/src/project-view-mode.js +0 -20
- package/dist/src/publicComponents/api-node.d.ts +0 -5
- package/dist/src/publicComponents/api-node.js +0 -5
- package/dist/src/publicComponents/base-node/base-handle.d.ts +0 -9
- package/dist/src/publicComponents/base-node/base-handle.js +0 -8
- package/dist/src/publicComponents/base-node/base-node.d.ts +0 -15
- package/dist/src/publicComponents/base-node/base-node.js +0 -30
- package/dist/src/publicComponents/base-node/code-display.d.ts +0 -9
- package/dist/src/publicComponents/base-node/code-display.js +0 -64
- package/dist/src/publicComponents/base-node/emits.d.ts +0 -5
- package/dist/src/publicComponents/base-node/emits.js +0 -5
- package/dist/src/publicComponents/base-node/feature-card.d.ts +0 -10
- package/dist/src/publicComponents/base-node/feature-card.js +0 -5
- package/dist/src/publicComponents/base-node/language-indicator.d.ts +0 -10
- package/dist/src/publicComponents/base-node/language-indicator.js +0 -29
- package/dist/src/publicComponents/base-node/node-header.d.ts +0 -13
- package/dist/src/publicComponents/base-node/node-header.js +0 -30
- package/dist/src/publicComponents/base-node/node-sidebar.d.ts +0 -14
- package/dist/src/publicComponents/base-node/node-sidebar.js +0 -9
- package/dist/src/publicComponents/base-node/subscribe.d.ts +0 -4
- package/dist/src/publicComponents/base-node/subscribe.js +0 -4
- package/dist/src/publicComponents/cron-node.d.ts +0 -4
- package/dist/src/publicComponents/cron-node.js +0 -6
- package/dist/src/publicComponents/event-node.d.ts +0 -4
- package/dist/src/publicComponents/event-node.js +0 -5
- package/dist/src/publicComponents/node-props.d.ts +0 -21
- package/dist/src/publicComponents/node-props.js +0 -1
- package/dist/src/publicComponents/noop-node.d.ts +0 -4
- package/dist/src/publicComponents/noop-node.js +0 -5
- package/dist/src/setupTests.d.ts +0 -1
- package/dist/src/setupTests.js +0 -1
- package/dist/src/stores/use-app-tabs-store.d.ts +0 -16
- package/dist/src/stores/use-app-tabs-store.js +0 -31
- package/dist/src/stores/use-flow-store.d.ts +0 -21
- package/dist/src/stores/use-flow-store.js +0 -16
- package/dist/src/stores/use-global-store.d.ts +0 -18
- package/dist/src/stores/use-global-store.js +0 -12
- package/dist/src/stores/use-motia-config-store.d.ts +0 -12
- package/dist/src/stores/use-motia-config-store.js +0 -24
- package/dist/src/stores/use-tabs-store.d.ts +0 -19
- package/dist/src/stores/use-tabs-store.js +0 -22
- package/dist/src/system-view-mode.d.ts +0 -1
- package/dist/src/system-view-mode.js +0 -10
- package/dist/src/types/endpoint.d.ts +0 -14
- package/dist/src/types/endpoint.js +0 -1
- package/dist/src/types/file.d.ts +0 -7
- package/dist/src/types/file.js +0 -1
- package/dist/src/types/flow.d.ts +0 -115
- package/dist/src/types/flow.js +0 -1
- package/dist/tsconfig.app.tsbuildinfo +0 -1
- package/dist/tsconfig.node.tsbuildinfo +0 -1
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validatePluginConfig = validatePluginConfig;
|
|
4
|
-
exports.validatePlugins = validatePlugins;
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
const zod_1 = require("zod");
|
|
7
|
-
const types_1 = require("./types");
|
|
8
|
-
const utils_1 = require("./utils");
|
|
9
|
-
/**
|
|
10
|
-
* Zod schema for WorkbenchPlugin configuration.
|
|
11
|
-
* Provides runtime type validation with detailed error messages.
|
|
12
|
-
*/
|
|
13
|
-
const WorkbenchPluginSchema = zod_1.z.object({
|
|
14
|
-
packageName: zod_1.z
|
|
15
|
-
.string()
|
|
16
|
-
.min(1, 'packageName is required and cannot be empty')
|
|
17
|
-
.refine((name) => name.startsWith('~/') || name.startsWith('@') || /^[a-z0-9-_]+$/i.test(name), {
|
|
18
|
-
message: 'packageName must be a valid npm package name or local path (starting with ~/)',
|
|
19
|
-
}),
|
|
20
|
-
componentName: zod_1.z.string().optional(),
|
|
21
|
-
position: zod_1.z
|
|
22
|
-
.enum(['top', 'bottom'])
|
|
23
|
-
.optional()
|
|
24
|
-
.refine((pos) => pos === undefined || (0, types_1.isValidPosition)(pos), {
|
|
25
|
-
message: 'position must be either "top" or "bottom"',
|
|
26
|
-
}),
|
|
27
|
-
label: zod_1.z.string().optional(),
|
|
28
|
-
labelIcon: zod_1.z.string().optional(),
|
|
29
|
-
cssImports: zod_1.z.array(zod_1.z.string()).optional(),
|
|
30
|
-
props: zod_1.z.record(zod_1.z.any(), zod_1.z.any()).optional(),
|
|
31
|
-
});
|
|
32
|
-
/**
|
|
33
|
-
* Validates a single plugin configuration.
|
|
34
|
-
*
|
|
35
|
-
* @param plugin - The plugin configuration to validate
|
|
36
|
-
* @param index - The index of the plugin in the array (for error messages)
|
|
37
|
-
* @returns A validation result with errors, warnings, and normalized plugin
|
|
38
|
-
*/
|
|
39
|
-
function validatePluginConfig(plugin, index) {
|
|
40
|
-
const errors = [];
|
|
41
|
-
const warnings = [];
|
|
42
|
-
if (typeof plugin !== 'object' || plugin === null) {
|
|
43
|
-
return {
|
|
44
|
-
valid: false,
|
|
45
|
-
errors: [`Plugin at index ${index}: expected object, got ${typeof plugin}`],
|
|
46
|
-
warnings: [],
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
const result = WorkbenchPluginSchema.safeParse(plugin);
|
|
51
|
-
if (!result.success) {
|
|
52
|
-
result.error.issues.forEach((err) => {
|
|
53
|
-
const path = err.path.join('.');
|
|
54
|
-
errors.push(`Plugin at index ${index}, field "${path}": ${err.message}`);
|
|
55
|
-
});
|
|
56
|
-
return { valid: false, errors, warnings };
|
|
57
|
-
}
|
|
58
|
-
const validatedPlugin = result.data;
|
|
59
|
-
if ((0, utils_1.isLocalPlugin)(validatedPlugin.packageName)) {
|
|
60
|
-
const resolvedPath = (0, utils_1.resolveLocalPath)(validatedPlugin.packageName);
|
|
61
|
-
if (!(0, fs_1.existsSync)(resolvedPath)) {
|
|
62
|
-
warnings.push(`Plugin at index ${index}: local path "${validatedPlugin.packageName}" does not exist at "${resolvedPath}". ` +
|
|
63
|
-
`Make sure the path is correct relative to the project root.`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (!validatedPlugin.label) {
|
|
67
|
-
warnings.push(`Plugin at index ${index}: "label" not specified, will use default "${types_1.CONSTANTS.DEFAULTS.LABEL}"`);
|
|
68
|
-
}
|
|
69
|
-
if (!validatedPlugin.labelIcon) {
|
|
70
|
-
warnings.push(`Plugin at index ${index}: "labelIcon" not specified, will use default "${types_1.CONSTANTS.DEFAULTS.ICON}"`);
|
|
71
|
-
}
|
|
72
|
-
if (!validatedPlugin.position) {
|
|
73
|
-
warnings.push(`Plugin at index ${index}: "position" not specified, will use default "${types_1.CONSTANTS.DEFAULTS.POSITION}"`);
|
|
74
|
-
}
|
|
75
|
-
if (validatedPlugin.props && Object.keys(validatedPlugin.props).length === 0) {
|
|
76
|
-
warnings.push(`Plugin at index ${index}: "props" is an empty object`);
|
|
77
|
-
}
|
|
78
|
-
if (validatedPlugin.cssImports) {
|
|
79
|
-
if (validatedPlugin.cssImports.length === 0) {
|
|
80
|
-
warnings.push(`Plugin at index ${index}: "cssImports" is an empty array`);
|
|
81
|
-
}
|
|
82
|
-
validatedPlugin.cssImports.forEach((cssImport, cssIndex) => {
|
|
83
|
-
if (!cssImport || cssImport.trim() === '') {
|
|
84
|
-
warnings.push(`Plugin at index ${index}: cssImport at index ${cssIndex} is empty or whitespace`);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
valid: true,
|
|
90
|
-
errors: [],
|
|
91
|
-
warnings,
|
|
92
|
-
plugin: validatedPlugin,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
return {
|
|
97
|
-
valid: false,
|
|
98
|
-
errors: [`Plugin at index ${index}: unexpected validation error: ${error}`],
|
|
99
|
-
warnings: [],
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Validates an array of plugin configurations.
|
|
105
|
-
*
|
|
106
|
-
* @param plugins - Array of plugin configurations to validate
|
|
107
|
-
* @param options - Validation options
|
|
108
|
-
* @returns Combined validation result for all plugins
|
|
109
|
-
*/
|
|
110
|
-
function validatePlugins(plugins, options = {}) {
|
|
111
|
-
const allErrors = [];
|
|
112
|
-
const allWarnings = [];
|
|
113
|
-
const validatedPlugins = [];
|
|
114
|
-
if (!Array.isArray(plugins)) {
|
|
115
|
-
return {
|
|
116
|
-
valid: false,
|
|
117
|
-
errors: [`Expected plugins to be an array, got ${typeof plugins}`],
|
|
118
|
-
warnings: [],
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
if (plugins.length === 0) {
|
|
122
|
-
console.warn('[motia-plugins] No plugins provided to validate');
|
|
123
|
-
return {
|
|
124
|
-
valid: true,
|
|
125
|
-
errors: [],
|
|
126
|
-
warnings: ['No plugins configured'],
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
for (let i = 0; i < plugins.length; i++) {
|
|
130
|
-
const result = validatePluginConfig(plugins[i], i);
|
|
131
|
-
allErrors.push(...result.errors);
|
|
132
|
-
allWarnings.push(...result.warnings);
|
|
133
|
-
if (result.valid && result.plugin) {
|
|
134
|
-
validatedPlugins.push(result.plugin);
|
|
135
|
-
}
|
|
136
|
-
if (options.failFast && result.errors.length > 0) {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const packageNames = validatedPlugins.map((p) => p.packageName);
|
|
141
|
-
const duplicates = packageNames.filter((name, index) => packageNames.indexOf(name) !== index);
|
|
142
|
-
if (duplicates.length > 0) {
|
|
143
|
-
const uniqueDuplicates = [...new Set(duplicates)];
|
|
144
|
-
uniqueDuplicates.forEach((dup) => {
|
|
145
|
-
allWarnings.push(`Duplicate package name found: "${dup}". This may cause conflicts.`);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
const valid = allErrors.length === 0;
|
|
149
|
-
if (valid) {
|
|
150
|
-
console.log(`[motia-plugins] ✓ Validated ${validatedPlugins.length} plugin(s) successfully`);
|
|
151
|
-
if (allWarnings.length > 0) {
|
|
152
|
-
console.warn(`[motia-plugins] Found ${allWarnings.length} warning(s)`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
console.error(`[motia-plugins] ✗ Validation failed with ${allErrors.length} error(s)`);
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
valid,
|
|
160
|
-
errors: allErrors,
|
|
161
|
-
warnings: allWarnings,
|
|
162
|
-
};
|
|
163
|
-
}
|
package/dist/src/App.d.ts
DELETED
package/dist/src/App.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { memo, useEffect, useMemo } from 'react';
|
|
3
|
-
import { FlowPage } from './components/flow/flow-page';
|
|
4
|
-
import { FlowTabMenuItem } from './components/flow/flow-tab-menu-item';
|
|
5
|
-
import { registerPluginTabs } from './lib/plugins';
|
|
6
|
-
import { getViewModeFromURL } from './lib/utils';
|
|
7
|
-
import { ProjectViewMode } from './project-view-mode';
|
|
8
|
-
import { TabLocation, useAppTabsStore } from './stores/use-app-tabs-store';
|
|
9
|
-
import { SystemViewMode } from './system-view-mode';
|
|
10
|
-
const TAB_IDS = {
|
|
11
|
-
FLOW: 'flow',
|
|
12
|
-
LOGS: 'logs',
|
|
13
|
-
};
|
|
14
|
-
const topTabs = [
|
|
15
|
-
{
|
|
16
|
-
id: TAB_IDS.FLOW,
|
|
17
|
-
tabLabel: FlowTabMenuItem,
|
|
18
|
-
content: FlowPage,
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
export const App = memo(() => {
|
|
22
|
-
const setTabs = useAppTabsStore((state) => state.setTabs);
|
|
23
|
-
const addTab = useAppTabsStore((state) => state.addTab);
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
const timeout = setTimeout(() => {
|
|
26
|
-
setTabs(TabLocation.TOP, topTabs);
|
|
27
|
-
registerPluginTabs(addTab);
|
|
28
|
-
}, 10);
|
|
29
|
-
return () => clearTimeout(timeout);
|
|
30
|
-
}, [setTabs, addTab]);
|
|
31
|
-
const viewMode = useMemo(getViewModeFromURL, []);
|
|
32
|
-
const ViewComponent = viewMode === 'project' ? ProjectViewMode : SystemViewMode;
|
|
33
|
-
return _jsx(ViewComponent, {});
|
|
34
|
-
});
|
|
35
|
-
App.displayName = 'App';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const NotFoundPage: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Header } from './header/header';
|
|
3
|
-
export const NotFoundPage = () => (_jsxs("div", { className: "grid grid-rows-[auto_1fr] h-screen bg-background text-foreground", children: [_jsx(Header, {}), _jsxs("div", { className: "flex flex-col items-center justify-center", children: [_jsx("h1", { className: "text-4xl font-bold mb-4", children: "404 \u2013 Page Not Found" }), _jsx("p", { className: "text-lg opacity-80 mb-6", children: "This route doesn\u2019t exist." })] })] }));
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const BottomPanel: import("react").MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { CollapsiblePanel, TabsContent, TabsList, TabsTrigger } from '@motiadev/ui';
|
|
3
|
-
import { memo } from 'react';
|
|
4
|
-
import { useShallow } from 'zustand/react/shallow';
|
|
5
|
-
import { TabLocation, useAppTabsStore } from '../stores/use-app-tabs-store';
|
|
6
|
-
import { useTabsStore } from '../stores/use-tabs-store';
|
|
7
|
-
const bottomTabsSelector = (state) => state.tabs[TabLocation.BOTTOM];
|
|
8
|
-
const bottomPanelId = 'bottom-panel';
|
|
9
|
-
export const BottomPanel = memo(() => {
|
|
10
|
-
const defaultTab = useTabsStore((state) => state.tab.bottom);
|
|
11
|
-
const setBottomTab = useTabsStore((state) => state.setBottomTab);
|
|
12
|
-
const tabs = useAppTabsStore(useShallow(bottomTabsSelector));
|
|
13
|
-
return (_jsx(CollapsiblePanel, { id: bottomPanelId, variant: 'tabs', defaultTab: defaultTab, onTabChange: setBottomTab, header: _jsx(TabsList, { children: tabs.map(({ id, tabLabel: Label }) => (_jsx(TabsTrigger, { value: id, "data-testid": `${id.toLowerCase()}-link`, className: "cursor-pointer", children: _jsx(Label, {}) }, id))) }), children: tabs.map(({ id, content: Element }) => (_jsx(TabsContent, { value: id, className: "h-full", children: _jsx(Element, {}) }, id))) }));
|
|
14
|
-
});
|
|
15
|
-
BottomPanel.displayName = 'BottomPanel';
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { cn, useThemeStore } from '@motiadev/ui';
|
|
3
|
-
import { BaseEdge as BaseReactFlowEdge, EdgeLabelRenderer, getSmoothStepPath } from '@xyflow/react';
|
|
4
|
-
import { cva } from 'class-variance-authority';
|
|
5
|
-
const labelVariants = cva('absolute pointer-events-all text-cs border p-1 px-2', {
|
|
6
|
-
variants: {
|
|
7
|
-
color: {
|
|
8
|
-
default: 'border-[#b3b3b3] bg-[#060014] text-gray-100 font-semibold border-solid rounded-full',
|
|
9
|
-
conditional: 'bg-amber-300 border-amber-950 text-amber-950 border-solid font-semibold italic rounded-lg',
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
defaultVariants: {
|
|
13
|
-
color: 'default',
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
export const BaseEdge = (props) => {
|
|
17
|
-
const theme = useThemeStore((state) => state.theme);
|
|
18
|
-
const { sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data } = props;
|
|
19
|
-
const label = data?.label;
|
|
20
|
-
const labelVariant = data?.labelVariant;
|
|
21
|
-
const virtualColor = theme === 'dark' ? 'rgb(225, 225, 225)' : 'rgb(85, 85, 85)';
|
|
22
|
-
const [edgePath, labelX, labelY] = getSmoothStepPath({
|
|
23
|
-
sourceX,
|
|
24
|
-
sourceY,
|
|
25
|
-
targetX,
|
|
26
|
-
targetY,
|
|
27
|
-
sourcePosition,
|
|
28
|
-
targetPosition,
|
|
29
|
-
borderRadius: 20,
|
|
30
|
-
offset: 10,
|
|
31
|
-
});
|
|
32
|
-
return (_jsxs(_Fragment, { children: [_jsx(BaseReactFlowEdge, { path: edgePath, style: {
|
|
33
|
-
stroke: data?.variant === 'virtual' ? virtualColor : '#0094FF',
|
|
34
|
-
strokeWidth: 2,
|
|
35
|
-
shapeRendering: 'geometricPrecision',
|
|
36
|
-
fill: 'none',
|
|
37
|
-
mixBlendMode: 'screen',
|
|
38
|
-
}, className: "edge-animated" }), label && (_jsx(EdgeLabelRenderer, { children: _jsx("div", { className: cn(labelVariants({ color: labelVariant })), style: { transform: `translateX(-50%) translateY(-50%) translate(${labelX}px, ${labelY}px)` }, children: _jsx("div", { className: "text-xs font-mono", children: label }) }) }))] }));
|
|
39
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const FlowLoader: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const FlowPage: import("react").MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element | null>;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useStreamItem } from '@motiadev/stream-client-react';
|
|
3
|
-
import { Button, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@motiadev/ui';
|
|
4
|
-
import { ReactFlowProvider } from '@xyflow/react';
|
|
5
|
-
import { ExternalLink, Workflow } from 'lucide-react';
|
|
6
|
-
import { memo, useMemo } from 'react';
|
|
7
|
-
import { useShallow } from 'zustand/react/shallow';
|
|
8
|
-
import { motiaAnalytics } from '@/lib/motia-analytics';
|
|
9
|
-
import { useFlowStore } from '@/stores/use-flow-store';
|
|
10
|
-
import { FlowView } from './flow-view';
|
|
11
|
-
export const FlowPage = memo(() => {
|
|
12
|
-
const selectedFlowId = useFlowStore((state) => state.selectedFlowId);
|
|
13
|
-
const flows = useFlowStore(useShallow((state) => Object.values(state.flows)));
|
|
14
|
-
const streamItemArgs = useMemo(() => ({ streamName: '__motia.flows', groupId: 'default', id: selectedFlowId ?? '' }), [selectedFlowId]);
|
|
15
|
-
const { data: flow } = useStreamItem(streamItemArgs);
|
|
16
|
-
const streamItemArgsConfig = useMemo(() => ({ streamName: '__motia.flowsConfig', groupId: 'default', id: selectedFlowId ?? '' }), [selectedFlowId]);
|
|
17
|
-
const { data: flowConfig } = useStreamItem(streamItemArgsConfig);
|
|
18
|
-
if (flows.length === 0 || flow?.error) {
|
|
19
|
-
return (_jsx("div", { className: "flex w-full h-full bg-background", children: _jsxs(Empty, { children: [_jsxs(EmptyHeader, { children: [_jsx(EmptyMedia, { variant: "icon", children: _jsx(Workflow, {}) }), flow?.error ? (_jsxs(_Fragment, { children: [_jsx(EmptyTitle, { children: "Error loading flow" }), _jsx(EmptyDescription, { children: flow.error })] })) : (_jsxs(_Fragment, { children: [_jsx(EmptyTitle, { children: "No flows registered" }), _jsx(EmptyDescription, { children: "You haven't registered any flows yet. Get started by registering your first flow." })] }))] }), _jsx(EmptyContent, { children: _jsx(Button, { variant: "link", asChild: true, size: "sm", children: _jsxs("a", { href: "https://www.motia.dev/docs/development-guide/flows", target: "_blank", rel: "noopener noreferrer", onClick: () => motiaAnalytics.track('flows_docs_link_clicked'), children: ["Learn more ", _jsx(ExternalLink, {})] }) }) })] }) }));
|
|
20
|
-
}
|
|
21
|
-
if (!flow)
|
|
22
|
-
return null;
|
|
23
|
-
return (_jsx(ReactFlowProvider, { children: _jsx(FlowView, { flow: flow, flowConfig: flowConfig }) }));
|
|
24
|
-
});
|
|
25
|
-
FlowPage.displayName = 'FlowPage';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const FlowTabMenuItem: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@motiadev/ui';
|
|
3
|
-
import { ChevronsUpDown, Workflow } from 'lucide-react';
|
|
4
|
-
import { useShallow } from 'zustand/react/shallow';
|
|
5
|
-
import { useFetchFlows } from '@/hooks/use-fetch-flows';
|
|
6
|
-
import { motiaAnalytics } from '@/lib/motia-analytics';
|
|
7
|
-
import { useFlowStore } from '@/stores/use-flow-store';
|
|
8
|
-
export const FlowTabMenuItem = () => {
|
|
9
|
-
useFetchFlows();
|
|
10
|
-
const selectFlowId = useFlowStore((state) => state.selectFlowId);
|
|
11
|
-
const flows = useFlowStore(useShallow((state) => Object.values(state.flows)));
|
|
12
|
-
const selectedFlowId = useFlowStore((state) => state.selectedFlowId);
|
|
13
|
-
const handleFlowSelect = (flowId) => {
|
|
14
|
-
selectFlowId(flowId);
|
|
15
|
-
motiaAnalytics.track('flow_selected', { flow: flowId });
|
|
16
|
-
};
|
|
17
|
-
return (_jsxs("div", { className: "flex flex-row justify-center items-center gap-2 cursor-pointer", children: [_jsx(Workflow, {}), flows.length > 0 && selectedFlowId ? selectedFlowId : 'Flows', flows.length > 0 && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("div", { className: "flex flex-row justify-center items-center gap-2 cursor-pointer", "data-testid": "flows-dropdown-trigger", children: _jsx(ChevronsUpDown, { className: "size-4" }) }) }), _jsx(DropdownMenuContent, { className: "bg-background text-foreground flows-dropdown", children: flows.map((item) => (_jsx(DropdownMenuItem, { "data-testid": `dropdown-${item}`, className: "cursor-pointer gap-2 flow-link", onSelect: () => handleFlowSelect(item), children: item }, `dropdown-${item}`))) })] }))] }));
|
|
18
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { type Edge as ReactFlowEdge, type Node as ReactFlowNode } from '@xyflow/react';
|
|
2
|
-
import type React from 'react';
|
|
3
|
-
import type { EdgeData, FlowConfigResponse, FlowResponse, NodeData } from '@/types/flow';
|
|
4
|
-
import '@xyflow/react/dist/style.css';
|
|
5
|
-
export type FlowNode = ReactFlowNode<NodeData>;
|
|
6
|
-
export type FlowEdge = ReactFlowEdge<EdgeData>;
|
|
7
|
-
type Props = {
|
|
8
|
-
flow: FlowResponse;
|
|
9
|
-
flowConfig: FlowConfigResponse;
|
|
10
|
-
};
|
|
11
|
-
export declare const FlowView: React.FC<Props>;
|
|
12
|
-
export {};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Background, BackgroundVariant, ReactFlow, } from '@xyflow/react';
|
|
3
|
-
import { useCallback, useState } from 'react';
|
|
4
|
-
import { BaseEdge } from './base-edge';
|
|
5
|
-
import { FlowLoader } from './flow-loader';
|
|
6
|
-
import { useGetFlowState } from './hooks/use-get-flow-state';
|
|
7
|
-
import { NodeOrganizer } from './node-organizer';
|
|
8
|
-
import '@xyflow/react/dist/style.css';
|
|
9
|
-
import { BackgroundEffect } from '@motiadev/ui';
|
|
10
|
-
const edgeTypes = {
|
|
11
|
-
base: BaseEdge,
|
|
12
|
-
};
|
|
13
|
-
export const FlowView = ({ flow, flowConfig }) => {
|
|
14
|
-
const { nodes, edges, onNodesChange, onEdgesChange, nodeTypes } = useGetFlowState(flow, flowConfig);
|
|
15
|
-
const [initialized, setInitialized] = useState(false);
|
|
16
|
-
const onInitialized = useCallback(() => setInitialized(true), []);
|
|
17
|
-
const onNodesChangeHandler = useCallback((changes) => onNodesChange(changes), [onNodesChange]);
|
|
18
|
-
if (!nodeTypes) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
return (_jsxs("div", { className: "w-full h-full relative", children: [!initialized && _jsx(FlowLoader, {}), _jsxs(ReactFlow, { minZoom: 0.1, nodes: nodes, edges: edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onNodesChange: onNodesChangeHandler, onEdgesChange: onEdgesChange, className: "isolate", children: [_jsx(BackgroundEffect, {}), _jsx(Background, { variant: BackgroundVariant.Dots, gap: 20, size: 1 }), _jsx(NodeOrganizer, { onInitialized: onInitialized, nodes: nodes, edges: edges })] })] }));
|
|
22
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type Edge, type Node } from '@xyflow/react';
|
|
2
|
-
import type React from 'react';
|
|
3
|
-
import type { EdgeData, FlowConfigResponse, FlowResponse, NodeData } from '@/types/flow';
|
|
4
|
-
export declare const useGetFlowState: (flow: FlowResponse, flowConfig: FlowConfigResponse) => {
|
|
5
|
-
nodes: Node<NodeData>[];
|
|
6
|
-
edges: Edge<EdgeData>[];
|
|
7
|
-
onNodesChange: import("@xyflow/react").OnNodesChange<Node<NodeData>>;
|
|
8
|
-
onEdgesChange: import("@xyflow/react").OnEdgesChange<Edge<EdgeData>>;
|
|
9
|
-
nodeTypes: Record<string, React.ComponentType<any>>;
|
|
10
|
-
};
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { useEdgesState, useNodesState } from '@xyflow/react';
|
|
2
|
-
import isEqual from 'fast-deep-equal';
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import { ApiFlowNode } from '../nodes/api-flow-node';
|
|
5
|
-
import { CronFlowNode } from '../nodes/cron-flow-node';
|
|
6
|
-
import { EventFlowNode } from '../nodes/event-flow-node';
|
|
7
|
-
import { NoopFlowNode } from '../nodes/noop-flow-node';
|
|
8
|
-
import { useSaveWorkflowConfig } from './use-save-workflow-config';
|
|
9
|
-
const DEFAULT_CONFIG = { x: 0, y: 0 };
|
|
10
|
-
const getNodePosition = (flowConfig, stepName) => {
|
|
11
|
-
return flowConfig?.config[stepName] || DEFAULT_CONFIG;
|
|
12
|
-
};
|
|
13
|
-
const nodeComponentCache = new Map();
|
|
14
|
-
const BASE_NODE_TYPES = {
|
|
15
|
-
event: EventFlowNode,
|
|
16
|
-
api: ApiFlowNode,
|
|
17
|
-
noop: NoopFlowNode,
|
|
18
|
-
cron: CronFlowNode,
|
|
19
|
-
};
|
|
20
|
-
async function importFlow(flow, flowConfig) {
|
|
21
|
-
const nodeTypes = { ...BASE_NODE_TYPES };
|
|
22
|
-
const customNodePromises = flow.steps
|
|
23
|
-
.filter((step) => step.nodeComponentPath)
|
|
24
|
-
.map(async (step) => {
|
|
25
|
-
const path = step.nodeComponentPath;
|
|
26
|
-
// Check cache first
|
|
27
|
-
if (nodeComponentCache.has(path)) {
|
|
28
|
-
nodeTypes[path] = nodeComponentCache.get(path);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
const module = await import(/* @vite-ignore */ `/@fs/${path}`);
|
|
33
|
-
const component = module.Node ?? module.default;
|
|
34
|
-
nodeComponentCache.set(path, component);
|
|
35
|
-
nodeTypes[path] = component;
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
console.error(`Failed to load custom node component: ${path}`, error);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
await Promise.all(customNodePromises);
|
|
42
|
-
const nodes = flow.steps.map((step) => ({
|
|
43
|
-
id: step.id,
|
|
44
|
-
type: step.nodeComponentPath || step.type,
|
|
45
|
-
filePath: step.filePath,
|
|
46
|
-
position: step.filePath ? getNodePosition(flowConfig, step.filePath) : DEFAULT_CONFIG,
|
|
47
|
-
data: { ...step, nodeConfig: step.filePath ? getNodePosition(flowConfig, step.filePath) : DEFAULT_CONFIG },
|
|
48
|
-
language: step.language,
|
|
49
|
-
}));
|
|
50
|
-
const edges = flow.edges.map((edge) => ({
|
|
51
|
-
...edge,
|
|
52
|
-
type: 'base',
|
|
53
|
-
}));
|
|
54
|
-
return { nodes, edges, nodeTypes };
|
|
55
|
-
}
|
|
56
|
-
export const useGetFlowState = (flow, flowConfig) => {
|
|
57
|
-
const [nodeTypes, setNodeTypes] = useState(BASE_NODE_TYPES);
|
|
58
|
-
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
|
59
|
-
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
|
60
|
-
const saveConfig = useSaveWorkflowConfig();
|
|
61
|
-
const flowIdRef = useRef('');
|
|
62
|
-
const saveTimeoutRef = useRef(null);
|
|
63
|
-
const lastSavedConfigRef = useRef(null);
|
|
64
|
-
const lastSavedFlowRef = useRef(null);
|
|
65
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
66
|
-
const memoizedFlowConfig = useMemo(() => flowConfig, [flowConfig?.id, flowConfig?.config]);
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
if (!flow || flow.error)
|
|
69
|
-
return;
|
|
70
|
-
const hasSameConfig = isEqual(lastSavedConfigRef.current, memoizedFlowConfig?.config);
|
|
71
|
-
const hasSameFlow = isEqual(lastSavedFlowRef.current, flow);
|
|
72
|
-
if (hasSameConfig && hasSameFlow)
|
|
73
|
-
return;
|
|
74
|
-
lastSavedConfigRef.current = memoizedFlowConfig?.config;
|
|
75
|
-
flowIdRef.current = flow.id;
|
|
76
|
-
lastSavedFlowRef.current = flow;
|
|
77
|
-
const importFlowAsync = async () => {
|
|
78
|
-
try {
|
|
79
|
-
const { nodes, edges, nodeTypes } = await importFlow(flow, flowConfig);
|
|
80
|
-
setNodes(nodes);
|
|
81
|
-
setEdges(edges);
|
|
82
|
-
setNodeTypes(nodeTypes);
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
console.error('Failed to import flow:', error);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
importFlowAsync();
|
|
89
|
-
}, [flow, memoizedFlowConfig, setNodes, setEdges, flowConfig]);
|
|
90
|
-
const saveFlowConfig = useCallback((nodesToSave) => {
|
|
91
|
-
if (saveTimeoutRef.current) {
|
|
92
|
-
clearTimeout(saveTimeoutRef.current);
|
|
93
|
-
}
|
|
94
|
-
saveTimeoutRef.current = setTimeout(async () => {
|
|
95
|
-
const steps = nodesToSave.reduce((acc, node) => {
|
|
96
|
-
if (node.data.filePath) {
|
|
97
|
-
acc[node.data.filePath] = {
|
|
98
|
-
x: Math.round(node.position.x),
|
|
99
|
-
y: Math.round(node.position.y),
|
|
100
|
-
};
|
|
101
|
-
if (node.data.nodeConfig?.sourceHandlePosition) {
|
|
102
|
-
acc[node.data.filePath].sourceHandlePosition = node.data.nodeConfig.sourceHandlePosition;
|
|
103
|
-
}
|
|
104
|
-
if (node.data.nodeConfig?.targetHandlePosition) {
|
|
105
|
-
acc[node.data.filePath].targetHandlePosition = node.data.nodeConfig.targetHandlePosition;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return acc;
|
|
109
|
-
}, {});
|
|
110
|
-
if (!isEqual(steps, lastSavedConfigRef.current)) {
|
|
111
|
-
lastSavedConfigRef.current = steps;
|
|
112
|
-
const newConfig = { id: flowIdRef.current, config: steps };
|
|
113
|
-
try {
|
|
114
|
-
await saveConfig(newConfig);
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
console.error('Failed to save flow config:', error);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}, 300);
|
|
121
|
-
}, [saveConfig]);
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
if (nodes.length > 0) {
|
|
124
|
-
saveFlowConfig(nodes);
|
|
125
|
-
}
|
|
126
|
-
return () => {
|
|
127
|
-
if (saveTimeoutRef.current) {
|
|
128
|
-
clearTimeout(saveTimeoutRef.current);
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
}, [nodes, saveFlowConfig]);
|
|
132
|
-
return useMemo(() => ({ nodes, edges, onNodesChange, onEdgesChange, nodeTypes }), [nodes, edges, onNodesChange, onEdgesChange, nodeTypes]);
|
|
133
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
2
|
-
export const useSaveWorkflowConfig = () => {
|
|
3
|
-
return useCallback(async (body) => {
|
|
4
|
-
try {
|
|
5
|
-
const response = await fetch(`/__motia/flows/${body.id}/config`, {
|
|
6
|
-
method: 'POST',
|
|
7
|
-
headers: {
|
|
8
|
-
'Content-Type': 'application/json',
|
|
9
|
-
},
|
|
10
|
-
body: JSON.stringify(body),
|
|
11
|
-
});
|
|
12
|
-
if (!response.ok) {
|
|
13
|
-
throw new Error(`Failed to save config: ${response.statusText}`);
|
|
14
|
-
}
|
|
15
|
-
return await response.json();
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
console.error('Error saving workflow config:', error);
|
|
19
|
-
throw error;
|
|
20
|
-
}
|
|
21
|
-
}, []);
|
|
22
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type Edge, type Node } from '@xyflow/react';
|
|
2
|
-
import type React from 'react';
|
|
3
|
-
import type { EdgeData, NodeData } from '@/types/flow';
|
|
4
|
-
type Props = {
|
|
5
|
-
onInitialized: () => void;
|
|
6
|
-
nodes: Node<NodeData>[];
|
|
7
|
-
edges: Edge<EdgeData>[];
|
|
8
|
-
};
|
|
9
|
-
export declare const NodeOrganizer: React.FC<Props>;
|
|
10
|
-
export {};
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { useNodesInitialized, useReactFlow } from '@xyflow/react';
|
|
2
|
-
import dagre from 'dagre';
|
|
3
|
-
import isEqual from 'fast-deep-equal';
|
|
4
|
-
import { useEffect, useRef } from 'react';
|
|
5
|
-
const organizeNodes = (nodes, edges) => {
|
|
6
|
-
const dagreGraph = new dagre.graphlib.Graph({ directed: true, compound: false, multigraph: false });
|
|
7
|
-
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
8
|
-
dagreGraph.setGraph({ rankdir: 'LR', ranksep: 0, nodesep: 20, edgesep: 0 });
|
|
9
|
-
nodes.forEach((node) => {
|
|
10
|
-
if (node.position.x !== 0 || node.position.y !== 0) {
|
|
11
|
-
dagreGraph.setNode(node.id, {
|
|
12
|
-
width: node.measured?.width,
|
|
13
|
-
height: node.measured?.height,
|
|
14
|
-
x: node.position.x,
|
|
15
|
-
y: node.position.y,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
dagreGraph.setNode(node.id, {
|
|
20
|
-
width: node.measured?.width,
|
|
21
|
-
height: node.measured?.height,
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
edges.forEach((edge) => {
|
|
26
|
-
if (typeof edge.label === 'string') {
|
|
27
|
-
dagreGraph.setEdge(edge.source, edge.target, {
|
|
28
|
-
label: edge.label ?? '',
|
|
29
|
-
width: edge.label.length * 40, // Add width for the label
|
|
30
|
-
height: 30, // Add height for the label
|
|
31
|
-
labelpos: 'c', // Position label in center
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
dagreGraph.setEdge(edge.source, edge.target);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
dagre.layout(dagreGraph);
|
|
39
|
-
return nodes.map((node) => {
|
|
40
|
-
if (node.position.x !== 0 || node.position.y !== 0) {
|
|
41
|
-
return node;
|
|
42
|
-
}
|
|
43
|
-
const { x, y } = dagreGraph.node(node.id);
|
|
44
|
-
const position = {
|
|
45
|
-
x: x - (node.measured?.width ?? 0) / 2,
|
|
46
|
-
y: y - (node.measured?.height ?? 0) / 2,
|
|
47
|
-
};
|
|
48
|
-
return { ...node, position };
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
export const NodeOrganizer = ({ onInitialized, nodes, edges }) => {
|
|
52
|
-
const { setNodes, getNodes, getEdges, fitView } = useReactFlow();
|
|
53
|
-
const nodesInitialized = useNodesInitialized();
|
|
54
|
-
const initialized = useRef(false);
|
|
55
|
-
const lastNodesRef = useRef([]);
|
|
56
|
-
const lastEdgesRef = useRef([]);
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (nodesInitialized) {
|
|
59
|
-
if (isEqual(lastNodesRef.current, nodes) && isEqual(lastEdgesRef.current, edges)) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
lastNodesRef.current = nodes;
|
|
63
|
-
lastEdgesRef.current = edges;
|
|
64
|
-
try {
|
|
65
|
-
const nodesToOrganize = nodes.some((node) => node.position.x === 0 && node.position.y === 0);
|
|
66
|
-
if (nodesToOrganize) {
|
|
67
|
-
const organizedNodes = organizeNodes(nodes, edges);
|
|
68
|
-
setNodes(organizedNodes);
|
|
69
|
-
}
|
|
70
|
-
if (!initialized.current) {
|
|
71
|
-
initialized.current = true;
|
|
72
|
-
onInitialized();
|
|
73
|
-
setTimeout(() => fitView(), 1);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
console.error('Error organizing nodes:', error);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}, [nodesInitialized, onInitialized, setNodes, getNodes, getEdges, fitView, nodes, edges]);
|
|
81
|
-
return null;
|
|
82
|
-
};
|