@motiadev/workbench 0.8.2-beta.140-246724 → 0.8.2-beta.140-516159

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.
Files changed (41) hide show
  1. package/dist/middleware.d.ts +8 -1
  2. package/dist/middleware.js +5 -3
  3. package/dist/src/App.js +43 -50
  4. package/dist/src/components/flow/flow-page.js +2 -1
  5. package/dist/src/components/observability/events/trace-log-event.js +2 -2
  6. package/dist/src/components/observability/trace-tab-label.d.ts +1 -0
  7. package/dist/src/components/observability/trace-tab-label.js +5 -0
  8. package/dist/src/components/root-motia.js +0 -2
  9. package/dist/src/components/states/state-tab-label.d.ts +1 -0
  10. package/dist/src/components/states/state-tab-label.js +5 -0
  11. package/dist/src/index.css +0 -1
  12. package/dist/src/lib/plugins.d.ts +1 -0
  13. package/dist/src/lib/plugins.js +55 -0
  14. package/dist/src/lib/utils.d.ts +5 -0
  15. package/dist/src/lib/utils.js +19 -0
  16. package/dist/src/project-view-mode.d.ts +1 -0
  17. package/dist/src/project-view-mode.js +18 -0
  18. package/dist/src/stores/use-app-tabs-store.d.ts +18 -0
  19. package/dist/src/stores/use-app-tabs-store.js +37 -0
  20. package/dist/src/stores/use-global-store.d.ts +0 -2
  21. package/dist/src/stores/use-global-store.js +0 -2
  22. package/dist/src/stores/use-tabs-store.js +7 -5
  23. package/dist/src/system-view-mode.d.ts +1 -0
  24. package/dist/src/system-view-mode.js +36 -0
  25. package/dist/tsconfig.app.tsbuildinfo +1 -1
  26. package/dist/tsconfig.node.tsbuildinfo +1 -1
  27. package/dist/vite-plugin-motia-plugins.d.ts +9 -0
  28. package/dist/vite-plugin-motia-plugins.js +69 -0
  29. package/package.json +6 -4
  30. package/dist/src/components/logs/log-detail.d.ts +0 -10
  31. package/dist/src/components/logs/log-detail.js +0 -37
  32. package/dist/src/components/logs/log-level-badge.d.ts +0 -5
  33. package/dist/src/components/logs/log-level-badge.js +0 -11
  34. package/dist/src/components/logs/log-level-dot.d.ts +0 -4
  35. package/dist/src/components/logs/log-level-dot.js +0 -17
  36. package/dist/src/components/logs/logs-page.d.ts +0 -1
  37. package/dist/src/components/logs/logs-page.js +0 -29
  38. package/dist/src/hooks/use-log-listener.d.ts +0 -1
  39. package/dist/src/hooks/use-log-listener.js +0 -7
  40. package/dist/src/stores/use-logs-store.d.ts +0 -14
  41. package/dist/src/stores/use-logs-store.js +0 -10
@@ -1,2 +1,9 @@
1
1
  import type { Express } from 'express';
2
- export declare const applyMiddleware: (app: Express, port: number, workbenchBase?: string) => Promise<void>;
2
+ import { type WorkbenchPlugin } from './vite-plugin-motia-plugins';
3
+ export type ApplyMiddlewareParams = {
4
+ app: Express;
5
+ port: number;
6
+ workbenchBase: string;
7
+ plugins: WorkbenchPlugin[];
8
+ };
9
+ export declare const applyMiddleware: ({ app, port, workbenchBase, plugins }: ApplyMiddlewareParams) => Promise<void>;
@@ -4,10 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.applyMiddleware = void 0;
7
+ const plugin_react_1 = __importDefault(require("@vitejs/plugin-react"));
7
8
  const fs_1 = __importDefault(require("fs"));
8
9
  const path_1 = __importDefault(require("path"));
9
10
  const vite_1 = require("vite");
