@motiadev/workbench 0.2.1-beta.55 → 0.2.1-beta.57
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/src/components/app-sidebar.js +9 -3
- package/dist/src/components/endpoints/endpoint-call.js +1 -1
- package/dist/src/components/endpoints/response-body.js +1 -1
- package/dist/src/components/endpoints/selected-endpoint.js +1 -1
- package/dist/src/components/logs/log-console.js +24 -4
- package/dist/src/components/logs/log-field.js +2 -2
- package/dist/src/components/logs/logs.js +1 -1
- package/dist/src/components/states/state-value.js +5 -5
- package/dist/src/components/ui/button.js +1 -1
- package/dist/src/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/src/components/ui/dropdown-menu.js +50 -0
- package/dist/src/components/ui/sidebar.js +3 -2
- package/dist/src/components/ui/theme-toggle.d.ts +2 -0
- package/dist/src/components/ui/theme-toggle.js +11 -0
- package/dist/src/hooks/use-theme.d.ts +6 -0
- package/dist/src/hooks/use-theme.js +28 -0
- package/dist/src/publicComponents/api-node.js +1 -1
- package/dist/src/publicComponents/base-node.js +1 -1
- package/dist/src/publicComponents/components/header-bar.js +1 -1
- package/dist/src/publicComponents/cron-node.js +1 -1
- package/dist/src/publicComponents/emits.js +1 -1
- package/dist/src/publicComponents/node-details.js +2 -2
- package/dist/src/publicComponents/noop-node.js +1 -1
- package/dist/src/publicComponents/subscribe.js +1 -1
- package/dist/src/route-wrapper.js +1 -1
- package/dist/src/routes/flow.js +1 -1
- package/dist/src/routes/index.js +1 -1
- package/dist/src/routes/logs-page.js +2 -2
- package/dist/src/routes/states-page.js +1 -1
- package/dist/src/stores/use-logs.d.ts +1 -1
- package/dist/src/stores/use-logs.js +52 -8
- package/dist/src/views/flow/arrow-head.d.ts +2 -1
- package/dist/src/views/flow/arrow-head.js +5 -1
- package/dist/src/views/flow/flow-loader.js +1 -1
- package/dist/src/views/flow/flow-view.js +9 -10
- package/dist/src/views/flow/legend.js +4 -5
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/package.json +3 -4
|
@@ -4,11 +4,17 @@ import { File, Link2, Logs, Workflow } from 'lucide-react';
|
|
|
4
4
|
import { Link, useLocation } from 'react-router';
|
|
5
5
|
import { Sidebar, SidebarButton, SidebarGroup } from './ui/sidebar';
|
|
6
6
|
import { Badge } from './ui/badge';
|
|
7
|
-
import { useLogs } from '
|
|
7
|
+
import { useLogs } from '@/stores/use-logs';
|
|
8
|
+
const BadgeCount = () => {
|
|
9
|
+
const unreadLogsCount = useLogs((state) => state.unreadLogsCount);
|
|
10
|
+
if (!unreadLogsCount) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return _jsx(Badge, { variant: "red-rounded", children: unreadLogsCount });
|
|
14
|
+
};
|
|
8
15
|
export const AppSidebar = () => {
|
|
9
16
|
const { flows } = useListFlows();
|
|
10
17
|
const { pathname } = useLocation();
|
|
11
18
|
const isActive = (flowId) => pathname.includes(`/flow/${flowId}`);
|
|
12
|
-
|
|
13
|
-
return (_jsxs(Sidebar, { children: [_jsxs(SidebarGroup, { title: "Motia", children: [_jsx(Link, { to: "/logs", children: _jsxs(SidebarButton, { isActive: pathname === '/logs', icon: _jsx(Logs, { className: "w-4 h-4" }), children: ["Logs", pathname !== '/logs' && unreadLogsCount > 0 && _jsx(Badge, { variant: "red-rounded", children: unreadLogsCount })] }) }), _jsx(Link, { to: "/states", children: _jsx(SidebarButton, { isActive: pathname === '/states', icon: _jsx(File, { className: "w-4 h-4" }), children: "States" }) }), _jsx(Link, { to: "/endpoints", children: _jsx(SidebarButton, { isActive: pathname === '/endpoints', icon: _jsx(Link2, { className: "w-4 h-4" }), children: "Endpoints" }) })] }), _jsx(SidebarGroup, { title: "Flows", children: flows.map((flow) => (_jsx(Link, { to: `/flow/${flow.id}`, children: _jsx(SidebarButton, { isActive: isActive(flow.id), icon: _jsx(Workflow, { className: "w-4 h-4" }), children: flow.name }) }, flow.id))) })] }));
|
|
19
|
+
return (_jsxs(Sidebar, { children: [_jsxs(SidebarGroup, { title: "Motia", children: [_jsx(Link, { to: "/logs", children: _jsxs(SidebarButton, { isActive: pathname === '/logs', icon: _jsx(Logs, { className: "w-4 h-4" }), children: ["Logs", pathname !== '/logs' && _jsx(BadgeCount, {})] }) }), _jsx(Link, { to: "/states", children: _jsx(SidebarButton, { isActive: pathname === '/states', icon: _jsx(File, { className: "w-4 h-4" }), children: "States" }) }), _jsx(Link, { to: "/endpoints", children: _jsx(SidebarButton, { isActive: pathname === '/endpoints', icon: _jsx(Link2, { className: "w-4 h-4" }), children: "Endpoints" }) })] }), _jsx(SidebarGroup, { title: "Flows", children: flows.map((flow) => (_jsx(Link, { to: `/flow/${flow.id}`, children: _jsx(SidebarButton, { isActive: isActive(flow.id), icon: _jsx(Workflow, { className: "w-4 h-4" }), children: flow.name }) }, flow.id))) })] }));
|
|
14
20
|
};
|
|
@@ -53,5 +53,5 @@ export const EndpointCall = ({ endpoint, onClose }) => {
|
|
|
53
53
|
setExecutionTime(executionTime);
|
|
54
54
|
setIsRequestLoading(false);
|
|
55
55
|
};
|
|
56
|
-
return (_jsxs("div", { className: "flex flex-col gap-2 overflow-y-auto", children: [_jsxs("div", { className: "text-xs flex flex-row gap-2 items-center justify-between w-full", children: [_jsx("span", { className: "font-bold", children: "Request" }), _jsx("div", { className: "flex flex-row gap-2 items-center hover:bg-white/10 rounded-md p-1", children: _jsx(X, { className: "cursor-pointer w-4 h-4", onClick: onClose }) })] }), _jsxs("div", { className: "flex flex-row gap-2 items-center", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-bold", children: endpoint.path })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description }), !!pathParams.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Path Params" }), _jsx("div", { className: "flex flex-col gap-4", children: pathParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param }), _jsx(Input, { className: "w-full", value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) })] }, param))) })] })), !!endpoint.queryParams?.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Query Params" }), _jsx("div", { className: "flex flex-col gap-4", children: endpoint.queryParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param.name }), _jsx(Input, { className: "w-full", value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) })] }, param.name))) })] })), shouldHaveBody && (_jsxs("div", { className: "flex flex-col gap-2 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Body" }), _jsx(Textarea, { className: "w-full font-mono font-medium min-h-[200px]", value: body, onChange: (e) => setBody(e.target.value) })] })), _jsxs(Button, { className: "w-fit", onClick: handleRequest, disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), responseCode !== undefined && (_jsxs("div", { className: "flex flex-col gap-2 rounded-lg bg-muted", children: [_jsxs("span", { className: "text-xs font-bold", children: [_jsx(EndpointBadge, { variant: responseCode >= 400 ? 'DELETE' : 'GET', children: responseCode }), " Execution time: ", _jsxs("span", { className: "text-muted-foreground", children: [executionTime, "ms"] })] }), isStreamed && (_jsxs("span", { className: "flex flex-row items-center font-medium text-muted-foreground text-xs", children: [_jsxs("span", { className: "ml-1 inline-block w-2 h-2 rounded-full bg-green-500 mr-2 relative", children: [_jsx("span", { className: "absolute inset-0 rounded-full bg-green-500 animate-[ping_1.5s_ease-in-out_infinite]" }), _jsx("span", { className: "absolute inset-0 rounded-full bg-green-500" })] }), "Object is being streamed, this is not the actual response from the API Endpoint"] })), _jsx("span", { className: "text-xs font-mono font-bold bg-black/50 p-2 rounded-lg whitespace-pre-wrap", children: JSON.stringify(responseBodyData, null, 2) })] }))] }));
|
|
56
|
+
return (_jsxs("div", { className: "flex flex-col gap-2 overflow-y-auto", children: [_jsxs("div", { className: "text-xs flex flex-row gap-2 items-center justify-between w-full", children: [_jsx("span", { className: "font-bold", children: "Request" }), _jsx("div", { className: "flex flex-row gap-2 items-center hover:bg-white/10 rounded-md p-1", children: _jsx(X, { className: "cursor-pointer w-4 h-4", onClick: onClose }) })] }), _jsxs("div", { className: "flex flex-row gap-2 items-center", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-bold", children: endpoint.path })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description }), !!pathParams.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Path Params" }), _jsx("div", { className: "flex flex-col gap-4", children: pathParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param }), _jsx(Input, { className: "w-full", value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) })] }, param))) })] })), !!endpoint.queryParams?.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Query Params" }), _jsx("div", { className: "flex flex-col gap-4", children: endpoint.queryParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param.name }), _jsx(Input, { className: "w-full", value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) })] }, param.name))) })] })), shouldHaveBody && (_jsxs("div", { className: "flex flex-col gap-2 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Body" }), _jsx(Textarea, { className: "w-full font-mono font-medium min-h-[200px]", value: body, onChange: (e) => setBody(e.target.value) })] })), _jsxs(Button, { className: "w-fit", onClick: handleRequest, disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), responseCode !== undefined && (_jsxs("div", { className: "flex flex-col gap-2 rounded-lg bg-muted", children: [_jsxs("span", { className: "text-xs font-bold", children: [_jsx(EndpointBadge, { variant: responseCode >= 400 ? 'DELETE' : 'GET', children: responseCode }), " Execution time: ", _jsxs("span", { className: "text-muted-foreground", children: [executionTime, "ms"] })] }), isStreamed && (_jsxs("span", { className: "flex flex-row items-center font-medium text-muted-foreground text-xs", children: [_jsxs("span", { className: "ml-1 inline-block w-2 h-2 rounded-full bg-green-500 mr-2 relative", children: [_jsx("span", { className: "absolute inset-0 rounded-full bg-green-500 animate-[ping_1.5s_ease-in-out_infinite]" }), _jsx("span", { className: "absolute inset-0 rounded-full bg-green-500" })] }), "Object is being streamed, this is not the actual response from the API Endpoint"] })), _jsx("span", { className: "text-xs font-mono font-bold dark:bg-black/50 bg-white/50 p-2 rounded-lg whitespace-pre-wrap", children: JSON.stringify(responseBodyData, null, 2) })] }))] }));
|
|
57
57
|
};
|
|
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
|
|
3
3
|
export const ResponseBody = ({ status, body }) => {
|
|
4
4
|
const { body: responseBody } = useJsonSchemaToJson(body);
|
|
5
|
-
return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: status }), _jsx("span", { className: "text-xs font-mono bg-black/50 p-2 rounded-lg whitespace-pre-wrap", children: responseBody })] }));
|
|
5
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: status }), _jsx("span", { className: "text-xs font-mono dark:bg-black/50 bg-white/50 p-2 rounded-lg whitespace-pre-wrap", children: responseBody })] }));
|
|
6
6
|
};
|
|
@@ -3,5 +3,5 @@ import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
|
|
|
3
3
|
import { ResponseBody } from './response-body';
|
|
4
4
|
export const SelectedEndpoint = ({ endpoint }) => {
|
|
5
5
|
const { body: requestBody } = useJsonSchemaToJson(endpoint.bodySchema);
|
|
6
|
-
return (_jsxs(_Fragment, { children: [endpoint.queryParams && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Query Params" }), _jsx("div", { className: "flex flex-col gap-2 flex-1 p-2 rounded-lg bg-muted table", children: endpoint.queryParams.map((param) => (_jsxs("span", { className: "text-xs table-row", children: [_jsx("span", { className: "font-bold table-cell", children: param.name }), _jsx("span", { className: "text-xs table-cell", children: param.description })] }, param.name))) })] })), requestBody && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Request Body" }), _jsx("span", { className: "text-xs font-mono bg-black/50 p-2 rounded-lg whitespace-pre-wrap", children: requestBody })] })), endpoint.responseSchema && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Response" }), Object.entries(endpoint.responseSchema).map(([status, schema]) => (_jsx(ResponseBody, { status: status, body: schema }, status)))] }))] }));
|
|
6
|
+
return (_jsxs(_Fragment, { children: [endpoint.queryParams && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Query Params" }), _jsx("div", { className: "flex flex-col gap-2 flex-1 p-2 rounded-lg bg-muted table", children: endpoint.queryParams.map((param) => (_jsxs("span", { className: "text-xs table-row", children: [_jsx("span", { className: "font-bold table-cell", children: param.name }), _jsx("span", { className: "text-xs table-cell", children: param.description })] }, param.name))) })] })), requestBody && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Request Body" }), _jsx("span", { className: "text-xs font-mono dark:bg-black/50 bg-white/50 p-2 rounded-lg whitespace-pre-wrap", children: requestBody })] })), endpoint.responseSchema && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Response" }), Object.entries(endpoint.responseSchema).map(([status, schema]) => (_jsx(ResponseBody, { status: status, body: schema }, status)))] }))] }));
|
|
7
7
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useLogs } from '@/stores/use-logs';
|
|
3
|
-
import { motion, AnimatePresence } from 'framer-motion';
|
|
4
3
|
import { ChevronDown, ChevronUp, Trash2 } from 'lucide-react';
|
|
5
4
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
6
5
|
import { Button } from '../ui/button';
|
|
@@ -8,13 +7,20 @@ import { cn } from '@/lib/utils';
|
|
|
8
7
|
import { Logs } from './logs';
|
|
9
8
|
const MIN_HEIGHT = 100;
|
|
10
9
|
const DEFAULT_HEIGHT = 200;
|
|
10
|
+
const ClearLogsButton = () => {
|
|
11
|
+
const hasLog = useLogs((state) => state.logs.length > 0);
|
|
12
|
+
const resetLogs = useLogs((state) => state.resetLogs);
|
|
13
|
+
if (!hasLog) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return (_jsxs(Button, { variant: "outline", onClick: resetLogs, children: [_jsx(Trash2, { className: "w-4 h-4" }), "Clear logs"] }));
|
|
17
|
+
};
|
|
11
18
|
export const LogConsole = () => {
|
|
12
19
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
13
20
|
const [height, setHeight] = useState(DEFAULT_HEIGHT);
|
|
14
|
-
const logs = useLogs((state) => state.logs);
|
|
15
|
-
const resetLogs = useLogs((state) => state.resetLogs);
|
|
16
21
|
const dragRef = useRef(null);
|
|
17
22
|
const [isDragging, setIsDragging] = useState(false);
|
|
23
|
+
const contentRef = useRef(null);
|
|
18
24
|
const toggleExpand = () => setIsExpanded(!isExpanded);
|
|
19
25
|
const handleMouseDown = useCallback((e) => {
|
|
20
26
|
e.preventDefault();
|
|
@@ -41,5 +47,19 @@ export const LogConsole = () => {
|
|
|
41
47
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
42
48
|
};
|
|
43
49
|
}, [isDragging]);
|
|
44
|
-
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (contentRef.current) {
|
|
52
|
+
if (isExpanded) {
|
|
53
|
+
contentRef.current.style.height = `${height}px`;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
contentRef.current.style.height = '0px';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}, [isExpanded, height]);
|
|
60
|
+
return (_jsxs("div", { className: "absolute bottom-0 left-0 right-0 bg-background/80 border border-solid border-border m-4 rounded-lg", children: [_jsx("div", { ref: dragRef, onMouseDown: handleMouseDown, className: cn('absolute -top-1 left-0 right-0 h-1 cursor-ns-resize hover:bg-background/40', isDragging && 'bg-background/40') }), _jsxs("div", { className: "text-muted-foreground flex justify-between w-full items-center p-4 gap-2", children: [_jsx("label", { className: "w-full text-left justify-start h-full text-md uppercase", children: "Logs" }), _jsx(ClearLogsButton, {}), _jsx(Button, { variant: "outline", onClick: toggleExpand, children: isExpanded ? _jsx(ChevronDown, { className: "w-4 h-4" }) : _jsx(ChevronUp, { className: "w-4 h-4" }) })] }), _jsx("div", { ref: contentRef, style: {
|
|
61
|
+
overflow: 'hidden',
|
|
62
|
+
transition: 'height 0.2s ease-out',
|
|
63
|
+
height: isExpanded ? `${height}px` : '0px',
|
|
64
|
+
}, children: _jsx(Logs, {}) })] }));
|
|
45
65
|
};
|
|
@@ -3,7 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { cn } from '../../lib/utils';
|
|
4
4
|
const Value = ({ value }) => {
|
|
5
5
|
const displayValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
6
|
-
return _jsx("div", { className: "text-gray-400 text-md", children: displayValue });
|
|
6
|
+
return _jsx("div", { className: "dark:text-gray-400 text-md", children: displayValue });
|
|
7
7
|
};
|
|
8
8
|
const LogValue = ({ value }) => {
|
|
9
9
|
if (React.isValidElement(value)) {
|
|
@@ -16,5 +16,5 @@ const LogValue = ({ value }) => {
|
|
|
16
16
|
return _jsx(Value, { value: value });
|
|
17
17
|
};
|
|
18
18
|
export const LogField = ({ label, value, className }) => {
|
|
19
|
-
return (_jsx("div", { className: cn('flex row text-
|
|
19
|
+
return (_jsx("div", { className: cn('flex row text-foreground p-2', className), children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-md font-semibold", children: label }), _jsx("span", { className: "", children: value ? _jsx(LogValue, { value: value }) : _jsx(Value, { value: value }) })] }) }));
|
|
20
20
|
};
|
|
@@ -14,5 +14,5 @@ export const Logs = () => {
|
|
|
14
14
|
const handleLogClick = (log) => {
|
|
15
15
|
setSelectedLog(log);
|
|
16
16
|
};
|
|
17
|
-
return (_jsxs("div", { className: "overflow-y-auto h-full text-bold p-4", children: [_jsx(LogDetail, { log: selectedLog, onClose: () => setSelectedLog(undefined) }), _jsx(Table, { children: _jsx(TableBody, { className: "text-md font-mono", children: logs.map((log, index) => (_jsxs(TableRow, { className: "cursor-pointer even:bg-muted/50 border-0", onClick: () => handleLogClick(log), children: [_jsxs(TableCell, { className: "whitespace-nowrap flex items-center gap-2", children: [_jsx(LogLevelDot, { level: log.level }), timestamp(log.time)] }), _jsx(TableCell, { className: "whitespace-nowrap text-md
|
|
17
|
+
return (_jsxs("div", { className: "overflow-y-auto h-full text-bold p-4", children: [_jsx(LogDetail, { log: selectedLog, onClose: () => setSelectedLog(undefined) }), _jsx(Table, { children: _jsx(TableBody, { className: "text-md font-mono", children: logs.map((log, index) => (_jsxs(TableRow, { className: "cursor-pointer even:bg-muted/50 border-0", onClick: () => handleLogClick(log), children: [_jsxs(TableCell, { className: "whitespace-nowrap flex items-center gap-2", children: [_jsx(LogLevelDot, { level: log.level }), timestamp(log.time)] }), _jsx(TableCell, { className: "whitespace-nowrap text-md cursor-pointer hover:text-primary text-muted-foreground text-xs font-mono", children: log.traceId }), _jsx(TableCell, { className: "whitespace-nowrap text-md font-mono", children: log.step }), _jsx(TableCell, { className: "whitespace-nowrap text-md font-mono max-w-[500px] truncate w-full", children: log.msg })] }, index))) }) })] }));
|
|
18
18
|
};
|
|
@@ -3,13 +3,13 @@ import { cva } from 'class-variance-authority';
|
|
|
3
3
|
import { SquareMinus, SquarePlus } from 'lucide-react';
|
|
4
4
|
import React, { useState } from 'react';
|
|
5
5
|
import { cn } from '../../lib/utils';
|
|
6
|
-
const valueVariants = cva('text-
|
|
6
|
+
const valueVariants = cva('text-muted-foreground text-sm py-2', {
|
|
7
7
|
variants: {
|
|
8
8
|
variant: {
|
|
9
9
|
boolean: 'text-sky-400',
|
|
10
10
|
number: 'text-teal-400',
|
|
11
|
-
undefined: 'text-
|
|
12
|
-
string: 'text-
|
|
11
|
+
undefined: 'text-muted-foreground',
|
|
12
|
+
string: 'text-muted-foreground',
|
|
13
13
|
object: 'text-gray-800',
|
|
14
14
|
},
|
|
15
15
|
},
|
|
@@ -43,9 +43,9 @@ export const StateValue = ({ value, label, isRoot = false }) => {
|
|
|
43
43
|
const [openBracket, closeBracket] = isRoot ? [] : isArray ? ['[', ']'] : ['{', '}'];
|
|
44
44
|
if (typeof value === 'object' && !!value) {
|
|
45
45
|
const valueObject = value;
|
|
46
|
-
return (_jsxs("div", { className: "flex flex-col gap-2", children: [(label || openBracket) && (_jsxs("div", { className: "flex gap-1 items-center text-md font-semibold hover:bg-gray-800 rounded-md py-2", onClick: toggle, children: [isOpen ? (_jsx(SquareMinus, { className: "w-4 h-4 text-
|
|
46
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [(label || openBracket) && (_jsxs("div", { className: "flex gap-1 items-center text-md font-semibold hover:bg-gray-800 rounded-md py-2", onClick: toggle, children: [isOpen ? (_jsx(SquareMinus, { className: "w-4 h-4 text-muted-foreground" })) : (_jsx(SquarePlus, { className: "w-4 h-4 text-muted-foreground" })), label, ' ', !isRoot && (_jsxs("span", { className: "text-muted-foreground text-sm", children: [openBracket, !isOpen && ` ... ${closeBracket}`] }))] })), isOpen &&
|
|
47
47
|
!!valueObject &&
|
|
48
|
-
Object.keys(valueObject).map((key) => (_jsx("div", { className: cn('flex flex-col gap-2', !isRoot && 'ml-4'), children: _jsx("span", { className: "text-md", children: _jsx(StateValue, { label: isArray ? undefined : key, value: valueObject[key] }) }) }, key))), !isRoot && isOpen && _jsx("span", { className: "text-
|
|
48
|
+
Object.keys(valueObject).map((key) => (_jsx("div", { className: cn('flex flex-col gap-2', !isRoot && 'ml-4'), children: _jsx("span", { className: "text-md", children: _jsx(StateValue, { label: isArray ? undefined : key, value: valueObject[key] }) }) }, key))), !isRoot && isOpen && _jsx("span", { className: "text-muted-foreground text-sm", children: closeBracket })] }));
|
|
49
49
|
}
|
|
50
50
|
return _jsx(Value, { value: undefined, variant: "object" });
|
|
51
51
|
};
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { Slot } from '@radix-ui/react-slot';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
import { cn } from '@/lib/utils';
|
|
6
|
-
const buttonVariants = cva('inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', {
|
|
6
|
+
const buttonVariants = cva('inline-flex items-center cursor-pointer justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', {
|
|
7
7
|
variants: {
|
|
8
8
|
variant: {
|
|
9
9
|
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
|
+
declare function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function DropdownMenuPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function DropdownMenuTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function DropdownMenuContent({ className, sideOffset, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
declare function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare function DropdownMenuItem({ className, inset, variant, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
9
|
+
inset?: boolean;
|
|
10
|
+
variant?: 'default' | 'destructive';
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
declare function DropdownMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
declare function DropdownMenuRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
declare function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
declare function DropdownMenuLabel({ className, inset, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
16
|
+
inset?: boolean;
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
declare function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
declare function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
declare function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
declare function DropdownMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
22
|
+
inset?: boolean;
|
|
23
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
declare function DropdownMenuSubContent({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
|
4
|
+
import { cn } from '@/lib/utils';
|
|
5
|
+
function DropdownMenu({ ...props }) {
|
|
6
|
+
return _jsx(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
|
|
7
|
+
}
|
|
8
|
+
function DropdownMenuPortal({ ...props }) {
|
|
9
|
+
return _jsx(DropdownMenuPrimitive.Portal, { "data-slot": "dropdown-menu-portal", ...props });
|
|
10
|
+
}
|
|
11
|
+
function DropdownMenuTrigger({ ...props }) {
|
|
12
|
+
return _jsx(DropdownMenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
13
|
+
}
|
|
14
|
+
function DropdownMenuContent({ className, sideOffset = 4, ...props }) {
|
|
15
|
+
return (_jsx(DropdownMenuPrimitive.Portal, { children: _jsx(DropdownMenuPrimitive.Content, { "data-slot": "dropdown-menu-content", sideOffset: sideOffset, className: cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', className), ...props }) }));
|
|
16
|
+
}
|
|
17
|
+
function DropdownMenuGroup({ ...props }) {
|
|
18
|
+
return _jsx(DropdownMenuPrimitive.Group, { "data-slot": "dropdown-menu-group", ...props });
|
|
19
|
+
}
|
|
20
|
+
function DropdownMenuItem({ className, inset, variant = 'default', ...props }) {
|
|
21
|
+
return (_jsx(DropdownMenuPrimitive.Item, { "data-slot": "dropdown-menu-item", "data-inset": inset, "data-variant": variant, className: cn("focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props }));
|
|
22
|
+
}
|
|
23
|
+
function DropdownMenuCheckboxItem({ className, children, checked, ...props }) {
|
|
24
|
+
return (_jsxs(DropdownMenuPrimitive.CheckboxItem, { "data-slot": "dropdown-menu-checkbox-item", className: cn("focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), checked: checked, ...props, children: [_jsx("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(CheckIcon, { className: "size-4" }) }) }), children] }));
|
|
25
|
+
}
|
|
26
|
+
function DropdownMenuRadioGroup({ ...props }) {
|
|
27
|
+
return _jsx(DropdownMenuPrimitive.RadioGroup, { "data-slot": "dropdown-menu-radio-group", ...props });
|
|
28
|
+
}
|
|
29
|
+
function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
30
|
+
return (_jsxs(DropdownMenuPrimitive.RadioItem, { "data-slot": "dropdown-menu-radio-item", className: cn("focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [_jsx("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(CircleIcon, { className: "size-2 fill-current" }) }) }), children] }));
|
|
31
|
+
}
|
|
32
|
+
function DropdownMenuLabel({ className, inset, ...props }) {
|
|
33
|
+
return (_jsx(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", "data-inset": inset, className: cn('px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', className), ...props }));
|
|
34
|
+
}
|
|
35
|
+
function DropdownMenuSeparator({ className, ...props }) {
|
|
36
|
+
return (_jsx(DropdownMenuPrimitive.Separator, { "data-slot": "dropdown-menu-separator", className: cn('bg-border -mx-1 my-1 h-px', className), ...props }));
|
|
37
|
+
}
|
|
38
|
+
function DropdownMenuShortcut({ className, ...props }) {
|
|
39
|
+
return (_jsx("span", { "data-slot": "dropdown-menu-shortcut", className: cn('text-muted-foreground ml-auto text-xs tracking-widest', className), ...props }));
|
|
40
|
+
}
|
|
41
|
+
function DropdownMenuSub({ ...props }) {
|
|
42
|
+
return _jsx(DropdownMenuPrimitive.Sub, { "data-slot": "dropdown-menu-sub", ...props });
|
|
43
|
+
}
|
|
44
|
+
function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
|
|
45
|
+
return (_jsxs(DropdownMenuPrimitive.SubTrigger, { "data-slot": "dropdown-menu-sub-trigger", "data-inset": inset, className: cn('focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8', className), ...props, children: [children, _jsx(ChevronRightIcon, { className: "ml-auto size-4" })] }));
|
|
46
|
+
}
|
|
47
|
+
function DropdownMenuSubContent({ className, ...props }) {
|
|
48
|
+
return (_jsx(DropdownMenuPrimitive.SubContent, { "data-slot": "dropdown-menu-sub-content", className: cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg', className), ...props }));
|
|
49
|
+
}
|
|
50
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
|
|
@@ -3,13 +3,14 @@ import { useState } from 'react';
|
|
|
3
3
|
import { cn } from '@/lib/utils';
|
|
4
4
|
import { PanelLeftClose, PanelLeftOpen } from 'lucide-react';
|
|
5
5
|
import { Button } from './button';
|
|
6
|
+
import { ThemeToggle } from './theme-toggle';
|
|
6
7
|
export const Sidebar = ({ children }) => {
|
|
7
8
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
8
|
-
return (_jsxs("div", { className: cn('max-h-screen overflow-y-auto transition-[width] duration-300 border-r border-
|
|
9
|
+
return (_jsxs("div", { className: cn('max-h-screen overflow-y-auto transition-[width] duration-300 border-r border-sidebar-border bg-sidebar text-sidebar-foreground border-solid overflow-hidden', isCollapsed ? 'w-[50px]' : 'w-[250px]'), children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-4 py-3", children: [!isCollapsed && _jsx(ThemeToggle, {}), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => setIsCollapsed(!isCollapsed), children: isCollapsed ? _jsx(PanelLeftOpen, { className: "w-4 h-4" }) : _jsx(PanelLeftClose, { className: "w-4 h-4" }) })] }), !isCollapsed && _jsx("div", { className: "overflow-y-auto w-[250px]", children: children })] }));
|
|
9
10
|
};
|
|
10
11
|
export const SidebarGroup = ({ children, title }) => {
|
|
11
12
|
return (_jsxs("div", { className: "flex flex-col", children: [_jsx("h2", { className: "text-xs font-bold text-muted-foreground px-4 py-2 uppercase", children: title }), children] }));
|
|
12
13
|
};
|
|
13
14
|
export const SidebarButton = ({ children, isActive, icon }) => {
|
|
14
|
-
return (_jsxs("div", { className: cn('flex text-sm font-medium items-center gap-2 px-4 py-3 ', isActive && 'bg-
|
|
15
|
+
return (_jsxs("div", { className: cn('flex text-sm font-medium items-center gap-2 px-4 py-3 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground transition-colors cursor-pointer', isActive && 'bg-sidebar-accent text-sidebar-accent-foreground'), children: [_jsx("div", { className: "text-sidebar-foreground/70", children: icon }), children] }));
|
|
15
16
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Moon, Sun } from 'lucide-react';
|
|
3
|
+
import { useTheme } from '@/hooks/use-theme';
|
|
4
|
+
import { cn } from '@/lib/utils';
|
|
5
|
+
export const ThemeToggle = () => {
|
|
6
|
+
const { theme, setTheme } = useTheme();
|
|
7
|
+
const toggleTheme = () => {
|
|
8
|
+
setTheme(theme === 'light' ? 'dark' : 'light');
|
|
9
|
+
};
|
|
10
|
+
return (_jsxs("button", { onClick: toggleTheme, className: "relative flex items-center cursor-pointer w-16 h-8 bg-muted rounded-full p-1 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", "aria-label": `Switch to ${theme === 'light' ? 'dark' : 'light'} theme`, children: [_jsx("div", { className: cn('absolute w-6 h-6 bg-background border border-border rounded-full shadow-sm transition-transform duration-200 ease-in-out', theme === 'dark' ? 'translate-x-8' : 'translate-x-0') }), _jsx("div", { className: "flex items-center justify-center w-6 h-6 z-10", children: _jsx(Sun, { className: cn('h-3.5 w-3.5 transition-colors duration-200', theme === 'light' ? 'text-foreground' : 'text-muted-foreground') }) }), _jsx("div", { className: "flex items-center justify-center w-6 h-6 z-10 ml-2", children: _jsx(Moon, { className: cn('h-3.5 w-3.5 transition-colors duration-200', theme === 'dark' ? 'text-foreground' : 'text-muted-foreground') }) })] }));
|
|
11
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
const storageKey = 'motia-workbench-theme';
|
|
3
|
+
const defaultTheme = localStorage.getItem(storageKey) || 'system';
|
|
4
|
+
const updateTheme = (theme) => {
|
|
5
|
+
const root = window.document.body;
|
|
6
|
+
root.classList.remove('light', 'dark');
|
|
7
|
+
if (theme === 'system') {
|
|
8
|
+
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
9
|
+
root.classList.add(systemTheme);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
root.classList.add(theme);
|
|
13
|
+
};
|
|
14
|
+
export const useTheme = () => {
|
|
15
|
+
const [theme, _setTheme] = useState(defaultTheme);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
updateTheme(defaultTheme);
|
|
18
|
+
}, []);
|
|
19
|
+
const setTheme = useCallback((newTheme) => {
|
|
20
|
+
localStorage.setItem(storageKey, newTheme);
|
|
21
|
+
_setTheme(newTheme);
|
|
22
|
+
updateTheme(newTheme);
|
|
23
|
+
}, []);
|
|
24
|
+
return {
|
|
25
|
+
theme,
|
|
26
|
+
setTheme,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -3,5 +3,5 @@ import { Webhook } from 'lucide-react';
|
|
|
3
3
|
import { BaseNode } from './base-node';
|
|
4
4
|
import { DetailItem, NodeDetails } from './node-details';
|
|
5
5
|
export const ApiNode = ({ data, children }) => {
|
|
6
|
-
return (_jsxs(BaseNode, { variant: "api", title: data.name, language: data.language, disableSourceHandle: !data.emits?.length && !data.virtualEmits?.length, disableTargetHandle: !data.subscribes?.length && !data.virtualSubscribes?.length, children: [data.description && _jsx("div", { className: "text-sm text-muted-foreground", children: data.description }), children, data.webhookUrl && (_jsxs("div", { className: "flex gap-1 items-center text-xs text-muted-foreground", children: [_jsx(Webhook, { className: "w-3 h-3 text-muted-foreground/40" }), _jsx("div", { className: "font-mono", children: data.webhookUrl })] })), _jsx(NodeDetails, { type: "api", name: data.name, subscribes: data.subscribes, emits: data.emits, description: data.description, language: data.language, children: _jsx(DetailItem, { label: "Webhook URL", children: _jsxs("div", { className: "flex gap-1 items-center text-xs text-
|
|
6
|
+
return (_jsxs(BaseNode, { variant: "api", title: data.name, language: data.language, disableSourceHandle: !data.emits?.length && !data.virtualEmits?.length, disableTargetHandle: !data.subscribes?.length && !data.virtualSubscribes?.length, children: [data.description && _jsx("div", { className: "text-sm text-muted-foreground", children: data.description }), children, data.webhookUrl && (_jsxs("div", { className: "flex gap-1 items-center text-xs text-muted-foreground", children: [_jsx(Webhook, { className: "w-3 h-3 text-muted-foreground/40" }), _jsx("div", { className: "font-mono", children: data.webhookUrl })] })), _jsx(NodeDetails, { type: "api", name: data.name, subscribes: data.subscribes, emits: data.emits, description: data.description, language: data.language, children: _jsx(DetailItem, { label: "Webhook URL", children: _jsxs("div", { className: "flex gap-1 items-center text-xs text-muted-foreground", children: [_jsx(Webhook, { className: "w-3 h-3 text-muted-foreground/40" }), _jsx("div", { className: "font-mono", children: data.webhookUrl })] }) }) })] }));
|
|
7
7
|
};
|
|
@@ -16,7 +16,7 @@ const baseDot = cva('w-[6px] h-[6px] rounded-full', {
|
|
|
16
16
|
},
|
|
17
17
|
});
|
|
18
18
|
const Dot = ({ variant }) => (_jsx("div", { className: cn(baseDot({ variant })) }));
|
|
19
|
-
const HeaderBar = ({ text, variant, children, }) => (_jsxs("div", { className: "text-sm text-
|
|
19
|
+
const HeaderBar = ({ text, variant, children, }) => (_jsxs("div", { className: "text-sm text-foreground flex justify-between items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Dot, { variant: variant }), _jsx("span", { children: text })] }), children] }));
|
|
20
20
|
export const BaseNode = (props) => {
|
|
21
21
|
const { title, variant, children, disableSourceHandle, disableTargetHandle, language } = props;
|
|
22
22
|
return (_jsx("div", { className: "p-[1px] rounded-lg max-w-[350px] ", children: _jsx("div", { className: "rounded-lg bg-background p-4 border border-muted border-solid", style: {
|
|
@@ -12,4 +12,4 @@ const baseDot = cva('w-[6px] h-[6px] rounded-full', {
|
|
|
12
12
|
},
|
|
13
13
|
});
|
|
14
14
|
const Dot = ({ variant }) => (_jsx("div", { className: cn(baseDot({ variant })) }));
|
|
15
|
-
export const HeaderBar = ({ text, variant, children, }) => (_jsxs("div", { className: "text-sm text-
|
|
15
|
+
export const HeaderBar = ({ text, variant, children, }) => (_jsxs("div", { className: "text-sm text-foreground flex justify-between items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Dot, { variant: variant }), _jsx("span", { children: text })] }), children] }));
|
|
@@ -3,5 +3,5 @@ import { BaseNode } from './base-node';
|
|
|
3
3
|
import { Clock } from 'lucide-react';
|
|
4
4
|
import { NodeDetails, DetailItem } from './node-details';
|
|
5
5
|
export const CronNode = ({ data }) => {
|
|
6
|
-
return (_jsxs(BaseNode, { variant: "cron", title: data.name, language: data.language, headerChildren: _jsx(Clock, { className: "w-4 h-4 text-purple-400" }), disableTargetHandle: !data.virtualSubscribes?.length, disableSourceHandle: !data.virtualEmits?.length && !data.emits?.length, children: [_jsx("div", { className: "text-sm text-
|
|
6
|
+
return (_jsxs(BaseNode, { variant: "cron", title: data.name, language: data.language, headerChildren: _jsx(Clock, { className: "w-4 h-4 text-purple-400" }), disableTargetHandle: !data.virtualSubscribes?.length, disableSourceHandle: !data.virtualEmits?.length && !data.emits?.length, children: [_jsx("div", { className: "text-sm text-muted-foreground", children: data.description }), _jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-2", children: [_jsx(Clock, { className: "w-3 h-3" }), " ", data.cronExpression] }), _jsx(NodeDetails, { type: "cron", name: data.name, description: data.description, language: data.language, children: _jsx(DetailItem, { label: "Cron Expression", children: _jsx("div", { className: "flex gap-1 items-center text-xs text-muted-foreground", children: _jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-2", children: [_jsx(Clock, { className: "w-3 h-3" }), " ", data.cronExpression] }) }) }) })] }));
|
|
7
7
|
};
|
|
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Send } from 'lucide-react';
|
|
3
3
|
const toTopic = (emit) => typeof emit === 'string' ? emit : emit.topic;
|
|
4
4
|
export const Emits = ({ emits }) => {
|
|
5
|
-
return (_jsx(_Fragment, { children: emits.map((emit) => (_jsxs("div", { className: "flex gap-2 items-center text-xs text-
|
|
5
|
+
return (_jsx(_Fragment, { children: emits.map((emit) => (_jsxs("div", { className: "flex gap-2 items-center text-xs text-muted-foreground", "data-testid": `emits__${toTopic(emit)}`, children: [_jsx(Send, { className: "w-4 h-4 text-muted-foreground/60" }), _jsx("div", { className: "font-mono tracking-wider", children: toTopic(emit) })] }, toTopic(emit)))) }));
|
|
6
6
|
};
|
|
@@ -11,9 +11,9 @@ import { colorMap } from './colorMap';
|
|
|
11
11
|
import { HeaderBar } from './components/header-bar';
|
|
12
12
|
export const DetailItem = (props) => {
|
|
13
13
|
const { label, children } = props;
|
|
14
|
-
return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { className: "text-
|
|
14
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { className: "text-foreground", children: label }), children] }));
|
|
15
15
|
};
|
|
16
16
|
export const NodeDetails = (props) => {
|
|
17
17
|
const { name, type, subscribes, emits, description, language, children } = props;
|
|
18
|
-
return (_jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: _jsx("div", { className: "flex justify-end gap-2", children: _jsx("div", { className: "border border-solid border-
|
|
18
|
+
return (_jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: _jsx("div", { className: "flex justify-end gap-2", children: _jsx("div", { className: "border border-solid border-border/50 p-1 rounded-md cursor-pointer", children: _jsx(ChevronRight, { className: "w-4 h-4" }) }) }) }), _jsxs(DialogContent, { className: "border border-solid", style: { borderColor: colorMap[type] }, children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: _jsx(HeaderBar, { variant: type, text: name }) }) }), _jsx(DialogDescription, { children: _jsxs("div", { className: "flex flex-col gap-6", children: [description && (_jsx(DetailItem, { label: "Description", children: _jsx("span", { className: "text-sm text-muted-foreground", children: description }) })), _jsx(DetailItem, { label: "Language", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LanguageIndicator, { language: language }), _jsx("span", { className: "capitalize", children: language })] }) }), subscribes && (_jsx(DetailItem, { label: "Subscribes", children: _jsx(Subscribe, { subscribes: subscribes }) })), emits && (_jsx(DetailItem, { label: "Emits", children: _jsx(Emits, { emits: emits }) })), children] }) })] })] }));
|
|
19
19
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { BaseNode } from './base-node';
|
|
3
3
|
export const NoopNode = ({ data, children }) => {
|
|
4
|
-
return (_jsxs(BaseNode, { variant: "noop", title: data.name, disableSourceHandle: !data.virtualEmits.length, disableTargetHandle: !data.subscribes?.length, children: [data.description && _jsx("div", { className: "text-sm max-w-[300px] text-
|
|
4
|
+
return (_jsxs(BaseNode, { variant: "noop", title: data.name, disableSourceHandle: !data.virtualEmits.length, disableTargetHandle: !data.subscribes?.length, children: [data.description && _jsx("div", { className: "text-sm max-w-[300px] text-muted-foreground", children: data.description }), children] }));
|
|
5
5
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Eye } from 'lucide-react';
|
|
3
3
|
export const Subscribe = ({ subscribes }) => {
|
|
4
|
-
return (_jsx(_Fragment, { children: subscribes.map((subscribe) => (_jsxs("div", { className: "flex gap-2 items-center text-xs text-
|
|
4
|
+
return (_jsx(_Fragment, { children: subscribes.map((subscribe) => (_jsxs("div", { className: "flex gap-2 items-center text-xs text-muted-foreground", "data-testid": `subscribes__${subscribe}`, children: [_jsx(Eye, { className: "w-4 h-4 text-muted-foreground/60" }), _jsx("div", { className: "font-mono tracking-wider", children: subscribe })] }, subscribe))) }));
|
|
5
5
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { AppSidebar } from './components/app-sidebar';
|
|
3
3
|
import { ReactFlowProvider } from '@xyflow/react';
|
|
4
|
-
export const RouteWrapper = ({ children }) => (_jsx("div", { className: "flex flex-row", children: _jsxs(ReactFlowProvider, { children: [_jsx(AppSidebar, {}), _jsx("div", { className: "flex-1", children: children })] }) }));
|
|
4
|
+
export const RouteWrapper = ({ children }) => (_jsx("div", { className: "flex flex-row bg-background text-foreground", children: _jsxs(ReactFlowProvider, { children: [_jsx(AppSidebar, {}), _jsx("div", { className: "flex-1", children: children })] }) }));
|
package/dist/src/routes/flow.js
CHANGED
|
@@ -18,5 +18,5 @@ export const Flow = () => {
|
|
|
18
18
|
useEffect(fetchFlow, [fetchFlow]);
|
|
19
19
|
if (!flow)
|
|
20
20
|
return null;
|
|
21
|
-
return (_jsx("div", { className: "w-full h-screen", children: _jsx(FlowView, { flow: flow, flowConfig: flowConfig }) }));
|
|
21
|
+
return (_jsx("div", { className: "w-full h-screen bg-background", children: _jsx(FlowView, { flow: flow, flowConfig: flowConfig }) }));
|
|
22
22
|
};
|
package/dist/src/routes/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Button } from '../components/ui/button';
|
|
3
3
|
export const Index = () => {
|
|
4
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center w-full h-screen gap-10 bg-gradient-to-r from-
|
|
4
|
+
return (_jsxs("div", { className: "flex flex-col items-center justify-center w-full h-screen gap-10 bg-gradient-to-r from-background via-background to-muted", children: [_jsx("h1", { className: "text-5xl font-extrabold max-w-[600px] text-center text-foreground", children: "Code-first framework for intelligent workflows" }), _jsx("div", { className: "max-w-[600px] text-center text-xl font-medium text-muted-foreground", children: "Write in any language. Automate anything. From AI agents to backend automation, Motia runs event-driven workflows with zero overhead." }), _jsx("div", { className: "p-[1px] min-w-[600px] rounded-lg shadow-lg border border-border", children: _jsx("div", { className: "rounded-lg bg-card p-8 font-semibold text-xl min-h-[100px] flex items-center", children: _jsxs("div", { className: "flex items-center gap-2 font-mono", children: [_jsx("span", { className: "text-primary", children: "$" }), _jsx("span", { className: "text-card-foreground", children: "npx motia generate step" })] }) }) }), _jsxs("div", { className: "flex flex-col gap-8 items-center", children: [_jsx("span", { className: "text-muted-foreground text-xl", children: "or" }), _jsx("a", { href: "https://motia.dev/docs", target: "_blank", children: _jsx(Button, { size: "lg", className: "text-xl py-6 px-8", children: "Read developer docs" }) })] })] }));
|
|
5
5
|
};
|
|
@@ -3,10 +3,10 @@ import { Logs } from '../components/logs/logs';
|
|
|
3
3
|
import { useLogs } from '../stores/use-logs';
|
|
4
4
|
import { useEffect } from 'react';
|
|
5
5
|
export const LogsPage = () => {
|
|
6
|
-
const
|
|
6
|
+
const setUnreadLogsCount = useLogs((state) => state.setUnreadLogsCount);
|
|
7
7
|
useEffect(() => {
|
|
8
8
|
setUnreadLogsCount(0);
|
|
9
9
|
return () => setUnreadLogsCount(0);
|
|
10
10
|
}, [setUnreadLogsCount]);
|
|
11
|
-
return (_jsxs("div", { className: "w-full h-screen overflow-hidden", children: [_jsxs("header", { className: "p-4
|
|
11
|
+
return (_jsxs("div", { className: "w-full h-screen overflow-hidden bg-background text-foreground", children: [_jsxs("header", { className: "p-4 border-b border-border", children: [_jsx("h1", { className: "text-2xl font-bold text-foreground", children: "Logs" }), _jsx("span", { className: "text-sm text-muted-foreground", children: "Check all logs saved locally" })] }), _jsx(Logs, {})] }));
|
|
12
12
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { States } from '../components/states/states';
|
|
3
3
|
export const StatesPage = () => {
|
|
4
|
-
return (_jsxs("div", { className: "w-full h-screen overflow-hidden", children: [_jsxs("header", { className: "p-4
|
|
4
|
+
return (_jsxs("div", { className: "w-full h-screen overflow-hidden bg-background text-foreground", children: [_jsxs("header", { className: "p-4 border-b border-border", children: [_jsx("h1", { className: "text-xl font-bold text-foreground", children: "State details" }), _jsx("span", { className: "text-sm text-muted-foreground", children: "Check all states saved locally along with all fields" })] }), _jsx(States, {})] }));
|
|
5
5
|
};
|
|
@@ -13,4 +13,4 @@ export type LogsState = {
|
|
|
13
13
|
unreadLogsCount: number;
|
|
14
14
|
setUnreadLogsCount: (count: number) => void;
|
|
15
15
|
};
|
|
16
|
-
export declare
|
|
16
|
+
export declare function useLogs<SelectorOutput = LogsState>(selector?: (state: LogsState) => SelectorOutput): SelectorOutput;
|
|
@@ -1,8 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { useSyncExternalStore, useCallback } from 'react';
|
|
2
|
+
const listeners = new Set();
|
|
3
|
+
let currentLogs = [];
|
|
4
|
+
let currentUnreadLogsCount = 0;
|
|
5
|
+
let memoizedSnapshot;
|
|
6
|
+
const updateMemoizedSnapshot = () => {
|
|
7
|
+
memoizedSnapshot = {
|
|
8
|
+
logs: currentLogs,
|
|
9
|
+
unreadLogsCount: currentUnreadLogsCount,
|
|
10
|
+
addLog: storeActions.addLog,
|
|
11
|
+
resetLogs: storeActions.resetLogs,
|
|
12
|
+
setUnreadLogsCount: storeActions.setUnreadLogsCount,
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
const notify = () => {
|
|
16
|
+
listeners.forEach((listener) => listener());
|
|
17
|
+
};
|
|
18
|
+
const storeActions = {
|
|
19
|
+
addLog: (log) => {
|
|
20
|
+
currentLogs = [log, ...currentLogs];
|
|
21
|
+
currentUnreadLogsCount += 1;
|
|
22
|
+
updateMemoizedSnapshot();
|
|
23
|
+
notify();
|
|
24
|
+
},
|
|
25
|
+
resetLogs: () => {
|
|
26
|
+
if (currentLogs.length === 0 && currentUnreadLogsCount === 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
currentLogs = [];
|
|
30
|
+
currentUnreadLogsCount = 0;
|
|
31
|
+
updateMemoizedSnapshot();
|
|
32
|
+
notify();
|
|
33
|
+
},
|
|
34
|
+
setUnreadLogsCount: (count) => {
|
|
35
|
+
if (currentUnreadLogsCount === count) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
currentUnreadLogsCount = count;
|
|
39
|
+
updateMemoizedSnapshot();
|
|
40
|
+
notify();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
updateMemoizedSnapshot();
|
|
44
|
+
// Stable subscribe function so React doesn't unnecessarily tear down the subscription
|
|
45
|
+
const subscribe = (onStoreChange) => {
|
|
46
|
+
listeners.add(onStoreChange);
|
|
47
|
+
return () => listeners.delete(onStoreChange);
|
|
48
|
+
};
|
|
49
|
+
export function useLogs(selector = (state) => state) {
|
|
50
|
+
const getSnapshot = useCallback(() => selector(memoizedSnapshot), [selector]);
|
|
51
|
+
return useSyncExternalStore(subscribe, getSnapshot);
|
|
52
|
+
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
export const ArrowHead = (props) =>
|
|
2
|
+
export const ArrowHead = (props) => {
|
|
3
|
+
// Use CSS custom property for theme-aware coloring, fallback to provided color
|
|
4
|
+
const strokeColor = props.color || 'var(--arrow-color, #B3B3B3)';
|
|
5
|
+
return (_jsxs("marker", { id: props.id, viewBox: "-5 -5 10 10", markerUnits: "strokeWidth", markerWidth: "10", markerHeight: "10", className: props.className, children: [_jsx("line", { x1: 0, y1: 0, x2: 2, y2: -2, stroke: strokeColor, strokeWidth: "1", strokeOpacity: "1", strokeLinecap: "round" }), _jsx("line", { x1: -2, y1: -2, x2: 0, y2: 0, stroke: strokeColor, strokeWidth: "1", strokeOpacity: "1", strokeLinecap: "round" })] }));
|
|
6
|
+
};
|