@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,816 @@
1
+ import { useEffect, useRef, useCallback, useState } from 'react';
2
+ import {
3
+ EditorView,
4
+ keymap,
5
+ highlightActiveLine,
6
+ highlightSpecialChars,
7
+ ViewPlugin,
8
+ ViewUpdate,
9
+ Decoration,
10
+ DecorationSet,
11
+ gutter,
12
+ GutterMarker,
13
+ } from '@codemirror/view';
14
+ import {
15
+ EditorState,
16
+ Extension,
17
+ RangeSetBuilder,
18
+ RangeSet,
19
+ StateField,
20
+ StateEffect,
21
+ } from '@codemirror/state';
22
+ import { json, jsonParseLinter } from '@codemirror/lang-json';
23
+ import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
24
+ import {
25
+ bracketMatching,
26
+ foldGutter,
27
+ foldKeymap,
28
+ foldEffect,
29
+ foldable,
30
+ } from '@codemirror/language';
31
+ import {
32
+ closeBrackets,
33
+ closeBracketsKeymap,
34
+ autocompletion,
35
+ completionKeymap,
36
+ } from '@codemirror/autocomplete';
37
+ import { linter, lintKeymap } from '@codemirror/lint';
38
+ import { cn } from '../../lib/utils';
39
+ import {
40
+ CODEMIRROR_IOSEVKA_FONT_STACK,
41
+ useCodeMirrorVscodePalette,
42
+ useCodeMirrorVscodeTheme,
43
+ type CodeMirrorVscodePalette,
44
+ } from './codemirror-vscode-theme';
45
+ import type { UpstreamSlot } from '../flow-editor/node-config-panel/types';
46
+ import { getIconComponent, formatNodeTypeLabel } from '../flow-editor/node-config-panel/utils';
47
+ import { createPortal } from 'react-dom';
48
+ import { useInvectPortalClass } from '../../hooks/use-invect-portal-class';
49
+
50
+ interface CodeMirrorJsonEditorProps {
51
+ value: string;
52
+ onChange?: (value: string) => void;
53
+ readOnly?: boolean;
54
+ className?: string;
55
+ minHeight?: string;
56
+ /** Disable JSON syntax error highlighting (useful for output panels showing plain text) */
57
+ disableLinting?: boolean;
58
+ /** Upstream slot metadata for inline run controls */
59
+ upstreamSlots?: UpstreamSlot[];
60
+ /** Called when user clicks the run/retry button for a slot */
61
+ onRunSlot?: (slot: UpstreamSlot) => void;
62
+ /** JSON keys to auto-fold when content is set (e.g. ['previous_nodes']) */
63
+ defaultFoldKeys?: string[];
64
+ }
65
+
66
+ // Theme extension for styling
67
+ function createEditorTheme(palette: CodeMirrorVscodePalette) {
68
+ return EditorView.theme({
69
+ '&': {
70
+ fontSize: '11px',
71
+ fontFamily: CODEMIRROR_IOSEVKA_FONT_STACK,
72
+ '--cm-vscode-widget-surface': palette.surface,
73
+ '--cm-vscode-widget-surface-alt': palette.surfaceAlt,
74
+ '--cm-vscode-widget-border': palette.border,
75
+ '--cm-vscode-widget-fg': palette.foreground,
76
+ '--cm-vscode-widget-fg-muted': palette.foregroundMuted,
77
+ '--cm-vscode-widget-fg-inverse': palette.foregroundInverse,
78
+ '--cm-vscode-widget-accent': palette.accent,
79
+ '--cm-vscode-widget-accent-hover': palette.accentHover,
80
+ '--cm-vscode-widget-selection': palette.selection,
81
+ },
82
+ '.cm-scroller, .cm-content, .cm-gutters, .cm-lineNumbers, .cm-tooltip': {
83
+ fontFamily: CODEMIRROR_IOSEVKA_FONT_STACK,
84
+ },
85
+ '.cm-content': {
86
+ padding: '8px 0',
87
+ },
88
+ '.cm-line': {
89
+ padding: '0 8px',
90
+ },
91
+ '.cm-gutters': {
92
+ paddingRight: '4px',
93
+ },
94
+ '.cm-lineNumbers .cm-gutterElement': {
95
+ padding: '0 8px 0 4px',
96
+ minWidth: '32px',
97
+ fontSize: '10px',
98
+ },
99
+ '.cm-foldGutter .cm-gutterElement': {
100
+ padding: '0 4px',
101
+ },
102
+ '&.cm-focused': {
103
+ outline: 'none',
104
+ },
105
+ '.cm-scroller': {
106
+ overflow: 'auto',
107
+ },
108
+ '.cm-tooltip': {
109
+ backgroundColor: palette.surface,
110
+ border: `1px solid ${palette.border}`,
111
+ color: palette.foreground,
112
+ boxShadow: `0 6px 18px color-mix(in srgb, ${palette.border} 35%, transparent)`,
113
+ },
114
+ '.cm-tooltip-autocomplete ul li[aria-selected]': {
115
+ backgroundColor: palette.selection,
116
+ color: palette.foreground,
117
+ },
118
+ '.cm-panels': {
119
+ backgroundColor: palette.surface,
120
+ color: palette.foreground,
121
+ borderColor: palette.border,
122
+ },
123
+ '.cm-panels.cm-panels-top': {
124
+ borderBottom: `1px solid ${palette.border}`,
125
+ },
126
+ '.cm-panels.cm-panels-bottom': {
127
+ borderTop: `1px solid ${palette.border}`,
128
+ },
129
+ '.cm-panel button': {
130
+ backgroundColor: palette.surfaceAlt,
131
+ color: palette.foreground,
132
+ border: `1px solid ${palette.border}`,
133
+ },
134
+ '.cm-panel button:hover': {
135
+ backgroundColor: palette.surface,
136
+ borderColor: palette.accent,
137
+ },
138
+ '.cm-lintRange-error': {
139
+ backgroundImage: 'none',
140
+ textDecoration: `underline wavy ${palette.error}`,
141
+ textDecorationSkipInk: 'none',
142
+ },
143
+ '.cm-lintRange-warning': {
144
+ backgroundImage: 'none',
145
+ textDecoration: `underline wavy ${palette.function}`,
146
+ textDecorationSkipInk: 'none',
147
+ },
148
+ '.cm-lint-marker-error': {
149
+ content: "'●'",
150
+ color: palette.error,
151
+ },
152
+ '.cm-lint-marker-warning': {
153
+ content: "'●'",
154
+ color: palette.function,
155
+ },
156
+ '.cm-tooltip-lint': {
157
+ padding: '4px 8px',
158
+ fontSize: '12px',
159
+ },
160
+ '.cm-completionLabel': {
161
+ fontSize: '12px',
162
+ },
163
+ '.cm-completionMatchedText': {
164
+ fontWeight: '600',
165
+ textDecoration: 'none',
166
+ },
167
+ });
168
+ }
169
+
170
+ // Read-only styling
171
+ const readOnlyTheme = EditorView.theme({
172
+ '.cm-content': {
173
+ cursor: 'default',
174
+ },
175
+ });
176
+
177
+ // =====================================
178
+ // Upstream Slot Gutter System
179
+ // =====================================
180
+
181
+ // StateEffect to update upstream slots from React
182
+ const setUpstreamSlots = StateEffect.define<UpstreamSlot[]>();
183
+
184
+ // Module-level callback ref (set from React, read by gutter click handler)
185
+ let slotCallbackRef: ((slot: UpstreamSlot) => void) | null = null;
186
+
187
+ // Module-level hover callbacks (set from React, called by gutter marker DOM events)
188
+ interface SlotHoverInfo {
189
+ slot: UpstreamSlot;
190
+ rect: DOMRect;
191
+ }
192
+ let slotHoverEnterRef: ((info: SlotHoverInfo) => void) | null = null;
193
+ let slotHoverLeaveRef: (() => void) | null = null;
194
+
195
+ // StateField that holds the current upstream slots
196
+ const upstreamSlotsField = StateField.define<UpstreamSlot[]>({
197
+ create: () => [],
198
+ update(value, tr) {
199
+ for (const effect of tr.effects) {
200
+ if (effect.is(setUpstreamSlots)) {
201
+ return effect.value;
202
+ }
203
+ }
204
+ return value;
205
+ },
206
+ });
207
+
208
+ /**
209
+ * Find the top-level JSON key on a given document line.
210
+ * Returns null if the line does not start a top-level property (e.g. nested lines, braces).
211
+ */
212
+ function findTopLevelKeyOnLine(lineText: string): string | null {
213
+ // Match lines like: "some_key": ... (with optional leading whitespace)
214
+ const m = lineText.match(/^\s*"([^"]+)"\s*:/);
215
+ return m ? m[1] : null;
216
+ }
217
+
218
+ // Play icon SVG (idle / resolved)
219
+ const PLAY_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>`;
220
+ // Spinner SVG (loading)
221
+ const SPINNER_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg>`;
222
+ // Warning SVG (error)
223
+ const WARNING_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>`;
224
+
225
+ class SlotGutterMarker extends GutterMarker {
226
+ constructor(readonly slot: UpstreamSlot) {
227
+ super();
228
+ }
229
+
230
+ eq(other: SlotGutterMarker): boolean {
231
+ return (
232
+ this.slot.sourceNodeId === other.slot.sourceNodeId && this.slot.status === other.slot.status
233
+ );
234
+ }
235
+
236
+ toDOM(): HTMLElement {
237
+ const wrapper = document.createElement('div');
238
+
239
+ let svg: string;
240
+ let className: string;
241
+
242
+ switch (this.slot.status) {
243
+ case 'loading':
244
+ svg = SPINNER_SVG;
245
+ className = 'cm-slot-loading';
246
+ break;
247
+ case 'error':
248
+ svg = WARNING_SVG;
249
+ className = 'cm-slot-error';
250
+ break;
251
+ case 'resolved':
252
+ svg = PLAY_SVG;
253
+ className = 'cm-slot-resolved';
254
+ break;
255
+ default: // idle
256
+ svg = PLAY_SVG;
257
+ className = 'cm-slot-idle';
258
+ break;
259
+ }
260
+
261
+ wrapper.className = `cm-slot-marker ${className}`;
262
+ wrapper.innerHTML = svg;
263
+
264
+ // Hover handlers — drive the React popover
265
+ const slot = this.slot;
266
+ wrapper.addEventListener('mouseenter', () => {
267
+ if (slotHoverEnterRef) {
268
+ slotHoverEnterRef({ slot, rect: wrapper.getBoundingClientRect() });
269
+ }
270
+ });
271
+ wrapper.addEventListener('mouseleave', () => {
272
+ if (slotHoverLeaveRef) {
273
+ slotHoverLeaveRef();
274
+ }
275
+ });
276
+
277
+ return wrapper;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Gutter that shows run controls for upstream slots.
283
+ * Markers appear on lines that hold a top-level JSON key matching a slot key.
284
+ */
285
+ const slotGutter = gutter({
286
+ class: 'cm-slot-gutter',
287
+ markers: (view) => {
288
+ const slots = view.state.field(upstreamSlotsField);
289
+ if (slots.length === 0) {
290
+ return RangeSet.empty;
291
+ }
292
+
293
+ const slotsByKey = new Map(slots.map((s) => [s.key, s]));
294
+ const builder = new RangeSetBuilder<GutterMarker>();
295
+
296
+ for (let i = 1; i <= view.state.doc.lines; i++) {
297
+ const line = view.state.doc.line(i);
298
+ const key = findTopLevelKeyOnLine(line.text);
299
+ if (key && slotsByKey.has(key)) {
300
+ // oxlint-disable-next-line typescript/no-non-null-assertion -- guarded by has() check above
301
+ builder.add(line.from, line.from, new SlotGutterMarker(slotsByKey.get(key)!));
302
+ }
303
+ }
304
+
305
+ return builder.finish();
306
+ },
307
+ domEventHandlers: {
308
+ click: (view, line) => {
309
+ const slots = view.state.field(upstreamSlotsField);
310
+ if (slots.length === 0) {
311
+ return false;
312
+ }
313
+
314
+ const lineText = view.state.doc.lineAt(line.from).text;
315
+ const key = findTopLevelKeyOnLine(lineText);
316
+ if (!key) {
317
+ return false;
318
+ }
319
+
320
+ const slot = slots.find((s) => s.key === key);
321
+ if (!slot || slot.status === 'loading') {
322
+ return false;
323
+ }
324
+
325
+ if (slotCallbackRef) {
326
+ slotCallbackRef(slot);
327
+ }
328
+ return true;
329
+ },
330
+ },
331
+ });
332
+
333
+ /**
334
+ * ViewPlugin that dims `null` values on lines matching unresolved or loading slots.
335
+ */
336
+ const slotValueDecorations = ViewPlugin.fromClass(
337
+ class {
338
+ decorations: DecorationSet;
339
+
340
+ constructor(view: EditorView) {
341
+ this.decorations = this.build(view);
342
+ }
343
+
344
+ update(update: ViewUpdate) {
345
+ if (
346
+ update.docChanged ||
347
+ update.viewportChanged ||
348
+ update.transactions.some((t) => t.effects.some((e) => e.is(setUpstreamSlots)))
349
+ ) {
350
+ this.decorations = this.build(update.view);
351
+ }
352
+ }
353
+
354
+ build(view: EditorView): DecorationSet {
355
+ const slots = view.state.field(upstreamSlotsField);
356
+ if (slots.length === 0) {
357
+ return Decoration.none;
358
+ }
359
+
360
+ const slotsByKey = new Map(slots.map((s) => [s.key, s]));
361
+ const builder = new RangeSetBuilder<Decoration>();
362
+
363
+ for (let i = 1; i <= view.state.doc.lines; i++) {
364
+ const line = view.state.doc.line(i);
365
+ const key = findTopLevelKeyOnLine(line.text);
366
+ if (!key) {
367
+ continue;
368
+ }
369
+
370
+ const slot = slotsByKey.get(key);
371
+ if (!slot) {
372
+ continue;
373
+ }
374
+
375
+ if (slot.status === 'idle') {
376
+ builder.add(line.from, line.from, Decoration.line({ class: 'cm-slot-idle-line' }));
377
+ } else if (slot.status === 'loading') {
378
+ builder.add(line.from, line.from, Decoration.line({ class: 'cm-slot-loading-line' }));
379
+ } else if (slot.status === 'error') {
380
+ builder.add(line.from, line.from, Decoration.line({ class: 'cm-slot-error-line' }));
381
+ }
382
+ }
383
+
384
+ return builder.finish();
385
+ }
386
+ },
387
+ { decorations: (v) => v.decorations },
388
+ );
389
+
390
+ // Slot gutter styles
391
+ const slotGutterStyles = `
392
+ .cm-slot-gutter {
393
+ width: 18px;
394
+ min-width: 18px;
395
+ }
396
+ .cm-slot-gutter .cm-gutterElement {
397
+ display: flex;
398
+ align-items: center;
399
+ justify-content: center;
400
+ padding: 0 1px;
401
+ }
402
+ .cm-slot-marker {
403
+ display: flex;
404
+ align-items: center;
405
+ justify-content: center;
406
+ cursor: pointer;
407
+ width: 14px;
408
+ height: 14px;
409
+ }
410
+ .cm-slot-marker svg {
411
+ width: 11px;
412
+ height: 11px;
413
+ }
414
+
415
+ /* Idle — always visible, accent color */
416
+ .cm-slot-idle svg {
417
+ color: var(--cm-vscode-widget-accent);
418
+ opacity: 1;
419
+ transition: opacity 0.15s, filter 0.15s;
420
+ }
421
+ .cm-slot-idle:hover svg {
422
+ filter: brightness(1.2);
423
+ }
424
+
425
+ /* Loading */
426
+ .cm-slot-loading {
427
+ cursor: default;
428
+ }
429
+ .cm-slot-loading svg {
430
+ color: var(--cm-vscode-widget-accent);
431
+ animation: cm-slot-spin 0.8s linear infinite;
432
+ }
433
+ @keyframes cm-slot-spin {
434
+ to { transform: rotate(360deg); }
435
+ }
436
+
437
+ /* Resolved — always visible, subtle */
438
+ .cm-slot-resolved svg {
439
+ color: var(--cm-vscode-widget-accent);
440
+ opacity: 0.35;
441
+ transition: opacity 0.15s;
442
+ }
443
+ .cm-slot-resolved:hover svg {
444
+ opacity: 1;
445
+ }
446
+
447
+ /* Error */
448
+ .cm-slot-error svg {
449
+ color: var(--cm-vscode-widget-error, #f85149);
450
+ }
451
+ .cm-slot-error:hover svg {
452
+ filter: brightness(1.2);
453
+ }
454
+
455
+ /* Line decorations */
456
+ .cm-slot-idle-line {
457
+ opacity: 0.55;
458
+ }
459
+ .cm-slot-loading-line {
460
+ opacity: 0.55;
461
+ animation: cm-slot-shimmer 1.5s ease-in-out infinite;
462
+ }
463
+ @keyframes cm-slot-shimmer {
464
+ 0%, 100% { opacity: 0.35; }
465
+ 50% { opacity: 0.65; }
466
+ }
467
+ .cm-slot-error-line {
468
+ background: color-mix(in srgb, var(--cm-vscode-widget-error, #f85149) 6%, transparent);
469
+ }
470
+ `;
471
+
472
+ let slotStylesInjected = false;
473
+ function injectSlotStyles() {
474
+ if (slotStylesInjected) {
475
+ return;
476
+ }
477
+ const style = document.createElement('style');
478
+ style.setAttribute('data-codemirror-slots', 'true');
479
+ style.textContent = slotGutterStyles;
480
+ document.head.appendChild(style);
481
+ slotStylesInjected = true;
482
+ }
483
+
484
+ /**
485
+ * Programmatically fold JSON keys matching the given list.
486
+ * Finds lines starting with `"key":` and folds the value region.
487
+ */
488
+ function foldMatchingKeys(view: EditorView, keys: string[]) {
489
+ const effects: Array<ReturnType<typeof foldEffect.of>> = [];
490
+ for (let i = 1; i <= view.state.doc.lines; i++) {
491
+ const line = view.state.doc.line(i);
492
+ const trimmed = line.text.trimStart();
493
+ for (const key of keys) {
494
+ if (trimmed.startsWith(`"${key}"`)) {
495
+ const range = foldable(view.state, line.from, line.to);
496
+ if (range) {
497
+ effects.push(foldEffect.of(range));
498
+ }
499
+ }
500
+ }
501
+ }
502
+ if (effects.length > 0) {
503
+ view.dispatch({ effects });
504
+ }
505
+ }
506
+
507
+ export function CodeMirrorJsonEditor({
508
+ value,
509
+ onChange,
510
+ readOnly = false,
511
+ className,
512
+ minHeight = '200px',
513
+ disableLinting = false,
514
+ upstreamSlots,
515
+ onRunSlot,
516
+ defaultFoldKeys,
517
+ }: CodeMirrorJsonEditorProps) {
518
+ const vscodePalette = useCodeMirrorVscodePalette();
519
+ const vscodeTheme = useCodeMirrorVscodeTheme();
520
+ const containerRef = useRef<HTMLDivElement>(null);
521
+ const viewRef = useRef<EditorView | null>(null);
522
+ const onChangeRef = useRef(onChange);
523
+ const initialValueRef = useRef(value);
524
+
525
+ const hasSlots = Boolean(upstreamSlots && upstreamSlots.length > 0);
526
+
527
+ // Hover state for slot popover
528
+ const [hoveredSlotInfo, setHoveredSlotInfo] = useState<SlotHoverInfo | null>(null);
529
+ const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
530
+
531
+ // Inject slot gutter styles and update callback refs
532
+ useEffect(() => {
533
+ if (hasSlots) {
534
+ injectSlotStyles();
535
+ }
536
+ if (onRunSlot) {
537
+ slotCallbackRef = onRunSlot;
538
+ }
539
+
540
+ slotHoverEnterRef = (info: SlotHoverInfo) => {
541
+ if (hoverTimeoutRef.current) {
542
+ clearTimeout(hoverTimeoutRef.current);
543
+ hoverTimeoutRef.current = null;
544
+ }
545
+ setHoveredSlotInfo(info);
546
+ };
547
+ slotHoverLeaveRef = () => {
548
+ // Small grace period so the popover doesn't flicker when moving between marker and popover
549
+ hoverTimeoutRef.current = setTimeout(() => {
550
+ setHoveredSlotInfo(null);
551
+ }, 100);
552
+ };
553
+
554
+ return () => {
555
+ if (slotCallbackRef === onRunSlot) {
556
+ slotCallbackRef = null;
557
+ }
558
+ slotHoverEnterRef = null;
559
+ slotHoverLeaveRef = null;
560
+ if (hoverTimeoutRef.current) {
561
+ clearTimeout(hoverTimeoutRef.current);
562
+ }
563
+ };
564
+ }, [onRunSlot, hasSlots]);
565
+
566
+ // Keep refs up to date
567
+ useEffect(() => {
568
+ onChangeRef.current = onChange;
569
+ }, [onChange]);
570
+
571
+ // Update initial value ref for editor creation
572
+ useEffect(() => {
573
+ initialValueRef.current = value;
574
+ }, [value]);
575
+
576
+ // Create the editor
577
+ useEffect(() => {
578
+ if (!containerRef.current) {
579
+ return;
580
+ }
581
+
582
+ const extensions: Extension[] = [
583
+ foldGutter(),
584
+ highlightSpecialChars(),
585
+ history(),
586
+ bracketMatching(),
587
+ closeBrackets(), // Auto-close brackets, quotes, etc.
588
+ autocompletion(), // Basic autocompletion
589
+ EditorView.lineWrapping,
590
+ highlightActiveLine(),
591
+ json(),
592
+ vscodeTheme,
593
+ createEditorTheme(vscodePalette),
594
+ EditorView.domEventHandlers({
595
+ keydown: (event) => {
596
+ event.stopPropagation();
597
+ return false;
598
+ },
599
+ keyup: (event) => {
600
+ event.stopPropagation();
601
+ return false;
602
+ },
603
+ }),
604
+ keymap.of([
605
+ ...defaultKeymap,
606
+ ...historyKeymap,
607
+ ...foldKeymap,
608
+ ...closeBracketsKeymap,
609
+ ...completionKeymap,
610
+ ...(!disableLinting ? lintKeymap : []),
611
+ ]),
612
+ ];
613
+
614
+ // Add JSON linting unless disabled (for output panels showing plain text)
615
+ if (!disableLinting) {
616
+ extensions.push(linter(jsonParseLinter()));
617
+ }
618
+
619
+ // Add upstream slot gutter if slots are provided
620
+ if (hasSlots) {
621
+ extensions.push(upstreamSlotsField, slotGutter, slotValueDecorations);
622
+ }
623
+
624
+ if (readOnly) {
625
+ extensions.push(EditorState.readOnly.of(true), readOnlyTheme);
626
+ } else {
627
+ // Add update listener for editable mode
628
+ extensions.push(
629
+ EditorView.updateListener.of((update) => {
630
+ if (update.docChanged && onChangeRef.current) {
631
+ onChangeRef.current(update.state.doc.toString());
632
+ }
633
+ }),
634
+ );
635
+ }
636
+
637
+ const state = EditorState.create({
638
+ doc: initialValueRef.current,
639
+ extensions,
640
+ });
641
+
642
+ const view = new EditorView({
643
+ state,
644
+ parent: containerRef.current,
645
+ });
646
+
647
+ viewRef.current = view;
648
+
649
+ // Auto-fold specified keys after creation
650
+ if (defaultFoldKeys?.length) {
651
+ requestAnimationFrame(() => {
652
+ if (viewRef.current) {
653
+ foldMatchingKeys(viewRef.current, defaultFoldKeys);
654
+ }
655
+ });
656
+ }
657
+
658
+ return () => {
659
+ view.destroy();
660
+ viewRef.current = null;
661
+ };
662
+ }, [readOnly, hasSlots, vscodePalette, vscodeTheme, disableLinting]);
663
+
664
+ // Update content when value changes from outside
665
+ useEffect(() => {
666
+ const view = viewRef.current;
667
+ if (!view) {
668
+ return;
669
+ }
670
+
671
+ const currentContent = view.state.doc.toString();
672
+ if (currentContent !== value) {
673
+ view.dispatch({
674
+ changes: {
675
+ from: 0,
676
+ to: currentContent.length,
677
+ insert: value,
678
+ },
679
+ });
680
+ // Re-fold specified keys after content update
681
+ if (defaultFoldKeys?.length) {
682
+ requestAnimationFrame(() => {
683
+ if (viewRef.current) {
684
+ foldMatchingKeys(viewRef.current, defaultFoldKeys);
685
+ }
686
+ });
687
+ }
688
+ }
689
+ }, [value]);
690
+
691
+ // Push upstream slot data into the editor state
692
+ useEffect(() => {
693
+ const view = viewRef.current;
694
+ if (!view || !hasSlots) {
695
+ return;
696
+ }
697
+
698
+ view.dispatch({
699
+ effects: setUpstreamSlots.of(upstreamSlots ?? []),
700
+ });
701
+ }, [upstreamSlots, hasSlots]);
702
+
703
+ // Handle copy for read-only mode
704
+ const handleCopy = useCallback(() => {
705
+ if (readOnly) {
706
+ navigator.clipboard.writeText(value);
707
+ }
708
+ }, [readOnly, value]);
709
+
710
+ return (
711
+ <div
712
+ ref={containerRef}
713
+ className={cn(
714
+ 'bg-background overflow-hidden h-full flex flex-col [&_.cm-editor]:flex-1 [&_.cm-editor]:min-h-0 [&_.cm-scroller]:overflow-auto',
715
+ readOnly && 'cursor-default',
716
+ className,
717
+ )}
718
+ style={minHeight ? { minHeight } : undefined}
719
+ onDoubleClick={readOnly ? handleCopy : undefined}
720
+ title={readOnly ? 'Double-click to copy' : undefined}
721
+ >
722
+ {hoveredSlotInfo &&
723
+ createPortal(
724
+ <SlotPopover
725
+ slot={hoveredSlotInfo.slot}
726
+ anchorRect={hoveredSlotInfo.rect}
727
+ onMouseEnter={() => {
728
+ // Cancel the leave timeout so popover stays open while hovered
729
+ if (hoverTimeoutRef.current) {
730
+ clearTimeout(hoverTimeoutRef.current);
731
+ hoverTimeoutRef.current = null;
732
+ }
733
+ }}
734
+ onMouseLeave={() => {
735
+ setHoveredSlotInfo(null);
736
+ }}
737
+ />,
738
+ document.body,
739
+ )}
740
+ </div>
741
+ );
742
+ }
743
+
744
+ // =====================================
745
+ // Slot Popover (React component rendered via portal)
746
+ // =====================================
747
+
748
+ function SlotPopover({
749
+ slot,
750
+ anchorRect,
751
+ onMouseEnter,
752
+ onMouseLeave,
753
+ }: {
754
+ slot: UpstreamSlot;
755
+ anchorRect: DOMRect;
756
+ onMouseEnter: () => void;
757
+ onMouseLeave: () => void;
758
+ }) {
759
+ const portalClass = useInvectPortalClass();
760
+ const Icon = getIconComponent(slot.sourceIcon);
761
+ const typeLabel = formatNodeTypeLabel(slot.sourceType);
762
+
763
+ // Position to the right of the gutter marker
764
+ const top = anchorRect.top + anchorRect.height / 2;
765
+ const left = anchorRect.right + 6;
766
+
767
+ return (
768
+ <div
769
+ className={portalClass}
770
+ style={{
771
+ position: 'fixed',
772
+ top,
773
+ left,
774
+ transform: 'translateY(-50%)',
775
+ zIndex: 9999,
776
+ pointerEvents: 'auto',
777
+ }}
778
+ onMouseEnter={onMouseEnter}
779
+ onMouseLeave={onMouseLeave}
780
+ >
781
+ <div className="rounded-lg border border-border bg-popover shadow-md min-w-[200px] max-w-[300px] overflow-hidden">
782
+ <div className="px-3 py-1.5 border-b border-border bg-muted/50">
783
+ <span className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
784
+ {slot.status === 'loading'
785
+ ? 'Running upstream node…'
786
+ : slot.status === 'resolved'
787
+ ? 'Re-run upstream node'
788
+ : slot.status === 'error'
789
+ ? 'Retry upstream node'
790
+ : 'Run upstream node'}
791
+ </span>
792
+ </div>
793
+ <div className="flex items-center gap-2.5 px-3 py-2">
794
+ <div className="flex items-center justify-center w-6 h-6 rounded-md shrink-0 bg-primary/10 text-primary">
795
+ <Icon className="w-3.5 h-3.5" />
796
+ </div>
797
+ <div className="flex flex-col gap-0.5 min-w-0">
798
+ <span className="text-xs font-medium truncate text-popover-foreground">
799
+ {slot.sourceLabel}
800
+ </span>
801
+ <span className="text-[11px] text-muted-foreground capitalize leading-snug">
802
+ {typeLabel}
803
+ </span>
804
+ </div>
805
+ </div>
806
+ {slot.status === 'error' && slot.error && (
807
+ <div className="px-3 py-1.5 border-t border-border bg-destructive/5">
808
+ <span className="text-[11px] leading-snug text-destructive line-clamp-2">
809
+ {slot.error}
810
+ </span>
811
+ </div>
812
+ )}
813
+ </div>
814
+ </div>
815
+ );
816
+ }