@motiadev/workbench 0.13.1-beta.163-660633 → 0.13.1-beta.163-584961

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 (236) hide show
  1. package/dist/index.d.ts +10 -189
  2. package/dist/index.html +1 -1
  3. package/dist/index.js +7 -1065
  4. package/dist/middleware.d.ts +8 -66
  5. package/dist/middleware.js +86 -694
  6. package/dist/motia-plugin/__tests__/generator.test.d.ts +1 -0
  7. package/dist/motia-plugin/__tests__/generator.test.js +97 -0
  8. package/dist/motia-plugin/__tests__/resolver.test.d.ts +1 -0
  9. package/dist/motia-plugin/__tests__/resolver.test.js +64 -0
  10. package/dist/motia-plugin/__tests__/validator.test.d.ts +1 -0
  11. package/dist/motia-plugin/__tests__/validator.test.js +59 -0
  12. package/dist/motia-plugin/generator.d.ts +78 -0
  13. package/dist/motia-plugin/{generator.ts → generator.js} +35 -37
  14. package/dist/motia-plugin/hmr.d.ts +22 -0
  15. package/dist/motia-plugin/hmr.js +100 -0
  16. package/dist/motia-plugin/index.d.ts +3 -0
  17. package/dist/motia-plugin/index.js +153 -0
  18. package/dist/motia-plugin/{resolver.ts → resolver.d.ts} +5 -38
  19. package/dist/motia-plugin/resolver.js +92 -0
  20. package/dist/motia-plugin/types.d.ts +169 -0
  21. package/dist/motia-plugin/types.js +36 -0
  22. package/dist/motia-plugin/{utils.ts → utils.d.ts} +4 -17
  23. package/dist/motia-plugin/utils.js +75 -0
  24. package/dist/motia-plugin/validator.d.ts +19 -0
  25. package/dist/motia-plugin/validator.js +163 -0
  26. package/dist/src/App.d.ts +2 -0
  27. package/dist/src/App.js +35 -0
  28. package/dist/src/components/NotFoundPage.d.ts +1 -0
  29. package/dist/src/components/NotFoundPage.js +3 -0
  30. package/dist/src/components/bottom-panel.d.ts +1 -0
  31. package/dist/src/components/bottom-panel.js +15 -0
  32. package/dist/src/components/flow/base-edge.d.ts +3 -0
  33. package/dist/src/components/flow/base-edge.js +39 -0
  34. package/dist/src/components/flow/flow-loader.d.ts +1 -0
  35. package/dist/src/components/flow/flow-loader.js +4 -0
  36. package/dist/src/components/flow/flow-page.d.ts +1 -0
  37. package/dist/src/components/flow/flow-page.js +25 -0
  38. package/dist/src/components/flow/flow-tab-menu-item.d.ts +1 -0
  39. package/dist/src/components/flow/flow-tab-menu-item.js +18 -0
  40. package/dist/src/components/flow/flow-view.d.ts +12 -0
  41. package/dist/src/components/flow/flow-view.js +22 -0
  42. package/dist/src/components/flow/hooks/use-get-flow-state.d.ts +10 -0
  43. package/dist/src/components/flow/hooks/use-get-flow-state.js +133 -0
  44. package/dist/src/components/flow/hooks/use-save-workflow-config.d.ts +2 -0
  45. package/dist/src/components/flow/hooks/use-save-workflow-config.js +22 -0
  46. package/dist/src/components/flow/node-organizer.d.ts +10 -0
  47. package/dist/src/components/flow/node-organizer.js +82 -0
  48. package/dist/src/components/flow/nodes/api-flow-node.d.ts +2 -0
  49. package/dist/src/components/flow/nodes/api-flow-node.js +5 -0
  50. package/dist/src/components/flow/nodes/cron-flow-node.d.ts +2 -0
  51. package/dist/src/components/flow/nodes/cron-flow-node.js +5 -0
  52. package/dist/src/components/flow/nodes/event-flow-node.d.ts +2 -0
  53. package/dist/src/components/flow/nodes/event-flow-node.js +5 -0
  54. package/dist/src/components/flow/nodes/noop-flow-node.d.ts +2 -0
  55. package/dist/src/components/flow/nodes/noop-flow-node.js +5 -0
  56. package/dist/src/components/header/deploy-button.d.ts +1 -0
  57. package/dist/src/components/header/deploy-button.js +28 -0
  58. package/dist/src/components/header/header.d.ts +2 -0
  59. package/dist/src/components/header/header.js +23 -0
  60. package/dist/src/components/root-motia.d.ts +2 -0
  61. package/dist/src/components/root-motia.js +7 -0
  62. package/dist/src/components/top-panel.d.ts +1 -0
  63. package/dist/src/components/top-panel.js +15 -0
  64. package/dist/src/components/tutorial/engine/tutorial-engine.d.ts +12 -0
  65. package/dist/src/components/tutorial/engine/tutorial-engine.js +36 -0
  66. package/dist/src/components/tutorial/engine/tutorial-types.d.ts +22 -0
  67. package/dist/src/components/tutorial/engine/tutorial-types.js +1 -0
  68. package/dist/src/components/tutorial/engine/workbench-xpath.d.ts +45 -0
  69. package/dist/src/components/tutorial/engine/workbench-xpath.js +45 -0
  70. package/dist/src/components/tutorial/hooks/tutorial-utils.d.ts +1 -0
  71. package/dist/src/components/tutorial/hooks/tutorial-utils.js +17 -0
  72. package/dist/src/components/tutorial/hooks/use-tutorial-engine.d.ts +15 -0
  73. package/dist/src/components/tutorial/hooks/use-tutorial-engine.js +183 -0
  74. package/dist/src/components/tutorial/hooks/use-tutorial.d.ts +5 -0
  75. package/dist/src/components/tutorial/hooks/use-tutorial.js +10 -0
  76. package/dist/src/components/tutorial/tutorial-button.d.ts +2 -0
  77. package/dist/src/components/tutorial/tutorial-button.js +21 -0
  78. package/dist/src/components/tutorial/tutorial-step.d.ts +14 -0
  79. package/dist/src/components/tutorial/tutorial-step.js +19 -0
  80. package/dist/src/components/tutorial/tutorial.css +2 -2
  81. package/dist/src/components/tutorial/tutorial.d.ts +2 -0
  82. package/dist/src/components/tutorial/tutorial.js +32 -0
  83. package/dist/src/components/ui/json-editor.d.ts +12 -0
  84. package/dist/src/components/ui/json-editor.js +35 -0
  85. package/dist/src/components/ui/table.d.ts +10 -0
  86. package/dist/src/components/ui/table.js +20 -0
  87. package/dist/src/components/ui/theme-toggle.d.ts +2 -0
  88. package/dist/src/components/ui/theme-toggle.js +19 -0
  89. package/dist/src/components/ui/tooltip.d.ts +6 -0
  90. package/dist/src/components/ui/tooltip.js +3 -0
  91. package/dist/src/hooks/use-debounced.d.ts +1 -0
  92. package/dist/src/hooks/use-debounced.js +18 -0
  93. package/dist/src/hooks/use-fetch-flows.d.ts +1 -0
  94. package/dist/src/hooks/use-fetch-flows.js +26 -0
  95. package/dist/src/hooks/use-mobile.d.ts +1 -0
  96. package/dist/src/hooks/use-mobile.js +15 -0
  97. package/dist/src/hooks/use-update-handle-positions.d.ts +10 -0
  98. package/dist/src/hooks/use-update-handle-positions.js +35 -0
  99. package/dist/src/index.css +5 -5
  100. package/dist/src/lib/__tests__/utils.test.d.ts +1 -0
  101. package/dist/src/lib/__tests__/utils.test.js +94 -0
  102. package/dist/src/lib/motia-analytics.d.ts +38 -0
  103. package/dist/src/lib/motia-analytics.js +132 -0
  104. package/dist/src/lib/plugins.d.ts +2 -0
  105. package/dist/src/lib/plugins.js +105 -0
  106. package/dist/src/lib/utils.d.ts +7 -0
  107. package/dist/src/lib/utils.js +34 -0
  108. package/dist/src/main.d.ts +2 -0
  109. package/dist/src/main.js +17 -0
  110. package/dist/src/project-view-mode.d.ts +1 -0
  111. package/dist/src/project-view-mode.js +20 -0
  112. package/dist/src/publicComponents/api-node.d.ts +5 -0
  113. package/dist/src/publicComponents/api-node.js +5 -0
  114. package/dist/src/publicComponents/base-node/base-handle.d.ts +9 -0
  115. package/dist/src/publicComponents/base-node/base-handle.js +8 -0
  116. package/dist/src/publicComponents/base-node/base-node.d.ts +15 -0
  117. package/dist/src/publicComponents/base-node/base-node.js +30 -0
  118. package/dist/src/publicComponents/base-node/code-display.d.ts +9 -0
  119. package/dist/src/publicComponents/base-node/code-display.js +64 -0
  120. package/dist/src/publicComponents/base-node/emits.d.ts +5 -0
  121. package/dist/src/publicComponents/base-node/emits.js +5 -0
  122. package/dist/src/publicComponents/base-node/feature-card.d.ts +10 -0
  123. package/dist/src/publicComponents/base-node/feature-card.js +5 -0
  124. package/dist/src/publicComponents/base-node/language-indicator.d.ts +10 -0
  125. package/dist/src/publicComponents/base-node/language-indicator.js +29 -0
  126. package/dist/src/publicComponents/base-node/node-header.d.ts +13 -0
  127. package/dist/src/publicComponents/base-node/node-header.js +30 -0
  128. package/dist/src/publicComponents/base-node/node-sidebar.d.ts +14 -0
  129. package/dist/src/publicComponents/base-node/node-sidebar.js +9 -0
  130. package/dist/src/publicComponents/base-node/subscribe.d.ts +4 -0
  131. package/dist/src/publicComponents/base-node/subscribe.js +4 -0
  132. package/dist/src/publicComponents/cron-node.d.ts +4 -0
  133. package/dist/src/publicComponents/cron-node.js +6 -0
  134. package/dist/src/publicComponents/event-node.d.ts +4 -0
  135. package/dist/src/publicComponents/event-node.js +5 -0
  136. package/dist/src/publicComponents/node-props.d.ts +21 -0
  137. package/dist/src/publicComponents/node-props.js +1 -0
  138. package/dist/src/publicComponents/noop-node.d.ts +4 -0
  139. package/dist/src/publicComponents/noop-node.js +5 -0
  140. package/dist/src/setupTests.d.ts +1 -0
  141. package/dist/src/setupTests.js +1 -0
  142. package/dist/src/stores/use-app-tabs-store.d.ts +16 -0
  143. package/dist/src/stores/use-app-tabs-store.js +31 -0
  144. package/dist/src/stores/use-flow-store.d.ts +21 -0
  145. package/dist/src/stores/use-flow-store.js +16 -0
  146. package/dist/src/stores/use-global-store.d.ts +18 -0
  147. package/dist/src/stores/use-global-store.js +12 -0
  148. package/dist/src/stores/use-motia-config-store.d.ts +12 -0
  149. package/dist/src/stores/use-motia-config-store.js +24 -0
  150. package/dist/src/stores/use-tabs-store.d.ts +19 -0
  151. package/dist/src/stores/use-tabs-store.js +22 -0
  152. package/dist/src/system-view-mode.d.ts +1 -0
  153. package/dist/src/system-view-mode.js +10 -0
  154. package/dist/src/types/endpoint.d.ts +14 -0
  155. package/dist/src/types/endpoint.js +1 -0
  156. package/dist/src/types/file.d.ts +7 -0
  157. package/dist/src/types/file.js +1 -0
  158. package/dist/src/types/flow.d.ts +115 -0
  159. package/dist/src/types/flow.js +1 -0
  160. package/dist/tsconfig.app.tsbuildinfo +1 -0
  161. package/dist/tsconfig.node.tsbuildinfo +1 -0
  162. package/package.json +51 -55
  163. package/dist/motia-plugin/__tests__/generator.test.ts +0 -129
  164. package/dist/motia-plugin/__tests__/resolver.test.ts +0 -82
  165. package/dist/motia-plugin/__tests__/validator.test.ts +0 -71
  166. package/dist/motia-plugin/hmr.ts +0 -123
  167. package/dist/motia-plugin/index.ts +0 -183
  168. package/dist/motia-plugin/types.ts +0 -198
  169. package/dist/motia-plugin/validator.ts +0 -197
  170. package/dist/src/App.tsx +0 -41
  171. package/dist/src/components/NotFoundPage.tsx +0 -11
  172. package/dist/src/components/bottom-panel.tsx +0 -39
  173. package/dist/src/components/flow/base-edge.tsx +0 -61
  174. package/dist/src/components/flow/flow-loader.tsx +0 -3
  175. package/dist/src/components/flow/flow-page.tsx +0 -75
  176. package/dist/src/components/flow/flow-tab-menu-item.tsx +0 -50
  177. package/dist/src/components/flow/flow-view.tsx +0 -66
  178. package/dist/src/components/flow/hooks/use-get-flow-state.tsx +0 -171
  179. package/dist/src/components/flow/hooks/use-save-workflow-config.ts +0 -25
  180. package/dist/src/components/flow/node-organizer.tsx +0 -103
  181. package/dist/src/components/flow/nodes/api-flow-node.tsx +0 -6
  182. package/dist/src/components/flow/nodes/cron-flow-node.tsx +0 -6
  183. package/dist/src/components/flow/nodes/event-flow-node.tsx +0 -6
  184. package/dist/src/components/flow/nodes/noop-flow-node.tsx +0 -6
  185. package/dist/src/components/header/deploy-button.tsx +0 -110
  186. package/dist/src/components/header/header.tsx +0 -39
  187. package/dist/src/components/root-motia.tsx +0 -10
  188. package/dist/src/components/top-panel.tsx +0 -40
  189. package/dist/src/components/tutorial/engine/tutorial-engine.ts +0 -26
  190. package/dist/src/components/tutorial/engine/tutorial-types.ts +0 -26
  191. package/dist/src/components/tutorial/engine/workbench-xpath.ts +0 -53
  192. package/dist/src/components/tutorial/hooks/tutorial-utils.ts +0 -26
  193. package/dist/src/components/tutorial/hooks/use-tutorial-engine.ts +0 -213
  194. package/dist/src/components/tutorial/hooks/use-tutorial.ts +0 -14
  195. package/dist/src/components/tutorial/tutorial-button.tsx +0 -46
  196. package/dist/src/components/tutorial/tutorial-step.tsx +0 -82
  197. package/dist/src/components/tutorial/tutorial.tsx +0 -59
  198. package/dist/src/components/ui/json-editor.tsx +0 -68
  199. package/dist/src/components/ui/table.tsx +0 -75
  200. package/dist/src/components/ui/theme-toggle.tsx +0 -54
  201. package/dist/src/components/ui/tooltip.tsx +0 -26
  202. package/dist/src/hooks/use-debounced.ts +0 -22
  203. package/dist/src/hooks/use-fetch-flows.ts +0 -33
  204. package/dist/src/hooks/use-mobile.ts +0 -19
  205. package/dist/src/hooks/use-update-handle-positions.ts +0 -42
  206. package/dist/src/lib/__tests__/utils.test.ts +0 -110
  207. package/dist/src/lib/motia-analytics.ts +0 -140
  208. package/dist/src/lib/plugins.tsx +0 -132
  209. package/dist/src/lib/utils.ts +0 -37
  210. package/dist/src/main.tsx +0 -30
  211. package/dist/src/project-view-mode.tsx +0 -32
  212. package/dist/src/publicComponents/api-node.tsx +0 -26
  213. package/dist/src/publicComponents/base-node/base-handle.tsx +0 -50
  214. package/dist/src/publicComponents/base-node/base-node.tsx +0 -114
  215. package/dist/src/publicComponents/base-node/code-display.tsx +0 -119
  216. package/dist/src/publicComponents/base-node/emits.tsx +0 -17
  217. package/dist/src/publicComponents/base-node/feature-card.tsx +0 -32
  218. package/dist/src/publicComponents/base-node/language-indicator.tsx +0 -131
  219. package/dist/src/publicComponents/base-node/node-header.tsx +0 -49
  220. package/dist/src/publicComponents/base-node/node-sidebar.tsx +0 -41
  221. package/dist/src/publicComponents/base-node/subscribe.tsx +0 -13
  222. package/dist/src/publicComponents/cron-node.tsx +0 -24
  223. package/dist/src/publicComponents/event-node.tsx +0 -20
  224. package/dist/src/publicComponents/node-props.tsx +0 -15
  225. package/dist/src/publicComponents/noop-node.tsx +0 -19
  226. package/dist/src/setupTests.ts +0 -1
  227. package/dist/src/stores/use-app-tabs-store.ts +0 -49
  228. package/dist/src/stores/use-flow-store.ts +0 -31
  229. package/dist/src/stores/use-global-store.ts +0 -24
  230. package/dist/src/stores/use-motia-config-store.ts +0 -36
  231. package/dist/src/stores/use-tabs-store.ts +0 -34
  232. package/dist/src/system-view-mode.tsx +0 -28
  233. package/dist/src/types/endpoint.ts +0 -12
  234. package/dist/src/types/file.ts +0 -7
  235. package/dist/src/types/flow.ts +0 -103
  236. package/eslint.config.cjs +0 -22
