@motiadev/workbench 0.6.0-beta.120 → 0.6.0-beta.122
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 +2 -0
- package/dist/index.html +21 -1
- package/dist/index.js +1 -0
- package/dist/middleware.js +15 -2
- package/dist/src/App.js +28 -5
- package/dist/src/components/endpoints/endpoint-body-panel.d.ts +1 -0
- package/dist/src/components/endpoints/endpoint-body-panel.js +2 -2
- package/dist/src/components/endpoints/endpoint-call.js +1 -1
- package/dist/src/components/endpoints/endpoint-description.js +1 -1
- package/dist/src/components/flow/hooks/use-get-flow-state.js +1 -0
- package/dist/src/components/flow/node-organizer.js +4 -38
- package/dist/src/components/header/header.js +1 -1
- package/dist/src/components/logs/logs-page.js +1 -1
- package/dist/src/components/observability/trace-item/trace-item.js +1 -1
- package/dist/src/components/observability/traces-groups.js +1 -1
- package/dist/src/components/sidebar/sidebar.js +1 -1
- package/dist/src/components/tutorial/engine/tutorial-engine.d.ts +12 -0
- package/dist/src/components/tutorial/engine/tutorial-engine.js +36 -0
- package/dist/src/components/tutorial/engine/tutorial-types.d.ts +22 -0
- package/dist/src/components/tutorial/engine/tutorial-types.js +1 -0
- package/dist/src/components/tutorial/engine/workbench-xpath.d.ts +40 -0
- package/dist/src/components/tutorial/engine/workbench-xpath.js +40 -0
- package/dist/src/components/tutorial/hooks/tutorial-utils.d.ts +1 -0
- package/dist/src/components/tutorial/hooks/tutorial-utils.js +17 -0
- package/dist/src/components/tutorial/hooks/use-tutorial-engine.d.ts +15 -0
- package/dist/src/components/tutorial/hooks/use-tutorial-engine.js +176 -0
- package/dist/src/components/tutorial/hooks/use-tutorial.d.ts +5 -0
- package/dist/src/components/tutorial/hooks/use-tutorial.js +10 -0
- package/dist/src/components/tutorial/tutorial-button.js +21 -0
- package/dist/src/components/tutorial/tutorial-step.d.ts +14 -0
- package/dist/src/components/tutorial/tutorial-step.js +18 -0
- package/dist/src/components/tutorial/tutorial.css +208 -0
- package/dist/src/components/tutorial/tutorial.d.ts +2 -0
- package/dist/src/components/tutorial/tutorial.js +32 -0
- package/dist/src/components/ui/theme-toggle.js +8 -0
- package/dist/src/publicComponents/base-node/base-node.js +3 -1
- package/dist/src/publicComponents/base-node/code-display.d.ts +9 -0
- package/dist/src/publicComponents/base-node/code-display.js +64 -0
- package/dist/src/publicComponents/base-node/feature-card.d.ts +10 -0
- package/dist/src/publicComponents/base-node/feature-card.js +5 -0
- package/dist/src/publicComponents/base-node/node-sidebar.d.ts +2 -0
- package/dist/src/publicComponents/base-node/node-sidebar.js +4 -5
- package/dist/src/stores/use-theme-store.d.ts +1 -2
- package/dist/src/types/file.d.ts +7 -0
- package/dist/src/types/file.js +1 -0
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/dist/tsconfig.node.tsbuildinfo +1 -1
- package/package.json +13 -12
- package/dist/src/components/ui/tutorial-button.js +0 -69
- /package/dist/src/components/{ui → tutorial}/tutorial-button.d.ts +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -6,3 +6,5 @@ export { BaseHandle } from './src/publicComponents/base-node/base-handle';
|
|
|
6
6
|
export { Position } from '@xyflow/react';
|
|
7
7
|
export type { EventNodeData, ApiNodeData } from './src/types/flow';
|
|
8
8
|
export type { ApiNodeProps, BaseNodeProps, CronNodeProps, EventNodeProps, NoopNodeProps, } from './src/publicComponents/node-props';
|
|
9
|
+
export type { TutorialStep } from './src/components/tutorial/engine/tutorial-types';
|
|
10
|
+
export { workbenchXPath } from './src/components/tutorial/engine/workbench-xpath';
|
package/dist/index.html
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
|
|
11
11
|
rel="stylesheet"
|
|
12
12
|
/>
|
|
13
|
+
<link
|
|
14
|
+
href="https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"
|
|
15
|
+
rel="stylesheet"
|
|
16
|
+
/>
|
|
13
17
|
<script src="https://cdn.amplitude.com/libs/analytics-browser-2.11.1-min.js.gz"></script>
|
|
14
18
|
<script>
|
|
15
19
|
window.amplitude.init('ab2408031a38aa5cb85587a27ecfc69c', {
|
|
@@ -25,10 +29,26 @@
|
|
|
25
29
|
|
|
26
30
|
<!-- Start of Reo Javascript -->
|
|
27
31
|
<script type="text/javascript">
|
|
28
|
-
!function()
|
|
32
|
+
!(function () {
|
|
33
|
+
var e, t, n
|
|
34
|
+
;(e = 'd8f0ce9cae8ae64'),
|
|
35
|
+
(t = function () {
|
|
36
|
+
Reo.init({ clientID: 'd8f0ce9cae8ae64' })
|
|
37
|
+
}),
|
|
38
|
+
((n = document.createElement('script')).src = 'https://static.reo.dev/' + e + '/reo.js'),
|
|
39
|
+
(n.defer = !0),
|
|
40
|
+
(n.onload = t),
|
|
41
|
+
document.head.appendChild(n)
|
|
42
|
+
})()
|
|
29
43
|
</script>
|
|
30
44
|
<!-- End of Reo Javascript -->
|
|
31
45
|
|
|
46
|
+
<script>
|
|
47
|
+
const importFile = async (path) => {
|
|
48
|
+
return await import(/* @vite-ignore */ `/@fs/${processCwd}/${path}`)
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
32
52
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
33
53
|
<title>Motia Workbench</title>
|
|
34
54
|
</head>
|
package/dist/index.js
CHANGED
|
@@ -4,3 +4,4 @@ export { NoopNode } from './src/publicComponents/noop-node';
|
|
|
4
4
|
export { BaseNode } from './src/publicComponents/base-node/base-node';
|
|
5
5
|
export { BaseHandle } from './src/publicComponents/base-node/base-handle';
|
|
6
6
|
export { Position } from '@xyflow/react';
|
|
7
|
+
export { workbenchXPath } from './src/components/tutorial/engine/workbench-xpath';
|
package/dist/middleware.js
CHANGED
|
@@ -8,6 +8,15 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const vite_1 = require("vite");
|
|
10
10
|
const plugin_react_1 = __importDefault(require("@vitejs/plugin-react"));
|
|
11
|
+
const processCwdPlugin = () => {
|
|
12
|
+
return {
|
|
13
|
+
name: 'html-transform',
|
|
14
|
+
transformIndexHtml: (html) => {
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
return html.replace('</head>', `<script>const processCwd = "${cwd}";</script></head>`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
};
|
|
11
20
|
const applyMiddleware = async (app) => {
|
|
12
21
|
const vite = await (0, vite_1.createServer)({
|
|
13
22
|
appType: 'spa',
|
|
@@ -17,13 +26,17 @@ const applyMiddleware = async (app) => {
|
|
|
17
26
|
allowedHosts: true,
|
|
18
27
|
host: true,
|
|
19
28
|
fs: {
|
|
20
|
-
allow: [
|
|
29
|
+
allow: [
|
|
30
|
+
__dirname, // workbench root
|
|
31
|
+
path_1.default.join(process.cwd(), './steps'), // steps directory
|
|
32
|
+
path_1.default.join(process.cwd(), './tutorial.tsx'), // tutorial file
|
|
33
|
+
],
|
|
21
34
|
},
|
|
22
35
|
},
|
|
23
36
|
resolve: {
|
|
24
37
|
alias: { '@': path_1.default.resolve(__dirname, './src') },
|
|
25
38
|
},
|
|
26
|
-
plugins: [(0, plugin_react_1.default)()],
|
|
39
|
+
plugins: [(0, plugin_react_1.default)(), processCwdPlugin()],
|
|
27
40
|
});
|
|
28
41
|
app.use(vite.middlewares);
|
|
29
42
|
app.use('*', async (req, res, next) => {
|
package/dist/src/App.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { CollapsiblePanel, CollapsiblePanelGroup, TabsContent, TabsList, TabsTrigger } from '@motiadev/ui';
|
|
3
|
-
import { ReactFlowProvider } from '@xyflow/react';
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CollapsiblePanel, CollapsiblePanelGroup, Panel, TabsContent, TabsList, TabsTrigger } from '@motiadev/ui';
|
|
4
3
|
import { analytics } from '@/lib/analytics';
|
|
4
|
+
import { ReactFlowProvider } from '@xyflow/react';
|
|
5
5
|
import { File, GanttChart, Link2, LogsIcon } from 'lucide-react';
|
|
6
|
-
import { useCallback, useMemo } from 'react';
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
7
|
import { EndpointsPage } from './components/endpoints/endpoints-page';
|
|
8
8
|
import { FlowPage } from './components/flow/flow-page';
|
|
9
9
|
import { FlowTabMenuItem } from './components/flow/flow-tab-menu-item';
|
|
@@ -12,6 +12,7 @@ import { LogsPage } from './components/logs/logs-page';
|
|
|
12
12
|
import { TracesPage } from './components/observability/traces-page';
|
|
13
13
|
import { APP_SIDEBAR_CONTAINER_ID } from './components/sidebar/sidebar';
|
|
14
14
|
import { StatesPage } from './components/states/states-page';
|
|
15
|
+
import { Tutorial } from './components/tutorial/tutorial';
|
|
15
16
|
import { useTabsStore } from './stores/use-tabs-store';
|
|
16
17
|
var TabLocation;
|
|
17
18
|
(function (TabLocation) {
|
|
@@ -22,6 +23,7 @@ export const App = () => {
|
|
|
22
23
|
const tab = useTabsStore((state) => state.tab);
|
|
23
24
|
const setTopTab = useTabsStore((state) => state.setTopTab);
|
|
24
25
|
const setBottomTab = useTabsStore((state) => state.setBottomTab);
|
|
26
|
+
const [viewMode, setViewMode] = useState('system');
|
|
25
27
|
const tabChangeCallbacks = useMemo(() => ({
|
|
26
28
|
[TabLocation.TOP]: setTopTab,
|
|
27
29
|
[TabLocation.BOTTOM]: setBottomTab,
|
|
@@ -30,5 +32,26 @@ export const App = () => {
|
|
|
30
32
|
analytics.track(`${location} tab changed`, { [`new.${location}`]: newTab, tab });
|
|
31
33
|
tabChangeCallbacks[location](newTab);
|
|
32
34
|
}, [tabChangeCallbacks, tab]);
|
|
33
|
-
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const url = new URL(window.location.href);
|
|
37
|
+
const viewMode = url.searchParams.get('view-mode');
|
|
38
|
+
if (viewMode) {
|
|
39
|
+
setViewMode(viewMode);
|
|
40
|
+
}
|
|
41
|
+
}, [setViewMode]);
|
|
42
|
+
if (viewMode === 'project') {
|
|
43
|
+
return (_jsxs("div", { className: "grid grid-rows-[auto_1fr] grid-cols-[1fr_auto] bg-background text-foreground h-screen ", children: [_jsx("main", { className: "m-2 overflow-hidden", role: "main", children: _jsx(Panel, { tabs: [
|
|
44
|
+
{
|
|
45
|
+
label: 'Flow',
|
|
46
|
+
labelComponent: _jsx(FlowTabMenuItem, {}),
|
|
47
|
+
content: (_jsx(ReactFlowProvider, { children: _jsx("div", { className: "h-[calc(100vh-100px)] w-full", children: _jsx(FlowPage, {}) }) })),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
label: 'Endpoint',
|
|
51
|
+
labelComponent: (_jsxs(_Fragment, { children: [_jsx(Link2, {}), "Endpoint"] })),
|
|
52
|
+
content: _jsx(EndpointsPage, {}),
|
|
53
|
+
},
|
|
54
|
+
] }) }), _jsx("div", { id: APP_SIDEBAR_CONTAINER_ID })] }));
|
|
55
|
+
}
|
|
56
|
+
return (_jsxs("div", { className: "grid grid-rows-[auto_1fr] grid-cols-[1fr_auto] bg-background text-foreground h-screen", children: [_jsx("div", { className: "col-span-2", children: _jsx(Header, {}) }), _jsx("main", { className: "m-2 overflow-hidden", role: "main", children: _jsxs(CollapsiblePanelGroup, { autoSaveId: "app-panel", direction: "vertical", className: "gap-1 h-full", "aria-label": "Workbench panels", children: [_jsxs(CollapsiblePanel, { id: "top-panel", variant: 'tabs', defaultTab: tab.top, onTabChange: onTabChange(TabLocation.TOP), header: _jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "flow", "data-testid": "flows-link", children: _jsx(FlowTabMenuItem, {}) }), _jsxs(TabsTrigger, { value: "endpoint", "data-testid": "endpoints-link", className: "cursor-pointer", children: [_jsx(Link2, {}), "Endpoint"] })] }), children: [_jsx(TabsContent, { value: "flow", className: "h-full", asChild: true, children: _jsx(ReactFlowProvider, { children: _jsx(FlowPage, {}) }) }), _jsx(TabsContent, { value: "endpoint", asChild: true, children: _jsx(EndpointsPage, {}) })] }), _jsxs(CollapsiblePanel, { id: "bottom-panel", variant: 'tabs', defaultTab: tab.bottom, onTabChange: onTabChange(TabLocation.BOTTOM), header: _jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "tracing", "data-testid": "traces-link", className: "cursor-pointer", children: [_jsx(GanttChart, {}), " Tracing"] }), _jsxs(TabsTrigger, { value: "logs", "data-testid": "logs-link", className: "cursor-pointer", children: [_jsx(LogsIcon, {}), "Logs"] }), _jsxs(TabsTrigger, { value: "states", "data-testid": "states-link", className: "cursor-pointer", children: [_jsx(File, {}), "States"] })] }), children: [_jsx(TabsContent, { value: "tracing", className: "max-h-fit", asChild: true, children: _jsx(TracesPage, {}) }), _jsx(TabsContent, { value: "logs", asChild: true, children: _jsx(LogsPage, {}) }), _jsx(TabsContent, { value: "states", asChild: true, children: _jsx(StatesPage, {}) })] })] }) }), _jsx("div", { id: APP_SIDEBAR_CONTAINER_ID }), _jsx(Tutorial, {})] }));
|
|
34
57
|
};
|
|
@@ -6,7 +6,7 @@ import ReactJson from 'react18-json-view';
|
|
|
6
6
|
import 'react18-json-view/src/dark.css';
|
|
7
7
|
import 'react18-json-view/src/style.css';
|
|
8
8
|
import { convertJsonSchemaToJson } from './hooks/utils';
|
|
9
|
-
export const EndpointBodyPanel = ({ endpoint, onChange, onValidate }) => {
|
|
9
|
+
export const EndpointBodyPanel = ({ endpoint, onChange, onValidate, panelName }) => {
|
|
10
10
|
const shouldHaveBody = ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase());
|
|
11
11
|
const { body, setBody } = useJsonSchemaToJson(endpoint.bodySchema);
|
|
12
12
|
const handleBodyChange = (body) => {
|
|
@@ -16,5 +16,5 @@ export const EndpointBodyPanel = ({ endpoint, onChange, onValidate }) => {
|
|
|
16
16
|
if (!shouldHaveBody) {
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
|
-
return (_jsx(Panel, { title: "Body", size: "sm", contentClassName: "p-0", "data-testid":
|
|
19
|
+
return (_jsx(Panel, { title: "Body", size: "sm", contentClassName: "p-0", "data-testid": `endpoint-body-panel__${panelName}`, children: onChange ? (_jsx(JsonEditor, { value: body, schema: endpoint.bodySchema, onChange: handleBodyChange, onValidate: onValidate })) : (_jsx(ReactJson, { src: convertJsonSchemaToJson(endpoint.bodySchema), theme: "default", enableClipboard: false, style: { backgroundColor: 'transparent' } })) }));
|
|
20
20
|
};
|
|
@@ -53,5 +53,5 @@ export const EndpointCall = ({ endpoint }) => {
|
|
|
53
53
|
setExecutionTime(executionTime);
|
|
54
54
|
setIsRequestLoading(false);
|
|
55
55
|
};
|
|
56
|
-
return (_jsxs("div", { className: "space-y-3", children: [_jsx(EndpointPathParamsPanel, { endpoint: endpoint, onChange: setPathParamsValues }), _jsx(EndpointQueryParamsPanel, { endpoint: endpoint, onChange: setQueryParamsValues }), _jsx(EndpointBodyPanel, { endpoint: endpoint, onChange: setBody, onValidate: setIsBodyValid }), _jsxs(Button, { className: "w-fit", onClick: handleRequest, variant: "accent", "data-testid": "endpoint-play-button", disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), _jsx(EndpointResponse, { responseCode: responseCode, responseBody: responseBody, executionTime: executionTime })] }));
|
|
56
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx(EndpointPathParamsPanel, { endpoint: endpoint, onChange: setPathParamsValues }), _jsx(EndpointQueryParamsPanel, { endpoint: endpoint, onChange: setQueryParamsValues }), _jsx(EndpointBodyPanel, { endpoint: endpoint, onChange: setBody, onValidate: setIsBodyValid, panelName: "call" }), _jsxs(Button, { className: "w-fit", onClick: handleRequest, variant: "accent", "data-testid": "endpoint-play-button", disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), _jsx(EndpointResponse, { responseCode: responseCode, responseBody: responseBody, executionTime: executionTime })] }));
|
|
57
57
|
};
|
|
@@ -4,7 +4,7 @@ import { EndpointPathParamsPanel } from './endpoint-path-params-panel';
|
|
|
4
4
|
import { EndpointQueryParamsPanel } from './endpoint-query-params-panel';
|
|
5
5
|
import { EndpointBodyPanel } from './endpoint-body-panel';
|
|
6
6
|
export const EndpointDescription = ({ endpoint }) => {
|
|
7
|
-
return (_jsxs("div", { className: "space-y-3", children: [_jsx(EndpointPathParamsPanel, { endpoint: endpoint }), _jsx(EndpointQueryParamsPanel, { endpoint: endpoint }), _jsx(EndpointBodyPanel, { endpoint: endpoint }), _jsx(EndpointResponseSchema, { items: Object.entries(endpoint?.responseSchema ?? {}).map(([status, schema]) => ({
|
|
7
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx(EndpointPathParamsPanel, { endpoint: endpoint }), _jsx(EndpointQueryParamsPanel, { endpoint: endpoint }), _jsx(EndpointBodyPanel, { endpoint: endpoint, panelName: "details" }), _jsx(EndpointResponseSchema, { items: Object.entries(endpoint?.responseSchema ?? {}).map(([status, schema]) => ({
|
|
8
8
|
responseCode: status,
|
|
9
9
|
bodySchema: schema,
|
|
10
10
|
})) })] }));
|
|
@@ -1,39 +1,5 @@
|
|
|
1
1
|
import { useNodesInitialized, useReactFlow } from '@xyflow/react';
|
|
2
|
-
import dagre from 'dagre';
|
|
3
2
|
import { useEffect, useRef } from 'react';
|
|
4
|
-
const organizeNodes = (nodes, edges) => {
|
|
5
|
-
const dagreGraph = new dagre.graphlib.Graph({ compound: true });
|
|
6
|
-
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
7
|
-
dagreGraph.setGraph({ rankdir: 'TB', ranksep: 80, nodesep: 60, edgesep: 20, ranker: 'tight-tree' });
|
|
8
|
-
nodes.forEach((node) => {
|
|
9
|
-
dagreGraph.setNode(node.id, { width: node.measured?.width, height: node.measured?.height });
|
|
10
|
-
});
|
|
11
|
-
edges.forEach((edge) => {
|
|
12
|
-
if (typeof edge.label === 'string') {
|
|
13
|
-
dagreGraph.setEdge(edge.source, edge.target, {
|
|
14
|
-
label: edge.label ?? '',
|
|
15
|
-
width: edge.label.length * 40, // Add width for the label
|
|
16
|
-
height: 30, // Add height for the label
|
|
17
|
-
labelpos: 'c', // Position label in center
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
dagreGraph.setEdge(edge.source, edge.target);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
dagre.layout(dagreGraph);
|
|
25
|
-
return nodes.map((node) => {
|
|
26
|
-
if (node.position.x !== 0 || node.position.y !== 0) {
|
|
27
|
-
return node;
|
|
28
|
-
}
|
|
29
|
-
const { x, y } = dagreGraph.node(node.id);
|
|
30
|
-
const position = {
|
|
31
|
-
x: x - (node.measured?.width ?? 0) / 2,
|
|
32
|
-
y: y - (node.measured?.height ?? 0) / 2,
|
|
33
|
-
};
|
|
34
|
-
return { ...node, position };
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
3
|
export const NodeOrganizer = ({ onInitialized }) => {
|
|
38
4
|
const { setNodes, getNodes, getEdges, fitView } = useReactFlow();
|
|
39
5
|
const nodesInitialized = useNodesInitialized();
|
|
@@ -41,10 +7,10 @@ export const NodeOrganizer = ({ onInitialized }) => {
|
|
|
41
7
|
useEffect(() => {
|
|
42
8
|
if (nodesInitialized && !initialized.current) {
|
|
43
9
|
initialized.current = true;
|
|
44
|
-
const nodes = getNodes()
|
|
45
|
-
const edges = getEdges()
|
|
46
|
-
const organizedNodes = organizeNodes(nodes, edges)
|
|
47
|
-
setNodes(organizedNodes)
|
|
10
|
+
// const nodes = getNodes() as Node<EventNodeData | ApiNodeData>[]
|
|
11
|
+
// const edges = getEdges() as Edge<EdgeData>[]
|
|
12
|
+
// const organizedNodes = organizeNodes(nodes, edges)
|
|
13
|
+
// setNodes(organizedNodes)
|
|
48
14
|
onInitialized();
|
|
49
15
|
setTimeout(async () => {
|
|
50
16
|
await fitView();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useThemeStore } from '@/stores/use-theme-store';
|
|
3
3
|
import { ThemeToggle } from '../ui/theme-toggle';
|
|
4
|
-
import { TutorialButton } from '../
|
|
4
|
+
import { TutorialButton } from '../tutorial/tutorial-button';
|
|
5
5
|
export const Header = () => {
|
|
6
6
|
const theme = useThemeStore((state) => state.theme);
|
|
7
7
|
return (_jsxs("header", { className: "min-h-16 px-4 gap-4 flex items-center bg-default text-default-foreground border-b", children: [_jsx("img", { src: `/motia-${theme}.png`, className: "h-5", id: "logo-icon", "data-testid": "logo-icon" }), _jsx("div", { className: "flex-1" }), _jsx(TutorialButton, {}), _jsx(ThemeToggle, {})] }));
|
|
@@ -22,7 +22,7 @@ export const LogsPage = () => {
|
|
|
22
22
|
log.step.toLowerCase().includes(search.toLowerCase()));
|
|
23
23
|
});
|
|
24
24
|
}, [logs, search]);
|
|
25
|
-
return (_jsxs("div", { className: "h-full flex flex-row", "data-testid": "logs-container", children: [_jsxs("div", { className: "flex-1 overflow-y-auto overflow-x-auto", children: [_jsxs("div", { className: "flex p-2 border-b gap-4", "data-testid": "logs-search-container", children: [_jsxs("div", { className: "flex-1 relative", children: [_jsx(Input, { variant: "shade", value: search, onChange: (e) => setSearch(e.target.value), className: "pr-10 font-medium" }), _jsx(CircleX, { className: "cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground", onClick: () => setSearch('') })] }), _jsxs(Button, { variant: "outline", onClick: resetLogs, children: [_jsx(Trash, {}), " Clear"] })] }), _jsx(Table, { children: _jsx(TableBody, { className: "font-mono font-medium", children: filteredLogs.map((log, index) => (_jsxs(TableRow, { className: cn('font-mono font-semibold cursor-pointer border-0', {
|
|
25
|
+
return (_jsxs("div", { className: "h-full flex flex-row", "data-testid": "logs-container", children: [_jsxs("div", { className: "flex-1 overflow-y-auto overflow-x-auto", children: [_jsxs("div", { className: "flex p-2 border-b gap-4", "data-testid": "logs-search-container", children: [_jsxs("div", { className: "flex-1 relative", children: [_jsx(Input, { variant: "shade", value: search, onChange: (e) => setSearch(e.target.value), className: "pr-10 font-medium" }), _jsx(CircleX, { className: "cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground", onClick: () => setSearch('') })] }), _jsxs(Button, { variant: "outline", onClick: resetLogs, children: [_jsx(Trash, {}), " Clear"] })] }), _jsx(Table, { children: _jsx(TableBody, { className: "font-mono font-medium", children: filteredLogs.map((log, index) => (_jsxs(TableRow, { "data-testid": "log-row", className: cn('font-mono font-semibold cursor-pointer border-0', {
|
|
26
26
|
'bg-muted-foreground/10 hover:bg-muted-foreground/20': selectedLogId === log.id,
|
|
27
27
|
'hover:bg-muted-foreground/10': selectedLogId !== log.id,
|
|
28
28
|
}), onClick: () => selectLogId(log.id), children: [_jsxs(TableCell, { "data-testid": `time-${index}`, className: "whitespace-nowrap flex items-center gap-2 text-muted-foreground", children: [_jsx(LogLevelDot, { level: log.level }), formatTimestamp(log.time)] }), _jsx(TableCell, { "data-testid": `trace-${log.traceId}`, className: "whitespace-nowrap cursor-pointer hover:text-primary text-muted-foreground", onClick: () => setSearch(log.traceId), children: log.traceId }), _jsx(TableCell, { "data-testid": `step-${index}`, "aria-label": log.step, className: "whitespace-nowrap", children: log.step }), _jsx(TableCell, { "data-testid": `msg-${index}`, "aria-label": log.msg, className: "whitespace-nowrap max-w-[500px] truncate w-full", children: log.msg })] }, index))) }) })] }), _jsx(LogDetail, { log: selectedLog, onClose: () => selectLogId(undefined) })] }));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from '@/lib/utils';
|
|
3
3
|
export const TraceItem = ({ trace, group, groupEndTime, onExpand }) => {
|
|
4
|
-
return (_jsxs("div", { className: "flex hover:bg-muted-foreground/10 relative cursor-pointer", onClick: () => onExpand(trace.id), children: [_jsx("div", { className: "flex items-center min-w-[200px] max-w-[200px] h-[32px] max-h-[32px] py-4 px-2 text-sm font-semibold text-foreground truncate sticky left-0 bg-card z-9", children: trace.name }), _jsx("div", { className: "flex w-full flex-row items-center hover:bg-muted/50 rounded-md",
|
|
4
|
+
return (_jsxs("div", { className: "flex hover:bg-muted-foreground/10 relative cursor-pointer", onClick: () => onExpand(trace.id), "data-testid": "trace-timeline-item", children: [_jsx("div", { className: "flex items-center min-w-[200px] max-w-[200px] h-[32px] max-h-[32px] py-4 px-2 text-sm font-semibold text-foreground truncate sticky left-0 bg-card z-9", children: trace.name }), _jsx("div", { className: "flex w-full flex-row items-center hover:bg-muted/50 rounded-md", children: _jsx("div", { className: "relative w-full h-[32px] flex items-center", children: _jsx("div", { className: cn('h-[24px] rounded-[4px] hover:opacity-80 transition-all duration-200', {
|
|
5
5
|
'bg-[repeating-linear-gradient(140deg,#BEFE29,#BEFE29_8px,#ABE625_8px,#ABE625_16px)]': trace.status === 'running',
|
|
6
6
|
'bg-[repeating-linear-gradient(140deg,#2862FE,#2862FE_8px,#2358E5_8px,#2358E5_16px)]': trace.status === 'completed',
|
|
7
7
|
'bg-[repeating-linear-gradient(140deg,#EA2069,#EA2069_8px,#D41E60_8px,#D41E60_16px)]': trace.status === 'failed',
|
|
@@ -11,5 +11,5 @@ export const TracesGroups = memo(({ groups, selectedGroupId, onGroupSelect }) =>
|
|
|
11
11
|
return `${duration}ms`;
|
|
12
12
|
return `${(duration / 1000).toFixed(1)}s`;
|
|
13
13
|
};
|
|
14
|
-
return (_jsx("div", { className: "overflow-auto", children: groups.length > 0 && (_jsx("div", { children: [...groups].reverse().map((group) => (_jsx("div", { "data-testid": `trace-${group.id}`, className: cn('cursor-pointer transition-colors', selectedGroupId === group.id ? 'bg-muted-foreground/10' : 'hover:bg-muted/70'), onClick: () => onGroupSelect(group), children: _jsxs("div", { className: "p-3 flex flex-col gap-1", children: [_jsxs("div", { className: "flex flex-row justify-between items-center gap-2", children: [_jsx("span", { className: "font-semibold text-lg", children: group.name }), _jsx(TraceStatusBadge, { status: group.status, duration: group.endTime ? formatDuration(group.endTime - group.startTime) : undefined })] }), _jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("div", { "data-testid": "trace-id", className: "text-xs text-muted-foreground font-mono tracking-[1px]", children: group.id }), _jsxs("span", { children: [group.metadata.totalSteps, " steps"] })] }), _jsxs("div", { className: "flex justify-between", children: [formatDistanceToNow(group.startTime), " ago"] }), group.metadata.activeSteps > 0 && (_jsxs("div", { className: "text-blue-600", children: [group.metadata.activeSteps, " active"] }))] })] }) }, group.id))) })) }));
|
|
14
|
+
return (_jsx("div", { className: "overflow-auto", children: groups.length > 0 && (_jsx("div", { children: [...groups].reverse().map((group) => (_jsx("div", { "data-testid": `trace-${group.id}`, className: cn('motia-trace-group cursor-pointer transition-colors', selectedGroupId === group.id ? 'bg-muted-foreground/10' : 'hover:bg-muted/70'), onClick: () => onGroupSelect(group), children: _jsxs("div", { className: "p-3 flex flex-col gap-1", children: [_jsxs("div", { className: "flex flex-row justify-between items-center gap-2", children: [_jsx("span", { className: "font-semibold text-lg", children: group.name }), _jsx(TraceStatusBadge, { status: group.status, duration: group.endTime ? formatDuration(group.endTime - group.startTime) : undefined })] }), _jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("div", { "data-testid": "trace-id", className: "text-xs text-muted-foreground font-mono tracking-[1px]", children: group.id }), _jsxs("span", { children: [group.metadata.totalSteps, " steps"] })] }), _jsxs("div", { className: "flex justify-between", children: [formatDistanceToNow(group.startTime), " ago"] }), group.metadata.activeSteps > 0 && (_jsxs("div", { className: "text-blue-600", children: [group.metadata.activeSteps, " active"] }))] })] }) }, group.id))) })) }));
|
|
15
15
|
});
|
|
@@ -35,5 +35,5 @@ export const Sidebar = ({ initialWidth, onClose, ...props }) => {
|
|
|
35
35
|
}, [sidebarId, onClose]);
|
|
36
36
|
return createPortal(_jsxs("div", { ...getRootProps(), className: "pr-2 py-2 relative", children: [_jsx("div", { ...getHandleProps({
|
|
37
37
|
reverse: true,
|
|
38
|
-
}), className: "flex h-6 w-6 items-center justify-center rounded-full bg-background border border-border absolute top-1/2 -translate-y-1/2 -left-4 z-20", children: _jsx(Equal, { className: "rotate-90 w-4 h-4 text-muted-foreground" }) }), _jsx(Panel, { ...props, variant: "outlined", className: "max-h-[calc(100vh-80px)] h-full" })] }), document.querySelector(`#${APP_SIDEBAR_CONTAINER_ID}`));
|
|
38
|
+
}), className: "flex h-6 w-6 items-center justify-center rounded-full bg-background border border-border absolute top-1/2 -translate-y-1/2 -left-4 z-20", children: _jsx(Equal, { className: "rotate-90 w-4 h-4 text-muted-foreground" }) }), _jsx(Panel, { ...props, variant: "outlined", className: "max-h-[calc(100vh-80px)] h-full", "data-testid": "sidebar-panel" })] }), document.querySelector(`#${APP_SIDEBAR_CONTAINER_ID}`));
|
|
39
39
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TutorialStep } from './tutorial-types';
|
|
2
|
+
declare class Tutorial {
|
|
3
|
+
steps: TutorialStep[];
|
|
4
|
+
private onStepsRegisteredCallbacks;
|
|
5
|
+
private onOpenCallbacks;
|
|
6
|
+
register(steps: TutorialStep[]): void;
|
|
7
|
+
onStepsRegistered(callback: () => void): void;
|
|
8
|
+
onOpen(callback: () => void): void;
|
|
9
|
+
open(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare const MotiaTutorial: Tutorial;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class Tutorial {
|
|
2
|
+
constructor() {
|
|
3
|
+
Object.defineProperty(this, "steps", {
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true,
|
|
7
|
+
value: []
|
|
8
|
+
});
|
|
9
|
+
Object.defineProperty(this, "onStepsRegisteredCallbacks", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: []
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(this, "onOpenCallbacks", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: []
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
register(steps) {
|
|
23
|
+
this.steps = steps;
|
|
24
|
+
this.onStepsRegisteredCallbacks.forEach((callback) => callback());
|
|
25
|
+
}
|
|
26
|
+
onStepsRegistered(callback) {
|
|
27
|
+
this.onStepsRegisteredCallbacks.push(callback);
|
|
28
|
+
}
|
|
29
|
+
onOpen(callback) {
|
|
30
|
+
this.onOpenCallbacks.push(callback);
|
|
31
|
+
}
|
|
32
|
+
open() {
|
|
33
|
+
this.onOpenCallbacks.forEach((callback) => callback());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export const MotiaTutorial = new Tutorial();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type TutorialActionClick = {
|
|
2
|
+
type: 'click';
|
|
3
|
+
selector: string;
|
|
4
|
+
optional?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type TutorialActionEditor = {
|
|
7
|
+
type: 'fill-editor';
|
|
8
|
+
content: Record<string, any>;
|
|
9
|
+
};
|
|
10
|
+
export type TutorialAction = TutorialActionClick | TutorialActionEditor;
|
|
11
|
+
export type TutorialImage = {
|
|
12
|
+
height: number;
|
|
13
|
+
src: string;
|
|
14
|
+
};
|
|
15
|
+
export type TutorialStep = {
|
|
16
|
+
title: string;
|
|
17
|
+
description: React.FC<void>;
|
|
18
|
+
image?: TutorialImage;
|
|
19
|
+
link?: string;
|
|
20
|
+
elementXpath?: string;
|
|
21
|
+
before?: TutorialAction[];
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const workbenchXPath: {
|
|
2
|
+
sidebarContainer: string;
|
|
3
|
+
closePanelButton: string;
|
|
4
|
+
bottomPanel: string;
|
|
5
|
+
flows: {
|
|
6
|
+
dropdownFlow: (flowId: string) => string;
|
|
7
|
+
feature: (featureId: string) => string;
|
|
8
|
+
previewButton: (stepId: string) => string;
|
|
9
|
+
node: (stepId: string) => string;
|
|
10
|
+
};
|
|
11
|
+
endpoints: {
|
|
12
|
+
endpoint: (method: string, path: string) => string;
|
|
13
|
+
callPanel: string;
|
|
14
|
+
callTab: string;
|
|
15
|
+
response: string;
|
|
16
|
+
playButton: string;
|
|
17
|
+
};
|
|
18
|
+
tracing: {
|
|
19
|
+
trace: (index: number) => string;
|
|
20
|
+
details: string;
|
|
21
|
+
timeline: (index: number) => string;
|
|
22
|
+
};
|
|
23
|
+
logs: {
|
|
24
|
+
container: string;
|
|
25
|
+
searchContainer: string;
|
|
26
|
+
traceColumn: (index: number) => string;
|
|
27
|
+
row: string;
|
|
28
|
+
};
|
|
29
|
+
states: {
|
|
30
|
+
container: string;
|
|
31
|
+
row: (index: number) => string;
|
|
32
|
+
};
|
|
33
|
+
links: {
|
|
34
|
+
flows: string;
|
|
35
|
+
endpoints: string;
|
|
36
|
+
tracing: string;
|
|
37
|
+
logs: string;
|
|
38
|
+
states: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const workbenchXPath = {
|
|
2
|
+
sidebarContainer: '//div[@data-testid="sidebar-panel"]',
|
|
3
|
+
closePanelButton: '//div[@id="app-sidebar-container"]//button[@data-testid="close-panel"]',
|
|
4
|
+
bottomPanel: '//div[@id="bottom-panel"]',
|
|
5
|
+
flows: {
|
|
6
|
+
dropdownFlow: (flowId) => `//div[@data-testid="dropdown-${flowId}"]`,
|
|
7
|
+
feature: (featureId) => `//div[@data-feature-id="${featureId}"]`,
|
|
8
|
+
previewButton: (stepId) => `//button[@data-testid="open-code-preview-button-${stepId}"]`,
|
|
9
|
+
node: (stepId) => `//div[@data-testid="node-${stepId}"]`,
|
|
10
|
+
},
|
|
11
|
+
endpoints: {
|
|
12
|
+
endpoint: (method, path) => `//div[@data-testid="endpoint-${method}-${path}"]`,
|
|
13
|
+
callPanel: '//div[@data-testid="endpoint-body-panel__call"]',
|
|
14
|
+
callTab: '//button[@data-testid="endpoint-call-tab"]',
|
|
15
|
+
response: '//div[@data-testid="endpoint-response-container"]',
|
|
16
|
+
playButton: '//button[@data-testid="endpoint-play-button"]',
|
|
17
|
+
},
|
|
18
|
+
tracing: {
|
|
19
|
+
trace: (index) => `(//div[contains(@class, 'motia-trace-group')])[${index}]`,
|
|
20
|
+
details: '//div[@data-testid="trace-details"]',
|
|
21
|
+
timeline: (index) => `(//div[@data-testid="trace-timeline-item"])[${index}]`,
|
|
22
|
+
},
|
|
23
|
+
logs: {
|
|
24
|
+
container: '//div[@data-testid="logs-container"]',
|
|
25
|
+
searchContainer: '//div[@data-testid="logs-search-container"]',
|
|
26
|
+
traceColumn: (index) => `(//td[starts-with(@data-testid, 'trace')])[${index}]`,
|
|
27
|
+
row: '//div[@data-testid="log-row"]',
|
|
28
|
+
},
|
|
29
|
+
states: {
|
|
30
|
+
container: '//div[@data-testid="states-container"]',
|
|
31
|
+
row: (index) => `(//tr[starts-with(@data-testid, 'item-')])[${index}]`,
|
|
32
|
+
},
|
|
33
|
+
links: {
|
|
34
|
+
flows: '//div[@data-testid="flows-dropdown-trigger"]',
|
|
35
|
+
endpoints: '//button[@data-testid="endpoints-link"]',
|
|
36
|
+
tracing: '//button[@data-testid="traces-link"]',
|
|
37
|
+
logs: '//button[@data-testid="logs-link"]',
|
|
38
|
+
states: '//button[@data-testid="states-link"]',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const waitForElementByXPath: (xpath: string, optional?: boolean, maxAttempts?: number, delayMs?: number) => Promise<HTMLElement | null>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const waitForElementByXPath = async (xpath, optional = false, maxAttempts = 50, delayMs = 50) => {
|
|
2
|
+
let attempts = 0;
|
|
3
|
+
while (attempts < maxAttempts) {
|
|
4
|
+
const result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
|
|
5
|
+
const targetElement = result?.singleNodeValue;
|
|
6
|
+
if (targetElement) {
|
|
7
|
+
return targetElement;
|
|
8
|
+
}
|
|
9
|
+
else if (optional) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
13
|
+
attempts++;
|
|
14
|
+
}
|
|
15
|
+
console.warn(`Element not found after maximum attempts: ${xpath}`);
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { TutorialImage } from '../engine/tutorial-types';
|
|
2
|
+
export declare const useTutorialEngine: () => {
|
|
3
|
+
ref: import("react").RefObject<HTMLDivElement | null>;
|
|
4
|
+
highlighterRef: import("react").RefObject<HTMLDivElement | null>;
|
|
5
|
+
title: string;
|
|
6
|
+
description: import("react").ReactNode;
|
|
7
|
+
image: TutorialImage | undefined;
|
|
8
|
+
link: string | undefined;
|
|
9
|
+
currentStep: number;
|
|
10
|
+
totalSteps: number;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
moveStep: (stepNumber: number) => Promise<void>;
|
|
13
|
+
currentStepRef: import("react").RefObject<number>;
|
|
14
|
+
manualOpenRef: import("react").RefObject<boolean>;
|
|
15
|
+
};
|