@invect/ui 0.0.1

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 (419) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/dist/Invect-CWpIwZ5F.js +92738 -0
  4. package/dist/Invect.d.ts +25 -0
  5. package/dist/InvectShell.d.ts +14 -0
  6. package/dist/api/agent-tools.api.d.ts +1 -0
  7. package/dist/api/client.d.ts +207 -0
  8. package/dist/api/credentials.api.d.ts +47 -0
  9. package/dist/api/executions.api.d.ts +43 -0
  10. package/dist/api/flows.api.d.ts +100 -0
  11. package/dist/api/index.d.ts +9 -0
  12. package/dist/api/node-data.api.d.ts +66 -0
  13. package/dist/api/query-keys.d.ts +22 -0
  14. package/dist/api/triggers.api.d.ts +44 -0
  15. package/dist/api/types.d.ts +147 -0
  16. package/dist/api/use-flow-run-stream.d.ts +12 -0
  17. package/dist/assets/invect-branding.d.ts +4 -0
  18. package/dist/assets/provider-icons/index.d.ts +8 -0
  19. package/dist/babel-C9OtljFZ.js +9721 -0
  20. package/dist/components/PageLayout.d.ts +17 -0
  21. package/dist/components/chat/ChatInput.d.ts +17 -0
  22. package/dist/components/chat/ChatMessageList.d.ts +14 -0
  23. package/dist/components/chat/ChatModelSelector.d.ts +11 -0
  24. package/dist/components/chat/ChatPanel.d.ts +19 -0
  25. package/dist/components/chat/ChatPromptOverlay.d.ts +13 -0
  26. package/dist/components/chat/ChatProviderSelector.d.ts +9 -0
  27. package/dist/components/chat/ChatSettingsPanel.d.ts +11 -0
  28. package/dist/components/chat/InlineCredentialSetup.d.ts +9 -0
  29. package/dist/components/chat/MarkdownRenderer.d.ts +7 -0
  30. package/dist/components/chat/chat-memory.d.ts +21 -0
  31. package/dist/components/chat/chat.store.d.ts +416 -0
  32. package/dist/components/chat/index.d.ts +5 -0
  33. package/dist/components/chat/use-chat.d.ts +28 -0
  34. package/dist/components/credentials/CreateCredentialModal.d.ts +13 -0
  35. package/dist/components/credentials/CredentialDetailDialog.d.ts +17 -0
  36. package/dist/components/credentials/EditCredentialModal.d.ts +11 -0
  37. package/dist/components/credentials/OAuth2ConnectButton.d.ts +38 -0
  38. package/dist/components/credentials/OAuth2ProviderSelector.d.ts +15 -0
  39. package/dist/components/credentials/credential-utils.d.ts +12 -0
  40. package/dist/components/credentials/index.d.ts +9 -0
  41. package/dist/components/dashboard/FailedRunsAlert.d.ts +3 -0
  42. package/dist/components/dashboard/FlowCard.d.ts +7 -0
  43. package/dist/components/dashboard/RecentActivityTable.d.ts +9 -0
  44. package/dist/components/dashboard/StatCard.d.ts +10 -0
  45. package/dist/components/dashboard/index.d.ts +5 -0
  46. package/dist/components/dashboard/status-helpers.d.ts +5 -0
  47. package/dist/components/flow-editor/ActionsSidebar.d.ts +2 -0
  48. package/dist/components/flow-editor/FlowEditor.d.ts +21 -0
  49. package/dist/components/flow-editor/FlowHeader.d.ts +9 -0
  50. package/dist/components/flow-editor/FlowLayout.d.ts +24 -0
  51. package/dist/components/flow-editor/ModeSwitcher.d.ts +7 -0
  52. package/dist/components/flow-editor/NodeSidebar.d.ts +24 -0
  53. package/dist/components/flow-editor/RunControls.d.ts +12 -0
  54. package/dist/components/flow-editor/ToolConfigPanel.d.ts +16 -0
  55. package/dist/components/flow-editor/ValidationPanel.d.ts +5 -0
  56. package/dist/components/flow-editor/flow-editor.store.d.ts +1 -0
  57. package/dist/components/flow-editor/index.d.ts +6 -0
  58. package/dist/components/flow-editor/inline-edit.d.ts +10 -0
  59. package/dist/components/flow-editor/node-config-panel/ConfigFieldWithTemplate.d.ts +26 -0
  60. package/dist/components/flow-editor/node-config-panel/CredentialCombobox.d.ts +21 -0
  61. package/dist/components/flow-editor/node-config-panel/CredentialsSection.d.ts +19 -0
  62. package/dist/components/flow-editor/node-config-panel/DroppableInput.d.ts +20 -0
  63. package/dist/components/flow-editor/node-config-panel/DynamicSelectField.d.ts +22 -0
  64. package/dist/components/flow-editor/node-config-panel/JsonPreviewPanel.d.ts +25 -0
  65. package/dist/components/flow-editor/node-config-panel/NodeConfigPanel.d.ts +14 -0
  66. package/dist/components/flow-editor/node-config-panel/NodeConfigPanelHeader.d.ts +15 -0
  67. package/dist/components/flow-editor/node-config-panel/ParametersSection.d.ts +16 -0
  68. package/dist/components/flow-editor/node-config-panel/SearchableSelectField.d.ts +17 -0
  69. package/dist/components/flow-editor/node-config-panel/SwitchCasesField.d.ts +18 -0
  70. package/dist/components/flow-editor/node-config-panel/hooks/index.d.ts +2 -0
  71. package/dist/components/flow-editor/node-config-panel/hooks/use-node-config-panel-state.d.ts +24 -0
  72. package/dist/components/flow-editor/node-config-panel/hooks/use-node-execution.d.ts +46 -0
  73. package/dist/components/flow-editor/node-config-panel/hooks/use-upstream-slots.d.ts +16 -0
  74. package/dist/components/flow-editor/node-config-panel/panels/AgentToolsPanel.d.ts +18 -0
  75. package/dist/components/flow-editor/node-config-panel/panels/ConfigurationPanel.d.ts +49 -0
  76. package/dist/components/flow-editor/node-config-panel/panels/DataMapperPane.d.ts +40 -0
  77. package/dist/components/flow-editor/node-config-panel/panels/InputPanel.d.ts +49 -0
  78. package/dist/components/flow-editor/node-config-panel/panels/OutputPanel.d.ts +7 -0
  79. package/dist/components/flow-editor/node-config-panel/panels/index.d.ts +4 -0
  80. package/dist/components/flow-editor/node-config-panel/types.d.ts +19 -0
  81. package/dist/components/flow-editor/node-config-panel/use-node-config-panel-store.d.ts +49 -0
  82. package/dist/components/flow-editor/node-config-panel/use-node-config-state.d.ts +26 -0
  83. package/dist/components/flow-editor/node-config-panel/use-run-node.d.ts +16 -0
  84. package/dist/components/flow-editor/node-config-panel/utils.d.ts +9 -0
  85. package/dist/components/flow-editor/serialize-to-sdk.d.ts +20 -0
  86. package/dist/components/flow-editor/toolbar-context.d.ts +2 -0
  87. package/dist/components/flow-editor/use-copy-paste.d.ts +7 -0
  88. package/dist/components/flow-editor/use-copy-paste.types.d.ts +38 -0
  89. package/dist/components/flow-editor/use-flow-editor.d.ts +44 -0
  90. package/dist/components/flow-runs-table/FlowRunsTable.d.ts +6 -0
  91. package/dist/components/flow-runs-table/index.d.ts +1 -0
  92. package/dist/components/flow-viewer/FlowRunsView.d.ts +7 -0
  93. package/dist/components/flow-viewer/FlowStatusView.d.ts +21 -0
  94. package/dist/components/flow-viewer/RunSelector.d.ts +13 -0
  95. package/dist/components/flow-viewer/RunsSidebar.d.ts +14 -0
  96. package/dist/components/flow-viewer/agent-tool-executions-list.d.ts +7 -0
  97. package/dist/components/flow-viewer/index.d.ts +1 -0
  98. package/dist/components/flow-viewer/logs-panel.d.ts +18 -0
  99. package/dist/components/flow-viewer/use-execution-log-data.d.ts +113 -0
  100. package/dist/components/graph/BatchFlowEdge.d.ts +33 -0
  101. package/dist/components/graph/LayoutSelector.d.ts +9 -0
  102. package/dist/components/graph/index.d.ts +47 -0
  103. package/dist/components/graph/styleUtils.d.ts +124 -0
  104. package/dist/components/nodes/AgentConfigPanel.d.ts +24 -0
  105. package/dist/components/nodes/AgentNode.d.ts +8 -0
  106. package/dist/components/nodes/AgentToolsBox.d.ts +41 -0
  107. package/dist/components/nodes/NodeAppendix.d.ts +19 -0
  108. package/dist/components/nodes/NodeStatusIndicator.d.ts +30 -0
  109. package/dist/components/nodes/NodeViewContext.d.ts +18 -0
  110. package/dist/components/nodes/ToolParamField.d.ts +28 -0
  111. package/dist/components/nodes/ToolSelectorModal.d.ts +80 -0
  112. package/dist/components/nodes/ToolSelectorParts.d.ts +30 -0
  113. package/dist/components/nodes/UniversalNode.d.ts +2 -0
  114. package/dist/components/nodes/createContextAwareNodes.d.ts +6 -0
  115. package/dist/components/nodes/index.d.ts +22 -0
  116. package/dist/components/nodes/nodeRegistry.d.ts +13 -0
  117. package/dist/components/nodes/withNodeContext.d.ts +7 -0
  118. package/dist/components/shared/InvectLoader.d.ts +8 -0
  119. package/dist/components/shared/InvectLogo.d.ts +9 -0
  120. package/dist/components/shared/ProviderIcon.d.ts +23 -0
  121. package/dist/components/side-menu/side-menu.d.ts +4 -0
  122. package/dist/components/sidebar/BaseSidebar.d.ts +17 -0
  123. package/dist/components/sidebar/index.d.ts +1 -0
  124. package/dist/components/triggers/CronPreview.d.ts +12 -0
  125. package/dist/components/triggers/index.d.ts +1 -0
  126. package/dist/components/ui/alert-dialog.d.ts +18 -0
  127. package/dist/components/ui/badge.d.ts +9 -0
  128. package/dist/components/ui/button.d.ts +13 -0
  129. package/dist/components/ui/card.d.ts +9 -0
  130. package/dist/components/ui/codemirror-js-editor.d.ts +25 -0
  131. package/dist/components/ui/codemirror-json-editor.d.ts +18 -0
  132. package/dist/components/ui/codemirror-nunjucks-editor.d.ts +13 -0
  133. package/dist/components/ui/codemirror-vscode-theme.d.ts +24 -0
  134. package/dist/components/ui/collapsible.d.ts +6 -0
  135. package/dist/components/ui/command.d.ts +18 -0
  136. package/dist/components/ui/dialog.d.ts +18 -0
  137. package/dist/components/ui/dropdown-menu.d.ts +25 -0
  138. package/dist/components/ui/empty-state.d.ts +21 -0
  139. package/dist/components/ui/input.d.ts +3 -0
  140. package/dist/components/ui/label.d.ts +4 -0
  141. package/dist/components/ui/popover.d.ts +10 -0
  142. package/dist/components/ui/resizable.d.ts +8 -0
  143. package/dist/components/ui/scroll-area.d.ts +5 -0
  144. package/dist/components/ui/select.d.ts +18 -0
  145. package/dist/components/ui/separator.d.ts +4 -0
  146. package/dist/components/ui/slider.d.ts +4 -0
  147. package/dist/components/ui/switch.d.ts +3 -0
  148. package/dist/components/ui/table.d.ts +10 -0
  149. package/dist/components/ui/textarea.d.ts +3 -0
  150. package/dist/components/ui/tooltip.d.ts +7 -0
  151. package/dist/components/ui/tree-view.d.ts +107 -0
  152. package/dist/contexts/AgentToolCallbacksContext.d.ts +23 -0
  153. package/dist/contexts/ApiContext.d.ts +11 -0
  154. package/dist/contexts/FlowDataContext.d.ts +9 -0
  155. package/dist/contexts/NodeRegistryContext.d.ts +14 -0
  156. package/dist/contexts/PluginRegistryContext.d.ts +39 -0
  157. package/dist/contexts/ThemeProvider.d.ts +18 -0
  158. package/dist/contexts/ValidationContext.d.ts +22 -0
  159. package/dist/demo/DemoInvect.d.ts +11 -0
  160. package/dist/demo/FlowViewer.d.ts +31 -0
  161. package/dist/demo/demo-api-client.d.ts +33 -0
  162. package/dist/demo/index.d.ts +6 -0
  163. package/dist/demo/sample-data.d.ts +1538 -0
  164. package/dist/demo.d.ts +2 -0
  165. package/dist/demo.js +2774 -0
  166. package/dist/estree-ClbRfS-1.js +7076 -0
  167. package/dist/fonts/geist-cyrillic-wght-normal.woff2 +0 -0
  168. package/dist/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
  169. package/dist/fonts/geist-latin-wght-normal.woff2 +0 -0
  170. package/dist/fonts/iosevka-latin-400-normal.woff2 +0 -0
  171. package/dist/hooks/index.d.ts +1 -0
  172. package/dist/hooks/use-document-title.d.ts +1 -0
  173. package/dist/hooks/use-flow-data.d.ts +22 -0
  174. package/dist/hooks/use-invect-portal-class.d.ts +21 -0
  175. package/dist/hooks/useFlowEditorStore.d.ts +1 -0
  176. package/dist/index.css +3 -0
  177. package/dist/index.d.ts +22 -0
  178. package/dist/index.js +717 -0
  179. package/dist/lib/utils.d.ts +2 -0
  180. package/dist/prettier.d.ts +13 -0
  181. package/dist/routes/all-flow-runs.d.ts +5 -0
  182. package/dist/routes/credentials.d.ts +5 -0
  183. package/dist/routes/flow-route-layout.d.ts +19 -0
  184. package/dist/routes/flow-runs.d.ts +5 -0
  185. package/dist/routes/flow.d.ts +5 -0
  186. package/dist/routes/home.d.ts +5 -0
  187. package/dist/services/index.d.ts +1 -0
  188. package/dist/standalone-C3Df7W52.js +3463 -0
  189. package/dist/stores/executionViewStore.d.ts +64 -0
  190. package/dist/stores/flow-editor.store.d.ts +137 -0
  191. package/dist/stores/flowEditorStore.d.ts +1 -0
  192. package/dist/stores/index.d.ts +2 -0
  193. package/dist/stores/uiStore.d.ts +45 -0
  194. package/dist/types/agent-tools.types.d.ts +53 -0
  195. package/dist/types/index.d.ts +2 -0
  196. package/dist/types/node-definition.types.d.ts +85 -0
  197. package/dist/types/plugin.types.d.ts +100 -0
  198. package/dist/utils/credentialBranding.d.ts +8 -0
  199. package/dist/utils/credentialFiltering.d.ts +20 -0
  200. package/dist/utils/flowTransformations.d.ts +16 -0
  201. package/dist/utils/layoutUtils.d.ts +23 -0
  202. package/dist/utils/nodeReferenceUtils.d.ts +37 -0
  203. package/dist/vendor.d.ts +5 -0
  204. package/package.json +130 -0
  205. package/src/.DS_Store +0 -0
  206. package/src/Invect.tsx +229 -0
  207. package/src/InvectShell.tsx +55 -0
  208. package/src/api/agent-tools.api.ts +23 -0
  209. package/src/api/client.ts +899 -0
  210. package/src/api/credentials.api.ts +197 -0
  211. package/src/api/executions.api.ts +228 -0
  212. package/src/api/flows.api.ts +195 -0
  213. package/src/api/index.ts +17 -0
  214. package/src/api/node-data.api.ts +167 -0
  215. package/src/api/query-keys.ts +44 -0
  216. package/src/api/triggers.api.ts +120 -0
  217. package/src/api/types.ts +212 -0
  218. package/src/api/use-flow-run-stream.ts +206 -0
  219. package/src/app.css +560 -0
  220. package/src/assets/.DS_Store +0 -0
  221. package/src/assets/favicon.ico +0 -0
  222. package/src/assets/fonts/geist-cyrillic-wght-normal.woff2 +0 -0
  223. package/src/assets/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
  224. package/src/assets/fonts/geist-latin-wght-normal.woff2 +0 -0
  225. package/src/assets/fonts/iosevka-latin-400-normal.woff2 +0 -0
  226. package/src/assets/invect-branding.ts +51 -0
  227. package/src/assets/provider-icons/anthropic.svg +1 -0
  228. package/src/assets/provider-icons/anthropic_light.svg +1 -0
  229. package/src/assets/provider-icons/github.svg +1 -0
  230. package/src/assets/provider-icons/github_light.svg +1 -0
  231. package/src/assets/provider-icons/gmail.svg +1 -0
  232. package/src/assets/provider-icons/google_calendar.svg +1 -0
  233. package/src/assets/provider-icons/google_docs.svg +1 -0
  234. package/src/assets/provider-icons/google_drive.svg +1 -0
  235. package/src/assets/provider-icons/google_sheets.svg +1 -0
  236. package/src/assets/provider-icons/index.ts +55 -0
  237. package/src/assets/provider-icons/linear.svg +1 -0
  238. package/src/assets/provider-icons/openai.svg +1 -0
  239. package/src/assets/provider-icons/postgres.svg +1 -0
  240. package/src/assets/provider-icons/slack.svg +1 -0
  241. package/src/assets/small-loader-dark.svg +22 -0
  242. package/src/assets/small-loader-light.svg +22 -0
  243. package/src/assets/small.svg +7 -0
  244. package/src/components/.DS_Store +0 -0
  245. package/src/components/PageLayout.tsx +55 -0
  246. package/src/components/chat/ChatInput.tsx +115 -0
  247. package/src/components/chat/ChatMessageList.tsx +788 -0
  248. package/src/components/chat/ChatModelSelector.tsx +208 -0
  249. package/src/components/chat/ChatPanel.tsx +243 -0
  250. package/src/components/chat/ChatPromptOverlay.tsx +150 -0
  251. package/src/components/chat/ChatProviderSelector.tsx +135 -0
  252. package/src/components/chat/ChatSettingsPanel.tsx +277 -0
  253. package/src/components/chat/InlineCredentialSetup.tsx +343 -0
  254. package/src/components/chat/MarkdownRenderer.tsx +140 -0
  255. package/src/components/chat/chat-memory.ts +88 -0
  256. package/src/components/chat/chat.store.ts +479 -0
  257. package/src/components/chat/index.ts +5 -0
  258. package/src/components/chat/use-chat.ts +473 -0
  259. package/src/components/credentials/CreateCredentialModal.tsx +609 -0
  260. package/src/components/credentials/CredentialDetailDialog.tsx +882 -0
  261. package/src/components/credentials/EditCredentialModal.tsx +399 -0
  262. package/src/components/credentials/OAuth2ConnectButton.tsx +288 -0
  263. package/src/components/credentials/OAuth2ProviderSelector.tsx +360 -0
  264. package/src/components/credentials/credential-utils.ts +99 -0
  265. package/src/components/credentials/index.ts +10 -0
  266. package/src/components/dashboard/FailedRunsAlert.tsx +67 -0
  267. package/src/components/dashboard/FlowCard.tsx +64 -0
  268. package/src/components/dashboard/RecentActivityTable.tsx +92 -0
  269. package/src/components/dashboard/StatCard.tsx +32 -0
  270. package/src/components/dashboard/index.ts +5 -0
  271. package/src/components/dashboard/status-helpers.tsx +102 -0
  272. package/src/components/flow-editor/ActionsSidebar.tsx +503 -0
  273. package/src/components/flow-editor/FlowEditor.tsx +1002 -0
  274. package/src/components/flow-editor/FlowHeader.tsx +87 -0
  275. package/src/components/flow-editor/FlowLayout.tsx +117 -0
  276. package/src/components/flow-editor/ModeSwitcher.tsx +49 -0
  277. package/src/components/flow-editor/NodeSidebar.tsx +343 -0
  278. package/src/components/flow-editor/RunControls.tsx +109 -0
  279. package/src/components/flow-editor/ToolConfigPanel.tsx +434 -0
  280. package/src/components/flow-editor/ValidationPanel.tsx +167 -0
  281. package/src/components/flow-editor/flow-editor.store.ts +2 -0
  282. package/src/components/flow-editor/index.ts +6 -0
  283. package/src/components/flow-editor/inline-edit.tsx +111 -0
  284. package/src/components/flow-editor/node-config-panel/ConfigFieldWithTemplate.tsx +334 -0
  285. package/src/components/flow-editor/node-config-panel/CredentialCombobox.tsx +217 -0
  286. package/src/components/flow-editor/node-config-panel/CredentialsSection.tsx +154 -0
  287. package/src/components/flow-editor/node-config-panel/DroppableInput.tsx +45 -0
  288. package/src/components/flow-editor/node-config-panel/DynamicSelectField.tsx +223 -0
  289. package/src/components/flow-editor/node-config-panel/JsonPreviewPanel.tsx +134 -0
  290. package/src/components/flow-editor/node-config-panel/NodeConfigPanel.tsx +650 -0
  291. package/src/components/flow-editor/node-config-panel/NodeConfigPanelHeader.tsx +91 -0
  292. package/src/components/flow-editor/node-config-panel/ParametersSection.tsx +144 -0
  293. package/src/components/flow-editor/node-config-panel/SearchableSelectField.tsx +126 -0
  294. package/src/components/flow-editor/node-config-panel/SwitchCasesField.tsx +212 -0
  295. package/src/components/flow-editor/node-config-panel/hooks/index.ts +2 -0
  296. package/src/components/flow-editor/node-config-panel/hooks/use-node-config-panel-state.ts +284 -0
  297. package/src/components/flow-editor/node-config-panel/hooks/use-node-execution.ts +287 -0
  298. package/src/components/flow-editor/node-config-panel/hooks/use-upstream-slots.ts +310 -0
  299. package/src/components/flow-editor/node-config-panel/panels/AgentToolsPanel.tsx +837 -0
  300. package/src/components/flow-editor/node-config-panel/panels/ConfigurationPanel.tsx +383 -0
  301. package/src/components/flow-editor/node-config-panel/panels/DataMapperPane.tsx +456 -0
  302. package/src/components/flow-editor/node-config-panel/panels/InputPanel.tsx +338 -0
  303. package/src/components/flow-editor/node-config-panel/panels/OutputPanel.tsx +109 -0
  304. package/src/components/flow-editor/node-config-panel/panels/index.ts +4 -0
  305. package/src/components/flow-editor/node-config-panel/types.ts +20 -0
  306. package/src/components/flow-editor/node-config-panel/use-node-config-panel-store.ts +283 -0
  307. package/src/components/flow-editor/node-config-panel/use-node-config-state.ts +172 -0
  308. package/src/components/flow-editor/node-config-panel/use-run-node.ts +147 -0
  309. package/src/components/flow-editor/node-config-panel/utils.ts +73 -0
  310. package/src/components/flow-editor/serialize-to-sdk.ts +204 -0
  311. package/src/components/flow-editor/toolbar-context.ts +9 -0
  312. package/src/components/flow-editor/use-copy-paste.ts +575 -0
  313. package/src/components/flow-editor/use-copy-paste.types.ts +35 -0
  314. package/src/components/flow-editor/use-flow-editor.ts +241 -0
  315. package/src/components/flow-runs-table/FlowRunsTable.tsx +631 -0
  316. package/src/components/flow-runs-table/index.ts +1 -0
  317. package/src/components/flow-viewer/FlowRunsView.tsx +268 -0
  318. package/src/components/flow-viewer/FlowStatusView.tsx +351 -0
  319. package/src/components/flow-viewer/RunSelector.tsx +422 -0
  320. package/src/components/flow-viewer/RunsSidebar.tsx +125 -0
  321. package/src/components/flow-viewer/agent-tool-executions-list.tsx +298 -0
  322. package/src/components/flow-viewer/index.ts +1 -0
  323. package/src/components/flow-viewer/logs-panel.tsx +567 -0
  324. package/src/components/flow-viewer/use-execution-log-data.ts +374 -0
  325. package/src/components/graph/BatchFlowEdge.tsx +229 -0
  326. package/src/components/graph/LayoutSelector.tsx +42 -0
  327. package/src/components/graph/index.ts +61 -0
  328. package/src/components/graph/styleUtils.ts +375 -0
  329. package/src/components/nodes/.DS_Store +0 -0
  330. package/src/components/nodes/AgentConfigPanel.tsx +1033 -0
  331. package/src/components/nodes/AgentNode.tsx +298 -0
  332. package/src/components/nodes/AgentToolsBox.tsx +193 -0
  333. package/src/components/nodes/NodeAppendix.tsx +98 -0
  334. package/src/components/nodes/NodeStatusIndicator.tsx +74 -0
  335. package/src/components/nodes/NodeViewContext.tsx +45 -0
  336. package/src/components/nodes/ToolParamField.tsx +282 -0
  337. package/src/components/nodes/ToolSelectorModal.tsx +648 -0
  338. package/src/components/nodes/ToolSelectorParts.tsx +505 -0
  339. package/src/components/nodes/UniversalNode.tsx +356 -0
  340. package/src/components/nodes/createContextAwareNodes.ts +19 -0
  341. package/src/components/nodes/index.ts +45 -0
  342. package/src/components/nodes/nodeRegistry.ts +50 -0
  343. package/src/components/nodes/withNodeContext.tsx +55 -0
  344. package/src/components/shared/InvectLoader.tsx +59 -0
  345. package/src/components/shared/InvectLogo.tsx +59 -0
  346. package/src/components/shared/ProviderIcon.tsx +115 -0
  347. package/src/components/side-menu/side-menu.tsx +267 -0
  348. package/src/components/sidebar/BaseSidebar.tsx +148 -0
  349. package/src/components/sidebar/index.ts +1 -0
  350. package/src/components/triggers/CronPreview.tsx +243 -0
  351. package/src/components/triggers/index.ts +1 -0
  352. package/src/components/ui/alert-dialog.tsx +152 -0
  353. package/src/components/ui/badge.tsx +39 -0
  354. package/src/components/ui/button.tsx +58 -0
  355. package/src/components/ui/card.tsx +75 -0
  356. package/src/components/ui/codemirror-js-editor.tsx +432 -0
  357. package/src/components/ui/codemirror-json-editor.tsx +816 -0
  358. package/src/components/ui/codemirror-nunjucks-editor.tsx +451 -0
  359. package/src/components/ui/codemirror-vscode-theme.ts +243 -0
  360. package/src/components/ui/collapsible.tsx +12 -0
  361. package/src/components/ui/command.tsx +162 -0
  362. package/src/components/ui/dialog.tsx +140 -0
  363. package/src/components/ui/dropdown-menu.tsx +232 -0
  364. package/src/components/ui/empty-state.tsx +93 -0
  365. package/src/components/ui/input.tsx +26 -0
  366. package/src/components/ui/label.tsx +19 -0
  367. package/src/components/ui/popover.tsx +53 -0
  368. package/src/components/ui/resizable.tsx +61 -0
  369. package/src/components/ui/scroll-area.tsx +56 -0
  370. package/src/components/ui/select.tsx +179 -0
  371. package/src/components/ui/separator.tsx +26 -0
  372. package/src/components/ui/slider.tsx +58 -0
  373. package/src/components/ui/switch.tsx +22 -0
  374. package/src/components/ui/table.tsx +90 -0
  375. package/src/components/ui/textarea.tsx +23 -0
  376. package/src/components/ui/tooltip.tsx +54 -0
  377. package/src/components/ui/tree-view.tsx +574 -0
  378. package/src/contexts/AgentToolCallbacksContext.tsx +31 -0
  379. package/src/contexts/ApiContext.tsx +51 -0
  380. package/src/contexts/FlowDataContext.tsx +21 -0
  381. package/src/contexts/NodeRegistryContext.tsx +54 -0
  382. package/src/contexts/PluginRegistryContext.tsx +182 -0
  383. package/src/contexts/ThemeProvider.tsx +106 -0
  384. package/src/contexts/ValidationContext.tsx +122 -0
  385. package/src/demo/DemoInvect.tsx +42 -0
  386. package/src/demo/FlowViewer.tsx +294 -0
  387. package/src/demo/demo-api-client.ts +246 -0
  388. package/src/demo/index.ts +28 -0
  389. package/src/demo/sample-data.ts +1980 -0
  390. package/src/hooks/index.ts +1 -0
  391. package/src/hooks/use-document-title.ts +8 -0
  392. package/src/hooks/use-flow-data.ts +144 -0
  393. package/src/hooks/use-invect-portal-class.ts +27 -0
  394. package/src/hooks/useFlowEditorStore.ts +2 -0
  395. package/src/index.ts +70 -0
  396. package/src/lib/utils.ts +6 -0
  397. package/src/prettier.d.ts +13 -0
  398. package/src/routes/all-flow-runs.tsx +27 -0
  399. package/src/routes/credentials.tsx +362 -0
  400. package/src/routes/flow-route-layout.tsx +113 -0
  401. package/src/routes/flow-runs.tsx +22 -0
  402. package/src/routes/flow.tsx +22 -0
  403. package/src/routes/home.tsx +282 -0
  404. package/src/services/index.ts +6 -0
  405. package/src/stores/executionViewStore.ts +211 -0
  406. package/src/stores/flow-editor.store.ts +738 -0
  407. package/src/stores/flowEditorStore.ts +2 -0
  408. package/src/stores/index.ts +10 -0
  409. package/src/stores/uiStore.ts +189 -0
  410. package/src/types/agent-tools.types.ts +64 -0
  411. package/src/types/index.ts +5 -0
  412. package/src/types/node-definition.types.ts +104 -0
  413. package/src/types/plugin.types.ts +123 -0
  414. package/src/utils/credentialBranding.ts +116 -0
  415. package/src/utils/credentialFiltering.ts +68 -0
  416. package/src/utils/flowTransformations.ts +137 -0
  417. package/src/utils/layoutUtils.ts +127 -0
  418. package/src/utils/nodeReferenceUtils.ts +135 -0
  419. package/src/vendor.d.ts +7 -0
