@motiadev/workbench 0.13.0-beta.162-945354 → 0.13.0-beta.162-080298

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 (237) hide show
  1. package/LICENSE +93 -21
  2. package/dist/index.d.ts +10 -189
  3. package/dist/index.html +1 -1
  4. package/dist/index.js +7 -524
  5. package/dist/middleware.d.ts +8 -66
  6. package/dist/middleware.js +86 -684
  7. package/dist/motia-plugin/__tests__/generator.test.d.ts +1 -0
  8. package/dist/motia-plugin/__tests__/generator.test.js +97 -0
  9. package/dist/motia-plugin/__tests__/resolver.test.d.ts +1 -0
  10. package/dist/motia-plugin/__tests__/resolver.test.js +64 -0
  11. package/dist/motia-plugin/__tests__/validator.test.d.ts +1 -0
  12. package/dist/motia-plugin/__tests__/validator.test.js +59 -0
  13. package/dist/motia-plugin/generator.d.ts +78 -0
  14. package/dist/motia-plugin/{generator.ts → generator.js} +35 -37
  15. package/dist/motia-plugin/hmr.d.ts +22 -0
  16. package/dist/motia-plugin/hmr.js +100 -0
  17. package/dist/motia-plugin/index.d.ts +3 -0
  18. package/dist/motia-plugin/index.js +153 -0
  19. package/dist/motia-plugin/{resolver.ts → resolver.d.ts} +5 -38
  20. package/dist/motia-plugin/resolver.js +92 -0
  21. package/dist/motia-plugin/types.d.ts +169 -0
  22. package/dist/motia-plugin/types.js +36 -0
  23. package/dist/motia-plugin/{utils.ts → utils.d.ts} +4 -17
  24. package/dist/motia-plugin/utils.js +75 -0
  25. package/dist/motia-plugin/validator.d.ts +19 -0
  26. package/dist/motia-plugin/validator.js +163 -0
  27. package/dist/src/App.d.ts +2 -0
  28. package/dist/src/App.js +35 -0
  29. package/dist/src/components/NotFoundPage.d.ts +1 -0
  30. package/dist/src/components/NotFoundPage.js +3 -0
  31. package/dist/src/components/bottom-panel.d.ts +1 -0
  32. package/dist/src/components/bottom-panel.js +15 -0
  33. package/dist/src/components/flow/base-edge.d.ts +3 -0
  34. package/dist/src/components/flow/base-edge.js +39 -0
  35. package/dist/src/components/flow/flow-loader.d.ts +1 -0
  36. package/dist/src/components/flow/flow-loader.js +4 -0
  37. package/dist/src/components/flow/flow-page.d.ts +1 -0
  38. package/dist/src/components/flow/flow-page.js +25 -0
  39. package/dist/src/components/flow/flow-tab-menu-item.d.ts +1 -0
  40. package/dist/src/components/flow/flow-tab-menu-item.js +18 -0
  41. package/dist/src/components/flow/flow-view.d.ts +12 -0
  42. package/dist/src/components/flow/flow-view.js +22 -0
  43. package/dist/src/components/flow/hooks/use-get-flow-state.d.ts +10 -0
  44. package/dist/src/components/flow/hooks/use-get-flow-state.js +133 -0
  45. package/dist/src/components/flow/hooks/use-save-workflow-config.d.ts +2 -0
  46. package/dist/src/components/flow/hooks/use-save-workflow-config.js +22 -0
  47. package/dist/src/components/flow/node-organizer.d.ts +10 -0
  48. package/dist/src/components/flow/node-organizer.js +82 -0
  49. package/dist/src/components/flow/nodes/api-flow-node.d.ts +2 -0
  50. package/dist/src/components/flow/nodes/api-flow-node.js +5 -0
  51. package/dist/src/components/flow/nodes/cron-flow-node.d.ts +2 -0
  52. package/dist/src/components/flow/nodes/cron-flow-node.js +5 -0
  53. package/dist/src/components/flow/nodes/event-flow-node.d.ts +2 -0
  54. package/dist/src/components/flow/nodes/event-flow-node.js +5 -0
  55. package/dist/src/components/flow/nodes/noop-flow-node.d.ts +2 -0
  56. package/dist/src/components/flow/nodes/noop-flow-node.js +5 -0
  57. package/dist/src/components/header/deploy-button.d.ts +1 -0
  58. package/dist/src/components/header/deploy-button.js +28 -0
  59. package/dist/src/components/header/header.d.ts +2 -0
  60. package/dist/src/components/header/header.js +23 -0
  61. package/dist/src/components/root-motia.d.ts +2 -0
  62. package/dist/src/components/root-motia.js +7 -0
  63. package/dist/src/components/top-panel.d.ts +1 -0
  64. package/dist/src/components/top-panel.js +15 -0
  65. package/dist/src/components/tutorial/engine/tutorial-engine.d.ts +12 -0
  66. package/dist/src/components/tutorial/engine/tutorial-engine.js +36 -0
  67. package/dist/src/components/tutorial/engine/tutorial-types.d.ts +22 -0
  68. package/dist/src/components/tutorial/engine/tutorial-types.js +1 -0
  69. package/dist/src/components/tutorial/engine/workbench-xpath.d.ts +45 -0
  70. package/dist/src/components/tutorial/engine/workbench-xpath.js +45 -0
  71. package/dist/src/components/tutorial/hooks/tutorial-utils.d.ts +1 -0
  72. package/dist/src/components/tutorial/hooks/tutorial-utils.js +17 -0
  73. package/dist/src/components/tutorial/hooks/use-tutorial-engine.d.ts +15 -0
  74. package/dist/src/components/tutorial/hooks/use-tutorial-engine.js +183 -0
  75. package/dist/src/components/tutorial/hooks/use-tutorial.d.ts +5 -0
  76. package/dist/src/components/tutorial/hooks/use-tutorial.js +10 -0
  77. package/dist/src/components/tutorial/tutorial-button.d.ts +2 -0
  78. package/dist/src/components/tutorial/tutorial-button.js +21 -0
  79. package/dist/src/components/tutorial/tutorial-step.d.ts +14 -0
  80. package/dist/src/components/tutorial/tutorial-step.js +19 -0
  81. package/dist/src/components/tutorial/tutorial.css +2 -2
  82. package/dist/src/components/tutorial/tutorial.d.ts +2 -0
  83. package/dist/src/components/tutorial/tutorial.js +32 -0
  84. package/dist/src/components/ui/json-editor.d.ts +12 -0
  85. package/dist/src/components/ui/json-editor.js +35 -0
  86. package/dist/src/components/ui/table.d.ts +10 -0
  87. package/dist/src/components/ui/table.js +20 -0
  88. package/dist/src/components/ui/theme-toggle.d.ts +2 -0
  89. package/dist/src/components/ui/theme-toggle.js +19 -0
  90. package/dist/src/components/ui/tooltip.d.ts +6 -0
  91. package/dist/src/components/ui/tooltip.js +3 -0
  92. package/dist/src/hooks/use-debounced.d.ts +1 -0
  93. package/dist/src/hooks/use-debounced.js +18 -0
  94. package/dist/src/hooks/use-fetch-flows.d.ts +1 -0
  95. package/dist/src/hooks/use-fetch-flows.js +26 -0
  96. package/dist/src/hooks/use-mobile.d.ts +1 -0
  97. package/dist/src/hooks/use-mobile.js +15 -0
  98. package/dist/src/hooks/use-update-handle-positions.d.ts +10 -0
  99. package/dist/src/hooks/use-update-handle-positions.js +35 -0
  100. package/dist/src/index.css +5 -5
  101. package/dist/src/lib/__tests__/utils.test.d.ts +1 -0
  102. package/dist/src/lib/__tests__/utils.test.js +94 -0
  103. package/dist/src/lib/motia-analytics.d.ts +38 -0
  104. package/dist/src/lib/motia-analytics.js +132 -0
  105. package/dist/src/lib/plugins.d.ts +2 -0
  106. package/dist/src/lib/plugins.js +105 -0
  107. package/dist/src/lib/utils.d.ts +7 -0
  108. package/dist/src/lib/utils.js +34 -0
  109. package/dist/src/main.d.ts +2 -0
  110. package/dist/src/main.js +17 -0
  111. package/dist/src/project-view-mode.d.ts +1 -0
  112. package/dist/src/project-view-mode.js +20 -0
  113. package/dist/src/publicComponents/api-node.d.ts +5 -0
  114. package/dist/src/publicComponents/api-node.js +5 -0
  115. package/dist/src/publicComponents/base-node/base-handle.d.ts +9 -0
  116. package/dist/src/publicComponents/base-node/base-handle.js +8 -0
  117. package/dist/src/publicComponents/base-node/base-node.d.ts +15 -0
  118. package/dist/src/publicComponents/base-node/base-node.js +30 -0
  119. package/dist/src/publicComponents/base-node/code-display.d.ts +9 -0
  120. package/dist/src/publicComponents/base-node/code-display.js +64 -0
  121. package/dist/src/publicComponents/base-node/emits.d.ts +5 -0
  122. package/dist/src/publicComponents/base-node/emits.js +5 -0
  123. package/dist/src/publicComponents/base-node/feature-card.d.ts +10 -0
  124. package/dist/src/publicComponents/base-node/feature-card.js +5 -0
  125. package/dist/src/publicComponents/base-node/language-indicator.d.ts +10 -0
  126. package/dist/src/publicComponents/base-node/language-indicator.js +29 -0
  127. package/dist/src/publicComponents/base-node/node-header.d.ts +13 -0
  128. package/dist/src/publicComponents/base-node/node-header.js +30 -0
  129. package/dist/src/publicComponents/base-node/node-sidebar.d.ts +14 -0
  130. package/dist/src/publicComponents/base-node/node-sidebar.js +9 -0
  131. package/dist/src/publicComponents/base-node/subscribe.d.ts +4 -0
  132. package/dist/src/publicComponents/base-node/subscribe.js +4 -0
  133. package/dist/src/publicComponents/cron-node.d.ts +4 -0
  134. package/dist/src/publicComponents/cron-node.js +6 -0
  135. package/dist/src/publicComponents/event-node.d.ts +4 -0
  136. package/dist/src/publicComponents/event-node.js +5 -0
  137. package/dist/src/publicComponents/node-props.d.ts +21 -0
  138. package/dist/src/publicComponents/node-props.js +1 -0
  139. package/dist/src/publicComponents/noop-node.d.ts +4 -0
  140. package/dist/src/publicComponents/noop-node.js +5 -0
  141. package/dist/src/setupTests.d.ts +1 -0
  142. package/dist/src/setupTests.js +1 -0
  143. package/dist/src/stores/use-app-tabs-store.d.ts +16 -0
  144. package/dist/src/stores/use-app-tabs-store.js +31 -0
  145. package/dist/src/stores/use-flow-store.d.ts +21 -0
  146. package/dist/src/stores/use-flow-store.js +16 -0
  147. package/dist/src/stores/use-global-store.d.ts +18 -0
  148. package/dist/src/stores/use-global-store.js +12 -0
  149. package/dist/src/stores/use-motia-config-store.d.ts +12 -0
  150. package/dist/src/stores/use-motia-config-store.js +24 -0
  151. package/dist/src/stores/use-tabs-store.d.ts +19 -0
  152. package/dist/src/stores/use-tabs-store.js +22 -0
  153. package/dist/src/system-view-mode.d.ts +1 -0
  154. package/dist/src/system-view-mode.js +10 -0
  155. package/dist/src/types/endpoint.d.ts +14 -0
  156. package/dist/src/types/endpoint.js +1 -0
  157. package/dist/src/types/file.d.ts +7 -0
  158. package/dist/src/types/file.js +1 -0
  159. package/dist/src/types/flow.d.ts +115 -0
  160. package/dist/src/types/flow.js +1 -0
  161. package/dist/tsconfig.app.tsbuildinfo +1 -0
  162. package/dist/tsconfig.node.tsbuildinfo +1 -0
  163. package/package.json +49 -62
  164. package/dist/motia-plugin/__tests__/generator.test.ts +0 -129
  165. package/dist/motia-plugin/__tests__/resolver.test.ts +0 -82
  166. package/dist/motia-plugin/__tests__/validator.test.ts +0 -71
  167. package/dist/motia-plugin/hmr.ts +0 -123
  168. package/dist/motia-plugin/index.ts +0 -183
  169. package/dist/motia-plugin/types.ts +0 -198
  170. package/dist/motia-plugin/validator.ts +0 -197
  171. package/dist/src/App.tsx +0 -41
  172. package/dist/src/components/NotFoundPage.tsx +0 -11
  173. package/dist/src/components/bottom-panel.tsx +0 -39
  174. package/dist/src/components/flow/base-edge.tsx +0 -61
  175. package/dist/src/components/flow/flow-loader.tsx +0 -3
  176. package/dist/src/components/flow/flow-page.tsx +0 -75
  177. package/dist/src/components/flow/flow-tab-menu-item.tsx +0 -50
  178. package/dist/src/components/flow/flow-view.tsx +0 -66
  179. package/dist/src/components/flow/hooks/use-get-flow-state.tsx +0 -171
  180. package/dist/src/components/flow/hooks/use-save-workflow-config.ts +0 -25
  181. package/dist/src/components/flow/node-organizer.tsx +0 -103
  182. package/dist/src/components/flow/nodes/api-flow-node.tsx +0 -6
  183. package/dist/src/components/flow/nodes/cron-flow-node.tsx +0 -6
  184. package/dist/src/components/flow/nodes/event-flow-node.tsx +0 -6
  185. package/dist/src/components/flow/nodes/noop-flow-node.tsx +0 -6
  186. package/dist/src/components/header/deploy-button.tsx +0 -110
  187. package/dist/src/components/header/header.tsx +0 -39
  188. package/dist/src/components/root-motia.tsx +0 -10
  189. package/dist/src/components/top-panel.tsx +0 -40
  190. package/dist/src/components/tutorial/engine/tutorial-engine.ts +0 -26
  191. package/dist/src/components/tutorial/engine/tutorial-types.ts +0 -26
  192. package/dist/src/components/tutorial/engine/workbench-xpath.ts +0 -53
  193. package/dist/src/components/tutorial/hooks/tutorial-utils.ts +0 -26
  194. package/dist/src/components/tutorial/hooks/use-tutorial-engine.ts +0 -213
  195. package/dist/src/components/tutorial/hooks/use-tutorial.ts +0 -14
  196. package/dist/src/components/tutorial/tutorial-button.tsx +0 -46
  197. package/dist/src/components/tutorial/tutorial-step.tsx +0 -82
  198. package/dist/src/components/tutorial/tutorial.tsx +0 -59
  199. package/dist/src/components/ui/json-editor.tsx +0 -68
  200. package/dist/src/components/ui/table.tsx +0 -75
  201. package/dist/src/components/ui/theme-toggle.tsx +0 -54
  202. package/dist/src/components/ui/tooltip.tsx +0 -26
  203. package/dist/src/hooks/use-debounced.ts +0 -22
  204. package/dist/src/hooks/use-fetch-flows.ts +0 -33
  205. package/dist/src/hooks/use-mobile.ts +0 -19
  206. package/dist/src/hooks/use-update-handle-positions.ts +0 -42
  207. package/dist/src/lib/__tests__/utils.test.ts +0 -110
  208. package/dist/src/lib/motia-analytics.ts +0 -140
  209. package/dist/src/lib/plugins.tsx +0 -132
  210. package/dist/src/lib/utils.ts +0 -37
  211. package/dist/src/main.tsx +0 -30
  212. package/dist/src/project-view-mode.tsx +0 -32
  213. package/dist/src/publicComponents/api-node.tsx +0 -26
  214. package/dist/src/publicComponents/base-node/base-handle.tsx +0 -50
  215. package/dist/src/publicComponents/base-node/base-node.tsx +0 -114
  216. package/dist/src/publicComponents/base-node/code-display.tsx +0 -119
  217. package/dist/src/publicComponents/base-node/emits.tsx +0 -17
  218. package/dist/src/publicComponents/base-node/feature-card.tsx +0 -32
  219. package/dist/src/publicComponents/base-node/language-indicator.tsx +0 -131
  220. package/dist/src/publicComponents/base-node/node-header.tsx +0 -49
  221. package/dist/src/publicComponents/base-node/node-sidebar.tsx +0 -41
  222. package/dist/src/publicComponents/base-node/subscribe.tsx +0 -13
  223. package/dist/src/publicComponents/cron-node.tsx +0 -24
  224. package/dist/src/publicComponents/event-node.tsx +0 -20
  225. package/dist/src/publicComponents/node-props.tsx +0 -15
  226. package/dist/src/publicComponents/noop-node.tsx +0 -19
  227. package/dist/src/setupTests.ts +0 -1
  228. package/dist/src/stores/use-app-tabs-store.ts +0 -49
  229. package/dist/src/stores/use-flow-store.ts +0 -31
  230. package/dist/src/stores/use-global-store.ts +0 -24
  231. package/dist/src/stores/use-motia-config-store.ts +0 -36
  232. package/dist/src/stores/use-tabs-store.ts +0 -34
  233. package/dist/src/system-view-mode.tsx +0 -28
  234. package/dist/src/types/endpoint.ts +0 -12
  235. package/dist/src/types/file.ts +0 -7
  236. package/dist/src/types/flow.ts +0 -103
  237. package/eslint.config.cjs +0 -22
