@motiadev/workbench 0.13.0-beta.162-846200 → 0.13.0-beta.162-813224

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 -1065
  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 +51 -55
  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,53 +0,0 @@
1
- export const workbenchXPath = {
2
- sidebarContainer: '//div[@data-testid="sidebar-panel"]',
3
- closePanelButton: '//div[@id="app-sidebar-container"]//button[@data-testid="close-panel"]',
4
- bottomPanel: '//div[@id="bottom-panel"]',
5
-
6
- flows: {
7
- dropdownFlow: (flowId: string) => `//div[@data-testid="dropdown-${flowId}"]`,
8
- feature: (featureId: string) => `//div[@data-feature-id="${featureId}"]`,
9
- previewButton: (stepId: string) => `//button[@data-testid="open-code-preview-button-${stepId}"]`,
10
- node: (stepId: string) => `//div[@data-testid="node-${stepId}"]`,
11
- },
12
-
13
- endpoints: {
14
- endpointsList: '//div[@data-testid="endpoints-list"]',
15
- endpoint: (method: string, path: string) => `//div[@data-testid="endpoint-${method}-${path}"]`,
16
- callPanel: '//div[@data-testid="endpoint-details-panel"]',
17
- specButton: '//button[@data-testid="endpoint-spec-button"]',
18
-
19
- bodyTab: '//button[@data-testid="endpoint-body-tab"]',
20
- headersTab: '//button[@data-testid="endpoint-headers-tab"]',
21
- paramsTab: '//button[@data-testid="endpoint-params-tab"]',
22
-
23
- callTab: '//button[@data-testid="endpoint-body-tab"]', // deprecated
24
- response: '//div[@data-testid="endpoint-response-container"]',
25
- playButton: '//button[@data-testid="endpoint-play-button"]',
26
- },
27
-
28
- tracing: {
29
- trace: (index: number) => `(//button[contains(@class, 'motia-trace-group')])[${index}]`,
30
- details: '//div[@data-testid="trace-details"]',
31
- timeline: (index: number) => `(//div[@data-testid="trace-timeline-item"])[${index}]`,
32
- },
33
-
34
- logs: {
35
- container: '//div[@data-testid="logs-container"]',
36
- searchContainer: '//div[@data-testid="logs-search-container"]',
37
- traceColumn: (index: number) => `(//td[starts-with(@data-testid, 'trace')])[${index}]`,
38
- row: '//div[@data-testid="log-row"]',
39
- },
40
-
41
- states: {
42
- container: '//div[@data-testid="states-container"]',
43
- row: (index: number) => `(//tr[starts-with(@data-testid, 'item-')])[${index}]`,
44
- },
45
-
46
- links: {
47
- flows: '//div[@data-testid="flows-dropdown-trigger"]',
48
- endpoints: '//button[@data-testid="endpoints-link"]',
49
- tracing: '//button[@data-testid="tracing-link"]',
50
- logs: '//button[@data-testid="logs-link"]',
51
- states: '//button[@data-testid="states-link"]',
52
- },
53
- }
@@ -1,26 +0,0 @@
1
- export const waitForElementByXPath = async (
2
- xpath: string,
3
- optional: boolean = false,
4
- maxAttempts: number = 50,
5
- delayMs: number = 50,
6
- ): Promise<HTMLElement | null> => {
7
- let attempts = 0
8
-
9
- while (attempts < maxAttempts) {
10
- const result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
11
- const targetElement = result?.singleNodeValue as HTMLElement | undefined
12
-
13
- if (targetElement) {
14
- return targetElement
15
- } else if (optional) {
16
- return null
17
- }
18
-
19
- await new Promise((resolve) => setTimeout(resolve, delayMs))
20
- attempts++
21
- }
22
-
23
- console.warn(`Element not found after maximum attempts: ${xpath}`)
24
-
25
- return null
26
- }
@@ -1,213 +0,0 @@
1
- import { useEffect, useRef, useState } from 'react'
2
- import { MotiaTutorial } from '../engine/tutorial-engine'
3
- import type { TutorialImage } from '../engine/tutorial-types'
4
- import { waitForElementByXPath } from './tutorial-utils'
5
-
6
- export const useTutorialEngine = () => {
7
- const ref = useRef<HTMLDivElement>(null)
8
- const highlighterRef = useRef<HTMLDivElement>(null)
9
-
10
- const [title, setTitle] = useState('')
11
- const [description, setDescription] = useState<React.ReactNode | undefined>(undefined)
12
- const [image, setImage] = useState<TutorialImage | undefined>(undefined)
13
- const [link, setLink] = useState<string | undefined>(undefined)
14
- const [currentStep, setCurrentStep] = useState(0)
15
- const [totalSteps, setTotalSteps] = useState(MotiaTutorial.steps.length)
16
-
17
- const manualOpenRef = useRef(false)
18
- const loading = useRef(false)
19
- const currentStepRef = useRef(0)
20
-
21
- const moveComponent = (x: number, y: number) => {
22
- if (ref.current) {
23
- ref.current.style.position = 'absolute'
24
- ref.current.style.left = `${x}px`
25
- ref.current.style.top = `${y}px`
26
- }
27
- }
28
-
29
- const moveStep = async (stepNumber: number) => {
30
- const container = ref.current
31
-
32
- if (container && !loading.current) {
33
- if (stepNumber >= MotiaTutorial.steps.length) {
34
- onClose()
35
- return
36
- }
37
-
38
- if (container.parentElement) {
39
- container.style.transition = 'all 0.3s ease-in-out'
40
- container.parentElement.style.opacity = '1'
41
- container.parentElement.style.display = 'block'
42
- }
43
-
44
- loading.current = true
45
- currentStepRef.current = stepNumber
46
-
47
- const step = MotiaTutorial.steps[stepNumber]
48
-
49
- // Run any before actions
50
- if (step.before) {
51
- for (const action of step.before) {
52
- const monaco = (window as any).monaco
53
- if (action.type === 'click') {
54
- const element = await waitForElementByXPath(action.selector, action.optional)
55
-
56
- if (element) {
57
- element.scrollIntoView({ behavior: 'smooth', block: 'center' })
58
- element.click()
59
- element.dispatchEvent(
60
- new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', keyCode: 13 }),
61
- )
62
- }
63
- } else if (action.type === 'fill-editor' && monaco) {
64
- monaco.editor?.getEditors?.()?.[0]?.setValue(JSON.stringify(action.content, null, 2))
65
- }
66
- }
67
- }
68
-
69
- setCurrentStep(stepNumber + 1)
70
- setTitle(step.title)
71
- setDescription(await step.description())
72
- setImage(step.image)
73
- setLink(step.link)
74
-
75
- setTimeout(async () => {
76
- const { height, width } = container.getBoundingClientRect()
77
-
78
- if (step.elementXpath) {
79
- const targetElement = await waitForElementByXPath(step.elementXpath)
80
-
81
- if (!targetElement) {
82
- console.warn(`Element not found after maximum attempts: ${step.elementXpath}`)
83
- loading.current = false
84
- return
85
- }
86
-
87
- if (targetElement && highlighterRef.current) {
88
- const { top, left, width, height } = targetElement.getBoundingClientRect()
89
- highlighterRef.current.style.top = `${top - 0}px`
90
- highlighterRef.current.style.left = `${left - 0}px`
91
- highlighterRef.current.style.width = `${width + 0}px`
92
- highlighterRef.current.style.height = `${height + 0}px`
93
- }
94
-
95
- // Position tutorial relative to target element
96
- if (targetElement) {
97
- const targetRect = targetElement.getBoundingClientRect()
98
- const spaceBelow = window.innerHeight - targetRect.bottom
99
- const spaceAbove = targetRect.top
100
- const spaceRight = window.innerWidth - targetRect.right
101
- const spaceLeft = targetRect.left
102
-
103
- // Helper function to adjust horizontal position within viewport bounds
104
- const adjustHorizontalPosition = (x: number) => {
105
- // this is important to avoid the tutorial from overflowing at far left or right
106
- return Math.max(20, Math.min(x, window.innerWidth - width - 20))
107
- }
108
-
109
- // Try to position below first
110
- if (spaceBelow >= height + 20) {
111
- const x = targetRect.left + targetRect.width / 2 - width / 2
112
- moveComponent(adjustHorizontalPosition(x), targetRect.bottom + 20)
113
- }
114
- // Try above if not enough space below
115
- else if (spaceAbove >= height + 20) {
116
- const x = targetRect.left + targetRect.width / 2 - width / 2
117
- moveComponent(adjustHorizontalPosition(x), targetRect.top - height - 20)
118
- }
119
- // Try right side
120
- else if (spaceRight >= width + 20) {
121
- moveComponent(targetRect.right + 20, targetRect.top + targetRect.height / 2 - height / 2)
122
- }
123
- // Try left side
124
- else if (spaceLeft >= width + 20) {
125
- moveComponent(targetRect.left - width - 20, targetRect.top + targetRect.height / 2 - height / 2)
126
- }
127
- }
128
- } else {
129
- if (highlighterRef.current) {
130
- highlighterRef.current.style.top = '50%'
131
- highlighterRef.current.style.left = '50%'
132
- highlighterRef.current.style.width = '1px'
133
- highlighterRef.current.style.height = '1px'
134
- }
135
- // Fallback to center of screen
136
- moveComponent(window.innerWidth / 2 - width / 2, window.innerHeight / 2 - height / 2)
137
- }
138
- }, 1)
139
-
140
- loading.current = false
141
- }
142
- }
143
-
144
- const onClose = () => {
145
- if (ref.current?.parentElement) {
146
- ref.current.parentElement.style.transition = 'opacity 0.3s ease-out'
147
- ref.current.parentElement.style.opacity = '0'
148
- localStorage.setItem('motia-tutorial-closed', 'true')
149
-
150
- setTimeout(() => {
151
- if (ref.current?.parentElement) {
152
- ref.current.parentElement.style.display = 'none'
153
- setCurrentStep(0)
154
- }
155
- }, 300)
156
- }
157
- }
158
-
159
- useEffect(() => {
160
- importFile('tutorial/tutorial.tsx')
161
- .then((module) => {
162
- if (Array.isArray(module.steps) && module.steps.length > 0) {
163
- MotiaTutorial.register(module.steps)
164
- }
165
- })
166
- .catch((error) => {
167
- // Tutorial file is optional, so we don't need to throw an error
168
- console.log('Tutorial file not found or could not be loaded:', error.message)
169
- })
170
- }, [])
171
-
172
- useEffect(() => {
173
- const container = ref.current
174
-
175
- if (container?.parentElement) {
176
- container.parentElement.style.display = 'none'
177
- }
178
-
179
- const onOpen = () => {
180
- if (container?.parentElement) {
181
- setTotalSteps(MotiaTutorial.steps.length)
182
- moveStep(0)
183
- }
184
- }
185
-
186
- MotiaTutorial.onOpen(() => {
187
- manualOpenRef.current = true
188
- onOpen()
189
- })
190
-
191
- MotiaTutorial.onStepsRegistered(() => {
192
- if (localStorage.getItem('motia-tutorial-closed') !== 'true') {
193
- manualOpenRef.current = false
194
- onOpen()
195
- }
196
- })
197
- }, [])
198
-
199
- return {
200
- ref,
201
- highlighterRef,
202
- title,
203
- description,
204
- image,
205
- link,
206
- currentStep,
207
- totalSteps,
208
- onClose,
209
- moveStep,
210
- currentStepRef,
211
- manualOpenRef,
212
- }
213
- }
@@ -1,14 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
- import { MotiaTutorial } from '../engine/tutorial-engine'
3
- import type { TutorialStep } from '../engine/tutorial-types'
4
-
5
- export const useTutorial = () => {
6
- const open = () => MotiaTutorial.open()
7
- const [steps, setSteps] = useState<TutorialStep[]>([])
8
-
9
- useEffect(() => {
10
- MotiaTutorial.onStepsRegistered(() => setSteps(MotiaTutorial.steps))
11
- }, [])
12
-
13
- return { open, steps }
14
- }
@@ -1,46 +0,0 @@
1
- import { Button } from '@motiadev/ui'
2
- import { Book } from 'lucide-react'
3
- import type { FC } from 'react'
4
- import { motiaAnalytics } from '../../lib/motia-analytics'
5
- import { Tooltip } from '../ui/tooltip'
6
- import { useTutorial } from './hooks/use-tutorial'
7
-
8
- export const TutorialButton: FC = () => {
9
- const { open, steps } = useTutorial()
10
- const isTutorialFlowMissing = steps.length === 0
11
-
12
- const onTutorialButtonClick = () => {
13
- if (!isTutorialFlowMissing) {
14
- open()
15
- }
16
-
17
- motiaAnalytics.track('tutorial_button_clicked', { isTutorialFlowMissing })
18
- }
19
-
20
- const trigger = (
21
- <Button data-testid="tutorial-trigger" variant="default" onClick={() => onTutorialButtonClick()}>
22
- <Book className="h-4 w-4" />
23
- Tutorial
24
- </Button>
25
- )
26
-
27
- if (isTutorialFlowMissing) {
28
- return (
29
- <Tooltip
30
- content={
31
- <div className="flex flex-col gap-4 p-4 max-w-[320px]">
32
- <p className="text-sm wrap-break-word p-0 m-0">
33
- In order to start the tutorial, you need to download the tutorial steps using the Motia CLI. In your
34
- terminal execute the following command to create a new project:
35
- </p>
36
- <pre className="text-sm font-bold">npx motia@latest create</pre>
37
- </div>
38
- }
39
- >
40
- {trigger}
41
- </Tooltip>
42
- )
43
- }
44
-
45
- return trigger
46
- }
@@ -1,82 +0,0 @@
1
- import { BackgroundEffect } from '@motiadev/ui'
2
- import type React from 'react'
3
- import { forwardRef, useEffect } from 'react'
4
- import type { TutorialImage } from './engine/tutorial-types'
5
-
6
- type TutorialStepProps = {
7
- step: number
8
- totalSteps: number
9
- title: string
10
- description: React.ReactNode
11
- link?: string
12
- image?: TutorialImage
13
- onNext: () => void
14
- onClose: () => void
15
- }
16
-
17
- export const TutorialStep = forwardRef<HTMLDivElement, TutorialStepProps>(
18
- ({ step, totalSteps, title, description, link, image, onNext, onClose }, ref) => {
19
- useEffect(() => {
20
- const handleKeyDown = (e: KeyboardEvent) => {
21
- if (step > 0 && e.key === 'Escape') {
22
- onClose()
23
- } else if (step > 0 && e.key === 'ArrowRight') {
24
- onNext()
25
- }
26
- }
27
- window.addEventListener('keydown', handleKeyDown)
28
-
29
- return () => window.removeEventListener('keydown', handleKeyDown)
30
- }, [onClose, onNext, step])
31
-
32
- return (
33
- <div ref={ref} className="driver-popover ">
34
- {image && (
35
- <img
36
- src={image.src}
37
- alt="Step visual"
38
- className="driver-popover-image object-cover"
39
- style={{ height: image.height, width: '100%' }}
40
- />
41
- )}
42
-
43
- <div className="isolate relative">
44
- <BackgroundEffect />
45
- <div className="driver-popover-title">
46
- <h2 className="popover-title">{title}</h2>
47
- </div>
48
-
49
- <div className="driver-popover-description">{description}</div>
50
-
51
- {link && (
52
- <a href={link} target="_blank" className="text-foreground text-xs font-semibold px-4 hover:underline">
53
- Learn more
54
- </a>
55
- )}
56
-
57
- <div className="driver-popover-footer flex items-center justify-between">
58
- <div className="text-sm text-muted-foreground font-semibold">
59
- {step} <span className="text-foreground">/</span> {totalSteps}
60
- </div>
61
-
62
- <div className="driver-popover-navigation-btns driver-popover-navigation-btns-hint flex gap-2">
63
- <button className="driver-popover-next-btn" onClick={onNext}>
64
- {step < totalSteps ? 'Continue' : 'Finish'}
65
- </button>
66
- </div>
67
- </div>
68
-
69
- {step < totalSteps && (
70
- <div className="tutorial-opt-out-container">
71
- <button className="tutorial-opt-out-button" onClick={onClose}>
72
- Close
73
- </button>
74
- </div>
75
- )}
76
- </div>
77
- </div>
78
- )
79
- },
80
- )
81
-
82
- TutorialStep.displayName = 'TutorialStep'
@@ -1,59 +0,0 @@
1
- import { motiaAnalytics } from '../../lib/motia-analytics'
2
- import { useTutorialEngine } from './hooks/use-tutorial-engine'
3
- import { TutorialStep } from './tutorial-step'
4
- import './tutorial.css'
5
-
6
- export const Tutorial = () => {
7
- const engine = useTutorialEngine()
8
-
9
- const onNext = () => {
10
- const currentStep = engine.currentStepRef.current
11
- const nextStep = currentStep + 1
12
-
13
- engine.moveStep(nextStep)
14
-
15
- if (engine.currentStep === engine.totalSteps) {
16
- motiaAnalytics.track('tutorial_completed', {
17
- manualOpen: engine.manualOpenRef.current,
18
- })
19
- } else {
20
- motiaAnalytics.track('tutorial_next_step', {
21
- step: nextStep,
22
- manualOpen: engine.manualOpenRef.current,
23
- })
24
- }
25
- }
26
-
27
- const onClose = () => {
28
- motiaAnalytics.track('tutorial_closed', {
29
- step: engine.currentStepRef.current,
30
- manualOpen: engine.manualOpenRef.current,
31
- })
32
- engine.onClose()
33
- }
34
-
35
- return (
36
- <div>
37
- {/* backdrop container */}
38
- <div className="fixed inset-0 z-[9999]" />
39
-
40
- {/* Highlighter container */}
41
- <div
42
- className="absolute top-5 left-5 w-full h-full rounded-lg shadow-[0_0_0_9999px_rgba(0,0,0,0.5)] z-[10000] pointer-events-none"
43
- ref={engine.highlighterRef}
44
- />
45
-
46
- <TutorialStep
47
- ref={engine.ref}
48
- step={engine.currentStep}
49
- totalSteps={engine.totalSteps}
50
- title={engine.title}
51
- description={engine.description}
52
- link={engine.link}
53
- image={engine.image}
54
- onNext={onNext}
55
- onClose={onClose}
56
- />
57
- </div>
58
- )
59
- }
@@ -1,68 +0,0 @@
1
- import Editor, { useMonaco } from '@monaco-editor/react'
2
- import { useThemeStore } from '@motiadev/ui'
3
- import { type FC, useEffect, useMemo } from 'react'
4
-
5
- type JsonEditorProps = {
6
- value: string
7
- height?: number | string
8
- schema?: Record<string, unknown>
9
- onChange?: (value: string) => void
10
- onValidate?: (isValid: boolean) => void
11
- language?: 'json' | string
12
- readOnly?: boolean
13
- }
14
-
15
- export const JsonEditor: FC<JsonEditorProps> = ({
16
- value,
17
- height = 300,
18
- schema,
19
- onChange,
20
- onValidate,
21
- language = 'json',
22
- readOnly = false,
23
- }) => {
24
- const monaco = useMonaco()
25
- const theme = useThemeStore((state) => state.theme)
26
- const editorTheme = useMemo(() => (theme === 'dark' ? 'vs-dark' : 'light'), [theme])
27
-
28
- useEffect(() => {
29
- if (!monaco) return
30
-
31
- // @ts-expect-error - monaco.languages.typescript.javascriptDefaults is not typed
32
- monaco.languages.typescript.javascriptDefaults.setCompilerOptions({ isolatedModules: true })
33
- // @ts-expect-error - monaco.languages.json.jsonDefaults is not typed
34
- monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
35
- schemas: schema
36
- ? [
37
- {
38
- uri: window.location.href,
39
- fileMatch: ['*'],
40
- schema,
41
- },
42
- ]
43
- : [],
44
- })
45
- }, [monaco, schema, language])
46
-
47
- return (
48
- <Editor
49
- data-testid="json-editor"
50
- height={height}
51
- language={language}
52
- value={value}
53
- theme={editorTheme}
54
- onChange={(value) => {
55
- if (!value) {
56
- onValidate?.(false)
57
- }
58
- onChange?.(value ?? '')
59
- }}
60
- onValidate={(markers) => onValidate?.(markers.length === 0)}
61
- options={{
62
- readOnly,
63
- scrollBeyondLastLine: false,
64
- minimap: { enabled: false },
65
- }}
66
- />
67
- )
68
- }
@@ -1,75 +0,0 @@
1
- import { cn } from '@motiadev/ui'
2
- import * as React from 'react'
3
-
4
- const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
5
- ({ className, ...props }, ref) => (
6
- <div className="relative w-full overflow-auto">
7
- <table ref={ref} className={cn('w-full caption-bottom text-sm', className)} {...props} />
8
- </div>
9
- ),
10
- )
11
- Table.displayName = 'Table'
12
-
13
- const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
14
- ({ className, ...props }, ref) => <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />,
15
- )
16
- TableHeader.displayName = 'TableHeader'
17
-
18
- const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
19
- ({ className, ...props }, ref) => (
20
- <tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />
21
- ),
22
- )
23
- TableBody.displayName = 'TableBody'
24
-
25
- const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
26
- ({ className, ...props }, ref) => (
27
- <tfoot ref={ref} className={cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', className)} {...props} />
28
- ),
29
- )
30
- TableFooter.displayName = 'TableFooter'
31
-
32
- const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
33
- ({ className, ...props }, ref) => (
34
- <tr
35
- ref={ref}
36
- className={cn('border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', className)}
37
- {...props}
38
- />
39
- ),
40
- )
41
- TableRow.displayName = 'TableRow'
42
-
43
- const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
44
- ({ className, ...props }, ref) => (
45
- <th
46
- ref={ref}
47
- className={cn(
48
- 'h-10 px-2 text-left align-middle text-md font-medium bg-muted text-muted-foreground font-bold [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
49
- className,
50
- )}
51
- {...props}
52
- />
53
- ),
54
- )
55
- TableHead.displayName = 'TableHead'
56
-
57
- const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
58
- ({ className, ...props }, ref) => (
59
- <td
60
- ref={ref}
61
- className={cn('p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', className)}
62
- {...props}
63
- />
64
- ),
65
- )
66
- TableCell.displayName = 'TableCell'
67
-
68
- const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(
69
- ({ className, ...props }, ref) => (
70
- <caption ref={ref} className={cn('mt-4 text-sm text-muted-foreground', className)} {...props} />
71
- ),
72
- )
73
- TableCaption.displayName = 'TableCaption'
74
-
75
- export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }