@motiadev/workbench 0.5.11-beta.120-214453 → 0.5.11-beta.120-559255

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,9 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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';
2
3
  import { analytics } from '@/lib/analytics';
3
- import { CollapsiblePanel, CollapsiblePanelGroup, TabsContent, TabsList, TabsTrigger } from '@motiadev/ui';
4
4
  import { ReactFlowProvider } from '@xyflow/react';
5
5
  import { File, GanttChart, Link2, LogsIcon } from 'lucide-react';
6
- import { useCallback, useMemo } from 'react';
6
+ import { useCallback, useEffect, useMemo, useState } from 'react';
7
7
  import { EndpointsPage } from './components/endpoints/endpoints-page';
8
8
  import { FlowPage } from './components/flow/flow-page';
9
9
  import { FlowTabMenuItem } from './components/flow/flow-tab-menu-item';
@@ -23,6 +23,7 @@ export const App = () => {
23
23
  const tab = useTabsStore((state) => state.tab);
24
24
  const setTopTab = useTabsStore((state) => state.setTopTab);
25
25
  const setBottomTab = useTabsStore((state) => state.setBottomTab);
26
+ const [viewMode, setViewMode] = useState('system');
26
27
  const tabChangeCallbacks = useMemo(() => ({
27
28
  [TabLocation.TOP]: setTopTab,
28
29
  [TabLocation.BOTTOM]: setBottomTab,
@@ -31,5 +32,26 @@ export const App = () => {
31
32
  analytics.track(`${location} tab changed`, { [`new.${location}`]: newTab, tab });
32
33
  tabChangeCallbacks[location](newTab);
33
34
  }, [tabChangeCallbacks, tab]);
35
+ useEffect(() => {
36
+ const url = new URL(window.location.href);
37
+ const viewMode = url.searchParams.get('view-mode');
38
+ if (viewMode) {
39
+ setViewMode(viewMode);
40
+ }
41
+ }, [setViewMode]);
42
+ if (viewMode === 'project') {
43
+ return (_jsxs("div", { className: "grid grid-rows-[auto_1fr] grid-cols-[1fr_auto] bg-background text-foreground h-screen ", children: [_jsx("main", { className: "m-2 overflow-hidden", role: "main", children: _jsx(Panel, { tabs: [
44
+ {
45
+ label: 'Flow',
46
+ labelComponent: _jsx(FlowTabMenuItem, {}),
47
+ content: (_jsx(ReactFlowProvider, { children: _jsx("div", { className: "h-[calc(100vh-100px)] w-full", children: _jsx(FlowPage, {}) }) })),
48
+ },
49
+ {
50
+ label: 'Endpoint',
51
+ labelComponent: (_jsxs(_Fragment, { children: [_jsx(Link2, {}), "Endpoint"] })),
52
+ content: _jsx(EndpointsPage, {}),
53
+ },
54
+ ] }) }), _jsx("div", { id: APP_SIDEBAR_CONTAINER_ID })] }));
55
+ }
34
56
  return (_jsxs("div", { className: "grid grid-rows-[auto_1fr] grid-cols-[1fr_auto] bg-background text-foreground h-screen", children: [_jsx("div", { className: "col-span-2", children: _jsx(Header, {}) }), _jsx("main", { className: "m-2 overflow-hidden", role: "main", children: _jsxs(CollapsiblePanelGroup, { autoSaveId: "app-panel", direction: "vertical", className: "gap-1 h-full", "aria-label": "Workbench panels", children: [_jsxs(CollapsiblePanel, { id: "top-panel", variant: 'tabs', defaultTab: tab.top, onTabChange: onTabChange(TabLocation.TOP), header: _jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "flow", "data-testid": "flows-link", children: _jsx(FlowTabMenuItem, {}) }), _jsxs(TabsTrigger, { value: "endpoint", "data-testid": "endpoints-link", className: "cursor-pointer", children: [_jsx(Link2, {}), "Endpoint"] })] }), children: [_jsx(TabsContent, { value: "flow", className: "h-full", asChild: true, children: _jsx(ReactFlowProvider, { children: _jsx(FlowPage, {}) }) }), _jsx(TabsContent, { value: "endpoint", asChild: true, children: _jsx(EndpointsPage, {}) })] }), _jsxs(CollapsiblePanel, { id: "bottom-panel", variant: 'tabs', defaultTab: tab.bottom, onTabChange: onTabChange(TabLocation.BOTTOM), header: _jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "tracing", "data-testid": "traces-link", className: "cursor-pointer", children: [_jsx(GanttChart, {}), " Tracing"] }), _jsxs(TabsTrigger, { value: "logs", "data-testid": "logs-link", className: "cursor-pointer", children: [_jsx(LogsIcon, {}), "Logs"] }), _jsxs(TabsTrigger, { value: "states", "data-testid": "states-link", className: "cursor-pointer", children: [_jsx(File, {}), "States"] })] }), children: [_jsx(TabsContent, { value: "tracing", className: "max-h-fit", asChild: true, children: _jsx(TracesPage, {}) }), _jsx(TabsContent, { value: "logs", asChild: true, children: _jsx(LogsPage, {}) }), _jsx(TabsContent, { value: "states", asChild: true, children: _jsx(StatesPage, {}) })] })] }) }), _jsx("div", { id: APP_SIDEBAR_CONTAINER_ID }), _jsx(Tutorial, {})] }));