@@ -1,66 +0,0 @@
1
- import {
2
- Background,
3
- BackgroundVariant,
4
- type NodeChange,
5
- type OnNodesChange,
6
- ReactFlow,
7
- type Edge as ReactFlowEdge,
8
- type Node as ReactFlowNode,
9
- } from '@xyflow/react'
10
- import type React from 'react'
11
- import { useCallback, useState } from 'react'
12
- import type { EdgeData, FlowConfigResponse, FlowResponse, NodeData } from '../../types/flow'
13
- import { BaseEdge } from './base-edge'
14
- import { FlowLoader } from './flow-loader'
15
- import { useGetFlowState } from './hooks/use-get-flow-state'
16
- import { NodeOrganizer } from './node-organizer'
17
-
18
- import '@xyflow/react/dist/style.css'
19
- import { BackgroundEffect } from '@motiadev/ui'
20
-
21
- export type FlowNode = ReactFlowNode<NodeData>
22
- export type FlowEdge = ReactFlowEdge<EdgeData>
23
-
24
- const edgeTypes = {
25
- base: BaseEdge,
26
- }
27
-
28
- type Props = {
29
- flow: FlowResponse
30
- flowConfig: FlowConfigResponse
31
- }
32
-
33
- export const FlowView: React.FC<Props> = ({ flow, flowConfig }) => {
34
- const { nodes, edges, onNodesChange, onEdgesChange, nodeTypes } = useGetFlowState(flow, flowConfig)
35
- const [initialized, setInitialized] = useState(false)
36
- const onInitialized = useCallback(() => setInitialized(true), [])
37
-
38
- const onNodesChangeHandler = useCallback<OnNodesChange<FlowNode>>(
39
- (changes: NodeChange<FlowNode>[]) => onNodesChange(changes),
40
- [onNodesChange],
41
- )
42
-
43
- if (!nodeTypes) {
44
- return null
45
- }
46
-
47
- return (
48
- <div className="w-full h-full relative">
49
- {!initialized && <FlowLoader />}
50
- <ReactFlow
51
- minZoom={0.1}
52
- nodes={nodes}
53
- edges={edges}
54
- nodeTypes={nodeTypes}
55
- edgeTypes={edgeTypes}
56
- onNodesChange={onNodesChangeHandler}
57
- onEdgesChange={onEdgesChange}
58
- className="isolate"
59
- >
60
- <BackgroundEffect />
61
- <Background variant={BackgroundVariant.Dots} gap={20} size={1} />
62
- <NodeOrganizer onInitialized={onInitialized} nodes={nodes} edges={edges} />
63
- </ReactFlow>
64
- </div>
65
- )
66
- }
@@ -1,171 +0,0 @@
1
- import { type Edge, type Node, useEdgesState, useNodesState } from '@xyflow/react'
2
- import isEqual from 'fast-deep-equal'
3
- import type React from 'react'
4
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5
- import type { EdgeData, FlowConfigResponse, FlowResponse, NodeConfig, NodeData } from '../../../types/flow'
6
- import { ApiFlowNode } from '../nodes/api-flow-node'
7
- import { CronFlowNode } from '../nodes/cron-flow-node'
8
- import { EventFlowNode } from '../nodes/event-flow-node'
9
- import { NoopFlowNode } from '../nodes/noop-flow-node'
10
- import { useSaveWorkflowConfig } from './use-save-workflow-config'
11
-
12
- const DEFAULT_CONFIG: NodeConfig = { x: 0, y: 0 }
13
-
14
- const getNodePosition = (flowConfig: FlowConfigResponse | null, stepName: string): NodeConfig => {
15
- return flowConfig?.config[stepName] || DEFAULT_CONFIG
16
- }
17
-
18
- type FlowState = {
19
- nodes: Node<NodeData>[]
20
- edges: Edge<EdgeData>[]
21
- nodeTypes: Record<string, React.ComponentType<any>>
22
- }
23
-
24
- const nodeComponentCache = new Map<string, React.ComponentType<any>>()
25
-
26
- const BASE_NODE_TYPES: Record<string, React.ComponentType<any>> = {
27
- event: EventFlowNode,
28
- api: ApiFlowNode,
29
- noop: NoopFlowNode,
30
- cron: CronFlowNode,
31
- }
32
-
33
- async function importFlow(flow: FlowResponse, flowConfig: FlowConfigResponse | null): Promise<FlowState> {
34
- const nodeTypes: Record<string, React.ComponentType<any>> = { ...BASE_NODE_TYPES }
35
-
36
- const customNodePromises = flow.steps
37
- .filter((step) => step.nodeComponentPath)
38
- .map(async (step) => {
39
- const path = step.nodeComponentPath!
40
-
41
- // Check cache first
42
- if (nodeComponentCache.has(path)) {
43
- nodeTypes[path] = nodeComponentCache.get(path)!
44
- return
45
- }
46
-
47
- try {
48
- const module = await import(/* @vite-ignore */ `/@fs/${path}`)
49
- const component = module.Node ?? module.default
50
- nodeComponentCache.set(path, component)
51
- nodeTypes[path] = component
52
- } catch (error) {
53
- console.error(`Failed to load custom node component: ${path}`, error)
54
- }
55
- })
56
-
57
- await Promise.all(customNodePromises)
58
-
59
- const nodes: Node<NodeData>[] = flow.steps.map((step) => ({
60
- id: step.id,
61
- type: step.nodeComponentPath || step.type,
62
- filePath: step.filePath,
63
- position: step.filePath ? getNodePosition(flowConfig, step.filePath) : DEFAULT_CONFIG,
64
- data: { ...step, nodeConfig: step.filePath ? getNodePosition(flowConfig, step.filePath) : DEFAULT_CONFIG },
65
- language: step.language,
66
- }))
67
-
68
- const edges: Edge<EdgeData>[] = flow.edges.map((edge) => ({
69
- ...edge,
70
- type: 'base',
71
- }))
72
-
73
- return { nodes, edges, nodeTypes }
74
- }
75
-
76
- export const useGetFlowState = (flow: FlowResponse, flowConfig: FlowConfigResponse) => {
77
- const [nodeTypes, setNodeTypes] = useState<Record<string, React.ComponentType<any>>>(BASE_NODE_TYPES)
78
- const [nodes, setNodes, onNodesChange] = useNodesState<Node<NodeData>>([])
79
- const [edges, setEdges, onEdgesChange] = useEdgesState<Edge<EdgeData>>([])
80
-
81
- const saveConfig = useSaveWorkflowConfig()
82
-
83
- const flowIdRef = useRef<string>('')
84
- const saveTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null)
85
- const lastSavedConfigRef = useRef<FlowConfigResponse['config']>(null)
86
- const lastSavedFlowRef = useRef<FlowResponse>(null)
87
-
88
- // eslint-disable-next-line react-hooks/exhaustive-deps
89
- const memoizedFlowConfig = useMemo(() => flowConfig, [flowConfig?.id, flowConfig?.config])
90
-
91
- useEffect(() => {
92
- if (!flow || flow.error) return
93
- const hasSameConfig = isEqual(lastSavedConfigRef.current, memoizedFlowConfig?.config)
94
- const hasSameFlow = isEqual(lastSavedFlowRef.current, flow)
95
-
96
- if (hasSameConfig && hasSameFlow) return
97
-
98
- lastSavedConfigRef.current = memoizedFlowConfig?.config
99
- flowIdRef.current = flow.id
100
- lastSavedFlowRef.current = flow
101
-
102
- const importFlowAsync = async () => {
103
- try {
104
- const { nodes, edges, nodeTypes } = await importFlow(flow, flowConfig)
105
- setNodes(nodes)
106
- setEdges(edges)
107
- setNodeTypes(nodeTypes)
108
- } catch (error) {
109
- console.error('Failed to import flow:', error)
110
- }
111
- }
112
-
113
- importFlowAsync()
114
- }, [flow, memoizedFlowConfig, setNodes, setEdges, flowConfig])
115
-
116
- const saveFlowConfig = useCallback(
117
- (nodesToSave: Node<NodeData>[]) => {
118
- if (saveTimeoutRef.current) {
119
- clearTimeout(saveTimeoutRef.current)
120
- }
121
-
122
- saveTimeoutRef.current = setTimeout(async () => {
123
- const steps = nodesToSave.reduce<FlowConfigResponse['config']>((acc, node) => {
124
- if (node.data.filePath) {
125
- acc[node.data.filePath] = {
126
- x: Math.round(node.position.x),
127
- y: Math.round(node.position.y),
128
- }
129
-
130
- if (node.data.nodeConfig?.sourceHandlePosition) {
131
- acc[node.data.filePath].sourceHandlePosition = node.data.nodeConfig.sourceHandlePosition
132
- }
133
- if (node.data.nodeConfig?.targetHandlePosition) {
134
- acc[node.data.filePath].targetHandlePosition = node.data.nodeConfig.targetHandlePosition
135
- }
136
- }
137
- return acc
138
- }, {})
139
-
140
- if (!isEqual(steps, lastSavedConfigRef.current)) {
141
- lastSavedConfigRef.current = steps
142
- const newConfig = { id: flowIdRef.current, config: steps }
143
-
144
- try {
145
- await saveConfig(newConfig)
146
- } catch (error) {
147
- console.error('Failed to save flow config:', error)
148
- }
149
- }
150
- }, 300)
151
- },
152
- [saveConfig],
153
- )
154
-
155
- useEffect(() => {
156
- if (nodes.length > 0) {
157
- saveFlowConfig(nodes)
158
- }
159
-
160
- return () => {
161
- if (saveTimeoutRef.current) {
162
- clearTimeout(saveTimeoutRef.current)
163
- }
164
- }
165
- }, [nodes, saveFlowConfig])
166
-
167
- return useMemo(
168
- () => ({ nodes, edges, onNodesChange, onEdgesChange, nodeTypes }),
169
- [nodes, edges, onNodesChange, onEdgesChange, nodeTypes],
170
- )
171
- }
@@ -1,25 +0,0 @@
1
- import { useCallback } from 'react'
2
- import type { FlowConfigResponse } from '../../../types/flow'
3
-
4
- export const useSaveWorkflowConfig = () => {
5
- return useCallback(async (body: FlowConfigResponse) => {
6
- try {
7
- const response = await fetch(`/__motia/flows/${body.id}/config`, {
8
- method: 'POST',
9
- headers: {
10
- 'Content-Type': 'application/json',
11
- },
12
- body: JSON.stringify(body),
13
- })
14
-
15
- if (!response.ok) {
16
- throw new Error(`Failed to save config: ${response.statusText}`)
17
- }
18
-
19
- return await response.json()
20
- } catch (error) {
21
- console.error('Error saving workflow config:', error)
22
- throw error
23
- }
24
- }, [])
25
- }
@@ -1,103 +0,0 @@
1
- import { type Edge, type Node, useNodesInitialized, useReactFlow } from '@xyflow/react'
2
- import dagre from 'dagre'
3
- import isEqual from 'fast-deep-equal'
4
- import type React from 'react'
5
- import { useEffect, useRef } from 'react'
6
- import type { EdgeData, NodeData } from '../../types/flow'
7
-
8
- const organizeNodes = (nodes: Node<NodeData>[], edges: Edge<EdgeData>[]): Node<NodeData>[] => {
9
- const dagreGraph = new dagre.graphlib.Graph({ directed: true, compound: false, multigraph: false })
10
-
11
- dagreGraph.setDefaultEdgeLabel(() => ({}))
12
- dagreGraph.setGraph({ rankdir: 'LR', ranksep: 0, nodesep: 20, edgesep: 0 })
13
-
14
- nodes.forEach((node) => {
15
- if (node.position.x !== 0 || node.position.y !== 0) {
16
- dagreGraph.setNode(node.id, {
17
- width: node.measured?.width,
18
- height: node.measured?.height,
19
- x: node.position.x,
20
- y: node.position.y,
21
- })
22
- } else {
23
- dagreGraph.setNode(node.id, {
24
- width: node.measured?.width,
25
- height: node.measured?.height,
26
- })
27
- }
28
- })
29
-
30
- edges.forEach((edge) => {
31
- if (typeof edge.label === 'string') {
32
- dagreGraph.setEdge(edge.source, edge.target, {
33
- label: edge.label ?? '',
34
- width: edge.label.length * 40, // Add width for the label
35
- height: 30, // Add height for the label
36
- labelpos: 'c', // Position label in center
37
- })
38
- } else {
39
- dagreGraph.setEdge(edge.source, edge.target)
40
- }
41
- })
42
-
43
- dagre.layout(dagreGraph)
44
-
45
- return nodes.map((node) => {
46
- if (node.position.x !== 0 || node.position.y !== 0) {
47
- return node
48
- }
49
-
50
- const { x, y } = dagreGraph.node(node.id)
51
- const position = {
52
- x: x - (node.measured?.width ?? 0) / 2,
53
- y: y - (node.measured?.height ?? 0) / 2,
54
- }
55
-
56
- return { ...node, position }
57
- })
58
- }
59
-
60
- type Props = {
61
- onInitialized: () => void
62
- nodes: Node<NodeData>[]
63
- edges: Edge<EdgeData>[]
64
- }
65
-
66
- export const NodeOrganizer: React.FC<Props> = ({ onInitialized, nodes, edges }) => {
67
- const { setNodes, getNodes, getEdges, fitView } = useReactFlow()
68
- const nodesInitialized = useNodesInitialized()
69
- const initialized = useRef(false)
70
-
71
- const lastNodesRef = useRef<Node<NodeData>[]>([])
72
- const lastEdgesRef = useRef<Edge<EdgeData>[]>([])
73
-
74
- useEffect(() => {
75
- if (nodesInitialized) {
76
- if (isEqual(lastNodesRef.current, nodes) && isEqual(lastEdgesRef.current, edges)) {
77
- return
78
- }
79
-
80
- lastNodesRef.current = nodes
81
- lastEdgesRef.current = edges
82
-
83
- try {
84
- const nodesToOrganize = nodes.some((node) => node.position.x === 0 && node.position.y === 0)
85
-
86
- if (nodesToOrganize) {
87
- const organizedNodes = organizeNodes(nodes, edges)
88
- setNodes(organizedNodes)
89
- }
90
-
91
- if (!initialized.current) {
92
- initialized.current = true
93
- onInitialized()
94
- setTimeout(() => fitView(), 1)
95
- }
96
- } catch (error) {
97
- console.error('Error organizing nodes:', error)
98
- }
99
- }
100
- }, [nodesInitialized, onInitialized, setNodes, getNodes, getEdges, fitView, nodes, edges])
101
-
102
- return null
103
- }
@@ -1,6 +0,0 @@
1
- import { ApiNode } from '../../../publicComponents/api-node'
2
- import type { ApiNodeProps } from '../../../publicComponents/node-props'
3
-
4
- export const ApiFlowNode = ({ data }: ApiNodeProps) => {
5
- return <ApiNode data={data} />
6
- }
@@ -1,6 +0,0 @@
1
- import { CronNode } from '../../../publicComponents/cron-node'
2
- import type { CronNodeProps } from '../../../publicComponents/node-props'
3
-
4
- export const CronFlowNode = ({ data }: CronNodeProps) => {
5
- return <CronNode data={data} />
6
- }
@@ -1,6 +0,0 @@
1
- import { EventNode } from '../../../publicComponents/event-node'
2
- import type { EventNodeProps } from '../../../publicComponents/node-props'
3
-
4
- export const EventFlowNode = ({ data }: EventNodeProps) => {
5
- return <EventNode data={data} />
6
- }
@@ -1,6 +0,0 @@
1
- import type { NoopNodeProps } from '../../../publicComponents/node-props'
2
- import { NoopNode } from '../../../publicComponents/noop-node'
3
-
4
- export const NoopFlowNode = ({ data }: NoopNodeProps) => {
5
- return <NoopNode data={data} />
6
- }
@@ -1,110 +0,0 @@
1
- import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@motiadev/ui'
2
- import { Rocket } from 'lucide-react'
3
- import { useState } from 'react'
4
- import { motiaAnalytics } from '../../lib/motia-analytics'
5
-
6
- export const DeployButton = () => {
7
- const [isOpen, setIsOpen] = useState(false)
8
-
9
- const onDeployButtonClick = () => {
10
- motiaAnalytics.track('deploy_button_clicked')
11
- }
12
-
13
- const onDeployClick = () => {
14
- setIsOpen(false)
15
- motiaAnalytics.track('deploy_button_deploy_clicked')
16
- }
17
-
18
- const onClose = () => {
19
- setIsOpen(false)
20
- motiaAnalytics.track('deploy_button_closed')
21
- }
22
-
23
- const onMotiaCloudClick = () => {
24
- setIsOpen(true)
25
- motiaAnalytics.track('deploy_button_motia_cloud_clicked')
26
- }
27
-
28
- const onSelfHostedClick = () => {
29
- motiaAnalytics.track('deploy_button_self_hosted_clicked')
30
- window.open('https://www.motia.dev/docs/deployment-guide/self-hosted', '_blank')
31
- }
32
-
33
- return (
34
- <>
35
- {isOpen && (
36
- <div>
37
- {/* backdrop container */}
38
- <div className="fixed inset-0 z-[9999] bg-black/20 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
39
-
40
- <div className="driver-popover w-[600px]! fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[10000] animate-in fade-in-0 zoom-in-95">
41
- <img
42
- src="https://oxhhfuuoqzsaqthfairn.supabase.co/storage/v1/object/public/public-images/preview.png"
43
- alt="Motia Cloud"
44
- className="driver-popover-image object-cover"
45
- style={{ height: 393, width: '100%' }}
46
- />
47
-
48
- <div className="driver-popover-title">
49
- <h2 className="popover-title">Motia Cloud is Live!</h2>
50
- </div>
51
-
52
- <div className="driver-popover-description">
53
- Deploy to production in minutes, not hours. One click gets your Motia project live with enterprise-grade
54
- reliability. Seamlessly scale, rollback instantly, and monitor everything in real-time. Your code deserves
55
- infrastructure that just works.
56
- </div>
57
-
58
- <a
59
- href="https://www.motia.dev/docs/concepts/deployment/motia-cloud/features"
60
- target="_blank"
61
- className="text-foreground text-xs font-semibold px-4 hover:underline"
62
- rel="noopener"
63
- >
64
- Learn more about Motia Cloud
65
- </a>
66
-
67
- <div className="driver-popover-footer flex items-center justify-end">
68
- <div className="driver-popover-navigation-btns flex gap-6">
69
- <button
70
- className="tutorial-opt-out-button text-sm! font-semibold! text-muted-foreground!"
71
- onClick={onClose}
72
- >
73
- Close
74
- </button>
75
- <a
76
- href="https://motia.cloud?utm_source=workbench&utm_medium=referral"
77
- target="_blank"
78
- onClick={onDeployClick}
79
- rel="noopener"
80
- >
81
- <button className="driver-popover-next-btn">Deploy!</button>
82
- </a>
83
- </div>
84
- </div>
85
- </div>
86
- </div>
87
- )}
88
- <DropdownMenu>
89
- <DropdownMenuTrigger asChild>
90
- <Button
91
- variant="ghost"
92
- className="font-semibold text-sm dark:bg-white dark:text-black dark:hover:bg-white/90 bg-black/90 hover:bg-black/80 text-white"
93
- onClick={onDeployButtonClick}
94
- >
95
- <Rocket />
96
- Deploy
97
- </Button>
98
- </DropdownMenuTrigger>
99
- <DropdownMenuContent className="bg-background text-foreground w-56">
100
- <DropdownMenuItem className="cursor-pointer h-10 font-semibold" onClick={onMotiaCloudClick}>
101
- Motia Cloud
102
- </DropdownMenuItem>
103
- <DropdownMenuItem className="cursor-pointer h-10 font-semibold" onClick={onSelfHostedClick}>
104
- Self-Hosted (Docker)
105
- </DropdownMenuItem>
106
- </DropdownMenuContent>
107
- </DropdownMenu>
108
- </>
109
- )
110
- }
@@ -1,39 +0,0 @@
1
- import { useThemeStore } from '@motiadev/ui'
2
- import type React from 'react'
3
- import { memo, useEffect } from 'react'
4
- // @ts-expect-error: PNG asset types
5
- import motiaLogoDark from '../../assets/motia-dark.png'
6
- // @ts-expect-error: PNG asset types
7
- import motiaLogoLight from '../../assets/motia-light.png'
8
- import { useMotiaConfigStore } from '../../stores/use-motia-config-store'
9
- import { Tutorial } from '../tutorial/tutorial'
10
- import { TutorialButton } from '../tutorial/tutorial-button'
11
- import { ThemeToggle } from '../ui/theme-toggle'
12
- import { DeployButton } from './deploy-button'
13
-
14
- export const Header: React.FC = memo(() => {
15
- const theme = useThemeStore((state) => state.theme)
16
- const logo = theme === 'light' ? motiaLogoLight : motiaLogoDark
17
-
18
- const config = useMotiaConfigStore((state) => state.config)
19
- const fetchConfig = useMotiaConfigStore((state) => state.fetchConfig)
20
-
21
- useEffect(() => {
22
- fetchConfig()
23
- }, [])
24
-
25
- const isDevMode = config?.isDev ?? false
26
- const isTutorialDisabled = config?.isTutorialDisabled ?? true
27
-
28
- return (
29
- <header className="min-h-16 px-4 gap-4 flex items-center bg-default text-default-foreground border-b">
30
- <img src={logo} className="h-5" id="logo-icon" data-testid="logo-icon" />
31
- <div className="flex-1" />
32
- <ThemeToggle />
33
- {isDevMode && !isTutorialDisabled && <TutorialButton />}
34
- {isDevMode && <DeployButton />}
35
- {!isTutorialDisabled && <Tutorial />}
36
- </header>
37
- )
38
- })
39
- Header.displayName = 'Header'
@@ -1,10 +0,0 @@
1
- import type { PropsWithChildren } from 'react'
2
- import { memo } from 'react'
3
- import { useAnalytics } from '../lib/motia-analytics'
4
-
5
- export const RootMotia: React.FC<PropsWithChildren> = memo(({ children }) => {
6
- useAnalytics()
7
-
8
- return children
9
- })
10
- RootMotia.displayName = 'RootMotia'
@@ -1,40 +0,0 @@
1
- import { CollapsiblePanel, TabsContent, TabsList, TabsTrigger } 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
- import { useTabsStore } from '../stores/use-tabs-store'
6
-
7
- const topTabsSelector = (state: AppTabsState) => state.tabs[TabLocation.TOP]
8
- const topPanelId = 'top-panel'
9
-
10
- export const TopPanel = memo(() => {
11
- const defaultTab = useTabsStore((state) => state.tab.top)
12
- const setTopTab = useTabsStore((state) => state.setTopTab)
13
- const tabs = useAppTabsStore(useShallow(topTabsSelector))
14
-
15
- return (
16
- <CollapsiblePanel
17
- id={topPanelId}
18
- variant={'tabs'}
19
- defaultTab={defaultTab}
20
- onTabChange={setTopTab}
21
- withResizeHandle
22
- header={
23
- <TabsList>
24
- {tabs.map(({ id, tabLabel: Label }) => (
25
- <TabsTrigger key={id} value={id} data-testid={`${id.toLowerCase()}-link`} className="cursor-pointer">
26
- <Label />
27
- </TabsTrigger>
28
- ))}
29
- </TabsList>
30
- }
31
- >
32
- {tabs.map(({ id, content: Element }) => (
33
- <TabsContent key={id} value={id} className="h-full">
34
- <Element />
35
- </TabsContent>
36
- ))}
37
- </CollapsiblePanel>
38
- )
39
- })
40
- TopPanel.displayName = 'TopPanel'
@@ -1,26 +0,0 @@
1
- import type { TutorialStep } from './tutorial-types'
2
-
3
- class Tutorial {
4
- public steps: TutorialStep[] = []
5
- private onStepsRegisteredCallbacks: (() => void)[] = []
6
- private onOpenCallbacks: (() => void)[] = []
7
-
8
- register(steps: TutorialStep[]) {
9
- this.steps = steps
10
- this.onStepsRegisteredCallbacks.forEach((callback) => callback())
11
- }
12
-
13
- onStepsRegistered(callback: () => void) {
14
- this.onStepsRegisteredCallbacks.push(callback)
15
- }
16
-
17
- onOpen(callback: () => void) {
18
- this.onOpenCallbacks.push(callback)
19
- }
20
-
21
- open() {
22
- this.onOpenCallbacks.forEach((callback) => callback())
23
- }
24
- }
25
-
26
- export const MotiaTutorial = new Tutorial()
@@ -1,26 +0,0 @@
1
- export type TutorialActionClick = {
2
- type: 'click'
3
- selector: string
4
- optional?: boolean
5
- }
6
-
7
- export type TutorialActionEditor = {
8
- type: 'fill-editor'
9
- content: Record<string, any>
10
- }
11
-
12
- export type TutorialAction = TutorialActionClick | TutorialActionEditor
13
-
14
- export type TutorialImage = {
15
- height: number
16
- src: string
17
- }
18
-
19
- export type TutorialStep = {
20
- title: string
21
- description: React.FC<void>
22
- image?: TutorialImage
23
- link?: string
24
- elementXpath?: string
25
- before?: TutorialAction[]
26
- }