@motiadev/workbench 0.3.1-beta.87 → 0.3.1-beta.88-406962

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 (219) hide show
  1. package/dist/index.d.ts +3 -4
  2. package/dist/index.html +3 -2
  3. package/dist/index.js +2 -3
  4. package/dist/public/icon.png +0 -0
  5. package/dist/public/motia-dark.png +0 -0
  6. package/dist/public/motia-light.png +0 -0
  7. package/dist/src/App.d.ts +2 -0
  8. package/dist/src/App.js +19 -0
  9. package/dist/src/components/endpoints/endpoint-badge.d.ts +1 -1
  10. package/dist/src/components/endpoints/endpoint-badge.js +8 -8
  11. package/dist/src/components/endpoints/endpoint-call.d.ts +3 -3
  12. package/dist/src/components/endpoints/endpoint-call.js +28 -23
  13. package/dist/src/components/endpoints/endpoint-response-schema.d.ts +10 -0
  14. package/dist/src/components/endpoints/endpoint-response-schema.js +17 -0
  15. package/dist/src/components/endpoints/endpoint-response.d.ts +8 -0
  16. package/dist/src/components/endpoints/endpoint-response.js +39 -0
  17. package/dist/src/components/endpoints/endpoints-page.js +14 -0
  18. package/dist/src/components/endpoints/hooks/use-get-endpoints.d.ts +1 -15
  19. package/dist/src/components/endpoints/json-editor.d.ts +9 -0
  20. package/dist/src/components/endpoints/json-editor.js +31 -0
  21. package/dist/src/components/flow/flow-page.d.ts +1 -0
  22. package/dist/src/components/flow/flow-page.js +20 -0
  23. package/dist/src/components/flow/flow-tab-menu-item.d.ts +1 -0
  24. package/dist/src/components/flow/flow-tab-menu-item.js +15 -0
  25. package/dist/src/{views → components}/flow/flow-view.d.ts +1 -2
  26. package/dist/src/components/flow/flow-view.js +21 -0
  27. package/dist/src/components/flow/hooks/use-get-flow-state.d.ts +10 -0
  28. package/dist/src/{views → components}/flow/hooks/use-get-flow-state.js +17 -10
  29. package/dist/src/{views → components}/flow/hooks/use-organize-nodes.d.ts +1 -1
  30. package/dist/src/{views → components}/flow/hooks/use-save-workflow-config.d.ts +1 -1
  31. package/dist/src/components/flow/nodes/api-flow-node.d.ts +2 -0
  32. package/dist/src/{views → components}/flow/nodes/api-flow-node.js +1 -1
  33. package/dist/src/components/flow/nodes/cron-flow-node.d.ts +2 -0
  34. package/dist/src/components/flow/nodes/cron-flow-node.js +5 -0
  35. package/dist/src/{views → components}/flow/nodes/event-flow-node.d.ts +1 -1
  36. package/dist/src/components/flow/nodes/event-flow-node.js +5 -0
  37. package/dist/src/components/flow/nodes/noop-flow-node.d.ts +2 -0
  38. package/dist/src/{views → components}/flow/nodes/noop-flow-node.js +1 -1
  39. package/dist/src/components/header/header.js +3 -11
  40. package/dist/src/components/logs/log-detail.d.ts +3 -1
  41. package/dist/src/components/logs/log-detail.js +33 -11
  42. package/dist/src/components/logs/log-level-badge.js +6 -6
  43. package/dist/src/components/logs/log-level-dot.js +7 -7
  44. package/dist/src/components/logs/logs-page.js +26 -0
  45. package/dist/src/components/observability/events/code/function-call.js +1 -1
  46. package/dist/src/components/observability/events/trace-event.d.ts +1 -1
  47. package/dist/src/components/observability/events/trace-log-event.d.ts +1 -1
  48. package/dist/src/components/observability/events/trace-log-event.js +2 -2
  49. package/dist/src/components/observability/hooks/use-get-endtime.d.ts +1 -1
  50. package/dist/src/components/observability/trace-item/trace-item-detail.d.ts +1 -0
  51. package/dist/src/components/observability/trace-item/trace-item-detail.js +5 -4
  52. package/dist/src/components/observability/trace-item/trace-item.d.ts +1 -0
  53. package/dist/src/components/observability/trace-item/trace-item.js +11 -17
  54. package/dist/src/components/observability/trace-status.d.ts +6 -10
  55. package/dist/src/components/observability/trace-status.js +7 -32
  56. package/dist/src/components/observability/trace-timeline.js +16 -4
  57. package/dist/src/components/observability/traces-groups.d.ts +2 -2
  58. package/dist/src/components/observability/traces-groups.js +4 -4
  59. package/dist/src/components/observability/traces-page.js +12 -0
  60. package/dist/src/components/sidebar/sidebar.d.ts +8 -0
  61. package/dist/src/components/sidebar/sidebar.js +39 -0
  62. package/dist/src/components/states/hooks/states-hooks.d.ts +7 -3
  63. package/dist/src/components/states/hooks/states-hooks.js +5 -33
  64. package/dist/src/components/states/state-detail.d.ts +2 -1
  65. package/dist/src/components/states/state-detail.js +15 -9
  66. package/dist/src/components/states/state-value.js +7 -7
  67. package/dist/src/components/states/states-page.js +18 -0
  68. package/dist/src/components/ui/badge.d.ts +1 -1
  69. package/dist/src/components/ui/table.js +1 -1
  70. package/dist/src/components/ui/theme-toggle.js +4 -3
  71. package/dist/src/hooks/use-fetch-flows.d.ts +1 -5
  72. package/dist/src/hooks/use-fetch-flows.js +18 -13
  73. package/dist/src/hooks/use-log-listener.js +2 -2
  74. package/dist/src/hooks/use-update-handle-positions.d.ts +10 -0
  75. package/dist/src/hooks/use-update-handle-positions.js +35 -0
  76. package/dist/src/index.css +36 -176
  77. package/dist/src/lib/utils.d.ts +1 -0
  78. package/dist/src/lib/utils.js +4 -0
  79. package/dist/src/main.d.ts +1 -0
  80. package/dist/src/main.js +4 -11
  81. package/dist/src/publicComponents/api-node.js +2 -4
  82. package/dist/src/publicComponents/{base-handle.d.ts → base-node/base-handle.d.ts} +1 -0
  83. package/dist/src/publicComponents/base-node/base-handle.js +8 -0
  84. package/dist/src/publicComponents/base-node/base-node.d.ts +21 -0
  85. package/dist/src/publicComponents/base-node/base-node.js +13 -0
  86. package/dist/src/publicComponents/{emits.d.ts → base-node/emits.d.ts} +1 -1
  87. package/dist/src/publicComponents/base-node/emits.js +5 -0
  88. package/dist/src/{views/flow/nodes → publicComponents/base-node}/language-indicator.d.ts +1 -1
  89. package/dist/src/{views/flow/nodes → publicComponents/base-node}/language-indicator.js +8 -7
  90. package/dist/src/publicComponents/base-node/node-header.d.ts +12 -0
  91. package/dist/src/publicComponents/base-node/node-header.js +30 -0
  92. package/dist/src/publicComponents/base-node/node-sidebar.d.ts +18 -0
  93. package/dist/src/publicComponents/base-node/node-sidebar.js +20 -0
  94. package/dist/src/publicComponents/base-node/subscribe.js +4 -0
  95. package/dist/src/publicComponents/cron-node.d.ts +2 -1
  96. package/dist/src/publicComponents/cron-node.js +3 -4
  97. package/dist/src/publicComponents/event-node.d.ts +1 -5
  98. package/dist/src/publicComponents/event-node.js +4 -6
  99. package/dist/src/publicComponents/node-details.js +21 -12
  100. package/dist/src/publicComponents/node-props.d.ts +13 -6
  101. package/dist/src/publicComponents/noop-node.d.ts +3 -7
  102. package/dist/src/publicComponents/noop-node.js +3 -3
  103. package/dist/src/stores/use-flow-store.d.ts +18 -0
  104. package/dist/src/stores/use-flow-store.js +15 -0
  105. package/dist/src/stores/use-global-store.d.ts +24 -0
  106. package/dist/src/stores/use-global-store.js +20 -0
  107. package/dist/src/stores/{use-logs.d.ts → use-logs-store.d.ts} +1 -3
  108. package/dist/src/stores/use-logs-store.js +10 -0
  109. package/dist/src/stores/use-tabs-store.d.ts +17 -0
  110. package/dist/src/stores/use-tabs-store.js +13 -0
  111. package/dist/src/stores/use-theme-store.d.ts +17 -0
  112. package/dist/src/stores/use-theme-store.js +26 -0
  113. package/dist/src/types/endpoint.d.ts +14 -0
  114. package/dist/src/{views/flow/nodes/nodes.types.d.ts → types/flow.d.ts} +45 -0
  115. package/dist/src/types/flow.js +1 -0
  116. package/dist/tsconfig.app.tsbuildinfo +1 -1
  117. package/package.json +28 -26
  118. package/dist/public/.empty +0 -0
  119. package/dist/src/components/app-sidebar.d.ts +0 -1
  120. package/dist/src/components/app-sidebar.js +0 -12
  121. package/dist/src/components/endpoints/endpoints.d.ts +0 -1
  122. package/dist/src/components/endpoints/endpoints.js +0 -34
  123. package/dist/src/components/endpoints/selected-endpoint.d.ts +0 -7
  124. package/dist/src/components/endpoints/selected-endpoint.js +0 -7
  125. package/dist/src/components/logs/log-console.d.ts +0 -1
  126. package/dist/src/components/logs/log-console.js +0 -69
  127. package/dist/src/components/logs/log-field.d.ts +0 -7
  128. package/dist/src/components/logs/log-field.js +0 -20
  129. package/dist/src/components/logs/logs.d.ts +0 -1
  130. package/dist/src/components/logs/logs.js +0 -18
  131. package/dist/src/components/observability/observability-stats.d.ts +0 -5
  132. package/dist/src/components/observability/observability-stats.js +0 -17
  133. package/dist/src/components/states/states.d.ts +0 -1
  134. package/dist/src/components/states/states.js +0 -21
  135. package/dist/src/components/ui/BadgeCount.d.ts +0 -7
  136. package/dist/src/components/ui/BadgeCount.js +0 -13
  137. package/dist/src/components/ui/button.d.ts +0 -11
  138. package/dist/src/components/ui/button.js +0 -33
  139. package/dist/src/components/ui/card.d.ts +0 -8
  140. package/dist/src/components/ui/card.js +0 -16
  141. package/dist/src/components/ui/collapsible.d.ts +0 -5
  142. package/dist/src/components/ui/collapsible.js +0 -5
  143. package/dist/src/components/ui/dialog.d.ts +0 -19
  144. package/dist/src/components/ui/dialog.js +0 -22
  145. package/dist/src/components/ui/dropdown-menu.d.ts +0 -25
  146. package/dist/src/components/ui/dropdown-menu.js +0 -50
  147. package/dist/src/components/ui/input.d.ts +0 -3
  148. package/dist/src/components/ui/input.js +0 -8
  149. package/dist/src/components/ui/logo-icon.d.ts +0 -5
  150. package/dist/src/components/ui/logo-icon.js +0 -5
  151. package/dist/src/components/ui/navigation-menu.d.ts +0 -13
  152. package/dist/src/components/ui/navigation-menu.js +0 -30
  153. package/dist/src/components/ui/scroll-area.d.ts +0 -5
  154. package/dist/src/components/ui/scroll-area.js +0 -9
  155. package/dist/src/components/ui/select.d.ts +0 -13
  156. package/dist/src/components/ui/select.js +0 -25
  157. package/dist/src/components/ui/separator.d.ts +0 -4
  158. package/dist/src/components/ui/separator.js +0 -8
  159. package/dist/src/components/ui/sheet.d.ts +0 -25
  160. package/dist/src/components/ui/sheet.js +0 -36
  161. package/dist/src/components/ui/sidebar.d.ts +0 -12
  162. package/dist/src/components/ui/sidebar.js +0 -25
  163. package/dist/src/components/ui/skeleton.d.ts +0 -3
  164. package/dist/src/components/ui/skeleton.js +0 -6
  165. package/dist/src/components/ui/switch.d.ts +0 -4
  166. package/dist/src/components/ui/switch.js +0 -7
  167. package/dist/src/components/ui/tabs.d.ts +0 -7
  168. package/dist/src/components/ui/tabs.js +0 -12
  169. package/dist/src/components/ui/textarea.d.ts +0 -3
  170. package/dist/src/components/ui/textarea.js +0 -8
  171. package/dist/src/components/ui/tooltip.d.ts +0 -7
  172. package/dist/src/components/ui/tooltip.js +0 -11
  173. package/dist/src/hooks/use-list-flows.d.ts +0 -9
  174. package/dist/src/hooks/use-list-flows.js +0 -8
  175. package/dist/src/hooks/use-theme.d.ts +0 -6
  176. package/dist/src/hooks/use-theme.js +0 -28
  177. package/dist/src/publicComponents/base-handle.js +0 -10
  178. package/dist/src/publicComponents/base-node.d.ts +0 -16
  179. package/dist/src/publicComponents/base-node.js +0 -25
  180. package/dist/src/publicComponents/colorMap.d.ts +0 -6
  181. package/dist/src/publicComponents/colorMap.js +0 -6
  182. package/dist/src/publicComponents/components/header-bar.d.ts +0 -11
  183. package/dist/src/publicComponents/components/header-bar.js +0 -15
  184. package/dist/src/publicComponents/emits.js +0 -6
  185. package/dist/src/publicComponents/subscribe.js +0 -5
  186. package/dist/src/route-wrapper.d.ts +0 -2
  187. package/dist/src/route-wrapper.js +0 -5
  188. package/dist/src/routes/endpoints-page.js +0 -5
  189. package/dist/src/routes/flow.d.ts +0 -1
  190. package/dist/src/routes/flow.js +0 -11
  191. package/dist/src/routes/index.d.ts +0 -1
  192. package/dist/src/routes/index.js +0 -5
  193. package/dist/src/routes/logs-page.js +0 -12
  194. package/dist/src/routes/states-page.js +0 -5
  195. package/dist/src/routes/traces-page.js +0 -14
  196. package/dist/src/stores/use-logs.js +0 -52
  197. package/dist/src/views/flow/arrow-head.d.ts +0 -8
  198. package/dist/src/views/flow/arrow-head.js +0 -6
  199. package/dist/src/views/flow/flow-view.js +0 -48
  200. package/dist/src/views/flow/hooks/use-get-flow-state.d.ts +0 -52
  201. package/dist/src/views/flow/legend.d.ts +0 -4
  202. package/dist/src/views/flow/legend.js +0 -51
  203. package/dist/src/views/flow/nodes/api-flow-node.d.ts +0 -4
  204. package/dist/src/views/flow/nodes/event-flow-node.js +0 -5
  205. package/dist/src/views/flow/nodes/noop-flow-node.d.ts +0 -4
  206. /package/dist/src/{routes → components/endpoints}/endpoints-page.d.ts +0 -0
  207. /package/dist/src/{views → components}/flow/base-edge.d.ts +0 -0
  208. /package/dist/src/{views → components}/flow/base-edge.js +0 -0
  209. /package/dist/src/{views → components}/flow/flow-loader.d.ts +0 -0
  210. /package/dist/src/{views → components}/flow/flow-loader.js +0 -0
  211. /package/dist/src/{views → components}/flow/hooks/use-organize-nodes.js +0 -0
  212. /package/dist/src/{views → components}/flow/hooks/use-save-workflow-config.js +0 -0
  213. /package/dist/src/{views → components}/flow/node-organizer.d.ts +0 -0
  214. /package/dist/src/{views → components}/flow/node-organizer.js +0 -0
  215. /package/dist/src/{routes → components/logs}/logs-page.d.ts +0 -0
  216. /package/dist/src/{routes → components/observability}/traces-page.d.ts +0 -0
  217. /package/dist/src/{routes → components/states}/states-page.d.ts +0 -0
  218. /package/dist/src/publicComponents/{subscribe.d.ts → base-node/subscribe.d.ts} +0 -0
  219. /package/dist/src/{views/flow/nodes/nodes.types.js → types/endpoint.js} +0 -0