10
- const plugin_react_1 = __importDefault(require("@vitejs/plugin-react"));
11
+ const vite_plugin_motia_plugins_1 = __importDefault(require("./vite-plugin-motia-plugins"));
11
12
  const processCwdPlugin = () => {
12
13
  return {
13
14
  name: 'html-transform',
@@ -35,7 +36,7 @@ const reoPlugin = () => {
35
36
  },
36
37
  };
37
38
  };
38
- const applyMiddleware = async (app, port, workbenchBase = '') => {
39
+ const applyMiddleware = async ({ app, port, workbenchBase, plugins }) => {
39
40
  const vite = await (0, vite_1.createServer)({
40
41
  appType: 'spa',
41
42
  root: __dirname,
@@ -58,9 +59,10 @@ const applyMiddleware = async (app, port, workbenchBase = '') => {
58
59
  alias: {
59
60
  '@': path_1.default.resolve(__dirname, './src'),
60
61
  '@/assets': path_1.default.resolve(__dirname, './src/assets'),
62
+ // antd: path.join(process.cwd(), './node_modules/antd'),
61
63
  },
62
64
  },
63
- plugins: [(0, plugin_react_1.default)(), processCwdPlugin(), reoPlugin()],
65
+ plugins: [(0, plugin_react_1.default)(), processCwdPlugin(), reoPlugin(), (0, vite_plugin_motia_plugins_1.default)(plugins)],
64
66
  assetsInclude: ['**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.gif', '**/*.svg', '**/*.ico', '**/*.webp', '**/*.avif'],
65
67
  });
66
68
  app.use(workbenchBase, vite.middlewares);
package/dist/src/App.js CHANGED
@@ -1,56 +1,49 @@
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';
3
- import { analytics } from '@/lib/analytics';
4
- import { ReactFlowProvider } from '@xyflow/react';
5
- import { File, GanttChart, Link2, LogsIcon } from 'lucide-react';
6
- import { useCallback, useEffect, useMemo, useState } from 'react';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
7
3
  import { FlowPage } from './components/flow/flow-page';
8
4
  import { FlowTabMenuItem } from './components/flow/flow-tab-menu-item';
9
- import { Header } from './components/header/header';
10
- import { LogsPage } from './components/logs/logs-page';
5
+ import { TracingTabLabel } from './components/observability/trace-tab-label';
11
6
  import { TracesPage } from './components/observability/traces-page';
12
- import { APP_SIDEBAR_CONTAINER_ID } from '@motiadev/ui';
7
+ import { StatesTabLabel } from './components/states/state-tab-label';
13
8
  import { StatesPage } from './components/states/states-page';
14
- import { useTabsStore } from './stores/use-tabs-store';
15
- import { EndpointsPage } from '@motiadev/plugin-endpoint';
16
- var TabLocation;
17
- (function (TabLocation) {
18
- TabLocation["TOP"] = "top";
19
- TabLocation["BOTTOM"] = "bottom";
20
- })(TabLocation || (TabLocation = {}));
9
+ import { registerPluginTabs } from './lib/plugins';
10
+ import { getViewModeFromURL } from './lib/utils';
11
+ import { ProjectViewMode } from './project-view-mode';
12
+ import { setAppTabs, TabLocation } from './stores/use-app-tabs-store';
13
+ import { SystemViewMode } from './system-view-mode';
14
+ const TAB_IDS = {
15
+ FLOW: 'flow',
16
+ TRACING: 'tracing',
17
+ LOGS: 'logs',
18
+ STATES: 'states',
19
+ };
20
+ const registerDefaultTabs = () => {
21
+ const topTabs = [
22
+ {
23
+ id: TAB_IDS.FLOW,
24
+ tabLabel: FlowTabMenuItem,
25
+ content: FlowPage,
26
+ },
27
+ ];
28
+ const bottomTabs = [
29
+ {
30
+ id: TAB_IDS.TRACING,
31
+ tabLabel: TracingTabLabel,
32
+ content: TracesPage,
33
+ },
34
+ {
35
+ id: TAB_IDS.STATES,
36
+ tabLabel: StatesTabLabel,
37
+ content: StatesPage,
38
+ },
39
+ ];
40
+ setAppTabs(TabLocation.TOP, topTabs);
41
+ setAppTabs(TabLocation.BOTTOM, bottomTabs);
42
+ };
43
+ registerDefaultTabs();
44
+ registerPluginTabs();
21
45
  export const App = () => {
22
- const tab = useTabsStore((state) => state.tab);
23
- const setTopTab = useTabsStore((state) => state.setTopTab);
24
- const setBottomTab = useTabsStore((state) => state.setBottomTab);
25
- const [viewMode, setViewMode] = useState('system');
26
- const tabChangeCallbacks = useMemo(() => ({
27
- [TabLocation.TOP]: setTopTab,
28
- [TabLocation.BOTTOM]: setBottomTab,
29
- }), [setTopTab, setBottomTab]);
30
- const onTabChange = useCallback((location) => (newTab) => {
31
- analytics.track(`${location} tab changed`, { [`new.${location}`]: newTab, tab });
32
- tabChangeCallbacks[location](newTab);
33
- }, [tabChangeCallbacks, tab]);
34
- useEffect(() => {
35
- const url = new URL(window.location.href);
36
- const viewMode = url.searchParams.get('view-mode');
37
- if (viewMode) {
38
- setViewMode(viewMode);
39
- }
40
- }, [setViewMode]);
41
- if (viewMode === 'project') {
42
- return (_jsxs("div", { className: "grid grid-rows-1 grid-cols-[1fr_auto] bg-background text-foreground h-screen", children: [_jsx("main", { className: "m-2 overflow-hidden", role: "main", children: _jsx(Panel, { tabs: [
43
- {
44
- label: 'Flow',
45
- labelComponent: _jsx(FlowTabMenuItem, {}),
46
- content: (_jsx(ReactFlowProvider, { children: _jsx("div", { className: "h-[calc(100vh-100px)] w-full", children: _jsx(FlowPage, {}) }) })),
47
- },
48
- {
49
- label: 'Endpoint',
50
- labelComponent: (_jsxs(_Fragment, { children: [_jsx(Link2, {}), "Endpoint"] })),
51
- content: _jsx(EndpointsPage, {}),
52
- },
53
- ] }) }), _jsx("div", { id: APP_SIDEBAR_CONTAINER_ID })] }));
54
- }
55
- 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 })] }));
46
+ const viewMode = useMemo(getViewModeFromURL, []);
47
+ const ViewComponent = viewMode === 'project' ? ProjectViewMode : SystemViewMode;
48
+ return _jsx(ViewComponent, {});
56
49
  };
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useFlowStore } from '@/stores/use-flow-store';
3
3
  import { useStreamItem } from '@motiadev/stream-client-react';
