@motiadev/workbench 0.3.1-beta.88-041205 → 0.3.1-beta.88-987125

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/App.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { CollapsiblePanel, CollapsiblePanelGroup, TabsContent, TabsList, TabsTrigger } from '@motiadev/ui';
3
3
  import { ReactFlowProvider } from '@xyflow/react';
4
+ import { analytics } from '@/lib/analytics';
4
5
  import { File, GanttChart, Link2, LogsIcon } from 'lucide-react';
6
+ import React from 'react';
5
7
  import { EndpointsPage } from './components/endpoints/endpoints-page';
6
8
  import { FlowPage } from './components/flow/flow-page';
7
9
  import { FlowTabMenuItem } from './components/flow/flow-tab-menu-item';
@@ -11,9 +13,22 @@ import { TracesPage } from './components/observability/traces-page';
11
13
  import { APP_SIDEBAR_CONTAINER_ID } from './components/sidebar/sidebar';
12
14
  import { StatesPage } from './components/states/states-page';
13
15
  import { useTabsStore } from './stores/use-tabs-store';
16
+ var TabLocation;
17
+ (function (TabLocation) {
18
+ TabLocation["TOP"] = "top";
19
+ TabLocation["BOTTOM"] = "bottom";
20
+ })(TabLocation || (TabLocation = {}));
14
21
  export const App = () => {
15
22
  const tab = useTabsStore((state) => state.tab);
16
23
  const setTopTab = useTabsStore((state) => state.setTopTab);
17
24
  const setBottomTab = useTabsStore((state) => state.setBottomTab);
18
- 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: setTopTab, header: _jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "flow", children: _jsx(FlowTabMenuItem, {}) }), _jsxs(TabsTrigger, { value: "endpoint", 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: setBottomTab, header: _jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "tracing", children: [_jsx(GanttChart, {}), " Tracing"] }), _jsxs(TabsTrigger, { value: "logs", children: [_jsx(LogsIcon, {}), "Logs"] }), _jsxs(TabsTrigger, { value: "states", 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 })] }));
25
+ const tabChangeCallbacks = React.useMemo(() => ({
26
+ [TabLocation.TOP]: setTopTab,
27
+ [TabLocation.BOTTOM]: setBottomTab,
28
+ }), [setTopTab, setBottomTab]);
29
+ const onTabChange = React.useCallback((location) => (newTab) => {
30
+ analytics.track(`${location} tab changed`, { [`new.${location}`]: newTab, tab });
31
+ tabChangeCallbacks[location](newTab);
32
+ }, [tabChangeCallbacks]);
33
+ 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", 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", children: [_jsx(GanttChart, {}), " Tracing"] }), _jsxs(TabsTrigger, { value: "logs", "data-testid": "logs-link", children: [_jsx(LogsIcon, {}), "Logs"] }), _jsxs(TabsTrigger, { value: "states", "data-testid": "states-link", 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 })] }));
19
34
  };
@@ -69,7 +69,7 @@ export const EndpointCall = ({ endpoint, onClose }) => {
69
69
  icon: _jsx(X, { className: "cursor-pointer w-4 h-4", onClick: onClose }),
70
70
  onClick: onClose,
71
71
  },