@@ -1,15 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Link, useLocation } from 'react-router';
3
- import { BadgeCount } from '../ui/BadgeCount';
4
- import { LogoIcon } from '../ui/logo-icon';
5
- import { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, } from '../ui/navigation-menu';
2
+ import { useThemeStore } from '@/stores/use-theme-store';
6
3
  import { ThemeToggle } from '../ui/theme-toggle';
7
- import { useListFlows } from '@/hooks/use-list-flows';
8
- import { NavigationMenuSub } from '@radix-ui/react-navigation-menu';
9
- import { Workflow } from 'lucide-react';
10
4
  export const Header = () => {
11
- const { pathname } = useLocation();
12
- const { flows } = useListFlows();
13
- const isActive = (flowId) => pathname.includes(`/flow/${flowId}`);
14
- return (_jsxs("header", { className: "min-h-16 px-4 gap-1 flex items-center bg-header text-header-foreground border-b border-header-border", children: [_jsx(LogoIcon, { className: "h-8 w-8 mr-16" }), _jsx(NavigationMenu, { children: _jsxs(NavigationMenuList, { children: [_jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsxs(Link, { "data-testid": "header-logs-link", to: "/logs", className: 'flex flex-row items-center pr-2 relative', children: ["Logs", _jsx(BadgeCount, { className: "absolute top-1 right-0", dotOnly: true })] }) }) }), _jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsx(Link, { "data-testid": "header-traces-link", to: "/traces", children: "Traces" }) }) }), _jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsx(Link, { "data-testid": "header-states-link", to: "/states", children: "States" }) }) }), _jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsx(Link, { "data-testid": "header-endpoints-link", to: "/endpoints", children: "Endpoints" }) }) })] }) }), _jsx(NavigationMenu, { children: _jsxs(NavigationMenuList, { children: [_jsxs(NavigationMenuItem, { children: [_jsx(NavigationMenuTrigger, { className: 'cursor-pointer', "data-testid": "header-flows-link", children: "Flows" }), _jsx(NavigationMenuContent, { children: _jsx(NavigationMenuSub, { children: _jsx(NavigationMenuList, { className: "flex flex-col min-w-[200px]", children: flows.map((flow) => (_jsx(NavigationMenuItem, { className: "w-full", asChild: true, children: _jsx(NavigationMenuLink, { asChild: true, active: isActive(flow.id), children: _jsxs(Link, { "data-testid": `flow-${flow.name}-link`, to: `/flow/${flow.id}`, className: 'flex flex-row gap-2 items-center', children: [_jsx(Workflow, { className: "w-4 h-4" }), " ", flow.name] }) }) }, flow.id))) }) }) })] }), _jsx(NavigationMenuIndicator, {})] }) }), _jsx("div", { className: "ml-auto", children: _jsx(ThemeToggle, {}) })] }));
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, {})] }));
15
7
  };
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
- import { Log } from '../../stores/use-logs';
2
+ import 'react18-json-view/src/dark.css';
3
+ import 'react18-json-view/src/style.css';
4
+ import { Log } from '@/stores/use-logs-store';
3
5
  type Props = {
4
6
  log?: Log;
5
7
  onClose: () => void;
@@ -1,15 +1,37 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { LogLevelBadge } from './log-level-badge';
3
- import { LogField } from './log-field';
4
- import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../ui/sheet';
5
- const defaultProps = ['msg', 'time', 'level', 'step', 'flows', 'traceId'];
2
+ import { formatTimestamp } from '@/lib/utils';
3
+ import { useMemo, useState } from 'react';
4
+ import ReactJson from 'react18-json-view';
5
+ import 'react18-json-view/src/dark.css';
6
+ import 'react18-json-view/src/style.css';
7
+ import { LogLevelDot } from './log-level-dot';
8
+ import { Sidebar } from '@/components/sidebar/sidebar';
9
+ import { X } from 'lucide-react';
10
+ const defaultProps = ['id', 'msg', 'time', 'level', 'step', 'flows', 'traceId'];
6
11
  export const LogDetail = ({ log, onClose }) => {
7
- const isOpen = !!log;
8
- const onOpenChange = (open) => {
9
- if (!open) {
10
- onClose();
12
+ const [hasOtherProps, setHasOtherProps] = useState(false);
13
+ const otherPropsObject = useMemo(() => {
14
+ if (!log) {
15
+ return null;
11
16
  }
12
- };
13
- const otherProps = Object.keys(log ?? {}).filter((key) => !defaultProps.includes(key));
14
- return (_jsx(Sheet, { open: isOpen, onOpenChange: onOpenChange, children: _jsxs(SheetContent, { className: "flex flex-col h-full", children: [_jsxs(SheetHeader, { children: [_jsx(SheetTitle, { children: "Logs details" }), _jsx(SheetDescription, { children: "Log details and application." })] }), _jsx("div", { className: "font-mono overflow-y-auto", children: log && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex flex-row gap-2", children: [_jsx(LogField, { label: "Level", value: _jsx(LogLevelBadge, { level: log.level }), className: "flex-1" }), _jsx(LogField, { label: "Time", value: new Date(log.time).toLocaleString(), className: "flex-1" })] }), _jsxs("div", { className: "flex flex-row gap-2", children: [_jsx(LogField, { label: "Step", value: log.step, className: "flex-1" }), _jsx(LogField, { label: "Flows", value: log.flows.join(', '), className: "flex-1" })] }), _jsx(LogField, { label: "Trace ID", value: log.traceId }), _jsx(LogField, { label: "Message", value: log.msg }), otherProps.map((key) => (_jsx(LogField, { label: key, value: log[key] }, key)))] })) })] }) }));
17
+ const otherProps = Object.keys(log ?? {}).filter((key) => !defaultProps.includes(key));
18
+ setHasOtherProps(otherProps.length > 0);
19
+ return otherProps.reduce((acc, key) => {
20
+ acc[key] = log[key];
21
+ return acc;
22
+ }, {});
23
+ }, [log]);
24
+ if (!log) {
25
+ return null;
26
+ }
27
+ return (_jsx(Sidebar, { onClose: onClose, title: "Logs Details", subtitle: "Details including custom properties", actions: [{ icon: _jsx(X, {}), onClick: onClose, label: 'Close' }], details: [
28
+ {
29
+ label: 'Level',
30
+ value: (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LogLevelDot, { level: log.level }), _jsx("div", { className: "capitalize", children: log.level })] })),
31
+ },
32
+ { label: 'Time', value: formatTimestamp(log.time) },
33
+ { label: 'Step', value: log.step },
34
+ { label: 'Flows', value: log.flows.join(', ') },
35
+ { label: 'Trace ID', value: log.traceId },
36
+ ], children: hasOtherProps && _jsx(ReactJson, { src: otherPropsObject, theme: "default", enableClipboard: false }) }));
15
37
  };
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Badge } from '../ui/badge';
3
+ const map = {
4
+ info: 'info',
5
+ error: 'error',
6
+ warn: 'warning',
7
+ debug: 'info',
8
+ };
3
9
  export const LogLevelBadge = (props) => {
4
- const map = {
5
- info: 'info',
6
- error: 'error',
7
- warn: 'warning',
8
- debug: 'info',
9
- };
10
10
  return (_jsx(Badge, { variant: map[props.level], className: props.className, children: props.level }));
11
11
  };
@@ -1,14 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cva } from 'class-variance-authority';
3
- const badgeVariants = cva('text-xs font-medium tracking-wide w-2 h-2 rounded-full mx-1', {
3
+ const badgeVariants = cva('text-xs font-medium tracking-wide rounded-full h-[6px] w-[6px] m-[4px] outline-[2px]', {
4
4
  variants: {
5
5
  variant: {
6
- info: 'bg-sky-500',
7
- trace: 'bg-sky-500',
8
- debug: 'bg-sky-500',
9
- error: 'bg-rose-500',
10
- fatal: 'bg-rose-500',
11
- warn: 'bg-amber-500',
6
+ info: 'bg-[#2862FE] outline-[#2862FE]/20',
7
+ trace: 'bg-sky-500 outline-sky-500',
8
+ debug: 'bg-sky-500 outline-sky-500',
9
+ error: 'bg-rose-500 outline-rose-500',
10
+ fatal: 'bg-rose-500 outline-rose-500',
11
+ warn: 'bg-amber-500 outline-amber-500',
12
12
  },
13
13
  },
14
14
  });
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
3
+ import { formatTimestamp } from '@/lib/utils';
4
+ import { useGlobalStore } from '@/stores/use-global-store';
5
+ import { useLogsStore } from '@/stores/use-logs-store';
6
+ import { useMemo, useState } from 'react';
7
+ import { LogDetail } from './log-detail';
8
+ import { LogLevelDot } from './log-level-dot';
9
+ import { Button, Input } from '@motiadev/ui';
10
+ import { CircleX, Trash } from 'lucide-react';
11
+ export const LogsPage = () => {
12
+ const logs = useLogsStore((state) => state.logs);
13
+ const resetLogs = useLogsStore((state) => state.resetLogs);
14
+ const selectedLogId = useGlobalStore((state) => state.selectedLogId);
15
+ const selectLogId = useGlobalStore((state) => state.selectLogId);
16
+ const selectedLog = useMemo(() => (selectedLogId ? logs.find((log) => log.id === selectedLogId) : undefined), [logs, selectedLogId]);
17
+ const [search, setSearch] = useState('');
18
+ const filteredLogs = useMemo(() => {
19
+ return logs.filter((log) => {
20
+ return (log.msg.toLowerCase().includes(search.toLowerCase()) ||
21
+ log.traceId.toLowerCase().includes(search.toLowerCase()) ||
22
+ log.step.toLowerCase().includes(search.toLowerCase()));
23
+ });
24
+ }, [logs, search]);
25
+ return (_jsxs("div", { className: "h-full flex flex-row", children: [_jsxs("div", { className: "flex-1 overflow-y-auto overflow-x-auto", children: [_jsxs("div", { className: "flex p-2 border-b gap-4", children: [_jsxs("div", { className: "flex-1 relative", children: [_jsx(Input, { variant: "shade", value: search, onChange: (e) => setSearch(e.target.value), className: "pr-10 font-medium" }), _jsx(CircleX, { className: "cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground", onClick: () => setSearch('') })] }), _jsxs(Button, { variant: "outline", onClick: resetLogs, children: [_jsx(Trash, {}), " Clear"] })] }), _jsx(Table, { children: _jsx(TableBody, { className: "font-mono font-medium", children: filteredLogs.map((log, index) => (_jsxs(TableRow, { className: "cursor-pointer border-0", onClick: () => selectLogId(log.id), children: [_jsxs(TableCell, { "data-testid": `time-${index}`, className: "whitespace-nowrap flex items-center gap-2 text-muted-foreground", children: [_jsx(LogLevelDot, { level: log.level }), formatTimestamp(log.time)] }), _jsx(TableCell, { "data-testid": `trace-${log.traceId}`, className: "whitespace-nowrap cursor-pointer hover:text-primary text-muted-foreground", onClick: () => setSearch(log.traceId), children: log.traceId }), _jsx(TableCell, { "data-testid": `step-${index}`, "aria-label": log.step, className: "whitespace-nowrap", children: log.step }), _jsx(TableCell, { "data-testid": `msg-${index}`, "aria-label": log.msg, className: "whitespace-nowrap max-w-[500px] truncate w-full", children: log.msg })] }, index))) }) })] }), _jsx(LogDetail, { log: selectedLog, onClose: () => selectLogId(undefined) })] }));
26
+ };
@@ -7,7 +7,7 @@ export const Argument = ({ arg }) => {
7
7
  return _jsx("span", { className: "font-mono text-blue-100 font-bold bg-blue-500/50 px-2 rounded-md", children: "value" });
8
8
  }
9
9
  const entries = Object.entries(arg);
10
- return (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-mono text-green-500", children: '{ ' }), entries.map(([key, value], index) => (_jsxs("span", { children: [key, ": ", _jsx(Argument, { arg: value }), index < entries.length - 1 && _jsx(_Fragment, { children: ", " })] }, key))), _jsx("span", { className: "font-mono text-green-500", children: ' }' })] }));
10
+ return (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-mono text-green-500", children: '{ ' }), entries.map(([key, value], index) => (_jsxs("span", { children: [_jsx("span", { className: "font-mono text-green-500", children: key }), _jsx("span", { className: "font-mono text-muted-foreground", children: ":" }), " ", _jsx(Argument, { arg: value }), index < entries.length - 1 && _jsx(_Fragment, { children: ", " })] }, key))), _jsx("span", { className: "font-mono text-green-500", children: ' }' })] }));
11
11
  };
12
12
  export const FunctionCall = ({ topLevelClassName, objectName, functionName, args, callsQuantity }) => {
13
13
  const hasCalls = callsQuantity && callsQuantity > 1;
@@ -1,5 +1,5 @@
1
+ import { TraceEvent as TraceEventType } from '@/types/observability';
1
2
  import React from 'react';
2
- import { TraceEvent as TraceEventType } from '../../../types/observability';
3
3
  export declare const TraceEvent: React.FC<{
4
4
  event: TraceEventType;
5
5
  }>;
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
1
  import { LogEntry } from '@/types/observability';
2
+ import React from 'react';
3
3
  export declare const TraceLogEvent: React.FC<{
4
4
  event: LogEntry;
5
5
  }>;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { LogLevelBadge } from '@/components/logs/log-level-badge';
2
+ import { LogLevelDot } from '@/components/logs/log-level-dot';
3
3
  export const TraceLogEvent = ({ event }) => {
4
- return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LogLevelBadge, { level: event.level }), " ", event.message] }));
4
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LogLevelDot, { level: event.level }), " ", event.message] }));
5
5
  };