35
57
  };
@@ -3,6 +3,7 @@ export declare const workbenchXPath: {
3
3
  closePanelButton: string;
4
4
  bottomPanel: string;
5
5
  flows: {
6
+ dropdownFlow: (flowId: string) => string;
6
7
  feature: (featureId: string) => string;
7
8
  previewButton: (stepId: string) => string;
8
9
  node: (stepId: string) => string;
@@ -3,6 +3,7 @@ export const workbenchXPath = {
3
3
  closePanelButton: '//div[@id="app-sidebar-container"]//button[@data-testid="close-panel"]',
4
4
  bottomPanel: '//div[@id="bottom-panel"]',
5
5
  flows: {
6
+ dropdownFlow: (flowId) => `//div[@data-testid="dropdown-${flowId}"]`,
6
7
  feature: (featureId) => `//div[@data-feature-id="${featureId}"]`,
7
8
  previewButton: (stepId) => `//button[@data-testid="open-code-preview-button-${stepId}"]`,
8
9
  node: (stepId) => `//div[@data-testid="node-${stepId}"]`,
@@ -30,7 +31,7 @@ export const workbenchXPath = {
30
31
  row: (index) => `(//tr[starts-with(@data-testid, 'item-')])[${index}]`,
31
32
  },
32
33
  links: {
33
- flows: '//button[@data-testid="flows-link"]',
34
+ flows: '//div[@data-testid="flows-dropdown-trigger"]',
34
35
  endpoints: '//button[@data-testid="endpoints-link"]',
35
36
  tracing: '//button[@data-testid="traces-link"]',
36
37
  logs: '//button[@data-testid="logs-link"]',
@@ -11,4 +11,5 @@ export declare const useTutorialEngine: () => {
11
11
  onClose: () => void;
12
12
  moveStep: (stepNumber: number) => Promise<void>;
13
13
  currentStepRef: import("react").RefObject<number>;
14
+ manualOpenRef: import("react").RefObject<boolean>;
14
15
  };
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
- import { waitForElementByXPath } from './tutorial-utils';
3
2
  import { MotiaTutorial } from '../engine/tutorial-engine';
3
+ import { waitForElementByXPath } from './tutorial-utils';
4
4
  export const useTutorialEngine = () => {
5
5
  const ref = useRef(null);
6
6
  const highlighterRef = useRef(null);
@@ -10,6 +10,7 @@ export const useTutorialEngine = () => {
10
10
  const [link, setLink] = useState(undefined);
11
11
  const [currentStep, setCurrentStep] = useState(0);
12
12
  const [totalSteps, setTotalSteps] = useState(MotiaTutorial.steps.length);
13
+ const manualOpenRef = useRef(false);
13
14
  const loading = useRef(false);
14
15
  const currentStepRef = useRef(0);
15
16
  const moveComponent = (x, y) => {
@@ -147,9 +148,13 @@ export const useTutorialEngine = () => {
147
148
  moveStep(0);
148
149
  }
149
150
  };
150
- MotiaTutorial.onOpen(onOpen);
151
+ MotiaTutorial.onOpen(() => {
152
+ manualOpenRef.current = true;
153
+ onOpen();
154
+ });
151
155
  MotiaTutorial.onStepsRegistered(() => {
152
156
  if (localStorage.getItem('motia-tutorial-closed') !== 'true') {
157
+ manualOpenRef.current = false;
153
158
  onOpen();
154
159
  }
155
160
  });
@@ -166,5 +171,6 @@ export const useTutorialEngine = () => {
166
171
  onClose,
167
172
  moveStep,
168
173
  currentStepRef,
174
+ manualOpenRef,
169
175
  };
170
176
  };
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { analytics } from '@/lib/analytics';
2
3
  import { Button } from '@motiadev/ui';
3
4
  import { Book } from 'lucide-react';
4
- import { useTutorial } from './hooks/use-tutorial';
5
5
  import { Tooltip } from '../ui/tooltip';
6
+ import { useTutorial } from './hooks/use-tutorial';
6
7
  export const TutorialButton = () => {
7
8
  const { open, steps } = useTutorial();
8
9
  const isTutorialFlowMissing = steps.length === 0;
@@ -10,6 +11,7 @@ export const TutorialButton = () => {
10
11
  if (!isTutorialFlowMissing) {
11
12
  open();
12
13
  }
14
+ analytics.track('tutorial_button_clicked', { isTutorialFlowMissing });
13
15
  };
14
16
  const trigger = (_jsxs(Button, { "data-testid": "tutorial-trigger", variant: "default", onClick: () => onTutorialButtonClick(), children: [_jsx(Book, { className: "h-4 w-4" }), "Tutorial"] }));
15
17
  if (isTutorialFlowMissing) {
@@ -1,8 +1,32 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { analytics } from '@/lib/analytics';
2
3
  import { useTutorialEngine } from './hooks/use-tutorial-engine';
3
4
  import { TutorialStep } from './tutorial-step';
4
5
  import './tutorial.css';
5
6
  export const Tutorial = () => {
6
7
  const engine = useTutorialEngine();
7
- return (_jsxs("div", { children: [_jsx("div", { className: "fixed inset-0 z-[9999]" }), _jsx("div", { className: "absolute top-5 left-5 w-full h-full rounded-lg shadow-[0_0_0_9999px_rgba(0,0,0,0.5)] z-[10000] pointer-events-none", ref: engine.highlighterRef }), _jsx(TutorialStep, { ref: engine.ref, step: engine.currentStep, totalSteps: engine.totalSteps, title: engine.title, description: engine.description, link: engine.link, image: engine.image, onNext: () => engine.moveStep(engine.currentStepRef.current + 1), onClose: engine.onClose })] }));
8
+ const onNext = () => {
9
+ const currentStep = engine.currentStepRef.current;
10
+ const nextStep = currentStep + 1;
11
+ engine.moveStep(nextStep);
12
+ if (engine.currentStep === engine.totalSteps) {
13
+ analytics.track('tutorial_completed', {
14
+ manualOpen: engine.manualOpenRef.current,
15
+ });
16
+ }
17
+ else {
18
+ analytics.track('tutorial_next_step', {
19
+ step: nextStep,
20
+ manualOpen: engine.manualOpenRef.current,
21
+ });
22
+ }
23
+ };
24
+ const onClose = () => {
25
+ analytics.track('tutorial_closed', {
26
+ step: engine.currentStepRef.current,
27
+ manualOpen: engine.manualOpenRef.current,
28
+ });
29
+ engine.onClose();
30
+ };
31
+ return (_jsxs("div", { children: [_jsx("div", { className: "fixed inset-0 z-[9999]" }), _jsx("div", { className: "absolute top-5 left-5 w-full h-full rounded-lg shadow-[0_0_0_9999px_rgba(0,0,0,0.5)] z-[10000] pointer-events-none", ref: engine.highlighterRef }), _jsx(TutorialStep, { ref: engine.ref, step: engine.currentStep, totalSteps: engine.totalSteps, title: engine.title, description: engine.description, link: engine.link, image: engine.image, onNext: onNext, onClose: onClose })] }));
8
32
  };
@@ -2,11 +2,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Moon, Sun } from 'lucide-react';
3
3
  import { useThemeStore } from '@/stores/use-theme-store';
4
4
  import { cn } from '@/lib/utils';
5
+ import { useEffect } from 'react';
5
6
  export const ThemeToggle = () => {
6
7
  const theme = useThemeStore((state) => state.theme);
7
8
  const setTheme = useThemeStore((state) => state.setTheme);
8
9
  const toggleTheme = () => {
9
10
  setTheme(theme === 'light' ? 'dark' : 'light');
10
11
  };
12
+ useEffect(() => {
13
+ const url = new URL(window.location.href);
14
+ const colorScheme = url.searchParams.get('color-scheme');
15
+ if (colorScheme) {
16
+ setTheme(colorScheme);
17
+ }
18
+ }, [setTheme]);
11
19
  return (_jsxs("button", { onClick: toggleTheme, className: "relative flex items-center cursor-pointer w-16 h-8 border bg-muted-foreground/10 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') }) })] }));
12
20
  };
@@ -1,4 +1,5 @@
1
1
  import { Feature } from '@/types/file';
2
+ import React from 'react';
2
3
  type Props = {
3
4
  feature: Feature;
4
5
  highlighted: boolean;
@@ -1,4 +1,4 @@
1
- type Theme = 'dark' | 'light' | 'system';
1
+ export type Theme = 'dark' | 'light' | 'system';
2
2
  export type ThemeState = {
3
3
  theme: Theme;
4
4
  setTheme: (theme: Theme) => void;
@@ -14,4 +14,3 @@ export declare const useThemeStore: import("zustand").UseBoundStore<Omit<import(
14
14
  getOptions: () => Partial<import("zustand/middleware").PersistOptions<ThemeState, unknown>>;
15
15
  };
16
16
  }>;
17
- export {};