@@ -1,54 +0,0 @@
1
- import { cn, type Theme, useThemeStore } from '@motiadev/ui'
2
- import { Moon, Sun } from 'lucide-react'
3
- import type React from 'react'
4
- import { useEffect } from 'react'
5
-
6
- export const ThemeToggle: React.FC = () => {
7
- const theme = useThemeStore((state) => state.theme)
8
- const setTheme = useThemeStore((state) => state.setTheme)
9
-
10
- const toggleTheme = () => {
11
- setTheme(theme === 'light' ? 'dark' : 'light')
12
- }
13
-
14
- useEffect(() => {
15
- const url = new URL(window.location.href)
16
- const colorScheme = url.searchParams.get('color-scheme') as Theme
17
- if (colorScheme) {
18
- setTheme(colorScheme)
19
- }
20
- }, [setTheme])
21
-
22
- return (
23
- <button
24
- onClick={toggleTheme}
25
- 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"
26
- aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} theme`}
27
- >
28
- <div
29
- className={cn(
30
- 'absolute w-6 h-6 bg-background border border-border rounded-full shadow-sm transition-transform duration-200 ease-in-out',
31
- theme === 'dark' ? 'translate-x-8' : 'translate-x-0',
32
- )}
33
- />
34
-
35
- <div className="flex items-center justify-center w-6 h-6 z-10">
36
- <Sun
37
- className={cn(
38
- 'h-3.5 w-3.5 transition-colors duration-200',
39
- theme === 'light' ? 'text-foreground' : 'text-muted-foreground',
40
- )}
41
- />
42
- </div>
43
-
44
- <div className="flex items-center justify-center w-6 h-6 z-10 ml-2">
45
- <Moon
46
- className={cn(
47
- 'h-3.5 w-3.5 transition-colors duration-200',
48
- theme === 'dark' ? 'text-foreground' : 'text-muted-foreground',
49
- )}
50
- />
51
- </div>
52
- </button>
53
- )
54
- }
@@ -1,26 +0,0 @@
1
- import * as TooltipPrimitive from '@radix-ui/react-tooltip'
2
- import type { ReactNode } from 'react'
3
-
4
- export const Tooltip = ({
5
- children,
6
- content,
7
- disabled,
8
- }: {
9
- children: ReactNode
10
- content: string | ReactNode
11
- disabled?: boolean
12
- }) => (
13
- <TooltipPrimitive.Provider disableHoverableContent={disabled}>
14
- <TooltipPrimitive.Root>
15
- <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
16
- <TooltipPrimitive.Portal>
17
- <TooltipPrimitive.Content className="TooltipContent" side="bottom">
18
- <div className="p-2 bg-background text-popover-foreground text-sm rounded-lg border border-light-800">
19
- {content}
20
- </div>
21
- <TooltipPrimitive.Arrow className="TooltipArrow" />
22
- </TooltipPrimitive.Content>
23
- </TooltipPrimitive.Portal>
24
- </TooltipPrimitive.Root>
25
- </TooltipPrimitive.Provider>
26
- )
@@ -1,22 +0,0 @@
1
- import { useCallback, useEffect, useRef } from 'react'
2
-
3
- export const useDebounced = (fn: () => void, delay = 500) => {
4
- const saveTimeoutRef = useRef<NodeJS.Timeout>(null)
5
-
6
- const debouncedFn = useCallback(() => {
7
- if (saveTimeoutRef.current) {
8
- clearTimeout(saveTimeoutRef.current)
9
- }
10
- saveTimeoutRef.current = setTimeout(fn, delay)
11
- }, [fn, delay])
12
-
13
- useEffect(() => {
14
- return () => {
15
- if (saveTimeoutRef.current) {
16
- clearTimeout(saveTimeoutRef.current)
17
- }
18
- }
19
- }, [])
20
-
21
- return debouncedFn
22
- }
@@ -1,33 +0,0 @@
1
- import { useStreamGroup } from '@motiadev/stream-client-react'
2
- import { useEffect } from 'react'
3
- import { useFlowStore } from '../stores/use-flow-store'
4
- import type { FlowResponse } from '../types/flow'
5
-
6
- const streamGroupArgs = { streamName: '__motia.flows', groupId: 'default' }
7
-
8
- export const useFetchFlows = () => {
9
- const setFlows = useFlowStore((state) => state.setFlows)
10
- const selectFlowId = useFlowStore((state) => state.selectFlowId)
11
- const clearSelectedFlowId = useFlowStore((state) => state.clearSelectedFlowId)
12
- const selectedFlowId = useFlowStore((state) => state.selectedFlowId)
13
-
14
- const { data: flows } = useStreamGroup<FlowResponse>(streamGroupArgs)
15
-
16
- useEffect(() => {
17
- if (flows) setFlows(flows.map((flow) => flow.id))
18
- }, [flows, setFlows])
19
-
20
- useEffect(() => {
21
- const hasFlows = flows.length > 0
22
- const isSelectedFlowValid = selectedFlowId && flows.some((flow) => flow.id === selectedFlowId)
23
-
24
- if (!hasFlows && selectedFlowId) {
25
- clearSelectedFlowId()
26
- return
27
- }
28
-
29
- if (hasFlows && (!selectedFlowId || !isSelectedFlowValid)) {
30
- selectFlowId(flows[0].id)
31
- }
32
- }, [flows, selectedFlowId, selectFlowId, clearSelectedFlowId])
33
- }
@@ -1,19 +0,0 @@
1
- import * as React from 'react'
2
-
3
- const MOBILE_BREAKPOINT = 768
4
-
5
- export function useIsMobile() {
6
- const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7
-
8
- React.useEffect(() => {
9
- const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
- const onChange = () => {
11
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
- }
13
- mql.addEventListener('change', onChange)
14
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
- return () => mql.removeEventListener('change', onChange)
16
- }, [])
17
-
18
- return !!isMobile
19
- }
@@ -1,42 +0,0 @@
1
- import { Position, useReactFlow, useUpdateNodeInternals } from '@xyflow/react'
2
- import type { BaseNodeProps } from '../publicComponents/node-props'
3
-
4
- export const useHandlePositions = (data: BaseNodeProps) => {
5
- const reactFlow = useReactFlow()
6
- const updateNodeInternals = useUpdateNodeInternals()
7
- const sourcePosition = data.nodeConfig?.sourceHandlePosition === 'bottom' ? Position.Bottom : Position.Right
8
- const targetPosition = data.nodeConfig?.targetHandlePosition === 'top' ? Position.Top : Position.Left
9
-
10
- const updateSourcePosition = (position: 'bottom' | 'right') => {
11
- reactFlow.updateNode(data.id, {
12
- data: { ...data, nodeConfig: { ...data.nodeConfig, sourceHandlePosition: position } },
13
- })
14
- updateNodeInternals(data.id)
15
- }
16
-
17
- const updateTargetPosition = (position: 'top' | 'left') => {
18
- reactFlow.updateNode(data.id, {
19
- data: { ...data, nodeConfig: { ...data.nodeConfig, targetHandlePosition: position } },
20
- })
21
- updateNodeInternals(data.id)
22
- }
23
-
24
- const toggleTargetPosition = () => {
25
- const newPosition = targetPosition === Position.Top ? Position.Left : Position.Top
26
- updateTargetPosition(newPosition)
27
- }
28
-
29
- const toggleSourcePosition = () => {
30
- const newPosition = sourcePosition === Position.Bottom ? Position.Right : Position.Bottom
31
- updateSourcePosition(newPosition)
32
- }
33
-
34
- return {
35
- sourcePosition,
36
- targetPosition,
37
- updateSourcePosition,
38
- updateTargetPosition,
39
- toggleTargetPosition,
40
- toggleSourcePosition,
41
- }
42
- }
@@ -1,110 +0,0 @@
1
- import { formatDuration } from '../utils'
2
-
3
- describe('formatDuration', () => {
4
- describe('milliseconds', () => {
5
- it('should format values under 1 second as milliseconds', () => {
6
- expect(formatDuration(0)).toBe('0ms')
7
- expect(formatDuration(1)).toBe('1ms')
8
- expect(formatDuration(250)).toBe('250ms')
9
- expect(formatDuration(500)).toBe('500ms')
10
- expect(formatDuration(999)).toBe('999ms')
11
- })
12
- })
13
-
14
- describe('seconds', () => {
15
- it('should format values between 1 second and 1 minute as seconds with 1 decimal', () => {
16
- expect(formatDuration(1000)).toBe('1.0s')
17
- expect(formatDuration(1500)).toBe('1.5s')
18
- expect(formatDuration(15000)).toBe('15.0s')
19
- expect(formatDuration(45500)).toBe('45.5s')
20
- expect(formatDuration(59999)).toBe('60.0s')
21
- })
22
-
23
- it('should round to 1 decimal place', () => {
24
- expect(formatDuration(1234)).toBe('1.2s')
25
- expect(formatDuration(1567)).toBe('1.6s')
26
- expect(formatDuration(12345)).toBe('12.3s')
27
- })
28
- })
29
-
30
- describe('minutes', () => {
31
- it('should format values between 1 minute and 1 hour as minutes with 1 decimal', () => {
32
- expect(formatDuration(60000)).toBe('1.0min')
33
- expect(formatDuration(90000)).toBe('1.5min')
34
- expect(formatDuration(300000)).toBe('5.0min')
35
- expect(formatDuration(1800000)).toBe('30.0min')
36
- expect(formatDuration(3599999)).toBe('60.0min')
37
- })
38
-
39
- it('should round to 1 decimal place', () => {
40
- expect(formatDuration(123456)).toBe('2.1min')
41
- expect(formatDuration(567890)).toBe('9.5min')
42
- })
43
- })
44
-
45
- describe('hours', () => {
46
- it('should format values 1 hour or more as hours with 1 decimal', () => {
47
- expect(formatDuration(3600000)).toBe('1.0h')
48
- expect(formatDuration(5400000)).toBe('1.5h')
49
- expect(formatDuration(7200000)).toBe('2.0h')
50
- expect(formatDuration(36000000)).toBe('10.0h')
51
- })
52
-
53
- it('should round to 1 decimal place', () => {
54
- expect(formatDuration(3661000)).toBe('1.0h')
55
- expect(formatDuration(5432100)).toBe('1.5h')
56
- expect(formatDuration(9000000)).toBe('2.5h')
57
- })
58
- })
59
-
60
- describe('edge cases', () => {
61
- it('should return "N/A" for undefined', () => {
62
- expect(formatDuration(undefined)).toBe('N/A')
63
- })
64
-
65
- it('should return "N/A" for null', () => {
66
- expect(formatDuration(null as any)).toBe('N/A')
67
- })
68
-
69
- it('should return "N/A" for 0 when treated as falsy', () => {
70
- expect(formatDuration(0)).toBe('0ms')
71
- })
72
- })
73
-
74
- describe('boundary values', () => {
75
- it('should correctly handle millisecond-second boundary (999ms vs 1.0s)', () => {
76
- expect(formatDuration(999)).toBe('999ms')
77
- expect(formatDuration(1000)).toBe('1.0s')
78
- })
79
-
80
- it('should correctly handle second-minute boundary (59.9s vs 1.0min)', () => {
81
- expect(formatDuration(59999)).toBe('60.0s')
82
- expect(formatDuration(60000)).toBe('1.0min')
83
- })
84
-
85
- it('should correctly handle minute-hour boundary (59.9min vs 1.0h)', () => {
86
- expect(formatDuration(3599999)).toBe('60.0min')
87
- expect(formatDuration(3600000)).toBe('1.0h')
88
- })
89
- })
90
-
91
- describe('real-world scenarios', () => {
92
- it('should format typical API response times', () => {
93
- expect(formatDuration(50)).toBe('50ms')
94
- expect(formatDuration(150)).toBe('150ms')
95
- expect(formatDuration(2500)).toBe('2.5s')
96
- })
97
-
98
- it('should format typical workflow execution times', () => {
99
- expect(formatDuration(5000)).toBe('5.0s')
100
- expect(formatDuration(30000)).toBe('30.0s')
101
- expect(formatDuration(120000)).toBe('2.0min')
102
- })
103
-
104
- it('should format long-running tasks', () => {
105
- expect(formatDuration(600000)).toBe('10.0min')
106
- expect(formatDuration(1800000)).toBe('30.0min')
107
- expect(formatDuration(7200000)).toBe('2.0h')
108
- })
109
- })
110
- })
@@ -1,140 +0,0 @@
1
- import { useCallback } from 'react'
2
-
3
- interface AmplitudeInstance {
4
- setOptOut(optOut: boolean): void
5
- track(eventName: string, eventProperties?: Record<string, any>): void
6
- identify(userId: string, userProperties?: Record<string, any>): void
7
- setUserId(userId: string): void
8
- getUserId(): string | undefined
9
- }
10
-
11
- declare global {
12
- interface Window {
13
- amplitude: AmplitudeInstance
14
- }
15
- }
16
-
17
- interface AnalyticsUserData {
18
- userId: string
19
- projectId: string
20
- motiaVersion: string
21
- analyticsEnabled: boolean
22
- }
23
-
24
- class WorkbenchAnalytics {
25
- private isInitialized = false
26
- private userIdCache: string | null = null
27
- private projectIdCache: string | null = null
28
- private motiaVersion: string | null = null
29
-
30
- constructor() {
31
- this.initialize()
32
- }
33
-
34
- private async initialize() {
35
- if (typeof window !== 'undefined' && window.amplitude) {
36
- await this.fetchUserData()
37
- this.isInitialized = true
38
- this.identifyUser()
39
- }
40
- }
41
-
42
- private async fetchUserData(): Promise<void> {
43
- try {
44
- const response = await fetch('/motia/analytics/user')
45
- if (response.ok) {
46
- const data: AnalyticsUserData = await response.json()
47
- this.userIdCache = data.userId
48
- this.projectIdCache = data.projectId
49
- this.motiaVersion = data.motiaVersion
50
-
51
- window.amplitude.setOptOut(!data.analyticsEnabled)
52
- // Set the user ID in Amplitude to match backend
53
- if (window.amplitude && data.userId) {
54
- window.amplitude.setUserId(data.userId)
55
- }
56
- } else {
57
- console.warn('Failed to fetch user data from backend, using fallback')
58
- this.userIdCache = this.generateFallbackUserId()
59
- }
60
- } catch (error) {
61
- console.warn('Error fetching user data:', error)
62
- this.userIdCache = this.generateFallbackUserId()
63
- }
64
- }
65
-
66
- private generateFallbackUserId(): string {
67
- let userId = localStorage.getItem('motia-user-id')
68
- if (!userId) {
69
- userId = `user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
70
- localStorage.setItem('motia-user-id', userId)
71
- }
72
- return userId
73
- }
74
-
75
- private identifyUser() {
76
- if (!this.isInitialized || !this.userIdCache) return
77
-
78
- try {
79
- window.amplitude.identify(this.userIdCache, {
80
- project_id: this.projectIdCache,
81
- browser: this.getBrowserInfo(),
82
- screen_resolution: `${window.screen.width}x${window.screen.height}`,
83
- workbench_version: this.motiaVersion,
84
- })
85
- } catch (error) {
86
- console.warn('Analytics user identification failed:', error)
87
- }
88
- }
89
-
90
- private getBrowserInfo(): string {
91
- const ua = navigator.userAgent
92
- if (ua.includes('Chrome')) return 'Chrome'
93
- if (ua.includes('Firefox')) return 'Firefox'
94
- if (ua.includes('Safari')) return 'Safari'
95
- if (ua.includes('Edge')) return 'Edge'
96
- return 'Unknown'
97
- }
98
-
99
- // Method to get current user and project IDs for external use
100
- getAnalyticsIds() {
101
- return {
102
- userId: this.userIdCache,
103
- projectId: this.projectIdCache,
104
- }
105
- }
106
-
107
- // Simple track method that preserves the user binding
108
- track(eventName: string, properties?: Record<string, any>) {
109
- if (!this.isInitialized) return
110
-
111
- const eventProperties = {
112
- project_id: this.projectIdCache,
113
- source: 'frontend',
114
- ...properties,
115
- }
116
-
117
- try {
118
- window.amplitude.track(eventName, eventProperties)
119
- } catch (error) {
120
- console.warn('Analytics tracking failed:', error)
121
- }
122
- }
123
- }
124
-
125
- export const motiaAnalytics = new WorkbenchAnalytics()
126
-
127
- export const useAnalytics = () => {
128
- const track = useCallback((eventName: string, properties?: Record<string, any>) => {
129
- motiaAnalytics.track(eventName, properties)
130
- }, [])
131
-
132
- const getAnalyticsIds = useCallback(() => {
133
- return motiaAnalytics.getAnalyticsIds()
134
- }, [])
135
-
136
- return {
137
- track,
138
- getAnalyticsIds,
139
- }
140
- }
@@ -1,132 +0,0 @@
1
- import { plugins } from 'virtual:motia-plugins'
2
- import { DynamicIcon, dynamicIconImports, type IconName } from 'lucide-react/dynamic'
3
- import { memo } from 'react'
4
- import { type AppTab, TabLocation, useAppTabsStore } from '../stores/use-app-tabs-store'
5
- import { isValidTabLocation } from './utils'
6
-
7
- export const registerPluginTabs = (addTab: (position: TabLocation, tab: AppTab) => void): void => {
8
- if (!Array.isArray(plugins)) {
9
- console.warn('[Motia] Invalid plugins configuration: expected array')
10
- return
11
- }
12
-
13
- plugins.forEach((plugin, index) => {
14
- try {
15
- if (!plugin.label) {
16
- console.warn(`[Motia] Plugin at index ${index} missing label, skipping`)
17
- return
18
- }
19
-
20
- if (!plugin.component) {
21
- console.warn(`[Motia] Plugin "${plugin.label}" missing component, skipping`)
22
- return
23
- }
24
-
25
- const position = plugin.position || 'top'
26
- if (!isValidTabLocation(position)) {
27
- console.warn(`[Motia] Plugin "${plugin.label}" has invalid position "${position}", defaulting to "top"`)
28
- }
29
-
30
- const tabLocation = isValidTabLocation(position) ? position : TabLocation.TOP
31
-
32
- const PluginTabLabel = memo(() => {
33
- const hasIcon = Object.keys(dynamicIconImports).includes(plugin.labelIcon as IconName)
34
- const iconName = hasIcon ? (plugin.labelIcon as IconName) : 'toy-brick'
35
-
36
- if (!hasIcon) {
37
- console.warn(
38
- `[Motia] Plugin "${plugin.label}" has invalid icon "${plugin.labelIcon}", defaulting to "toy-brick"`,
39
- )
40
- }
41
-
42
- return (
43
- <>
44
- <DynamicIcon name={iconName} />
45
- <span>{plugin.label}</span>
46
- </>
47
- )
48
- })
49
- PluginTabLabel.displayName = `${plugin.label}TabLabel`
50
-
51
- const PluginContent = memo(() => {
52
- const Component = plugin.component
53
- const props = plugin.props || {}
54
-
55
- if (!Component) {
56
- return <div>Error: Plugin component not found</div>
57
- }
58
-
59
- return <Component {...props} />
60
- })
61
- PluginContent.displayName = `${plugin.label}Content`
62
-
63
- addTab(tabLocation, {
64
- id: plugin.label.toLowerCase(),
65
- tabLabel: PluginTabLabel,
66
- content: PluginContent,
67
- })
68
- } catch (error) {
69
- console.error(`[Motia] Error registering plugin "${plugin.label}":`, error)
70
- }
71
- })
72
- }
73
-
74
- const refreshPluginTabs = (nextPlugins: typeof plugins): void => {
75
- try {
76
- const state = useAppTabsStore.getState()
77
- const { removeTab, addTab } = state
78
-
79
- const idsToRefresh = new Set(nextPlugins.map((p) => (p.label || '').toLowerCase()))
80
-
81
- idsToRefresh.forEach((id) => {
82
- removeTab(TabLocation.TOP, id)
83
- removeTab(TabLocation.BOTTOM, id)
84
- })
85
-
86
- nextPlugins.forEach((plugin, index) => {
87
- try {
88
- if (!plugin.label || !plugin.component) return
89
-
90
- const position = plugin.position || 'top'
91
- const tabLocation = isValidTabLocation(position) ? position : TabLocation.TOP
92
-
93
- const PluginTabLabel = memo(() => {
94
- const hasIcon = Object.keys(dynamicIconImports).includes(plugin.labelIcon as IconName)
95
- const iconName: IconName = hasIcon ? (plugin.labelIcon as IconName) : 'toy-brick'
96
- return (
97
- <>
98
- <DynamicIcon name={iconName} />
99
- <span>{plugin.label}</span>
100
- </>
101
- )
102
- })
103
- PluginTabLabel.displayName = `${plugin.label}TabLabel_HMR_${index}`
104
-
105
- const PluginContent = memo(() => {
106
- const Component = plugin.component as React.ElementType
107
- const props = plugin.props || {}
108
- if (!Component) return <div>Error: Plugin component not found</div>
109
- return <Component {...props} />
110
- })
111
- PluginContent.displayName = `${plugin.label}Content_HMR_${index}`
112
-
113
- addTab(tabLocation, {
114
- id: plugin.label.toLowerCase(),
115
- tabLabel: PluginTabLabel,
116
- content: PluginContent,
117
- })
118
- } catch (error) {
119
- console.error(`[Motia] Error refreshing plugin "${plugin.label}":`, error)
120
- }
121
- })
122
- } catch (err) {
123
- console.error('[Motia] Failed to refresh plugin tabs via HMR:', err)
124
- }
125
- }
126
-
127
- if (import.meta.hot) {
128
- import.meta.hot.accept('virtual:motia-plugins', (mod) => {
129
- const next = (mod as unknown as { plugins?: typeof plugins })?.plugins || []
130
- refreshPluginTabs(next)
131
- })
132
- }
@@ -1,37 +0,0 @@
1
- import { TabLocation } from '../stores/use-app-tabs-store'
2
-
3
- export const formatDuration = (duration?: number) => {
4
- if (duration === undefined || duration === null) return 'N/A'
5
- if (duration < 1000) return `${duration}ms`
6
- if (duration < 60000) return `${(duration / 1000).toFixed(1)}s`
7
- if (duration < 3600000) return `${(duration / 60000).toFixed(1)}min`
8
- return `${(duration / 3600000).toFixed(1)}h`
9
- }
10
-
11
- export const formatTimestamp = (time: number) => {
12
- const date = new Date(Number(time))
13
- 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')}`
14
- }
15
-
16
- export type ViewMode = 'project' | 'system'
17
- export const DEFAULT_VIEW_MODE: ViewMode = 'system'
18
-
19
- export const getViewModeFromURL = (): ViewMode => {
20
- try {
21
- const url = new URL(window.location.href)
22
- const viewMode = url.searchParams.get('view-mode')
23
-
24
- if (viewMode === 'project' || viewMode === 'system') {
25
- return viewMode
26
- }
27
-
28
- return DEFAULT_VIEW_MODE
29
- } catch (error) {
30
- console.error('[Motia] Error parsing URL:', error)
31
- return DEFAULT_VIEW_MODE
32
- }
33
- }
34
-
35
- export const isValidTabLocation = (position: string): position is TabLocation => {
36
- return Object.values(TabLocation).includes(position as TabLocation)
37
- }
package/dist/src/main.tsx DELETED
@@ -1,30 +0,0 @@
1
- import { MotiaStreamProvider } from '@motiadev/stream-client-react'
2
- import { StrictMode } from 'react'
3
- import { createRoot } from 'react-dom/client'
4
- import { BrowserRouter, Route, Routes } from 'react-router-dom'
5
- import { App } from './App'
6
- import { NotFoundPage } from './components/NotFoundPage'
7
- import { RootMotia } from './components/root-motia'
8
- import './index.css'
9
-
10
- const rootElement = document.getElementById('root')!
11
- if (!rootElement.innerHTML) {
12
- const basePath = workbenchBase
13
- const root = createRoot(rootElement)
14
- const address = window.location.origin.replace('http', 'ws')
15
-
16
- root.render(
17
- <StrictMode>
18
- <MotiaStreamProvider address={address}>
19
- <RootMotia>
20
- <BrowserRouter>
21
- <Routes>
22
- <Route path={basePath} element={<App />} />
23
- <Route path="*" element={<NotFoundPage />} />
24
- </Routes>
25
- </BrowserRouter>
26
- </RootMotia>
27
- </MotiaStreamProvider>
28
- </StrictMode>,
29
- )
30
- }
@@ -1,32 +0,0 @@
1
- import { APP_SIDEBAR_CONTAINER_ID, Panel } from '@motiadev/ui'
2
- import { memo } from 'react'
3
- import { useShallow } from 'zustand/react/shallow'
4
- import { type AppTabsState, TabLocation, useAppTabsStore } from './stores/use-app-tabs-store'
5
-
6
- const topTabs = (state: AppTabsState) => state.tabs[TabLocation.TOP]
7
-
8
- export const ProjectViewMode = memo(() => {
9
- const tabs = useAppTabsStore(useShallow(topTabs))
10
-
11
- return (
12
- <div className="grid grid-rows-1 grid-cols-[1fr_auto] bg-background text-foreground h-screen ">
13
- <main className="m-2 overflow-hidden">
14
- <Panel
15
- contentClassName={'p-0'}
16
- tabs={tabs.map((tab) => {
17
- const Element = tab.content
18
- const LabelComponent = tab.tabLabel
19
- return {
20
- label: tab.id,
21
- labelComponent: <LabelComponent />,
22
- content: <Element />,
23
- 'data-testid': tab.id,
24
- }
25
- })}
26
- />
27
- </main>
28
- <div id={APP_SIDEBAR_CONTAINER_ID} />
29
- </div>
30
- )
31
- })
32
- ProjectViewMode.displayName = 'ProjectViewMode'