@@ -1,2 +1,2 @@
1
- import { TraceGroup } from '../../../types/observability';
1
+ import { TraceGroup } from '@/types/observability';
2
2
  export declare const useGetEndTime: (group: TraceGroup | undefined | null) => number;
@@ -2,6 +2,7 @@ import { Trace } from '@/types/observability';
2
2
  import React from 'react';
3
3
  type Props = {
4
4
  trace: Trace;
5
+ onClose: () => void;
5
6
  };
6
7
  export declare const TraceItemDetail: React.FC<Props>;
7
8
  export {};
@@ -1,10 +1,11 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Sidebar } from '@/components/sidebar/sidebar';
2
3
  import { Badge } from '@/components/ui/badge';
3
- import { Card, CardContent } from '@/components/ui/card';
4
- import { cn, formatDuration } from '@/lib/utils';
4
+ import { formatDuration } from '@/lib/utils';
5
+ import { X } from 'lucide-react';
5
6
  import { memo } from 'react';
6
7
  import { EventIcon } from '../events/event-icon';
7
8
  import { TraceEvent } from '../events/trace-event';
8
- export const TraceItemDetail = memo(({ trace }) => {
9
- return (_jsxs(Card, { className: cn('mt-2', trace.error && 'border-red-800/80 border-solid'), children: [_jsxs(CardContent, { className: "p-4", children: [_jsxs("div", { className: "flex items-center gap-4 text-sm text-muted-foreground mb-4", children: [trace.endTime && _jsxs("span", { children: ["Duration: ", formatDuration(trace.endTime - trace.startTime)] }), _jsx("div", { className: "bg-blue-500 font-bold text-xs px-[4px] py-[2px] rounded-sm text-blue-100", children: trace.entryPoint.type }), trace.correlationId && _jsxs(Badge, { variant: "outline", children: ["Correlated: ", trace.correlationId] })] }), _jsx("div", { className: "pl-6 border-l-1 border-gray-500/40 font-mono text-xs flex flex-col gap-3", children: trace.events.map((event, index) => (_jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute -left-[26px] top-[8px] w-1 h-1 rounded-full bg-emerald-500 outline outline-2 outline-emerald-500/50" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(EventIcon, { event: event }), _jsxs("span", { className: "text-sm font-mono text-muted-foreground", children: ["+", Math.floor(event.timestamp - trace.startTime), "ms"] }), _jsx(TraceEvent, { event: event })] })] }, index))) })] }), trace.error && (_jsxs("div", { className: "p-4 bg-red-800/10", children: [_jsx("div", { className: "text-sm text-red-800 dark:text-red-400 font-semibold", children: trace.error.message }), _jsx("div", { className: "text-sm text-red-800 dark:text-red-400 pl-4", children: trace.error.stack })] }))] }));
9
+ export const TraceItemDetail = memo(({ trace, onClose }) => {
10
+ return (_jsxs(Sidebar, { onClose: onClose, title: "Trace Details", subtitle: `Viewing details from step ${trace.name}`, actions: [{ icon: _jsx(X, {}), onClick: onClose, label: 'Close' }], children: [_jsxs("div", { className: "px-2 w-[800px] overflow-auto", children: [_jsxs("div", { className: "flex items-center gap-4 text-sm text-muted-foreground mb-4", children: [trace.endTime && _jsxs("span", { children: ["Duration: ", formatDuration(trace.endTime - trace.startTime)] }), _jsx("div", { className: "bg-blue-500 font-bold text-xs px-[4px] py-[2px] rounded-sm text-blue-100", children: trace.entryPoint.type }), trace.correlationId && _jsxs(Badge, { variant: "outline", children: ["Correlated: ", trace.correlationId] })] }), _jsx("div", { className: "pl-6 border-l-1 border-gray-500/40 font-mono text-xs flex flex-col gap-3", children: trace.events.map((event, index) => (_jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute -left-[26px] top-[8px] w-1 h-1 rounded-full bg-emerald-500 outline outline-2 outline-emerald-500/50" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(EventIcon, { event: event }), _jsxs("span", { className: "text-sm font-mono text-muted-foreground", children: ["+", Math.floor(event.timestamp - trace.startTime), "ms"] }), _jsx(TraceEvent, { event: event })] })] }, index))) })] }), trace.error && (_jsxs("div", { className: "p-4 bg-red-800/10", children: [_jsx("div", { className: "text-sm text-red-800 dark:text-red-400 font-semibold", children: trace.error.message }), _jsx("div", { className: "text-sm text-red-800 dark:text-red-400 pl-4", children: trace.error.stack })] }))] }));
10
11
  });
@@ -4,6 +4,7 @@ type Props = {
4
4
  trace: Trace;
5
5
  group: TraceGroup;
6
6
  groupEndTime: number;
7
+ onExpand: (traceId: string) => void;
7
8
  };
8
9
  export declare const TraceItem: React.FC<Props>;
9
10
  export {};
@@ -1,20 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from '@/lib/utils';
3
- import { ChevronDown } from 'lucide-react';
4
- import { useState } from 'react';
5
- import { TraceItemDetail } from './trace-item-detail';
6
- export const TraceItem = ({ trace, group, groupEndTime }) => {
7
- const [isExpanded, setIsExpanded] = useState(false);
8
- return (_jsxs("div", { children: [_jsxs("div", { className: "flex flex-row items-center hover:bg-muted/50 p-1 rounded-md cursor-pointer", onClick: () => setIsExpanded(!isExpanded), children: [_jsxs("div", { className: "flex items-center min-w-[200px] w-[200px] h-[20px]", children: [_jsx(ChevronDown, { className: cn('w-[20px] h-4 mr-2 transition-transform duration-50', {
9
- 'rotate-180': isExpanded,
10
- }) }), _jsx("h2", { className: "text-sm font-medium text-muted-foreground", children: trace.name })] }), _jsx("div", { className: "relative w-full", children: _jsx("div", { className: cn('h-[20px] rounded-[4px] cursor-pointer hover:opacity-80 transition-all duration-200', {
11
- 'bg-blue-500': trace.status === 'running',
12
- 'bg-green-500': trace.status === 'completed',
13
- 'bg-red-500': trace.status === 'failed',
14
- }), onClick: () => setIsExpanded(!isExpanded), style: {
15
- marginLeft: `${((trace.startTime - group.startTime) / (groupEndTime - group.startTime)) * 100}%`,
16
- width: trace.endTime
17
- ? `${((trace.endTime - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`
18
- : `${((Date.now() - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`,
19
- } }) })] }), isExpanded && _jsx(TraceItemDetail, { trace: trace })] }));
3
+ export const TraceItem = ({ trace, group, groupEndTime, onExpand }) => {
4
+ return (_jsxs("div", { className: "flex hover:bg-muted-foreground/10 relative cursor-pointer", onClick: () => onExpand(trace.id), children: [_jsx("div", { className: "flex items-center min-w-[200px] max-w-[200px] h-[32px] max-h-[32px] py-4 px-2 text-sm font-semibold text-foreground truncate sticky left-0 dark:bg-[#121212] bg-[#f1f1f1] z-9", children: trace.name }), _jsx("div", { className: "flex w-full flex-row items-center hover:bg-muted/50 rounded-md", children: _jsx("div", { className: "relative w-full h-[32px] flex items-center", children: _jsx("div", { className: cn('h-[24px] rounded-[4px] hover:opacity-80 transition-all duration-200', {
5
+ 'bg-[repeating-linear-gradient(140deg,#BEFE29,#BEFE29_8px,#ABE625_8px,#ABE625_16px)]': trace.status === 'running',
6
+ 'bg-[repeating-linear-gradient(140deg,#2862FE,#2862FE_8px,#2358E5_8px,#2358E5_16px)]': trace.status === 'completed',
7
+ 'bg-[repeating-linear-gradient(140deg,#EA2069,#EA2069_8px,#D41E60_8px,#D41E60_16px)]': trace.status === 'failed',
8
+ }), style: {
9
+ marginLeft: `${((trace.startTime - group.startTime) / (groupEndTime - group.startTime)) * 100}%`,
10
+ width: trace.endTime
11
+ ? `${((trace.endTime - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`
12
+ : `${((Date.now() - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`,
13
+ } }) }) })] }));
20
14
  };
@@ -1,12 +1,8 @@
1
+ import { TraceGroup } from '@/types/observability';
1
2
  import React from 'react';
2
- import { TraceGroup } from '../../types/observability';
3
- export declare const LoadingSpinner: React.FC<{
4
- className: string;
5
- }>;
6
- export declare const TraceStatus: React.FC<{
3
+ type Props = {
7
4
  status: TraceGroup['status'];
8
- name: string;
9
- }>;
10
- export declare const TraceStatusBadge: React.FC<{
11
- status: TraceGroup['status'];
12
- }>;
5
+ duration?: string;
6
+ };
7
+ export declare const TraceStatusBadge: React.FC<Props>;
8
+ export {};
@@ -1,33 +1,11 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cva } from 'class-variance-authority';
3
- import { CheckCircle, Clock, XCircle } from 'lucide-react';
4
- import { cn } from '../../lib/utils';
5
- export const LoadingSpinner = ({ className }) => {
6
- return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: cn('animate-spin', className), children: _jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
7
- };
8
- const getStatusIcon = (status) => {
9
- switch (status) {
10
- case 'running':
11
- case 'active':
12
- return _jsx(LoadingSpinner, { className: "w-4 h-4 text-blue-500" });
13
- case 'completed':
14
- return _jsx(CheckCircle, { className: "w-4 h-4 text-green-500" });
15
- case 'failed':
16
- return _jsx(XCircle, { className: "w-4 h-4 text-red-500" });
17
- case 'stalled':
18
- return _jsx(Clock, { className: "w-4 h-4 text-yellow-500" });
19
- default:
20
- return _jsx(Clock, { className: "w-4 h-4 text-gray-500" });
21
- }
22
- };
23
- const statusVariants = cva('inline-flex items-center rounded-lg px-3 py-[2px] uppercase text-[10px] font-bold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', {
3
+ const statusVariants = cva('inline-flex items-center rounded-full px-4 py-1 text-xs font-bold transition-colors', {
24
4
  variants: {
25
5
  status: {
26
- running: 'dark:bg-blue-800/30 dark:text-blue-500 bg-blue-100 text-blue-700',
27
- active: 'dark:bg-blue-800/30 dark:text-blue-500 bg-blue-100 text-blue-700',
28
- completed: 'dark:bg-green-800/30 dark:text-green-500 bg-green-100 text-green-700',
29
- failed: 'dark:bg-red-800/30 dark:text-red-500 bg-red-100 text-red-700',
30
- stalled: 'dark:bg-yellow-800/30 dark:text-yellow-500 bg-yellow-100 text-yellow-700',
6
+ running: 'dark:bg-accent-100 dark:text-accent-1000 bg-accent-200 text-accent-900 capitalize',
7
+ completed: 'bg-accent-1000 text-white',
8
+ failed: 'bg-destructive/10 text-destructive capitalize',
31
9
  default: 'dark:bg-gray-800/30 dark:text-gray-500 bg-gray-100 text-gray-800',
32
10
  },
33
11
  },
@@ -35,9 +13,6 @@ const statusVariants = cva('inline-flex items-center rounded-lg px-3 py-[2px] up
35
13
  status: 'default',
36
14
  },
37
15
  });
38
- export const TraceStatus = ({ status, name }) => {
39
- return (_jsxs("div", { className: "flex items-center gap-2", children: [getStatusIcon(status), _jsx("span", { className: "font-medium text-sm", children: name })] }));
40
- };
41
- export const TraceStatusBadge = ({ status }) => {
42
- return _jsx("div", { className: statusVariants({ status }), children: status });
16
+ export const TraceStatusBadge = ({ status, duration }) => {
17
+ return _jsx("div", { className: statusVariants({ status }), children: duration && status !== 'failed' ? duration : status });
43
18
  };
@@ -1,8 +1,12 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo } from 'react';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useGlobalStore } from '@/stores/use-global-store';
3
3
  import { useStreamGroup, useStreamItem } from '@motiadev/stream-client-react';
4
- import { TraceItem } from './trace-item/trace-item';
4
+ import { Button } from '@motiadev/ui';
5
+ import { Minus, Plus } from 'lucide-react';
6
+ import { memo, useMemo, useState } from 'react';
5
7
  import { useGetEndTime } from './hooks/use-get-endtime';
8
+ import { TraceItem } from './trace-item/trace-item';
9
+ import { TraceItemDetail } from './trace-item/trace-item-detail';
6
10
  export const TraceTimeline = memo(({ groupId }) => {
7
11
  const { data: group } = useStreamItem({
8
12
  streamName: 'motia-trace-group',
@@ -11,7 +15,15 @@ export const TraceTimeline = memo(({ groupId }) => {
11
15
  });
12
16
  const { data } = useStreamGroup({ streamName: 'motia-trace', groupId });
13
17
  const endTime = useGetEndTime(group);
18
+ const [zoom, setZoom] = useState(1);
19
+ const selectedTraceId = useGlobalStore((state) => state.selectedTraceId);
20
+ const selectTraceId = useGlobalStore((state) => state.selectTraceId);
21
+ const selectedTrace = useMemo(() => data?.find((trace) => trace.id === selectedTraceId), [data, selectedTraceId]);
22
+ const zoomMinus = () => {
23
+ if (zoom > 0.5)
24
+ setZoom(zoom - 0.1);
25
+ };
14
26
  if (!group)
15
27
  return null;
16
- return (_jsxs("div", { className: "flex flex-col p-2", children: [_jsxs("div", { className: "flex flex-row items-center px-2", children: [_jsx("div", { className: "flex items-center min-w-[200px] w-[200px] h-[20px]" }), _jsx("div", { className: "flex flex-1 relative", children: _jsxs("div", { className: "flex justify-between font-mono pb-2 w-full text-xs text-muted-foreground border-b border-blue-500", children: [_jsx("span", { children: "0ms" }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.25), "ms"] }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.5), "ms"] }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.75), "ms"] }), _jsxs("span", { children: [Math.floor(endTime - group.startTime), "ms"] }), _jsxs("div", { className: "absolute bottom-[-4px] w-full flex justify-between", children: [_jsx("span", { className: "w-[1px] h-2 bg-blue-500" }), _jsx("span", { className: "w-[1px] h-2 bg-blue-500" }), _jsx("span", { className: "w-[1px] h-2 bg-blue-500" }), _jsx("span", { className: "w-[1px] h-2 bg-blue-500" }), _jsx("span", { className: "w-[1px] h-2 bg-blue-500" })] })] }) })] }), _jsx("div", { className: "h-full overflow-auto p-2 space-y-2", children: data?.map((trace) => _jsx(TraceItem, { trace: trace, group: group, groupEndTime: endTime }, trace.id)) })] }));
28
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex flex-col flex-1 overflow-x-auto h-full relative", children: _jsxs("div", { className: "flex flex-col items-center min-w-full sticky top-0", style: { width: `${zoom * 1000}px` }, children: [_jsxs("div", { className: "flex flex-1 w-full sticky top-0 bg-background z-10", children: [_jsxs("div", { className: "w-full min-h-[37px] h-[37px] min-w-[200px] max-w-[200px] flex items-center justify-center gap-2 sticky left-0 top-0 dark:bg-[#121212] bg-[#f1f1f1]", children: [_jsx(Button, { variant: "icon", size: "sm", className: "px-2", onClick: zoomMinus, children: _jsx(Minus, { className: "w-4 h-4 cursor-pointer" }) }), _jsxs("span", { className: "text-sm font-bold text-muted-foreground", children: [Math.floor(zoom * 100), "%"] }), _jsx(Button, { variant: "icon", size: "sm", className: "px-2", onClick: () => setZoom(zoom + 0.1), children: _jsx(Plus, { className: "w-4 h-4 cursor-pointer" }) })] }), _jsxs("div", { className: "flex justify-between font-mono p-2 w-full text-xs text-muted-foreground dark:bg-[#131313] bg-[#f1f1f1]", children: [_jsx("span", { children: "0ms" }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.25), "ms"] }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.5), "ms"] }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.75), "ms"] }), _jsxs("span", { children: [Math.floor(endTime - group.startTime), "ms"] }), _jsxs("div", { className: "absolute bottom-[-4px] w-full flex justify-between", children: [_jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" })] })] })] }), _jsx("div", { className: "flex flex-col w-full h-full", children: data?.map((trace) => (_jsx(TraceItem, { trace: trace, group: group, groupEndTime: endTime, onExpand: selectTraceId }, trace.id))) })] }) }), selectedTrace && _jsx(TraceItemDetail, { trace: selectedTrace, onClose: () => selectTraceId(undefined) })] }));
17
29
  });
@@ -1,8 +1,8 @@
1
- import React from 'react';
2
1
  import { TraceGroup } from '@/types/observability';
2
+ import React from 'react';
3
3
  interface Props {
4
4
  groups: TraceGroup[];
5
- selectedGroupId: string | null;
5
+ selectedGroupId?: string;
6
6
  onGroupSelect: (group: TraceGroup) => void;
7
7
  }
8
8
  export declare const TracesGroups: React.FC<Props>;
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo } from 'react';
3
- import { Card, CardContent } from '@/components/ui/card';
2
+ import { cn } from '@/lib/utils';
4
3
  import { formatDistanceToNow } from 'date-fns';
5
- import { TraceStatus, TraceStatusBadge } from './trace-status';
4
+ import { memo } from 'react';
5
+ import { TraceStatusBadge } from './trace-status';
6
6
  export const TracesGroups = memo(({ groups, selectedGroupId, onGroupSelect }) => {
7
7
  const formatDuration = (duration) => {
8
8
  if (!duration)
@@ -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 (_jsxs("div", { className: "overflow-auto p-4 space-y-4 ", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h2", { className: "text-lg font-semibold", children: "Traces" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [groups.length, " total"] })] }), groups.length > 0 && (_jsx("div", { className: "space-y-2", children: [...groups].reverse().map((group) => (_jsx(Card, { "data-testid": `trace-${group.id}`, className: `cursor-pointer transition-colors hover:bg-muted/50 ${selectedGroupId === group.id ? 'ring-2 ring-primary' : ''}`, onClick: () => onGroupSelect(group), children: _jsxs(CardContent, { className: "p-3", children: [_jsxs("div", { className: "flex flex-row justify-between mb-2", children: [_jsx(TraceStatus, { status: group.status, name: group.name }), _jsx(TraceStatusBadge, { status: group.status })] }), _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-semibold 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: [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))) })) }));
15
15
  });
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TraceTimeline } from '@/components/observability/trace-timeline';
3
+ import { useStreamGroup } from '@motiadev/stream-client-react';
4
+ import { TracesGroups } from '@/components/observability/traces-groups';
5
+ import { useGlobalStore } from '../../stores/use-global-store';
6
+ export const TracesPage = () => {
7
+ const selectedGroupId = useGlobalStore((state) => state.selectedTraceGroupId);
8
+ const selectTraceGroupId = useGlobalStore((state) => state.selectTraceGroupId);
9
+ const { data } = useStreamGroup({ streamName: 'motia-trace-group', groupId: 'default' });
10
+ const handleGroupSelect = (group) => selectTraceGroupId(group.id);
11
+ return (_jsxs("div", { className: "flex flex-1 overflow-hidden h-full", children: [_jsx("div", { className: "max-w-1/3 border-r border-border overflow-auto h-full", "data-testid": "traces-container", children: _jsx(TracesGroups, { groups: data, selectedGroupId: selectedGroupId, onGroupSelect: handleGroupSelect }) }), _jsxs("div", { className: "flex-2 overflow-auto", "data-testid": "trace-details", children: [selectedGroupId && _jsx(TraceTimeline, { groupId: selectedGroupId }), !selectedGroupId && (_jsx("div", { className: "flex items-center justify-center h-full text-muted-foreground", children: "Select a trace or trace group to view the timeline" }))] })] }));
12
+ };
@@ -0,0 +1,8 @@
1
+ import { type PanelProps } from '@motiadev/ui';
2
+ import { FC } from 'react';
3
+ export declare const APP_SIDEBAR_CONTAINER_ID = "app-sidebar-container";
4
+ export type SidebarProps = PanelProps & {
5
+ onClose: () => void;
6
+ initialWidth?: number;
7
+ };
8
+ export declare const Sidebar: FC<SidebarProps>;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Panel } from '@motiadev/ui';
3
+ import { createPortal } from 'react-dom';
4
+ import { useEffect, useMemo } from 'react';
5
+ import { useResizable } from 'react-use-resizable';
6
+ import { Equal } from 'lucide-react';
7
+ export const APP_SIDEBAR_CONTAINER_ID = 'app-sidebar-container';
8
+ const CLOSE_PREVIOUS_SIDEBAR_EVENT = 'close-previous-sidebar';
9
+ export const Sidebar = ({ initialWidth, onClose, ...props }) => {
10
+ const sidebarId = useMemo(() => Symbol(), []);
11
+ const { getRootProps, getHandleProps } = useResizable({
12
+ lockVertical: true,
13
+ initialWidth: initialWidth ?? 400,
14
+ initialHeight: '100%',
15
+ onDragStart: () => {
16
+ document.body.style.userSelect = 'none';
17
+ },
18
+ onDragEnd: () => {
19
+ document.body.style.userSelect = '';
20
+ },
21
+ });
22
+ useEffect(() => {
23
+ const event = new CustomEvent(CLOSE_PREVIOUS_SIDEBAR_EVENT, { detail: { sidebarId } });
24
+ window.dispatchEvent(event);
25
+ const handleClose = (e) => {
26
+ const customEvent = e;
27
+ if (customEvent.detail.sidebarId !== sidebarId) {
28
+ onClose();
29
+ }
30
+ };
31
+ window.addEventListener(CLOSE_PREVIOUS_SIDEBAR_EVENT, handleClose);
32
+ return () => {
33
+ window.removeEventListener(CLOSE_PREVIOUS_SIDEBAR_EVENT, handleClose);
34
+ };
35
+ }, [sidebarId, onClose]);
36
+ return createPortal(_jsxs("div", { ...getRootProps(), className: "pr-2 py-2 relative", children: [_jsx("div", { ...getHandleProps({
37
+ reverse: true,
38
+ }), className: "flex h-6 w-6 items-center justify-center rounded-full bg-background border border-border absolute top-1/2 -translate-y-1/2 -left-4 z-20", children: _jsx(Equal, { className: "rotate-90 w-4 h-4 text-muted-foreground" }) }), _jsx(Panel, { ...props, className: "max-h-[calc(100vh-80px)] h-full" })] }), document.querySelector(`#${APP_SIDEBAR_CONTAINER_ID}`));
39
+ };
@@ -1,3 +1,7 @@
1
- export declare const useGetTraces: () => string[];
2
- export declare const useGetFields: (traceId: string | undefined) => string[];
3
- export declare const useGetValues: (traceId: string | undefined, field: string | undefined) => any;
1
+ export interface StateItem {
2
+ groupId: string;
3
+ key: string;
4
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null';
5
+ value: string | number | boolean | object | unknown[] | null;
6
+ }
7
+ export declare const useGetStateItems: () => StateItem[];
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useState } from 'react';
2
- export const useGetTraces = () => {
3
- const [traces, setTraces] = useState([]);
2
+ export const useGetStateItems = () => {
3
+ const [items, setItems] = useState([]);
4
4
  useEffect(() => {
5
5
  fetch('/motia/state')
6
6
  .then(async (res) => {
@@ -11,36 +11,8 @@ export const useGetTraces = () => {
11
11
  throw await res.json();
12
12
  }
13
13
  })
14
- .then(setTraces)
15
- .catch((err) => {
16
- console.error(err);
17
- });
14
+ .then(setItems)
15
+ .catch((err) => console.error(err));
18
16
  }, []);
19
- return traces;
20
- };
21
- export const useGetFields = (traceId) => {
22
- const [fields, setFields] = useState([]);
23
- useEffect(() => {
24
- if (!traceId) {
25
- setFields([]);
26
- }
27
- else {
28
- fetch(`/motia/state/${traceId}`)
29
- .then((res) => res.json())
30
- .then((data) => setFields(data));
31
- }
32
- }, [traceId]);
33
- return fields;
34
- };
35
- export const useGetValues = (traceId, field) => {
36
- const [values, setValues] = useState();
37
- useEffect(() => {
38
- setValues(undefined);
39
- if (traceId && field) {
40
- fetch(`/motia/state/${traceId}/${field}`)
41
- .then((res) => res.json())
42
- .then((data) => setValues(data));
43
- }
44
- }, [traceId, field]);
45
- return values;
17
+ return items;
46
18
  };
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
+ import { StateItem } from './hooks/states-hooks';
2
3
  type Props = {
3
- state?: any;
4
+ state: StateItem;
4
5
  onClose: () => void;
5
6
  };
6
7
  export declare const StateDetail: React.FC<Props>;
@@ -1,12 +1,18 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Sidebar } from '@/components/sidebar/sidebar';
3
+ import { Braces, X } from 'lucide-react';
4
+ import { useState } from 'react';
5
+ import JsonView from 'react18-json-view';
2
6
  import { StateValue } from './state-value';
3
- import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../ui/sheet';
4
7
  export const StateDetail = ({ state, onClose }) => {
5
- const isOpen = !!state;
6
- const onOpenChange = (open) => {
7
- if (!open) {
8
- onClose();
9
- }
10
- };
11
- return (_jsx(Sheet, { open: isOpen, onOpenChange: onOpenChange, children: _jsxs(SheetContent, { className: "flex flex-col h-full", children: [_jsxs(SheetHeader, { children: [_jsx(SheetTitle, { children: "State details" }), _jsx(SheetDescription, { children: "State details and application." })] }), _jsx("div", { className: "font-mono overflow-y-auto", children: state && (_jsx("div", { className: "flex flex-col gap-2", children: _jsx(StateValue, { value: state, isRoot: true }) })) })] }) }));
8
+ const [isCodeEnabled, setIsCodeEnabled] = useState(false);
9
+ return (_jsx(Sidebar, { onClose: onClose, title: "State details", subtitle: `${state.groupId} ${state.key}`, actions: [
10
+ {
11
+ active: isCodeEnabled,
12
+ icon: _jsx(Braces, {}),
13
+ onClick: () => setIsCodeEnabled(!isCodeEnabled),
14
+ label: 'Code',
15
+ },
16
+ { icon: _jsx(X, {}), onClick: onClose, label: 'Close' },
17
+ ], children: _jsx("div", { className: "flex flex-col gap-2", children: isCodeEnabled ? _jsx(JsonView, { src: state.value }) : _jsx(StateValue, { value: state.value, isRoot: true }) }) }));
12
18
  };