4
4
  import { FlowView } from './flow-view';
5
+ import { ReactFlowProvider } from '@xyflow/react';
5
6
  export const FlowPage = () => {
6
7
  const selectedFlowId = useFlowStore((state) => state.selectedFlowId);
7
8
  const { data: flow } = useStreamItem({
@@ -16,5 +17,5 @@ export const FlowPage = () => {
16
17
  });
17
18
  if (!flow || flow.error)
18
19
  return (_jsx("div", { className: "w-full h-full bg-background flex flex-col items-center justify-center", children: _jsx("p", { children: flow?.error }) }));
19
- return _jsx(FlowView, { flow: flow, flowConfig: flowConfig });
20
+ return (_jsx(ReactFlowProvider, { children: _jsx(FlowView, { flow: flow, flowConfig: flowConfig }) }));
20
21
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { LogLevelDot } from '@/components/logs/log-level-dot';
2
+ import { LevelDot } from '@motiadev/ui';
3
3
  export const TraceLogEvent = ({ event }) => {
4
- return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LogLevelDot, { level: event.level }), " ", event.message] }));
4
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LevelDot, { level: event.level }), " ", event.message] }));
5
5
  };
@@ -0,0 +1 @@
1
+ export declare const TracingTabLabel: import("react").MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { GanttChart } from 'lucide-react';
3
+ import { memo } from 'react';
4
+ export const TracingTabLabel = memo(() => (_jsxs(_Fragment, { children: [_jsx(GanttChart, { "aria-hidden": "true" }), _jsx("span", { children: "Tracing" })] })));
5
+ TracingTabLabel.displayName = 'TracingTabLabel';
@@ -1,7 +1,5 @@
1
1
  import { useAnalytics } from '@/lib/analytics';
