@motiadev/workbench 0.8.2-beta.140-628177 → 0.8.2-beta.140-111855
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/components.json +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5 -5
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +3 -3
- package/dist/motia-plugin/__tests__/generator.test.js +97 -0
- package/dist/motia-plugin/__tests__/resolver.test.d.ts +1 -0
- package/dist/motia-plugin/__tests__/resolver.test.js +64 -0
- package/dist/motia-plugin/__tests__/validator.test.d.ts +1 -0
- package/dist/motia-plugin/__tests__/validator.test.js +59 -0
- package/dist/motia-plugin/generator.d.ts +78 -0
- package/dist/motia-plugin/generator.js +128 -0
- package/dist/motia-plugin/hmr.d.ts +19 -0
- package/dist/motia-plugin/hmr.js +66 -0
- package/dist/motia-plugin/index.d.ts +27 -0
- package/dist/motia-plugin/index.js +118 -0
- package/dist/motia-plugin/resolver.d.ts +63 -0
- package/dist/motia-plugin/resolver.js +92 -0
- package/dist/motia-plugin/types.d.ts +169 -0
- package/dist/motia-plugin/types.js +36 -0
- package/dist/motia-plugin/utils.d.ts +57 -0
- package/dist/motia-plugin/utils.js +75 -0
- package/dist/motia-plugin/validator.d.ts +19 -0
- package/dist/motia-plugin/validator.js +163 -0
- package/dist/postcss.config.mjs +1 -1
- package/dist/src/App.d.ts +1 -1
- package/dist/src/App.js +1 -18
- package/dist/src/components/flow/base-edge.d.ts +2 -2
- package/dist/src/components/flow/base-edge.js +1 -1
- package/dist/src/components/flow/flow-page.js +2 -2
- package/dist/src/components/flow/flow-tab-menu-item.js +2 -2
- package/dist/src/components/flow/flow-view.d.ts +3 -3
- package/dist/src/components/flow/hooks/use-get-flow-state.d.ts +2 -2
- package/dist/src/components/flow/hooks/use-get-flow-state.js +0 -4
- package/dist/src/components/flow/hooks/use-save-workflow-config.d.ts +1 -1
- package/dist/src/components/flow/node-organizer.d.ts +3 -3
- package/dist/src/components/flow/nodes/api-flow-node.d.ts +1 -1
- package/dist/src/components/flow/nodes/cron-flow-node.d.ts +1 -1
- package/dist/src/components/flow/nodes/event-flow-node.d.ts +1 -1
- package/dist/src/components/flow/nodes/noop-flow-node.d.ts +1 -1
- package/dist/src/components/header/deploy-button.js +2 -2
- package/dist/src/components/header/header.d.ts +1 -1
- package/dist/src/components/header/header.js +2 -2
- package/dist/src/components/root-motia.d.ts +2 -1
- package/dist/src/components/tutorial/engine/tutorial-engine.d.ts +1 -1
- package/dist/src/components/tutorial/hooks/use-tutorial-engine.d.ts +1 -1
- package/dist/src/components/tutorial/hooks/use-tutorial.d.ts +1 -1
- package/dist/src/components/tutorial/tutorial-button.d.ts +1 -1
- package/dist/src/components/tutorial/tutorial-button.js +1 -1
- package/dist/src/components/tutorial/tutorial-step.d.ts +2 -2
- package/dist/src/components/tutorial/tutorial-step.js +1 -1
- package/dist/src/components/tutorial/tutorial.css +8 -8
- package/dist/src/components/ui/json-editor.d.ts +1 -1
- package/dist/src/components/ui/json-editor.js +1 -1
- package/dist/src/components/ui/table.js +1 -1
- package/dist/src/components/ui/theme-toggle.d.ts +1 -1
- package/dist/src/components/ui/tooltip.d.ts +1 -1
- package/dist/src/hooks/use-fetch-flows.js +1 -1
- package/dist/src/hooks/use-update-handle-positions.d.ts +1 -1
- package/dist/src/index.css +5 -5
- package/dist/src/lib/plugins.js +3 -3
- package/dist/src/main.js +2 -3
- package/dist/src/project-view-mode.js +1 -1
- package/dist/src/publicComponents/api-node.d.ts +2 -2
- package/dist/src/publicComponents/base-node/base-handle.d.ts +3 -2
- package/dist/src/publicComponents/base-node/base-node.d.ts +3 -2
- package/dist/src/publicComponents/base-node/base-node.js +1 -1
- package/dist/src/publicComponents/base-node/code-display.d.ts +2 -2
- package/dist/src/publicComponents/base-node/code-display.js +1 -1
- package/dist/src/publicComponents/base-node/emits.d.ts +2 -2
- package/dist/src/publicComponents/base-node/feature-card.d.ts +2 -2
- package/dist/src/publicComponents/base-node/language-indicator.d.ts +2 -2
- package/dist/src/publicComponents/base-node/node-header.d.ts +3 -2
- package/dist/src/publicComponents/base-node/node-sidebar.d.ts +2 -2
- package/dist/src/publicComponents/base-node/subscribe.d.ts +1 -1
- package/dist/src/publicComponents/cron-node.d.ts +3 -2
- package/dist/src/publicComponents/event-node.d.ts +3 -2
- package/dist/src/publicComponents/node-props.d.ts +1 -1
- package/dist/src/publicComponents/noop-node.d.ts +3 -2
- package/dist/src/stores/use-global-store.d.ts +0 -6
- package/dist/src/stores/use-global-store.js +0 -6
- package/dist/src/types/flow.d.ts +1 -1
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/dist/tsconfig.node.tsbuildinfo +1 -1
- package/motia-plugin/__tests__/generator.test.ts +129 -0
- package/motia-plugin/__tests__/resolver.test.ts +82 -0
- package/motia-plugin/__tests__/validator.test.ts +71 -0
- package/motia-plugin/generator.ts +130 -0
- package/motia-plugin/hmr.ts +78 -0
- package/motia-plugin/index.ts +143 -0
- package/motia-plugin/resolver.ts +96 -0
- package/motia-plugin/types.ts +198 -0
- package/motia-plugin/utils.ts +70 -0
- package/motia-plugin/validator.ts +197 -0
- package/package.json +9 -9
- package/postcss.config.mjs +1 -1
- package/dist/src/components/observability/events/code/function-call.d.ts +0 -13
- package/dist/src/components/observability/events/code/function-call.js +0 -16
- package/dist/src/components/observability/events/event-icon.d.ts +0 -7
- package/dist/src/components/observability/events/event-icon.js +0 -16
- package/dist/src/components/observability/events/trace-emit-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-emit-event.js +0 -5
- package/dist/src/components/observability/events/trace-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-event.js +0 -20
- package/dist/src/components/observability/events/trace-log-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-log-event.js +0 -5
- package/dist/src/components/observability/events/trace-state-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-state-event.js +0 -5
- package/dist/src/components/observability/events/trace-stream-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-stream-event.js +0 -5
- package/dist/src/components/observability/hooks/use-get-endtime.d.ts +0 -2
- package/dist/src/components/observability/hooks/use-get-endtime.js +0 -15
- package/dist/src/components/observability/trace-item/trace-item-detail.d.ts +0 -8
- package/dist/src/components/observability/trace-item/trace-item-detail.js +0 -10
- package/dist/src/components/observability/trace-item/trace-item.d.ts +0 -10
- package/dist/src/components/observability/trace-item/trace-item.js +0 -14
- package/dist/src/components/observability/trace-status.d.ts +0 -8
- package/dist/src/components/observability/trace-status.js +0 -18
- package/dist/src/components/observability/trace-tab-label.d.ts +0 -1
- package/dist/src/components/observability/trace-tab-label.js +0 -5
- package/dist/src/components/observability/trace-timeline.d.ts +0 -6
- package/dist/src/components/observability/trace-timeline.js +0 -30
- package/dist/src/components/observability/traces-groups.d.ts +0 -9
- package/dist/src/components/observability/traces-groups.js +0 -9
- package/dist/src/components/observability/traces-page.d.ts +0 -1
- package/dist/src/components/observability/traces-page.js +0 -33
- package/dist/src/components/states/hooks/states-hooks.d.ts +0 -13
- package/dist/src/components/states/hooks/states-hooks.js +0 -26
- package/dist/src/components/states/state-details.d.ts +0 -7
- package/dist/src/components/states/state-details.js +0 -3
- package/dist/src/components/states/state-editor.d.ts +0 -7
- package/dist/src/components/states/state-editor.js +0 -71
- package/dist/src/components/states/state-sidebar.d.ts +0 -8
- package/dist/src/components/states/state-sidebar.js +0 -17
- package/dist/src/components/states/state-tab-label.d.ts +0 -1
- package/dist/src/components/states/state-tab-label.js +0 -5
- package/dist/src/components/states/states-page.d.ts +0 -1
- package/dist/src/components/states/states-page.js +0 -56
- package/dist/src/types/observability.d.ts +0 -78
- package/dist/vite-plugin-motia-plugins.d.ts +0 -9
- package/dist/vite-plugin-motia-plugins.js +0 -69
- /package/dist/{src/types/observability.js → motia-plugin/__tests__/generator.test.d.ts} +0 -0
|
@@ -0,0 +1,163 @@
|
|
|
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()).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.errors.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/postcss.config.mjs
CHANGED
package/dist/src/App.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
1
|
+
import { type FC } from 'react';
|
|
2
2
|
export declare const App: FC;
|
package/dist/src/App.js
CHANGED
|
@@ -2,10 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { FlowPage } from './components/flow/flow-page';
|
|
4
4
|
import { FlowTabMenuItem } from './components/flow/flow-tab-menu-item';
|
|
5
|
-
import { TracingTabLabel } from './components/observability/trace-tab-label';
|
|
6
|
-
import { TracesPage } from './components/observability/traces-page';
|
|
7
|
-
import { StatesTabLabel } from './components/states/state-tab-label';
|
|
8
|
-
import { StatesPage } from './components/states/states-page';
|
|
9
5
|
import { registerPluginTabs } from './lib/plugins';
|
|
10
6
|
import { getViewModeFromURL } from './lib/utils';
|
|
11
7
|
import { ProjectViewMode } from './project-view-mode';
|
|
@@ -13,9 +9,7 @@ import { setAppTabs, TabLocation } from './stores/use-app-tabs-store';
|
|
|
13
9
|
import { SystemViewMode } from './system-view-mode';
|
|
14
10
|
const TAB_IDS = {
|
|
15
11
|
FLOW: 'flow',
|
|
16
|
-
TRACING: 'tracing',
|
|
17
12
|
LOGS: 'logs',
|
|
18
|
-
STATES: 'states',
|
|
19
13
|
};
|
|
20
14
|
const registerDefaultTabs = () => {
|
|
21
15
|
const topTabs = [
|
|
@@ -25,18 +19,7 @@ const registerDefaultTabs = () => {
|
|
|
25
19
|
content: FlowPage,
|
|
26
20
|
},
|
|
27
21
|
];
|
|
28
|
-
const bottomTabs = [
|
|
29
|
-
{
|
|
30
|
-
id: TAB_IDS.TRACING,
|
|
31
|
-
tabLabel: TracingTabLabel,
|
|
32
|
-
content: TracesPage,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: TAB_IDS.STATES,
|
|
36
|
-
tabLabel: StatesTabLabel,
|
|
37
|
-
content: StatesPage,
|
|
38
|
-
},
|
|
39
|
-
];
|
|
22
|
+
const bottomTabs = [];
|
|
40
23
|
setAppTabs(TabLocation.TOP, topTabs);
|
|
41
24
|
setAppTabs(TabLocation.BOTTOM, bottomTabs);
|
|
42
25
|
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { EdgeProps } from '@xyflow/react';
|
|
2
|
-
import React from 'react';
|
|
1
|
+
import { type EdgeProps } from '@xyflow/react';
|
|
2
|
+
import type React from 'react';
|
|
3
3
|
export declare const BaseEdge: React.FC<EdgeProps>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn, useThemeStore } from '@motiadev/ui';
|
|
2
3
|
import { BaseEdge as BaseReactFlowEdge, EdgeLabelRenderer, getSmoothStepPath } from '@xyflow/react';
|
|
3
4
|
import { cva } from 'class-variance-authority';
|
|
4
|
-
import { cn, useThemeStore } from '@motiadev/ui';
|
|
5
5
|
const labelVariants = cva('absolute pointer-events-all text-cs border p-1 px-2', {
|
|
6
6
|
variants: {
|
|
7
7
|
color: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useFlowStore } from '@/stores/use-flow-store';
|
|
3
2
|
import { useStreamItem } from '@motiadev/stream-client-react';
|
|
4
|
-
import { FlowView } from './flow-view';
|
|
5
3
|
import { ReactFlowProvider } from '@xyflow/react';
|
|
4
|
+
import { useFlowStore } from '@/stores/use-flow-store';
|
|
5
|
+
import { FlowView } from './flow-view';
|
|
6
6
|
export const FlowPage = () => {
|
|
7
7
|
const selectedFlowId = useFlowStore((state) => state.selectedFlowId);
|
|
8
8
|
const { data: flow } = useStreamItem({
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@motiadev/ui';
|
|
3
3
|
import { ChevronsUpDown, Workflow } from 'lucide-react';
|
|
4
|
-
import { useFlowStore } from '@/stores/use-flow-store';
|
|
5
|
-
import { useFetchFlows } from '@/hooks/use-fetch-flows';
|
|
6
4
|
import { useShallow } from 'zustand/react/shallow';
|
|
5
|
+
import { useFetchFlows } from '@/hooks/use-fetch-flows';
|
|
7
6
|
import { analytics } from '@/lib/analytics';
|
|
7
|
+
import { useFlowStore } from '@/stores/use-flow-store';
|
|
8
8
|
export const FlowTabMenuItem = () => {
|
|
9
9
|
useFetchFlows();
|
|
10
10
|
const selectFlowId = useFlowStore((state) => state.selectFlowId);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
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
4
|
import '@xyflow/react/dist/style.css';
|
|
5
5
|
export type FlowNode = ReactFlowNode<NodeData>;
|
|
6
6
|
export type FlowEdge = ReactFlowEdge<EdgeData>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { type Edge, type Node } from '@xyflow/react';
|
|
2
|
+
import type React from 'react';
|
|
1
3
|
import type { EdgeData, FlowConfigResponse, FlowResponse, NodeData } from '@/types/flow';
|
|
2
|
-
import { Edge, Node } from '@xyflow/react';
|
|
3
|
-
import React from 'react';
|
|
4
4
|
export declare const useGetFlowState: (flow: FlowResponse, flowConfig: FlowConfigResponse) => {
|
|
5
5
|
nodes: Node<NodeData>[];
|
|
6
6
|
edges: Edge<EdgeData>[];
|
|
@@ -10,9 +10,7 @@ const DEFAULT_CONFIG = { x: 0, y: 0 };
|
|
|
10
10
|
const getNodePosition = (flowConfig, stepName) => {
|
|
11
11
|
return flowConfig?.config[stepName] || DEFAULT_CONFIG;
|
|
12
12
|
};
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
13
|
const nodeComponentCache = new Map();
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
14
|
const BASE_NODE_TYPES = {
|
|
17
15
|
event: EventFlowNode,
|
|
18
16
|
api: ApiFlowNode,
|
|
@@ -20,7 +18,6 @@ const BASE_NODE_TYPES = {
|
|
|
20
18
|
cron: CronFlowNode,
|
|
21
19
|
};
|
|
22
20
|
async function importFlow(flow, flowConfig) {
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
21
|
const nodeTypes = { ...BASE_NODE_TYPES };
|
|
25
22
|
const customNodePromises = flow.steps
|
|
26
23
|
.filter((step) => step.nodeComponentPath)
|
|
@@ -57,7 +54,6 @@ async function importFlow(flow, flowConfig) {
|
|
|
57
54
|
return { nodes, edges, nodeTypes };
|
|
58
55
|
}
|
|
59
56
|
export const useGetFlowState = (flow, flowConfig) => {
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
57
|
const [nodeTypes, setNodeTypes] = useState(BASE_NODE_TYPES);
|
|
62
58
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
|
63
59
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { FlowConfigResponse } from '@/types/flow';
|
|
1
|
+
import type { FlowConfigResponse } from '@/types/flow';
|
|
2
2
|
export declare const useSaveWorkflowConfig: () => (body: FlowConfigResponse) => Promise<any>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { type Edge, type Node } from '@xyflow/react';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
import type { EdgeData, NodeData } from '@/types/flow';
|
|
4
4
|
type Props = {
|
|
5
5
|
onInitialized: () => void;
|
|
6
6
|
nodes: Node<NodeData>[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { ApiNodeProps } from '@/publicComponents/node-props';
|
|
1
|
+
import type { ApiNodeProps } from '@/publicComponents/node-props';
|
|
2
2
|
export declare const ApiFlowNode: ({ data }: ApiNodeProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CronNodeProps } from '@/publicComponents/node-props';
|
|
1
|
+
import type { CronNodeProps } from '@/publicComponents/node-props';
|
|
2
2
|
export declare const CronFlowNode: ({ data }: CronNodeProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { EventNodeProps } from '@/publicComponents/node-props';
|
|
1
|
+
import type { EventNodeProps } from '@/publicComponents/node-props';
|
|
2
2
|
export declare const EventFlowNode: ({ data }: EventNodeProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { NoopNodeProps } from '@/publicComponents/node-props';
|
|
1
|
+
import type { NoopNodeProps } from '@/publicComponents/node-props';
|
|
2
2
|
export declare const NoopFlowNode: ({ data }: NoopNodeProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { analytics } from '@/lib/analytics';
|
|
3
2
|
import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@motiadev/ui';
|
|
4
3
|
import { Rocket } from 'lucide-react';
|
|
5
4
|
import { useState } from 'react';
|
|
5
|
+
import { analytics } from '@/lib/analytics';
|
|
6
6
|
export const DeployButton = () => {
|
|
7
7
|
const [isOpen, setIsOpen] = useState(false);
|
|
8
8
|
const onDeployButtonClick = () => {
|
|
@@ -24,5 +24,5 @@ export const DeployButton = () => {
|
|
|
24
24
|
analytics.track('deploy_button_self_hosted_clicked');
|
|
25
25
|
window.open('https://www.motia.dev/docs/deployment-guide/self-hosted', '_blank');
|
|
26
26
|
};
|
|
27
|
-
return (_jsxs(_Fragment, { children: [isOpen && (_jsxs("div", { children: [_jsx("div", { className: "fixed inset-0 z-[9999] bg-black/20 backdrop-blur-sm", onClick: () => setIsOpen(false) }), _jsxs("div", { className: "driver-popover w-[600px]! fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[10000] animate-in fade-in-0 zoom-in-95", children: [_jsx("img", { src: "https://oxhhfuuoqzsaqthfairn.supabase.co/storage/v1/object/public/public-images/preview.png", alt: "Motia Cloud", className: "driver-popover-image object-cover", style: { height: 393, width: '100%' } }), _jsx("div", { className: "driver-popover-title", children: _jsx("h2", { className: "popover-title", children: "Motia Cloud is Live!" }) }), _jsx("div", { className: "driver-popover-description", children: "Deploy to production in minutes, not hours. One click gets your Motia project live with enterprise-grade reliability. Seamlessly scale, rollback instantly, and monitor everything in real-time. Your code deserves infrastructure that just works." }), _jsx("a", { href: "https://www.motia.dev/docs/concepts/deployment/motia-cloud/features", target: "_blank", className: "text-foreground text-xs font-semibold px-4 hover:underline", children: "Learn more about Motia Cloud" }), _jsx("div", { className: "driver-popover-footer flex items-center justify-end", children: _jsxs("div", { className: "driver-popover-navigation-btns flex gap-6", children: [_jsx("button", { className: "tutorial-opt-out-button text-sm! font-semibold! text-muted-foreground!", onClick: onClose, children: "Close" }), _jsx("a", { href: "https://motia.cloud?utm_source=workbench&utm_medium=referral", target: "_blank", onClick: onDeployClick, children: _jsx("button", { className: "driver-popover-next-btn", children: "Deploy!" }) })] }) })] })] })), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "font-semibold text-sm dark:bg-white dark:text-black dark:hover:bg-white/90 bg-black/90 hover:bg-black/80 text-white", onClick: onDeployButtonClick, children: [_jsx(Rocket, {}), "Deploy"] }) }), _jsxs(DropdownMenuContent, { className: "bg-background text-foreground w-56", children: [_jsx(DropdownMenuItem, { className: "cursor-pointer h-10 font-semibold", onClick: onMotiaCloudClick, children: "Motia Cloud" }), _jsx(DropdownMenuItem, { className: "cursor-pointer h-10 font-semibold", onClick: onSelfHostedClick, children: "Self-Hosted (Docker)" })] })] })] }));
|
|
27
|
+
return (_jsxs(_Fragment, { children: [isOpen && (_jsxs("div", { children: [_jsx("div", { className: "fixed inset-0 z-[9999] bg-black/20 backdrop-blur-sm", onClick: () => setIsOpen(false) }), _jsxs("div", { className: "driver-popover w-[600px]! fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[10000] animate-in fade-in-0 zoom-in-95", children: [_jsx("img", { src: "https://oxhhfuuoqzsaqthfairn.supabase.co/storage/v1/object/public/public-images/preview.png", alt: "Motia Cloud", className: "driver-popover-image object-cover", style: { height: 393, width: '100%' } }), _jsx("div", { className: "driver-popover-title", children: _jsx("h2", { className: "popover-title", children: "Motia Cloud is Live!" }) }), _jsx("div", { className: "driver-popover-description", children: "Deploy to production in minutes, not hours. One click gets your Motia project live with enterprise-grade reliability. Seamlessly scale, rollback instantly, and monitor everything in real-time. Your code deserves infrastructure that just works." }), _jsx("a", { href: "https://www.motia.dev/docs/concepts/deployment/motia-cloud/features", target: "_blank", className: "text-foreground text-xs font-semibold px-4 hover:underline", rel: "noopener", children: "Learn more about Motia Cloud" }), _jsx("div", { className: "driver-popover-footer flex items-center justify-end", children: _jsxs("div", { className: "driver-popover-navigation-btns flex gap-6", children: [_jsx("button", { className: "tutorial-opt-out-button text-sm! font-semibold! text-muted-foreground!", onClick: onClose, children: "Close" }), _jsx("a", { href: "https://motia.cloud?utm_source=workbench&utm_medium=referral", target: "_blank", onClick: onDeployClick, rel: "noopener", children: _jsx("button", { className: "driver-popover-next-btn", children: "Deploy!" }) })] }) })] })] })), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "font-semibold text-sm dark:bg-white dark:text-black dark:hover:bg-white/90 bg-black/90 hover:bg-black/80 text-white", onClick: onDeployButtonClick, children: [_jsx(Rocket, {}), "Deploy"] }) }), _jsxs(DropdownMenuContent, { className: "bg-background text-foreground w-56", children: [_jsx(DropdownMenuItem, { className: "cursor-pointer h-10 font-semibold", onClick: onMotiaCloudClick, children: "Motia Cloud" }), _jsx(DropdownMenuItem, { className: "cursor-pointer h-10 font-semibold", onClick: onSelfHostedClick, children: "Self-Hosted (Docker)" })] })] })] }));
|
|
28
28
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import type React from 'react';
|
|
2
2
|
export declare const Header: React.FC;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import motiaLogoDark from '@/assets/motia-dark.png';
|
|
3
|
-
import motiaLogoLight from '@/assets/motia-light.png';
|
|
4
2
|
import { useThemeStore } from '@motiadev/ui';
|
|
5
3
|
import { useEffect, useState } from 'react';
|
|
4
|
+
import motiaLogoDark from '@/assets/motia-dark.png';
|
|
5
|
+
import motiaLogoLight from '@/assets/motia-light.png';
|
|
6
6
|
import { Tutorial } from '../tutorial/tutorial';
|
|
7
7
|
import { TutorialButton } from '../tutorial/tutorial-button';
|
|
8
8
|
import { ThemeToggle } from '../ui/theme-toggle';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TutorialImage } from '../engine/tutorial-types';
|
|
1
|
+
import type { TutorialImage } from '../engine/tutorial-types';
|
|
2
2
|
export declare const useTutorialEngine: () => {
|
|
3
3
|
ref: import("react").RefObject<HTMLDivElement | null>;
|
|
4
4
|
highlighterRef: import("react").RefObject<HTMLDivElement | null>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
1
|
+
import type { FC } from 'react';
|
|
2
2
|
export declare const TutorialButton: FC;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { analytics } from '@/lib/analytics';
|
|
3
2
|
import { Button } from '@motiadev/ui';
|
|
4
3
|
import { Book } from 'lucide-react';
|
|
4
|
+
import { analytics } from '@/lib/analytics';
|
|
5
5
|
import { Tooltip } from '../ui/tooltip';
|
|
6
6
|
import { useTutorial } from './hooks/use-tutorial';
|
|
7
7
|
export const TutorialButton = () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, useEffect } from 'react';
|
|
3
2
|
import { BackgroundEffect } from '@motiadev/ui';
|
|
3
|
+
import { forwardRef, useEffect } from 'react';
|
|
4
4
|
export const TutorialStep = forwardRef(({ step, totalSteps, title, description, link, image, onNext, onClose }, ref) => {
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
const handleKeyDown = (e) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
@import
|
|
2
|
-
@import
|
|
3
|
-
@import
|
|
1
|
+
@import "@motiadev/ui/styles.css";
|
|
2
|
+
@import "@motiadev/ui/globals.css";
|
|
3
|
+
@import "tw-animate-css";
|
|
4
4
|
|
|
5
5
|
:root {
|
|
6
6
|
--tutorial-text-color: var(--dark-800);
|
|
@@ -58,7 +58,6 @@
|
|
|
58
58
|
|
|
59
59
|
.driver-popover-title {
|
|
60
60
|
padding: 16px 16px 0 16px;
|
|
61
|
-
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
.driver-popover-description {
|
|
@@ -122,7 +121,7 @@
|
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
.driver-popover-navigation-btns-hint:hover:before {
|
|
125
|
-
content:
|
|
124
|
+
content: "Use arrow keys to navigate";
|
|
126
125
|
position: absolute;
|
|
127
126
|
color: white;
|
|
128
127
|
top: -20px;
|
|
@@ -155,7 +154,8 @@
|
|
|
155
154
|
display: none;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
|
-
.driver-popover-next-btn,
|
|
157
|
+
.driver-popover-next-btn,
|
|
158
|
+
.driver-popover-next-btn:hover {
|
|
159
159
|
color: white;
|
|
160
160
|
padding: 16px 32px;
|
|
161
161
|
}
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
background: var(--tutorial-code-bg);
|
|
202
202
|
padding: 16px;
|
|
203
203
|
border-radius: 8px;
|
|
204
|
-
|
|
204
|
+
|
|
205
205
|
code {
|
|
206
206
|
font-size: 14px;
|
|
207
207
|
font-weight: 400;
|
|
@@ -212,4 +212,4 @@
|
|
|
212
212
|
|
|
213
213
|
.driver-popover-close-btn {
|
|
214
214
|
display: none;
|
|
215
|
-
}
|
|
215
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useThemeStore } from '@motiadev/ui';
|
|
3
2
|
import Editor, { useMonaco } from '@monaco-editor/react';
|
|
3
|
+
import { useThemeStore } from '@motiadev/ui';
|
|
4
4
|
import { useEffect, useMemo } from 'react';
|
|
5
5
|
export const JsonEditor = ({ value, height = 300, schema, onChange, onValidate, language = 'json', readOnly = false, }) => {
|
|
6
6
|
const monaco = useMonaco();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import * as React from 'react';
|
|
3
2
|
import { cn } from '@motiadev/ui';
|
|
3
|
+
import * as React from 'react';
|
|
4
4
|
const Table = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { className: "relative w-full overflow-auto", children: _jsx("table", { ref: ref, className: cn('w-full caption-bottom text-sm', className), ...props }) })));
|
|
5
5
|
Table.displayName = 'Table';
|
|
6
6
|
const TableHeader = React.forwardRef(({ className, ...props }, ref) => _jsx("thead", { ref: ref, className: cn('[&_tr]:border-b', className), ...props }));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import type React from 'react';
|
|
2
2
|
export declare const ThemeToggle: React.FC;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useFlowStore } from '@/stores/use-flow-store';
|
|
2
1
|
import { useStreamGroup } from '@motiadev/stream-client-react';
|
|
3
2
|
import { useEffect } from 'react';
|
|
3
|
+
import { useFlowStore } from '@/stores/use-flow-store';
|
|
4
4
|
export const useFetchFlows = () => {
|
|
5
5
|
const setFlows = useFlowStore((state) => state.setFlows);
|
|
6
6
|
const selectFlowId = useFlowStore((state) => state.selectFlowId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Position } from '@xyflow/react';
|
|
2
|
-
import { BaseNodeProps } from '../publicComponents/node-props';
|
|
2
|
+
import type { BaseNodeProps } from '../publicComponents/node-props';
|
|
3
3
|
export declare const useHandlePositions: (data: BaseNodeProps) => {
|
|
4
4
|
sourcePosition: Position;
|
|
5
5
|
targetPosition: Position;
|
package/dist/src/index.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
@import
|
|
2
|
-
@import
|
|
1
|
+
@import "@motiadev/ui/styles.css";
|
|
2
|
+
@import "@motiadev/ui/globals.css";
|
|
3
3
|
|
|
4
|
-
@import
|
|
4
|
+
@import "tw-animate-css";
|
|
5
5
|
@config "../tailwind.config.js";
|
|
6
6
|
|
|
7
7
|
@keyframes flowDash {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.font-mono {
|
|
30
|
-
font-family:
|
|
30
|
+
font-family: "IBM Plex Mono", "Courier New", monospace;
|
|
31
31
|
font-size: 14px;
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
.json-view {
|
|
41
41
|
border-radius: 8px;
|
|
42
42
|
padding: 12px 12px;
|
|
43
|
-
font-family:
|
|
43
|
+
font-family: "IBM Plex Mono", "Courier New", monospace;
|
|
44
44
|
font-size: 14px;
|
|
45
45
|
line-height: 1.5;
|
|
46
46
|
font-weight: 500;
|
package/dist/src/lib/plugins.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { plugins } from 'virtual:motia-plugins';
|
|
3
3
|
import { DynamicIcon, dynamicIconImports } from 'lucide-react/dynamic';
|
|
4
4
|
import { memo } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { addAppTab, TabLocation } from '@/stores/use-app-tabs-store';
|
|
6
6
|
import { isValidTabLocation } from './utils';
|
|
7
7
|
export const registerPluginTabs = () => {
|
|
8
8
|
if (!Array.isArray(plugins)) {
|
|
@@ -43,7 +43,7 @@ export const registerPluginTabs = () => {
|
|
|
43
43
|
});
|
|
44
44
|
PluginContent.displayName = `${plugin.label}Content`;
|
|
45
45
|
addAppTab(tabLocation, {
|
|
46
|
-
id: plugin.label,
|
|
46
|
+
id: plugin.label.toLowerCase(),
|
|
47
47
|
tabLabel: PluginTabLabel,
|
|
48
48
|
content: PluginContent,
|
|
49
49
|
});
|
package/dist/src/main.js
CHANGED
|
@@ -2,15 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { MotiaStreamProvider } from '@motiadev/stream-client-react';
|
|
3
3
|
import { StrictMode } from 'react';
|
|
4
4
|
import { createRoot } from 'react-dom/client';
|
|
5
|
-
import { BrowserRouter,
|
|
6
|
-
import { RootMotia } from './components/root-motia';
|
|
5
|
+
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
|
7
6
|
import { App } from './App';
|
|
8
7
|
import { NotFoundPage } from './components/NotFoundPage';
|
|
8
|
+
import { RootMotia } from './components/root-motia';
|
|
9
9
|
import '@motiadev/ui/globals.css';
|
|
10
10
|
import './index.css';
|
|
11
11
|
const rootElement = document.getElementById('root');
|
|
12
12
|
if (!rootElement.innerHTML) {
|
|
13
|
-
// eslint-disable-next-line no-undef
|
|
14
13
|
const basePath = workbenchBase;
|
|
15
14
|
const root = createRoot(rootElement);
|
|
16
15
|
const address = window.location.origin.replace('http', 'ws');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { TabLocation, useAppTabsStore } from './stores/use-app-tabs-store';
|
|
3
2
|
import { APP_SIDEBAR_CONTAINER_ID, Panel } from '@motiadev/ui';
|
|
4
3
|
import { useShallow } from 'zustand/react/shallow';
|
|
4
|
+
import { TabLocation, useAppTabsStore } from './stores/use-app-tabs-store';
|
|
5
5
|
const topTabs = (state) => state.tabs[TabLocation.TOP];
|
|
6
6
|
export const ProjectViewMode = () => {
|
|
7
7
|
const tabs = useAppTabsStore(useShallow(topTabs));
|