72
- ], children: [endpoint.description && (_jsx("div", { className: "rounded-lg border p-4 font-medium text-muted-foreground", children: endpoint.description })), !!pathParams.length && (_jsx(Panel, { title: "Path params", size: "sm", children: _jsx("table", { children: pathParams.map((param) => (_jsxs("tr", { children: [_jsx("td", { className: "flex flex-col font-bold leading-[36px]", children: param }), _jsx("td", { className: "w-2/3 pl-4", children: _jsx(Input, { className: "w-full", value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) }) })] }, param))) }) })), !!endpoint.queryParams?.length && (_jsx(Panel, { title: "Query params", size: "sm", children: _jsx("table", { children: endpoint.queryParams.map((param) => (_jsxs("tr", { children: [_jsxs("td", { className: "flex flex-col", children: [_jsx("span", { className: "font-bold", children: param.name }), _jsx("span", { className: "text-md text-muted-foreground", children: param.description })] }), _jsx("td", { className: "w-2/3 pl-4", children: _jsx(Input, { className: "w-full", value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) }) })] }, param.name))) }) })), shouldHaveBody && (_jsx(Panel, { title: "Body", size: "sm", contentClassName: "p-0", children: _jsx(JsonEditor, { value: body, schema: endpoint.bodySchema, 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 }), _jsx(EndpointResponseSchema, { items: Object.entries(endpoint?.responseSchema ?? {}).map(([status, schema]) => ({
72
+ ], children: [endpoint.description && (_jsx("div", { className: "rounded-lg border p-4 font-medium text-muted-foreground", children: endpoint.description })), !!pathParams.length && (_jsx(Panel, { title: "Path params", size: "sm", children: _jsx("table", { children: pathParams.map((param) => (_jsxs("tr", { children: [_jsx("td", { className: "flex flex-col font-bold leading-[36px]", children: param }), _jsx("td", { className: "w-2/3 pl-4", children: _jsx(Input, { className: "w-full", value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) }) })] }, param))) }) })), !!endpoint.queryParams?.length && (_jsx(Panel, { title: "Query params", size: "sm", children: _jsx("table", { children: endpoint.queryParams.map((param) => (_jsxs("tr", { children: [_jsxs("td", { className: "flex flex-col justify-start", children: [_jsx("span", { className: "font-bold", children: param.name }), _jsx("span", { className: "text-md text-muted-foreground", children: param.description })] }), _jsx("td", { className: "w-2/3 pl-4 align-top", children: _jsx(Input, { className: "w-full", value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) }) })] }, param.name))) }) })), shouldHaveBody && (_jsx(Panel, { title: "Body", size: "sm", contentClassName: "p-0", "data-testid": "endpoint-body-panel", children: _jsx(JsonEditor, { value: body, schema: endpoint.bodySchema, 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 }), _jsx(EndpointResponseSchema, { items: Object.entries(endpoint?.responseSchema ?? {}).map(([status, schema]) => ({
73
73
  responseCode: status,
74
74
  bodySchema: schema,
75
75
  })) })] }));
@@ -20,12 +20,10 @@ export const JsonEditor = ({ value, schema, onChange, onValidate }) => {
20
20
  ],
21
21
  });
22
22
  }, [monaco, schema]);
23
- return (_jsx(Editor, { height: "200px", language: "json", value: value, theme: editorTheme, onChange: (value) => {
23
+ return (_jsx(Editor, { "data-testid": "json-editor", height: "200px", language: "json", value: value, theme: editorTheme, onChange: (value) => {
24
+ if (!value) {
25
+ onValidate(false);
26
+ }
24
27
  onChange(value ?? '');
25
- }, onValidate: (markers) => {
26
- onValidate(markers.length === 0);
27
- }, options: {
28
- lineNumbers: 'off',
29
- minimap: { enabled: false },
30
- } }));
28
+ }, onValidate: (markers) => onValidate(markers.length === 0), options: { lineNumbers: 'off', minimap: { enabled: false } } }));
31
29
  };
@@ -11,5 +11,5 @@ export const FlowTabMenuItem = () => {
11
11
  if (flows.length === 0) {
12
12
  return null;
13
13
  }
14
- return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("div", { className: "flex flex-row justify-center items-center gap-2 cursor-pointer", children: [_jsx(Workflow, {}), selectedFlowId ?? 'No flow selected', _jsx(ChevronsUpDown, { className: "size-4" })] }) }), _jsx(DropdownMenuContent, { className: "bg-background text-foreground", children: flows.map((item) => (_jsx(DropdownMenuItem, { className: "cursor-pointer gap-2", onClick: () => selectFlowId(item), children: item }, `dropdown-${item}`))) })] }));
14
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("div", { "data-testid": "flows-dropdown-trigger", className: "flex flex-row justify-center items-center gap-2 cursor-pointer", children: [_jsx(Workflow, {}), selectedFlowId ?? 'No flow selected', _jsx(ChevronsUpDown, { className: "size-4" })] }) }), _jsx(DropdownMenuContent, { className: "bg-background text-foreground flows-dropdown", children: flows.map((item) => (_jsx(DropdownMenuItem, { "data-testid": `dropdown-${item}`, className: "cursor-pointer gap-2 flow-link", onClick: () => selectFlowId(item), children: item }, `dropdown-${item}`))) })] }));
15
15
  };
@@ -3,5 +3,5 @@ import { useThemeStore } from '@/stores/use-theme-store';
3
3
  import { ThemeToggle } from '../ui/theme-toggle';
4
4
  export const Header = () => {
5
5
  const theme = useThemeStore((state) => state.theme);
6
- return (_jsxs("header", { className: "min-h-16 px-4 gap-1 flex items-center bg-default text-default-foreground border-b", children: [_jsx("img", { src: `/motia-${theme}.png`, className: "h-5" }), _jsx("div", { className: "flex-1" }), _jsx(ThemeToggle, {})] }));
6
+ return (_jsxs("header", { className: "min-h-16 px-4 gap-1 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(ThemeToggle, {})] }));
7
7
  };
@@ -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: [group.endTime && _jsxs("span", { children: ["Duration: ", formatDuration(group.endTime - group.startTime)] }), _jsxs("span", { 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('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
  });
@@ -2,7 +2,6 @@ import React, { HTMLAttributes } from 'react';
2
2
  import { HandleProps } from '@xyflow/react';
3
3
  type Props = HandleProps & Omit<HTMLAttributes<HTMLDivElement>, 'id'> & {
4
4
  isHidden?: boolean;
5
- variant?: string | null;
6
5
  onTogglePosition?: () => void;
7
6
  };
8
7
  export declare const BaseHandle: React.FC<Props>;
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Position, Handle as RFHandle } from '@xyflow/react';
3
3
  import clsx from 'clsx';
4
4
  export const BaseHandle = (props) => {
5
- const { isHidden, position, variant, onTogglePosition, ...rest } = props;
5
+ const { isHidden, position, onTogglePosition, ...rest } = props;
6
6
  const isHorizontal = position === Position.Left || position === Position.Right;
7
7
  return (_jsx("div", { className: clsx('absolute w-[6px] h-[6px]', position === Position.Top && '-top-[4px]', position === Position.Bottom && '-bottom-[4px]', position === Position.Left && '-left-[4px]', position === Position.Right && '-right-[4px]', isHorizontal ? 'top-1/2 -mt-[2px]' : 'left-1/2 -ml-[2px]', isHidden && 'hidden'), onClick: onTogglePosition, children: _jsx(RFHandle, { ...rest, position: position, style: { background: 'rgb(30,118,231)' }, className: "\n bg-white/50\n !static\n !w-[6px]\n !h-[6px]\n !min-w-[6px]\n !min-h-[6px]\n !p-0\n !border-none\n !transform-none\n !rounded-full\n !outline-none\n !shadow-none\n " }) }));
8
8
  };
@@ -9,5 +9,5 @@ import { NodeSidebar } from './node-sidebar';
9
9
  export const BaseNode = ({ title, variant, children, disableSourceHandle, disableTargetHandle, language, subtitle, details, subscribes, emits, data, }) => {
10
10
  const [isOpen, setIsOpen] = useState(false);
11
11
  const { sourcePosition, targetPosition, toggleTargetPosition, toggleSourcePosition } = useHandlePositions(data);
12
- return (_jsxs("div", { className: "p-[1px] rounded-lg max-w-[350px]", children: [_jsx("div", { className: "rounded-lg bg-background border-2 border-muted border-solid border-muted-foreground/10", "data-testid": `node-${title?.toLowerCase().replace(/ /g, '-')}`, children: _jsxs("div", { className: "group relative", children: [_jsxs(NodeHeader, { text: title, variant: variant, subtitle: subtitle, children: [_jsx(LanguageIndicator, { language: language }), _jsx("div", { className: "flex justify-end gap-2", children: _jsx("div", { className: "p-[2px] cursor-pointer rounded-md hover:bg-muted-foreground/10", onClick: () => setIsOpen(true), children: _jsx(ChevronUp, { className: "w-4 h-4" }) }) })] }), children && _jsx("div", { className: "border-t-2 border-muted-foreground/10 p-4 space-y-3", children: children }), !disableTargetHandle && (_jsx(BaseHandle, { type: "target", position: targetPosition, variant: variant, onTogglePosition: toggleTargetPosition })), !disableSourceHandle && (_jsx(BaseHandle, { type: "source", position: sourcePosition, variant: variant, onTogglePosition: toggleSourcePosition }))] }) }), _jsx(NodeSidebar, { title: title, subtitle: subtitle, variant: variant, language: language, isOpen: isOpen, onClose: () => setIsOpen(false), details: details, subscribes: subscribes, emits: emits })] }));
12
+ return (_jsxs("div", { className: "p-[1px] rounded-lg max-w-[350px]", children: [_jsx("div", { className: "rounded-lg bg-background border-2 border-muted border-solid border-muted-foreground/10", "data-testid": `node-${title?.toLowerCase().replace(/ /g, '-')}`, children: _jsxs("div", { className: "group relative", children: [_jsxs(NodeHeader, { text: title, variant: variant, subtitle: subtitle, children: [_jsx(LanguageIndicator, { language: language }), _jsx("div", { className: "flex justify-end gap-2", children: _jsx("div", { className: "p-[2px] cursor-pointer rounded-md hover:bg-muted-foreground/10", onClick: () => setIsOpen(true), children: _jsx(ChevronUp, { className: "w-4 h-4" }) }) })] }), children && _jsx("div", { className: "border-t-2 border-muted-foreground/10 p-4 space-y-3", children: children }), !disableTargetHandle && (_jsx(BaseHandle, { type: "target", position: targetPosition, onTogglePosition: toggleTargetPosition })), !disableSourceHandle && (_jsx(BaseHandle, { type: "source", position: sourcePosition, onTogglePosition: toggleSourcePosition }))] }) }), _jsx(NodeSidebar, { title: title, subtitle: subtitle, variant: variant, language: language, isOpen: isOpen, onClose: () => setIsOpen(false), details: details, subscribes: subscribes, emits: emits })] }));
13
13
  };
@@ -1,3 +1,3 @@
1
- import { PropsWithChildren } from 'react';
1
+ import React, { type PropsWithChildren } from 'react';
2
2
  import { EventNodeProps } from './node-props';
3
3
  export declare const EventNode: React.FC<PropsWithChildren<EventNodeProps>>;