2
- import { useLogListener } from '@/hooks/use-log-listener';
3
2
  export const RootMotia = ({ children }) => {
4
- useLogListener();
5
3
  useAnalytics();
6
4
  return children;
7
5
  };
@@ -0,0 +1 @@
1
+ export declare const StatesTabLabel: import("react").MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import { File } from 'lucide-react';
4
+ export const StatesTabLabel = memo(() => (_jsxs(_Fragment, { children: [_jsx(File, { "aria-hidden": "true" }), _jsx("span", { children: "States" })] })));
5
+ StatesTabLabel.displayName = 'StatesTabLabel';
@@ -1,4 +1,3 @@
1
- @import '@motiadev/plugin-endpoint/styles.css';
2
1
  @import '@motiadev/ui/styles.css';
3
2
  @import '@motiadev/ui/globals.css';
4
3
 
@@ -0,0 +1 @@
1
+ export declare const registerPluginTabs: () => void;
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { addAppTab, TabLocation } from '@/stores/use-app-tabs-store';
3
+ import { DynamicIcon, dynamicIconImports } from 'lucide-react/dynamic';
4
+ import { memo } from 'react';
5
+ import { plugins } from 'virtual:motia-plugins';
6
+ import { isValidTabLocation } from './utils';
7
+ export const registerPluginTabs = () => {
8
+ if (!Array.isArray(plugins)) {
9
+ console.warn('[Motia] Invalid plugins configuration: expected array');
10
+ return;
11
+ }
12
+ plugins.forEach((plugin, index) => {
13
+ try {
14
+ if (!plugin.label) {
15
+ console.warn(`[Motia] Plugin at index ${index} missing label, skipping`);
16
+ return;
17
+ }
18
+ if (!plugin.component) {
19
+ console.warn(`[Motia] Plugin "${plugin.label}" missing component, skipping`);
20
+ return;
21
+ }
22
+ const position = plugin.position || 'top';
23
+ if (!isValidTabLocation(position)) {
24
+ console.warn(`[Motia] Plugin "${plugin.label}" has invalid position "${position}", defaulting to "top"`);
25
+ }
26
+ const tabLocation = isValidTabLocation(position) ? position : TabLocation.TOP;
27
+ const PluginTabLabel = memo(() => {
28
+ const hasIcon = Object.keys(dynamicIconImports).includes(plugin.labelIcon);
29
+ const iconName = hasIcon ? plugin.labelIcon : 'toy-brick';
30
+ if (!hasIcon) {
31
+ console.warn(`[Motia] Plugin "${plugin.label}" has invalid icon "${plugin.labelIcon}", defaulting to "toy-brick"`);
32
+ }
33
+ return (_jsxs(_Fragment, { children: [_jsx(DynamicIcon, { name: iconName }), _jsx("span", { children: plugin.label })] }));
34
+ });
35
+ PluginTabLabel.displayName = `${plugin.label}TabLabel`;
36
+ const PluginContent = memo(() => {
37
+ const Component = plugin.component;
38
+ const props = plugin.props || {};
39
+ if (!Component) {
40
+ return _jsx("div", { children: "Error: Plugin component not found" });
41
+ }
42
+ return _jsx(Component, { ...props });
43
+ });
44
+ PluginContent.displayName = `${plugin.label}Content`;
45
+ addAppTab(tabLocation, {
46
+ id: plugin.label,
47
+ tabLabel: PluginTabLabel,
48
+ content: PluginContent,
49
+ });
50
+ }
51
+ catch (error) {
52
+ console.error(`[Motia] Error registering plugin "${plugin.label}":`, error);
53
+ }
54
+ });
55
+ };
@@ -1,2 +1,7 @@
1
+ import { TabLocation } from '@/stores/use-app-tabs-store';
1
2
  export declare const formatDuration: (duration?: number) => string;