@@ -0,0 +1,268 @@
1
+ import React, { useRef, useState, useEffect } from 'react';
2
+ import { useNavigate, useSearchParams } from 'react-router';
3
+ import { FlowLayout } from '../flow-editor/FlowLayout';
4
+ import { ModeSwitcher } from '../flow-editor/ModeSwitcher';
5
+ import { RunControls } from '../flow-editor/RunControls';
6
+ import { FlowStatusView } from './FlowStatusView';
7
+ import { LogsPanel } from './logs-panel';
8
+ import { ChatPanel, ChatToggleButton } from '~/components/chat';
9
+ import { useFlowRuns, useFlowRun, useNodeExecutions } from '../../api/executions.api';
10
+ import { useFlowRunStream } from '../../api/use-flow-run-stream';
11
+ import { useFlowReactFlowData } from '../../api/flows.api';
12
+ import { FlowRun } from '@invect/core/types';
13
+ import { useExecutionLogData, SelectedExecutionAttempt } from './use-execution-log-data';
14
+ import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '../ui/resizable';
15
+ import { useFlowActions } from '../../routes/flow-route-layout';
16
+
17
+ export interface FlowRunsViewProps {
18
+ flowId: string;
19
+ flowVersion?: string;
20
+ basePath?: string;
21
+ }
22
+
23
+ // Runs view shell - displays the flow execution history and status
24
+ export function FlowRunsView({ flowId, flowVersion, basePath }: FlowRunsViewProps) {
25
+ const viewportRef = useRef<HTMLDivElement>(null);
26
+ const navigate = useNavigate();
27
+ const [searchParams] = useSearchParams();
28
+
29
+ // Get runId from URL query parameter if present
30
+ const urlRunId = searchParams.get('runId');
31
+
32
+ const [selectedRunId, setSelectedRunId] = useState<string | null>(urlRunId);
33
+ const [isLogsExpanded, setIsLogsExpanded] = useState(true);
34
+ const [selectedAttempt, setSelectedAttempt] = useState<SelectedExecutionAttempt | null>(null);
35
+ // focusNodeId is only set when user explicitly clicks - not on auto-select
36
+ const [focusNodeId, setFocusNodeId] = useState<string | null>(null);
37
+ const [recenterTrigger, setRecenterTrigger] = useState(0);
38
+
39
+ // Fetch selected run first to get its status for polling
40
+ const { data: selectedRun } = useFlowRun(selectedRunId || '');
41
+
42
+ // SSE stream — pushes updates into React Query caches for the selected run,
43
+ // eliminating the need for polling on useFlowRun, useFlowRuns, and useNodeExecutions.
44
+ useFlowRunStream(flowId, selectedRunId);
45
+
46
+ // Fetch runs list - uses cached data from the stream when available
47
+ const { data: executionsResponse } = useFlowRuns(flowId);
48
+ const runs = executionsResponse?.data ?? [];
49
+
50
+ const { data: nodeExecutionsData, isLoading: nodeExecutionsLoading } = useNodeExecutions(
51
+ selectedRunId || '',
52
+ );
53
+
54
+ // Fetch flow graph data with execution status — no longer polls; cache is
55
+ // updated by the SSE stream for node executions, and we derive status locally.
56
+ const { data: flowGraphData } = useFlowReactFlowData(flowId, {
57
+ version: flowVersion,
58
+ flowRunId: selectedRunId || undefined,
59
+ });
60
+
61
+ const nodeExecutions = nodeExecutionsData ?? [];
62
+
63
+ const { nodes: executionLogNodes } = useExecutionLogData({
64
+ nodes: flowGraphData?.nodes,
65
+ nodeExecutions,
66
+ });
67
+
68
+ // Select run from URL param when it becomes available in the list, or fallback to latest
69
+ useEffect(() => {
70
+ // If we have a URL runId, wait for it to appear in the runs list
71
+ if (urlRunId) {
72
+ const runExists = runs.some((r) => r.id === urlRunId);
73
+ if (runExists && selectedRunId !== urlRunId) {
74
+ setSelectedRunId(urlRunId);
75
+ }
76
+ return;
77
+ }
78
+
79
+ // No URL param - auto-select latest run if none selected
80
+ if (!selectedRunId && runs.length > 0) {
81
+ setSelectedRunId(runs[0].id);
82
+ }
83
+ }, [runs, selectedRunId, urlRunId]);
84
+
85
+ useEffect(() => {
86
+ if (!executionLogNodes.length) {
87
+ if (selectedAttempt !== null) {
88
+ setSelectedAttempt(null);
89
+ }
90
+ return;
91
+ }
92
+
93
+ if (selectedAttempt) {
94
+ const nodeMatch = executionLogNodes.find((node) => node.nodeId === selectedAttempt.nodeId);
95
+ const attemptExists = nodeMatch?.attempts.some(
96
+ (attempt) => attempt.id === selectedAttempt.attemptId,
97
+ );
98
+ if (attemptExists) {
99
+ return;
100
+ }
101
+ }
102
+
103
+ const firstNodeWithAttempt = executionLogNodes.find((node) => node.attempts.length > 0);
104
+ if (firstNodeWithAttempt) {
105
+ const lastAttempt = firstNodeWithAttempt.attempts[firstNodeWithAttempt.attempts.length - 1];
106
+ setSelectedAttempt({ nodeId: firstNodeWithAttempt.nodeId, attemptId: lastAttempt.id });
107
+ } else {
108
+ setSelectedAttempt(null);
109
+ }
110
+ }, [executionLogNodes, selectedAttempt]);
111
+
112
+ const handleModeChange = (newMode: 'edit' | 'runs') => {
113
+ if (newMode === 'edit') {
114
+ const editPath = flowVersion
115
+ ? `${basePath}/flow/${flowId}/version/${flowVersion}`
116
+ : `${basePath}/flow/${flowId}`;
117
+ navigate(editPath);
118
+ }
119
+ };
120
+
121
+ // Navigate to editor and open the node config panel for the given node
122
+ const handleEditNode = (nodeId: string) => {
123
+ const editPath = flowVersion
124
+ ? `${basePath}/flow/${flowId}/version/${flowVersion}`
125
+ : `${basePath}/flow/${flowId}`;
126
+ const params = new URLSearchParams();
127
+ params.set('openNode', nodeId);
128
+ if (selectedRunId) {
129
+ params.set('fromRunId', selectedRunId);
130
+ }
131
+ navigate(`${editPath}?${params.toString()}`);
132
+ };
133
+
134
+ // Handle node click in the flow graph - expand logs and select the node
135
+ const handleNodeClick = (nodeId: string) => {
136
+ // Find the node in execution logs
137
+ const nodeMatch = executionLogNodes.find((node) => node.nodeId === nodeId);
138
+
139
+ if (nodeMatch && nodeMatch.attempts.length > 0) {
140
+ // Select the latest attempt for this node
141
+ const lastAttempt = nodeMatch.attempts[nodeMatch.attempts.length - 1];
142
+ setSelectedAttempt({ nodeId: nodeMatch.nodeId, attemptId: lastAttempt.id });
143
+ // Set focusNodeId to trigger centering (user-initiated)
144
+ setFocusNodeId(nodeMatch.nodeId);
145
+
146
+ // Expand the logs panel if not already expanded
147
+ if (!isLogsExpanded) {
148
+ setIsLogsExpanded(true);
149
+ }
150
+ } else if (nodeMatch) {
151
+ // Node exists in logs but has no attempts - just expand and show node
152
+ setSelectedAttempt({ nodeId: nodeMatch.nodeId, attemptId: '' });
153
+ setFocusNodeId(nodeMatch.nodeId);
154
+ if (!isLogsExpanded) {
155
+ setIsLogsExpanded(true);
156
+ }
157
+ }
158
+ };
159
+
160
+ // Handle selection from logs panel - also triggers centering
161
+ const handleSelectAttempt = (attempt: SelectedExecutionAttempt) => {
162
+ setSelectedAttempt(attempt);
163
+ // Set focusNodeId to trigger centering (user-initiated)
164
+ setFocusNodeId(attempt.nodeId);
165
+ };
166
+
167
+ const runSelectorItems = runs.map((r: FlowRun) => ({
168
+ id: r.id,
169
+ status: r.status,
170
+ startedAt: r.startedAt,
171
+ completedAt: r.completedAt,
172
+ }));
173
+
174
+ // Flow actions from parent layout context (execute, active state)
175
+ const flowActions = useFlowActions();
176
+
177
+ return (
178
+ <div className="flex flex-col flex-1 h-full min-h-0 imp-page bg-imp-background text-imp-foreground">
179
+ <FlowLayout
180
+ modeSwitcher={<ModeSwitcher mode="runs" onModeChange={handleModeChange} />}
181
+ viewportRef={viewportRef}
182
+ chatPanel={
183
+ <ChatPanel
184
+ flowId={flowId}
185
+ basePath={basePath}
186
+ selectedRunId={selectedRunId ?? undefined}
187
+ viewMode="runs"
188
+ />
189
+ }
190
+ toolbarExtra={
191
+ <RunControls
192
+ onExecute={flowActions?.onExecute}
193
+ isExecuting={flowActions?.isExecuting}
194
+ isActive={flowActions?.isActive}
195
+ isTogglingActive={flowActions?.isTogglingActive}
196
+ onToggleActive={flowActions?.onToggleActive}
197
+ />
198
+ }
199
+ chatToggle={<ChatToggleButton />}
200
+ sidebar={<></>}
201
+ viewport={
202
+ <ResizablePanelGroup direction="vertical" className="h-full min-h-0">
203
+ <ResizablePanel defaultSize={55} minSize={20}>
204
+ <div className="relative h-full">
205
+ <FlowStatusView
206
+ flowId={flowId}
207
+ flowVersion={flowVersion}
208
+ basePath={basePath}
209
+ selectedRunId={selectedRunId}
210
+ selectedRun={selectedRun}
211
+ logsExpanded={isLogsExpanded}
212
+ onNodeClick={handleNodeClick}
213
+ onEditNode={handleEditNode}
214
+ focusNodeId={focusNodeId}
215
+ onFocusComplete={() => setFocusNodeId(null)}
216
+ recenterTrigger={recenterTrigger}
217
+ selectedNodeId={selectedAttempt?.nodeId}
218
+ />
219
+ </div>
220
+ </ResizablePanel>
221
+ {isLogsExpanded && (
222
+ <>
223
+ <ResizableHandle
224
+ withHandle
225
+ onDragging={(isDragging) => {
226
+ if (!isDragging) {
227
+ setRecenterTrigger((c) => c + 1);
228
+ }
229
+ }}
230
+ />
231
+ <ResizablePanel defaultSize={45} minSize={10}>
232
+ <LogsPanel
233
+ nodes={executionLogNodes}
234
+ selectedAttempt={selectedAttempt}
235
+ onSelectAttempt={handleSelectAttempt}
236
+ isExpanded={isLogsExpanded}
237
+ loading={nodeExecutionsLoading && !!selectedRunId}
238
+ onToggle={() => setIsLogsExpanded(!isLogsExpanded)}
239
+ runs={runSelectorItems}
240
+ selectedRunId={selectedRunId}
241
+ onSelectRun={setSelectedRunId}
242
+ />
243
+ </ResizablePanel>
244
+ </>
245
+ )}
246
+ {!isLogsExpanded && (
247
+ <div className="shrink-0">
248
+ <LogsPanel
249
+ nodes={executionLogNodes}
250
+ selectedAttempt={selectedAttempt}
251
+ onSelectAttempt={handleSelectAttempt}
252
+ isExpanded={isLogsExpanded}
253
+ loading={nodeExecutionsLoading && !!selectedRunId}
254
+ onToggle={() => setIsLogsExpanded(!isLogsExpanded)}
255
+ runs={runSelectorItems}
256
+ selectedRunId={selectedRunId}
257
+ onSelectRun={setSelectedRunId}
258
+ />
259
+ </div>
260
+ )}
261
+ </ResizablePanelGroup>
262
+ }
263
+ />
264
+ </div>
265
+ );
266
+ }
267
+
268
+ export default FlowRunsView;
@@ -0,0 +1,351 @@
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useNavigate } from 'react-router';
3
+ import {
4
+ ReactFlow,
5
+ type NodeTypes,
6
+ type EdgeTypes,
7
+ Controls,
8
+ Background,
9
+ useReactFlow,
10
+ useNodesInitialized,
11
+ useUpdateNodeInternals,
12
+ } from '@xyflow/react';
13
+
14
+ import { BatchFlowEdge, defaultEdgeOptions } from '../graph';
15
+ import { NodeViewProvider, createContextAwareNodes } from '../nodes';
16
+ import { getNodeComponent } from '../nodes/nodeRegistry';
17
+ import { withNodeContext } from '../nodes/withNodeContext';
18
+ import { FlowRunStatus, NodeExecutionStatus, ReactFlowNodeData } from '@invect/core/types';
19
+ import { FlowRun } from '@invect/core/types';
20
+ import { Node } from '@xyflow/react';
21
+ import { useTheme } from '~/contexts/ThemeProvider';
22
+ import { InvectLoader } from '../shared/InvectLoader';
23
+
24
+ // Import shared hook
25
+ import { useFlowData } from '../../hooks/use-flow-data';
26
+
27
+ // Base node types (enum-based) wrapped with view context
28
+ const baseNodeTypes = createContextAwareNodes();
29
+
30
+ const edgeTypes = {
31
+ // invect: BatchFlowEdge,
32
+ default: BatchFlowEdge,
33
+ } as EdgeTypes;
34
+
35
+ interface FlowStatusViewProps {
36
+ flowId: string;
37
+ flowVersion?: string;
38
+ basePath?: string;
39
+ selectedRunId: string | null;
40
+ selectedRun: FlowRun | null | undefined;
41
+ logsExpanded: boolean;
42
+ onNodeClick?: (nodeId: string) => void;
43
+ onEditNode?: (nodeId: string) => void;
44
+ /** Node ID to focus/center on (only set on user-initiated selection) */
45
+ focusNodeId?: string | null;
46
+ /** Callback when focus animation completes */
47
+ onFocusComplete?: () => void;
48
+ /** Incrementing counter that triggers a recenter (e.g. after panel resize) */
49
+ recenterTrigger?: number;
50
+ /** Currently selected node ID in the execution logs (for resize recenter) */
51
+ selectedNodeId?: string | null;
52
+ }
53
+
54
+ export function FlowStatusView({
55
+ flowId,
56
+ flowVersion,
57
+ basePath: _basePath = '',
58
+ selectedRunId,
59
+ selectedRun,
60
+ logsExpanded,
61
+ onNodeClick,
62
+ onEditNode,
63
+ focusNodeId,
64
+ onFocusComplete,
65
+ recenterTrigger,
66
+ selectedNodeId,
67
+ }: FlowStatusViewProps) {
68
+ const _navigate = useNavigate();
69
+ const reactFlowInstance = useReactFlow();
70
+ const { resolvedTheme } = useTheme();
71
+
72
+ // Get flow data — execution status is streamed via SSE (useFlowRunStream in
73
+ // FlowRunsView), so we only need a single fetch of the graph structure here.
74
+ const { flowData, loading, queryError, nodes, onNodesChange, edges, onEdgesChange } = useFlowData(
75
+ flowId,
76
+ flowVersion,
77
+ selectedRunId || undefined,
78
+ );
79
+
80
+ // Wait until React Flow has measured all nodes and registered their handles
81
+ // before rendering edges. Output handles use nested relative positioning with
82
+ // CSS transforms, so we add a short delay after nodesInitialized for the
83
+ // browser to compute final handle positions. Without this, edge source points
84
+ // appear slightly disconnected from output handles.
85
+ const nodesInitialized = useNodesInitialized();
86
+ const updateNodeInternals = useUpdateNodeInternals();
87
+ const [edgesReady, setEdgesReady] = useState(false);
88
+
89
+ // nodeTypes: AGENT gets a custom component, everything else renders as
90
+ // UniversalNode. Register action types from current flow nodes to avoid
91
+ // ReactFlow's fallback CSS class; "default" catches unknown types.
92
+ // Derive a stable key from the set of unique node types so the memo only
93
+ // recomputes when a genuinely new type appears (not on every node change).
94
+ const nodeTypeKeys = useMemo(() => {
95
+ const types = new Set<string>();
96
+ for (const node of nodes) {
97
+ const nodeType = (node.data as { type?: string })?.type ?? node.type;
98
+ if (nodeType) {
99
+ types.add(nodeType);
100
+ }
101
+ }
102
+ return [...types].sort().join(',');
103
+ }, [nodes]);
104
+
105
+ const nodeTypes = useMemo(() => {
106
+ const mapping = { ...baseNodeTypes };
107
+ for (const key of nodeTypeKeys.split(',')) {
108
+ if (key && !(key in mapping)) {
109
+ mapping[key] = withNodeContext(getNodeComponent(key));
110
+ }
111
+ }
112
+ return mapping as NodeTypes;
113
+ // eslint-disable-next-line react-hooks/exhaustive-deps
114
+ }, [nodeTypeKeys]);
115
+
116
+ // Fit view when logs panel is expanded/collapsed
117
+ React.useEffect(() => {
118
+ const timeout = setTimeout(() => {
119
+ if (typeof reactFlowInstance.fitView === 'function') {
120
+ reactFlowInstance.fitView({ padding: 0.5, maxZoom: 1.2, duration: 200 });
121
+ }
122
+ }, 150);
123
+
124
+ return () => clearTimeout(timeout);
125
+ }, [logsExpanded, reactFlowInstance]);
126
+
127
+ // Keep refs for values used by the recenter effect so it only fires on recenterTrigger
128
+ const recenterStateRef = useRef({ focusNodeId, selectedNodeId, nodes, reactFlowInstance });
129
+ recenterStateRef.current = { focusNodeId, selectedNodeId, nodes, reactFlowInstance };
130
+
131
+ // Recenter after panel resize — center on selected node or fit entire flow
132
+ React.useEffect(() => {
133
+ if (!recenterTrigger) {
134
+ return;
135
+ }
136
+
137
+ const {
138
+ focusNodeId: fid,
139
+ selectedNodeId: sid,
140
+ nodes: n,
141
+ reactFlowInstance: rf,
142
+ } = recenterStateRef.current;
143
+ if (!n.length) {
144
+ return;
145
+ }
146
+
147
+ const timeout = setTimeout(() => {
148
+ const centerId = fid || sid;
149
+ if (centerId) {
150
+ const targetNode = n.find((node) => node.id === centerId);
151
+ if (targetNode) {
152
+ const nodeWidth = targetNode.measured?.width ?? targetNode.width ?? 200;
153
+ const nodeHeight = targetNode.measured?.height ?? targetNode.height ?? 60;
154
+ const x = targetNode.position.x + nodeWidth / 2;
155
+ const y = targetNode.position.y + nodeHeight / 2;
156
+ rf.setCenter(x, y, { duration: 200, zoom: rf.getZoom() });
157
+ return;
158
+ }
159
+ }
160
+ rf.fitView({ padding: 0.5, maxZoom: 1.2, duration: 200 });
161
+ }, 50);
162
+
163
+ return () => clearTimeout(timeout);
164
+ // eslint-disable-next-line react-hooks/exhaustive-deps
165
+ }, [recenterTrigger]);
166
+
167
+ // Center on focused node when user explicitly clicks (not on auto-select)
168
+ useEffect(() => {
169
+ if (!focusNodeId || !nodes.length) {
170
+ return;
171
+ }
172
+
173
+ // Find the node in the current nodes array
174
+ const targetNode = nodes.find((n) => n.id === focusNodeId);
175
+ if (!targetNode) {
176
+ return;
177
+ }
178
+
179
+ // Use setCenter to pan and zoom to the node's position
180
+ // Adding a small delay to ensure the view has settled
181
+ const timeout = setTimeout(() => {
182
+ const nodeWidth = targetNode.measured?.width ?? targetNode.width ?? 200;
183
+ const nodeHeight = targetNode.measured?.height ?? targetNode.height ?? 60;
184
+
185
+ // Center on the node's center point and zoom in
186
+ const x = targetNode.position.x + nodeWidth / 2;
187
+ const y = targetNode.position.y + nodeHeight / 2;
188
+
189
+ // Zoom to 1.5x when focusing on a node (or current zoom if already higher)
190
+ const targetZoom = Math.max(1.5, reactFlowInstance.getZoom());
191
+ reactFlowInstance.setCenter(x, y, { duration: 300, zoom: targetZoom });
192
+
193
+ // Clear the focus after centering is complete
194
+ if (onFocusComplete) {
195
+ setTimeout(onFocusComplete, 350);
196
+ }
197
+ }, 50);
198
+
199
+ return () => clearTimeout(timeout);
200
+ }, [focusNodeId, nodes, reactFlowInstance, onFocusComplete]);
201
+
202
+ // Process nodes for batch execution state management
203
+ const processNodesForBatchExecution = (originalNodes: Node[]) => {
204
+ // If there's no execution or execution is not active, return nodes as-is
205
+ if (!selectedRun || !selectedRun.status) {
206
+ return originalNodes;
207
+ }
208
+
209
+ const isExecutionActive = [
210
+ FlowRunStatus.RUNNING,
211
+ FlowRunStatus.PENDING,
212
+ FlowRunStatus.PAUSED_FOR_BATCH,
213
+ ].includes(selectedRun.status);
214
+
215
+ return originalNodes.map((node) => {
216
+ const processedNode = { ...node };
217
+ const nodeData = node.data as ReactFlowNodeData;
218
+
219
+ if (isExecutionActive) {
220
+ // If flow is running via batch, set nodes to appropriate states
221
+ if (!nodeData.executionStatus) {
222
+ // Node hasn't been executed yet - set to waiting
223
+ processedNode.data = {
224
+ ...node.data,
225
+ executionStatus: NodeExecutionStatus.PENDING,
226
+ };
227
+ } else if (nodeData.executionStatus === NodeExecutionStatus.BATCH_SUBMITTED) {
228
+ // Node is in batch processing - show as running
229
+ processedNode.data = {
230
+ ...node.data,
231
+ executionStatus: NodeExecutionStatus.RUNNING,
232
+ };
233
+ }
234
+ // Otherwise keep the current execution status (SUCCESS, FAILED, RUNNING, etc.)
235
+ } else {
236
+ // Flow is not active - clean up temporary states but keep final states
237
+ if (
238
+ nodeData.executionStatus &&
239
+ [NodeExecutionStatus.PENDING, NodeExecutionStatus.RUNNING].includes(
240
+ nodeData.executionStatus,
241
+ )
242
+ ) {
243
+ const { executionStatus, executionOutput, executionError, ...cleanData } = nodeData;
244
+ processedNode.data = cleanData;
245
+ }
246
+ }
247
+
248
+ return processedNode;
249
+ });
250
+ };
251
+
252
+ const renderedNodes = useMemo(() => processNodesForBatchExecution(nodes), [nodes, selectedRun]);
253
+
254
+ // Build a fingerprint of execution statuses for the exact node set rendered
255
+ // into React Flow. This includes temporary run-state decoration applied in
256
+ // `processNodesForBatchExecution`, not just the raw fetched node data.
257
+ const executionFingerprint = useMemo(
258
+ () =>
259
+ renderedNodes
260
+ .map((n) => {
261
+ const d = n.data as { executionStatus?: string } | undefined;
262
+ return `${n.id}:${d?.executionStatus ?? ''}`;
263
+ })
264
+ .join(','),
265
+ [renderedNodes],
266
+ );
267
+
268
+ useEffect(() => {
269
+ if (nodesInitialized && renderedNodes.length > 0) {
270
+ // Force React Flow to re-measure handle positions for all rendered nodes.
271
+ // Execution status styling changes node borders and shifts handle DOM
272
+ // positions, but React Flow caches stale coordinates until internals are
273
+ // refreshed.
274
+ updateNodeInternals(renderedNodes.map((n) => n.id));
275
+
276
+ setEdgesReady(false);
277
+ const timeout = setTimeout(() => setEdgesReady(true), 80);
278
+ return () => clearTimeout(timeout);
279
+ }
280
+ setEdgesReady(false);
281
+ // eslint-disable-next-line react-hooks/exhaustive-deps
282
+ }, [nodesInitialized, renderedNodes.length, executionFingerprint, updateNodeInternals]);
283
+
284
+ const readyEdges = edgesReady ? edges : [];
285
+
286
+ // Get current version's validation status (versions don't have isValid anymore)
287
+ // const isValidFlow = displayVersion?.isValid;
288
+
289
+ // Loading state
290
+ if (loading) {
291
+ return <InvectLoader className="w-full h-full" iconClassName="h-16" label="Loading flow..." />;
292
+ }
293
+
294
+ // Error state
295
+ if (queryError) {
296
+ return (
297
+ <div className="flex items-center justify-center w-full h-full">
298
+ <div className="p-4 border border-red-200 rounded-lg bg-red-50">
299
+ <div className="text-red-800">{JSON.stringify(queryError)}</div>
300
+ </div>
301
+ </div>
302
+ );
303
+ }
304
+
305
+ // No flow state
306
+ if (!flowData) {
307
+ return (
308
+ <div className="flex items-center justify-center w-full h-full">
309
+ <div className="text-muted-foreground">No flow data available</div>
310
+ </div>
311
+ );
312
+ }
313
+
314
+ return (
315
+ <NodeViewProvider mode="view" onEditNode={onEditNode}>
316
+ <div style={{ width: '100%', height: '100%', background: 'var(--canvas-background)' }}>
317
+ <ReactFlow
318
+ nodes={renderedNodes}
319
+ edges={readyEdges}
320
+ onNodesChange={onNodesChange}
321
+ onEdgesChange={onEdgesChange}
322
+ nodeTypes={nodeTypes}
323
+ edgeTypes={edgeTypes}
324
+ colorMode={resolvedTheme}
325
+ defaultEdgeOptions={defaultEdgeOptions}
326
+ fitView={true}
327
+ fitViewOptions={{
328
+ padding: 0.5,
329
+ maxZoom: 1.2,
330
+ }}
331
+ minZoom={0.1}
332
+ maxZoom={4}
333
+ nodesDraggable={false}
334
+ nodesConnectable={false}
335
+ nodesFocusable={true}
336
+ edgesFocusable={false}
337
+ elementsSelectable={true}
338
+ onNodeClick={(_, node) => onNodeClick?.(node.id)}
339
+ proOptions={{ hideAttribution: true }}
340
+ panOnDrag={true}
341
+ zoomOnScroll={true}
342
+ zoomOnPinch={true}
343
+ zoomOnDoubleClick={true}
344
+ >
345
+ <Background />
346
+ <Controls />
347
+ </ReactFlow>
348
+ </div>
349
+ </NodeViewProvider>
350
+ );
351
+ }