2
3
  export declare const formatTimestamp: (time: number) => string;
4
+ export type ViewMode = 'project' | 'system';
5
+ export declare const DEFAULT_VIEW_MODE: ViewMode;
6
+ export declare const getViewModeFromURL: () => ViewMode;
7
+ export declare const isValidTabLocation: (position: string) => position is TabLocation;
@@ -1,3 +1,4 @@
1
+ import { TabLocation } from '@/stores/use-app-tabs-store';
1
2
  export const formatDuration = (duration) => {
2
3
  if (duration === undefined || duration === null)
3
4
  return 'N/A';
@@ -13,3 +14,21 @@ export const formatTimestamp = (time) => {
13
14
  const date = new Date(Number(time));
14
15
  return `${date.toLocaleDateString('en-US', { year: undefined, month: 'short', day: '2-digit' })}, ${date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h24' })}.${date.getMilliseconds().toString().padStart(3, '0')}`;
15
16
  };
17
+ export const DEFAULT_VIEW_MODE = 'system';
18
+ export const getViewModeFromURL = () => {
19
+ try {
20
+ const url = new URL(window.location.href);
21
+ const viewMode = url.searchParams.get('view-mode');
22
+ if (viewMode === 'project' || viewMode === 'system') {
23
+ return viewMode;
24
+ }
25
+ return DEFAULT_VIEW_MODE;
26
+ }
27
+ catch (error) {
28
+ console.error('[Motia] Error parsing URL:', error);
29
+ return DEFAULT_VIEW_MODE;
30
+ }
31
+ };
32
+ export const isValidTabLocation = (position) => {
33
+ return Object.values(TabLocation).includes(position);
34
+ };
@@ -0,0 +1 @@
1
+ export declare const ProjectViewMode: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TabLocation, useAppTabsStore } from './stores/use-app-tabs-store';
3
+ import { APP_SIDEBAR_CONTAINER_ID, Panel } from '@motiadev/ui';
4
+ import { useShallow } from 'zustand/react/shallow';
5
+ const topTabs = (state) => state.tabs[TabLocation.TOP];
6
+ export const ProjectViewMode = () => {
7
+ const tabs = useAppTabsStore(useShallow(topTabs));
8
+ return (_jsxs("div", { className: "grid grid-rows-1 grid-cols-[1fr_auto] bg-background text-foreground h-screen ", children: [_jsx("main", { className: "m-2 overflow-hidden", role: "main", children: _jsx(Panel, { contentClassName: 'p-0', tabs: tabs.map((tab) => {
9
+ const Element = tab.content;
10
+ const LabelComponent = tab.tabLabel;
11
+ return {
12
+ label: tab.id,
13
+ labelComponent: _jsx(LabelComponent, {}),
14
+ content: _jsx(Element, {}),
15
+ 'data-testid': tab.id,
16
+ };
17
+ }) }) }), _jsx("div", { id: APP_SIDEBAR_CONTAINER_ID })] }));
18
+ };
@@ -0,0 +1,18 @@
1
+ export declare enum TabLocation {
2
+ TOP = "top",
3
+ BOTTOM = "bottom"
4
+ }
5
+ export type AppTab = {
6
+ id: string;
7
+ tabLabel: React.ElementType;
8
+ content: React.ElementType;
9
+ };
10
+ export interface AppTabsState {
11
+ tabs: Record<TabLocation, AppTab[]>;
12
+ addTab: (position: TabLocation, tab: AppTab) => void;
13
+ setTabs: (position: TabLocation, tabs: AppTab[]) => void;
14
+ removeTab: (position: TabLocation, id: string) => void;
15
+ }
16
+ export declare const useAppTabsStore: import("zustand").UseBoundStore<import("zustand").StoreApi<AppTabsState>>;
17
+ export declare const setAppTabs: (position: TabLocation, tabs: AppTab[]) => void;
18
+ export declare const addAppTab: (position: TabLocation, tab: AppTab) => void;
@@ -0,0 +1,37 @@
1
+ import { create } from 'zustand';
2
+ export var TabLocation;
3
+ (function (TabLocation) {
4
+ TabLocation["TOP"] = "top";
5
+ TabLocation["BOTTOM"] = "bottom";
6
+ })(TabLocation || (TabLocation = {}));
7
+ const defaultTabs = {
8
+ [TabLocation.TOP]: [],
9
+ [TabLocation.BOTTOM]: [],
10
+ };
11
+ export const useAppTabsStore = create((set) => ({
12
+ tabs: defaultTabs,
13
+ addTab: (position, tab) => set((state) => ({
14
+ tabs: {
15
+ ...state.tabs,
16
+ [position]: [...state.tabs[position], tab],
17
+ },
18
+ })),
19
+ setTabs: (position, tabs) => set((state) => ({
20
+ tabs: {
21
+ ...state.tabs,
22
+ [position]: tabs,
23
+ },
24
+ })),
25
+ removeTab: (position, id) => set((state) => ({
26
+ tabs: {
27
+ ...state.tabs,
28
+ [position]: state.tabs[position].filter((tab) => tab.id !== id),
29
+ },
30
+ })),
31
+ }));
32
+ export const setAppTabs = (position, tabs) => {
33
+ useAppTabsStore.getState().setTabs(position, tabs);
34
+ };
35
+ export const addAppTab = (position, tab) => {
36
+ useAppTabsStore.getState().addTab(position, tab);
37
+ };
@@ -7,8 +7,6 @@ type UseGlobalStore = {
7
7
  selectTraceId: (traceId?: string) => void;
8
8
  selectedStateId?: string;
9
9
  selectStateId: (stateId?: string) => void;
10
- selectedLogId?: string;
11
- selectLogId: (logId?: string) => void;
12
10
  };
13
11
  export declare const useGlobalStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<UseGlobalStore>, "setState" | "persist"> & {
14
12
  setState(partial: UseGlobalStore | Partial<UseGlobalStore> | ((state: UseGlobalStore) => UseGlobalStore | Partial<UseGlobalStore>), replace?: false | undefined): unknown;
@@ -12,8 +12,6 @@ export const useGlobalStore = create(persist((set) => ({
12
12
  selectTraceId: (traceId) => set(select(traceId, 'selectedTraceId')),
13
13
  selectedStateId: undefined,
14
14
  selectStateId: (stateId) => set(select(stateId, 'selectedStateId')),
15
- selectedLogId: undefined,
16
- selectLogId: (logId) => set(select(logId, 'selectedLogId')),
17
15
  }), {
18
16
  name: 'motia-global-storage',
19
17
  storage: createJSONStorage(() => localStorage),
@@ -1,11 +1,13 @@
1
- import { create } from "zustand";
2
- import { createJSONStorage, persist } from "zustand/middleware";
1
+ import { create } from 'zustand';
2
+ import { createJSONStorage, persist } from 'zustand/middleware';
3
3
  export const useTabsStore = create(persist((set) => ({
4
4
  tab: {
5
- top: "flow",
6
- bottom: "tracing",
5
+ top: 'flow',
6
+ bottom: 'tracing',
7
+ },
8
+ setTopTab: (tab) => {
9
+ set((state) => ({ tab: { ...state.tab, top: tab } }));
7
10
  },
8
- setTopTab: (tab) => set((state) => ({ tab: { ...state.tab, top: tab } })),
9
11
  setBottomTab: (tab) => set((state) => ({ tab: { ...state.tab, bottom: tab } })),
10
12
  }), {
11
13
  name: 'motia-tabs-storage',
@@ -0,0 +1 @@
1
+ export declare const SystemViewMode: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { APP_SIDEBAR_CONTAINER_ID, CollapsiblePanel, CollapsiblePanelGroup, TabsContent, TabsList, TabsTrigger, } from '@motiadev/ui';
3
+ import { useCallback, useMemo } from 'react';
4
+ import { useShallow } from 'zustand/react/shallow';
5
+ import { Header } from './components/header/header';
6
+ import { analytics } from './lib/analytics';
7
+ import { TabLocation, useAppTabsStore } from './stores/use-app-tabs-store';
8
+ import { useTabsStore } from './stores/use-tabs-store';
9
+ const topTabs = (state) => state.tabs;
10
+ export const SystemViewMode = () => {
11
+ const tab = useTabsStore((state) => state.tab);
12
+ const setTopTab = useTabsStore((state) => state.setTopTab);
13
+ const setBottomTab = useTabsStore((state) => state.setBottomTab);
14
+ const tabs = useAppTabsStore(useShallow(topTabs));
15
+ const tabChangeCallbacks = useMemo(() => ({
16
+ [TabLocation.TOP]: setTopTab,
17
+ [TabLocation.BOTTOM]: setBottomTab,
18
+ }), [setTopTab, setBottomTab]);
19
+ const onTabChange = useCallback((location) => (newTab) => {
20
+ analytics.track(`${location} tab changed`, { [`new.${location}`]: newTab, tab });
21
+ tabChangeCallbacks[location](newTab);
22
+ }, [tabChangeCallbacks, tab]);
23
+ 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: [_jsx(CollapsiblePanel, { id: "top-panel", variant: 'tabs', defaultTab: tab.top, onTabChange: onTabChange(TabLocation.TOP), header: _jsx(TabsList, { children: tabs[TabLocation.TOP].map((tab) => {
24
+ const LabelComponent = tab.tabLabel;
25
+ return (_jsx(TabsTrigger, { value: tab.id, "data-testid": `${tab.id.toLowerCase()}-link`, className: "cursor-pointer", children: _jsx(LabelComponent, {}) }, tab.id));
26
+ }) }), children: tabs[TabLocation.TOP].map((tab) => {
27
+ const Element = tab.content;
28
+ return (_jsx(TabsContent, { value: tab.id, className: "h-full", children: _jsx(Element, {}) }, tab.id));
29
+ }) }), _jsx(CollapsiblePanel, { id: "bottom-panel", variant: 'tabs', defaultTab: tab.bottom, onTabChange: onTabChange(TabLocation.BOTTOM), header: _jsx(TabsList, { children: tabs[TabLocation.BOTTOM].map((tab) => {
30
+ const LabelComponent = tab.tabLabel;
31
+ return (_jsx(TabsTrigger, { value: tab.id, "data-testid": `${tab.id.toLowerCase()}-link`, className: "cursor-pointer", children: _jsx(LabelComponent, {}) }, tab.id));
32
+ }) }), children: tabs[TabLocation.BOTTOM].map((tab) => {
33
+ const Element = tab.content;
34
+ return (_jsx(TabsContent, { value: tab.id, className: "h-full", children: _jsx(Element, {}) }, tab.id));
35
+ }) })] }) }), _jsx("div", { id: APP_SIDEBAR_CONTAINER_ID })] }));
36
+ };