@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,575 @@
1
+ import { useEffect, useCallback, useRef } from 'react';
2
+ import type { Node, Edge, ReactFlowInstance } from '@xyflow/react';
3
+ import { nanoid } from 'nanoid';
4
+ import { useFlowEditorStore } from './flow-editor.store';
5
+ import { useNodeRegistry } from '~/contexts/NodeRegistryContext';
6
+ import { generateUniqueDisplayName, generateUniqueReferenceId } from '~/utils/nodeReferenceUtils';
7
+ import type { ClipboardData, ClipboardNode, ClipboardEdge } from './use-copy-paste.types';
8
+ import { serializeToSDK } from './serialize-to-sdk';
9
+ import { parseSDKText, type ParsedSDK } from '@invect/core/sdk';
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Types
13
+ // ---------------------------------------------------------------------------
14
+
15
+ interface UseCopyPasteOptions {
16
+ flowId: string;
17
+ reactFlowInstance: ReactFlowInstance | null;
18
+ }
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Helpers
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /** Deep clone JSON-safe data */
25
+ function cloneData<T>(data: T): T {
26
+ return JSON.parse(JSON.stringify(data));
27
+ }
28
+
29
+ /** Check if a keyboard event target is in an editable context (inputs, modals, etc.) */
30
+ function isEditingContext(el: HTMLElement): boolean {
31
+ if (
32
+ el.tagName === 'INPUT' ||
33
+ el.tagName === 'TEXTAREA' ||
34
+ el.tagName === 'SELECT' ||
35
+ el.isContentEditable
36
+ ) {
37
+ return true;
38
+ }
39
+ if (el.closest('.cm-editor') || el.closest('[role="dialog"]')) {
40
+ return true;
41
+ }
42
+ return false;
43
+ }
44
+
45
+ /**
46
+ * Strip credential IDs from params (used for cross-flow paste).
47
+ * Also strips credentialId from nested addedTools params.
48
+ */
49
+ function stripCredentials(params: Record<string, unknown>): Record<string, unknown> {
50
+ const cleaned = { ...params };
51
+ delete cleaned.credentialId;
52
+
53
+ if (Array.isArray(cleaned.addedTools)) {
54
+ cleaned.addedTools = (cleaned.addedTools as Array<Record<string, unknown>>).map((tool) => {
55
+ const toolCleaned = { ...tool };
56
+ if (typeof toolCleaned.params === 'object' && toolCleaned.params !== null) {
57
+ const toolParams = { ...(toolCleaned.params as Record<string, unknown>) };
58
+ delete toolParams.credentialId;
59
+ toolCleaned.params = toolParams;
60
+ }
61
+ return toolCleaned;
62
+ });
63
+ }
64
+
65
+ return cleaned;
66
+ }
67
+
68
+ /**
69
+ * Regenerate instanceIds for addedTools in agent node params.
70
+ */
71
+ function regenToolInstanceIds(params: Record<string, unknown>): Record<string, unknown> {
72
+ if (!Array.isArray(params.addedTools)) {
73
+ return params;
74
+ }
75
+
76
+ return {
77
+ ...params,
78
+ addedTools: (params.addedTools as Array<Record<string, unknown>>).map((tool) => ({
79
+ ...tool,
80
+ instanceId: nanoid(),
81
+ })),
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Convert parseSDKText output into a ClipboardData structure that
87
+ * materializePaste can consume. Maps FlowNodeDefinitions → ClipboardNode
88
+ * and edge tuples → ClipboardEdge.
89
+ */
90
+ function sdkResultToClipboard({ nodes, edges }: ParsedSDK, flowId: string): ClipboardData {
91
+ // Compute absolute positions for each node (use parsed position or default horizontal layout)
92
+ const positions = nodes.map((n, i) => n.position ?? { x: i * 250, y: 0 });
93
+
94
+ // Compute bounding box origin for relative positioning
95
+ const minX = positions.length > 0 ? Math.min(...positions.map((p) => p.x)) : 0;
96
+ const minY = positions.length > 0 ? Math.min(...positions.map((p) => p.y)) : 0;
97
+
98
+ const clipboardNodes: ClipboardNode[] = nodes.map((n, i) => {
99
+ const ref = n.referenceId ?? n.id;
100
+ const pos = positions[i];
101
+ return {
102
+ originalId: n.id,
103
+ type: n.type,
104
+ relativePosition: { x: pos.x - minX, y: pos.y - minY },
105
+ absolutePosition: pos,
106
+ data: {
107
+ display_name: n.label ?? ref,
108
+ reference_id: ref,
109
+ params: n.params ?? {},
110
+ ...(n.mapper !== undefined && { mapper: n.mapper }),
111
+ },
112
+ };
113
+ });
114
+
115
+ // Build referenceId → node id mapping for edge resolution
116
+ const refToId = new Map<string, string>();
117
+ for (const n of nodes) {
118
+ const ref = n.referenceId ?? n.id;
119
+ refToId.set(ref, n.id);
120
+ }
121
+
122
+ const clipboardEdges: ClipboardEdge[] = [];
123
+ for (const e of edges) {
124
+ if (Array.isArray(e)) {
125
+ const sourceId = refToId.get(e[0]) ?? `node-${e[0]}`;
126
+ const targetId = refToId.get(e[1]) ?? `node-${e[1]}`;
127
+ clipboardEdges.push({
128
+ originalId: `edge-${nanoid()}`,
129
+ source: sourceId,
130
+ target: targetId,
131
+ ...(e[2] ? { sourceHandle: e[2] } : {}),
132
+ });
133
+ } else {
134
+ const sourceId = refToId.get(e.from) ?? `node-${e.from}`;
135
+ const targetId = refToId.get(e.to) ?? `node-${e.to}`;
136
+ clipboardEdges.push({
137
+ originalId: `edge-${nanoid()}`,
138
+ source: sourceId,
139
+ target: targetId,
140
+ ...(e.handle ? { sourceHandle: e.handle } : {}),
141
+ });
142
+ }
143
+ }
144
+
145
+ return {
146
+ sourceFlowId: flowId,
147
+ nodes: clipboardNodes,
148
+ edges: clipboardEdges,
149
+ copyTime: Date.now(),
150
+ };
151
+ }
152
+
153
+ // ---------------------------------------------------------------------------
154
+ // Hook
155
+ // ---------------------------------------------------------------------------
156
+
157
+ export function useCopyPaste({ flowId, reactFlowInstance }: UseCopyPasteOptions) {
158
+ const clipboardRef = useRef<ClipboardData | null>(null);
159
+ const mousePositionRef = useRef<{ x: number; y: number } | null>(null);
160
+ const { getNodeDefinition } = useNodeRegistry();
161
+
162
+ // Track mouse position over the react-flow canvas for paste anchoring
163
+ useEffect(() => {
164
+ const handleMouseMove = (e: MouseEvent) => {
165
+ mousePositionRef.current = { x: e.clientX, y: e.clientY };
166
+ };
167
+ document.addEventListener('mousemove', handleMouseMove);
168
+ return () => document.removeEventListener('mousemove', handleMouseMove);
169
+ }, []);
170
+
171
+ // -------------------------------------------------------------------------
172
+ // Serialize selection into ClipboardData
173
+ // -------------------------------------------------------------------------
174
+ const serializeSelection = useCallback((): ClipboardData | null => {
175
+ const { nodes, edges } = useFlowEditorStore.getState();
176
+ const selectedNodes = nodes.filter((n) => n.selected);
177
+ if (selectedNodes.length === 0) {
178
+ return null;
179
+ }
180
+
181
+ const selectedIdSet = new Set(selectedNodes.map((n) => n.id));
182
+
183
+ // Only capture edges where both endpoints are selected
184
+ const internalEdges = edges.filter(
185
+ (e) => selectedIdSet.has(e.source) && selectedIdSet.has(e.target),
186
+ );
187
+
188
+ // Compute bounding box origin
189
+ const minX = Math.min(...selectedNodes.map((n) => n.position.x));
190
+ const minY = Math.min(...selectedNodes.map((n) => n.position.y));
191
+
192
+ const clipboardNodes: ClipboardNode[] = selectedNodes.map((node) => {
193
+ const data = node.data as Record<string, unknown>;
194
+ const params = (data.params as Record<string, unknown>) ?? {};
195
+
196
+ return {
197
+ originalId: node.id,
198
+ type: (data.type as string) ?? node.type ?? 'unknown',
199
+ relativePosition: {
200
+ x: node.position.x - minX,
201
+ y: node.position.y - minY,
202
+ },
203
+ absolutePosition: {
204
+ x: node.position.x,
205
+ y: node.position.y,
206
+ },
207
+ data: {
208
+ display_name: (data.display_name as string) ?? '',
209
+ reference_id: (data.reference_id as string) ?? '',
210
+ params: cloneData(params),
211
+ ...(data.mapper !== undefined && { mapper: cloneData(data.mapper) }),
212
+ ...(data._loop !== undefined && { _loop: cloneData(data._loop) }),
213
+ },
214
+ };
215
+ });
216
+
217
+ const clipboardEdges: ClipboardEdge[] = internalEdges.map((edge) => ({
218
+ originalId: edge.id,
219
+ source: edge.source,
220
+ target: edge.target,
221
+ ...(edge.sourceHandle !== undefined && { sourceHandle: edge.sourceHandle }),
222
+ ...(edge.targetHandle !== undefined && { targetHandle: edge.targetHandle }),
223
+ }));
224
+
225
+ return {
226
+ sourceFlowId: flowId,
227
+ nodes: clipboardNodes,
228
+ edges: clipboardEdges,
229
+ copyTime: Date.now(),
230
+ };
231
+ }, [flowId]);
232
+
233
+ // -------------------------------------------------------------------------
234
+ // Materialize ClipboardData into the store
235
+ // -------------------------------------------------------------------------
236
+ const materializePaste = useCallback(
237
+ (clipboard: ClipboardData, anchor: { x: number; y: number }) => {
238
+ const { nodes: existingNodes } = useFlowEditorStore.getState();
239
+ const isCrossFlow = clipboard.sourceFlowId !== flowId;
240
+ const isEmptyFlow = existingNodes.length === 0;
241
+
242
+ // Build the ID remap table
243
+ const idMap = new Map<string, string>();
244
+ for (const cn of clipboard.nodes) {
245
+ idMap.set(cn.originalId, `${cn.type}-${nanoid()}`);
246
+ }
247
+
248
+ // Track nodes we add so dedup works across the paste batch
249
+ const batchNodes: Array<{ data?: { display_name?: string; reference_id?: string } }> = [
250
+ ...existingNodes,
251
+ ];
252
+
253
+ const skippedNodeIds = new Set<string>();
254
+ const newNodes: Node[] = [];
255
+
256
+ for (const cn of clipboard.nodes) {
257
+ // maxInstances check
258
+ const definition = getNodeDefinition(cn.type);
259
+ if (definition?.maxInstances !== undefined) {
260
+ const currentCount = existingNodes.filter(
261
+ (n) => (n.data as Record<string, unknown>)?.type === cn.type,
262
+ ).length;
263
+ const pastedCount = newNodes.filter(
264
+ (n) => (n.data as Record<string, unknown>)?.type === cn.type,
265
+ ).length;
266
+ if (currentCount + pastedCount >= definition.maxInstances) {
267
+ skippedNodeIds.add(cn.originalId);
268
+ continue;
269
+ }
270
+ }
271
+
272
+ const newId = idMap.get(cn.originalId);
273
+ if (!newId) {
274
+ continue;
275
+ }
276
+ const displayName = generateUniqueDisplayName(cn.data.display_name, batchNodes);
277
+ const referenceId = generateUniqueReferenceId(displayName, batchNodes);
278
+
279
+ let params = cloneData(cn.data.params);
280
+ if (isCrossFlow) {
281
+ params = stripCredentials(params);
282
+ }
283
+ params = regenToolInstanceIds(params);
284
+
285
+ // When the flow is empty, use the absolute/content positions directly;
286
+ // otherwise anchor relative positions to the cursor/viewport point.
287
+ const position =
288
+ isEmptyFlow && cn.absolutePosition
289
+ ? { x: cn.absolutePosition.x, y: cn.absolutePosition.y }
290
+ : { x: anchor.x + cn.relativePosition.x, y: anchor.y + cn.relativePosition.y };
291
+
292
+ const node: Node = {
293
+ id: newId,
294
+ type: cn.type,
295
+ position,
296
+ selected: true,
297
+ data: {
298
+ id: newId,
299
+ type: cn.type,
300
+ display_name: displayName,
301
+ reference_id: referenceId,
302
+ status: 'idle',
303
+ params,
304
+ ...(cn.data.mapper !== undefined && { mapper: cloneData(cn.data.mapper) }),
305
+ ...(cn.data._loop !== undefined && { _loop: cloneData(cn.data._loop) }),
306
+ },
307
+ };
308
+
309
+ newNodes.push(node);
310
+ batchNodes.push(node);
311
+ }
312
+
313
+ // Remap edges — drop any whose endpoint was skipped
314
+ const newEdges: Edge[] = [];
315
+ for (const ce of clipboard.edges) {
316
+ if (skippedNodeIds.has(ce.source) || skippedNodeIds.has(ce.target)) {
317
+ continue;
318
+ }
319
+ const newSource = idMap.get(ce.source);
320
+ const newTarget = idMap.get(ce.target);
321
+ if (!newSource || !newTarget) {
322
+ continue;
323
+ }
324
+
325
+ newEdges.push({
326
+ id: `edge-${nanoid()}`,
327
+ source: newSource,
328
+ target: newTarget,
329
+ ...(ce.sourceHandle !== undefined && { sourceHandle: ce.sourceHandle }),
330
+ ...(ce.targetHandle !== undefined && { targetHandle: ce.targetHandle }),
331
+ });
332
+ }
333
+
334
+ if (newNodes.length > 0) {
335
+ console.log(
336
+ '[Copy/Paste] materializePaste: adding',
337
+ newNodes.length,
338
+ 'nodes,',
339
+ newEdges.length,
340
+ 'edges',
341
+ );
342
+ console.log(
343
+ '[Copy/Paste] newNodes:',
344
+ newNodes.map((n) => ({
345
+ id: n.id,
346
+ type: n.type,
347
+ pos: n.position,
348
+ data: {
349
+ type: (n.data as Record<string, unknown>).type,
350
+ display_name: (n.data as Record<string, unknown>).display_name,
351
+ },
352
+ })),
353
+ );
354
+ useFlowEditorStore.getState().pasteNodesAndEdges(newNodes, newEdges);
355
+ const storeState = useFlowEditorStore.getState();
356
+ console.log('[Copy/Paste] Store after paste: total nodes =', storeState.nodes.length);
357
+ } else {
358
+ console.log('[Copy/Paste] materializePaste: no nodes to add (all skipped?)');
359
+ }
360
+
361
+ if (skippedNodeIds.size > 0) {
362
+ console.log(
363
+ `[Copy/Paste] Skipped ${skippedNodeIds.size} node(s) — maxInstances limit reached`,
364
+ );
365
+ }
366
+ },
367
+ [flowId, getNodeDefinition],
368
+ );
369
+
370
+ // -------------------------------------------------------------------------
371
+ // Compute paste anchor point
372
+ // -------------------------------------------------------------------------
373
+ const getPasteAnchor = useCallback(
374
+ (offset?: { x: number; y: number }): { x: number; y: number } => {
375
+ if (offset) {
376
+ return offset;
377
+ }
378
+
379
+ // Try to paste at mouse cursor position in flow coords
380
+ if (reactFlowInstance && mousePositionRef.current) {
381
+ return reactFlowInstance.screenToFlowPosition(mousePositionRef.current);
382
+ }
383
+
384
+ // Fallback: center of viewport
385
+ if (reactFlowInstance) {
386
+ return reactFlowInstance.screenToFlowPosition({
387
+ x: window.innerWidth / 2,
388
+ y: window.innerHeight / 2,
389
+ });
390
+ }
391
+
392
+ return { x: 250, y: 150 };
393
+ },
394
+ [reactFlowInstance],
395
+ );
396
+
397
+ // -------------------------------------------------------------------------
398
+ // Read clipboard (in-memory first → system SDK parse fallback)
399
+ // -------------------------------------------------------------------------
400
+ const readClipboard = useCallback(async (): Promise<ClipboardData | null> => {
401
+ // Prefer in-memory ref — has correct relative positions from serializeSelection.
402
+ // This covers same-session copy/paste (the common case).
403
+ if (clipboardRef.current) {
404
+ return clipboardRef.current;
405
+ }
406
+
407
+ // Fall back to system clipboard — handles cross-app paste (SDK text from
408
+ // a text editor, another browser tab, etc.)
409
+ try {
410
+ const text = await navigator.clipboard.readText();
411
+ const parsed = parseSDKText(text);
412
+ if (parsed.nodes.length > 0) {
413
+ return sdkResultToClipboard(parsed, flowId);
414
+ }
415
+ } catch {
416
+ // Permission denied, parse failed, or empty
417
+ }
418
+
419
+ return null;
420
+ }, [flowId]);
421
+
422
+ // -------------------------------------------------------------------------
423
+ // Write to clipboard (system + in-memory)
424
+ // -------------------------------------------------------------------------
425
+ const writeClipboard = useCallback(async (data: ClipboardData) => {
426
+ clipboardRef.current = data;
427
+ try {
428
+ // Write SDK code to the system clipboard so pasting into a text editor
429
+ // (VS Code, etc.) produces importable TypeScript helper calls.
430
+ // Internal paste always reads from clipboardRef (in-memory).
431
+ const sdkText = serializeToSDK(data.nodes, data.edges);
432
+ await navigator.clipboard.writeText(sdkText);
433
+ } catch {
434
+ // System clipboard write failed — in-memory ref is still set
435
+ }
436
+ }, []);
437
+
438
+ // -------------------------------------------------------------------------
439
+ // Operations
440
+ // -------------------------------------------------------------------------
441
+
442
+ const copy = useCallback(async () => {
443
+ const data = serializeSelection();
444
+ if (!data) {
445
+ return;
446
+ }
447
+ await writeClipboard(data);
448
+ }, [serializeSelection, writeClipboard]);
449
+
450
+ const paste = useCallback(async () => {
451
+ console.log('[Copy/Paste] Paste triggered');
452
+ const clipboard = await readClipboard();
453
+ if (!clipboard) {
454
+ console.log('[Copy/Paste] No clipboard data available');
455
+ return;
456
+ }
457
+ console.log('[Copy/Paste] Clipboard data:', {
458
+ nodeCount: clipboard.nodes.length,
459
+ edgeCount: clipboard.edges.length,
460
+ });
461
+ const anchor = getPasteAnchor();
462
+ console.log('[Copy/Paste] Paste anchor:', anchor);
463
+ materializePaste(clipboard, anchor);
464
+ }, [readClipboard, getPasteAnchor, materializePaste]);
465
+
466
+ const cut = useCallback(async () => {
467
+ const data = serializeSelection();
468
+ if (!data) {
469
+ return;
470
+ }
471
+ await writeClipboard(data);
472
+
473
+ const selectedIds = useFlowEditorStore
474
+ .getState()
475
+ .nodes.filter((n) => n.selected)
476
+ .map((n) => n.id);
477
+
478
+ if (selectedIds.length > 0) {
479
+ useFlowEditorStore.getState().removeNodes(selectedIds);
480
+ }
481
+ }, [serializeSelection, writeClipboard]);
482
+
483
+ const duplicate = useCallback(() => {
484
+ const data = serializeSelection();
485
+ if (!data) {
486
+ return;
487
+ }
488
+
489
+ // Don't write to system clipboard — duplicate is internal
490
+ clipboardRef.current = data;
491
+
492
+ // Compute offset from original positions
493
+ const { nodes } = useFlowEditorStore.getState();
494
+ const selectedNodes = nodes.filter((n) => n.selected);
495
+ if (selectedNodes.length === 0) {
496
+ return;
497
+ }
498
+
499
+ const minX = Math.min(...selectedNodes.map((n) => n.position.x));
500
+ const minY = Math.min(...selectedNodes.map((n) => n.position.y));
501
+
502
+ materializePaste(data, { x: minX + 50, y: minY + 50 });
503
+ }, [serializeSelection, materializePaste]);
504
+
505
+ const deleteSelection = useCallback(() => {
506
+ const selectedIds = useFlowEditorStore
507
+ .getState()
508
+ .nodes.filter((n) => n.selected)
509
+ .map((n) => n.id);
510
+
511
+ if (selectedIds.length > 0) {
512
+ useFlowEditorStore.getState().removeNodes(selectedIds);
513
+ }
514
+ }, []);
515
+
516
+ // -------------------------------------------------------------------------
517
+ // Keyboard handler
518
+ // -------------------------------------------------------------------------
519
+ useEffect(() => {
520
+ const handler = async (e: KeyboardEvent) => {
521
+ const el = e.target as HTMLElement;
522
+
523
+ // Only handle when focus is on the ReactFlow canvas, not on editable elements
524
+ const isOnCanvas = el.closest('.react-flow') !== null;
525
+ const isBodyFocused = el.tagName === 'BODY';
526
+ const isEditing = isEditingContext(el);
527
+
528
+ if (e.metaKey || e.ctrlKey) {
529
+ console.log('[Copy/Paste] Keydown:', e.key, {
530
+ isOnCanvas,
531
+ isBodyFocused,
532
+ isEditing,
533
+ tagName: el.tagName,
534
+ classList: el.className.substring(0, 80),
535
+ });
536
+ }
537
+
538
+ if ((!isOnCanvas && !isBodyFocused) || isEditing) {
539
+ return;
540
+ }
541
+
542
+ // If the user has a text selection anywhere in the document, let the
543
+ // browser handle copy/cut natively (e.g. copying AI assistant output).
544
+ const hasTextSelection = (window.getSelection()?.toString().length ?? 0) > 0;
545
+
546
+ const isMod = e.metaKey || e.ctrlKey;
547
+
548
+ if (isMod && e.key === 'c') {
549
+ if (hasTextSelection) {
550
+ return;
551
+ } // let browser copy selected text
552
+ e.preventDefault();
553
+ await copy();
554
+ } else if (isMod && e.key === 'x') {
555
+ if (hasTextSelection) {
556
+ return;
557
+ }
558
+ e.preventDefault();
559
+ await cut();
560
+ } else if (isMod && e.key === 'v') {
561
+ e.preventDefault();
562
+ await paste();
563
+ } else if (isMod && e.key === 'd') {
564
+ e.preventDefault();
565
+ duplicate();
566
+ } else if (e.key === 'Delete' || e.key === 'Backspace') {
567
+ e.preventDefault();
568
+ deleteSelection();
569
+ }
570
+ };
571
+
572
+ document.addEventListener('keydown', handler);
573
+ return () => document.removeEventListener('keydown', handler);
574
+ }, [copy, cut, paste, duplicate, deleteSelection]);
575
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Shared types for the copy-paste system.
3
+ *
4
+ * Extracted so both `use-copy-paste.ts` and `serialize-to-sdk.ts` can
5
+ * reference the same clipboard payload shapes without circular imports.
6
+ */
7
+
8
+ export interface ClipboardData {
9
+ sourceFlowId: string;
10
+ nodes: ClipboardNode[];
11
+ edges: ClipboardEdge[];
12
+ copyTime: number;
13
+ }
14
+
15
+ export interface ClipboardNode {
16
+ originalId: string;
17
+ type: string;
18
+ relativePosition: { x: number; y: number };
19
+ absolutePosition?: { x: number; y: number };
20
+ data: {
21
+ display_name: string;
22
+ reference_id: string;
23
+ params: Record<string, unknown>;
24
+ mapper?: unknown;
25
+ _loop?: unknown;
26
+ };
27
+ }
28
+
29
+ export interface ClipboardEdge {
30
+ originalId: string;
31
+ source: string;
32
+ target: string;
33
+ sourceHandle?: string | null;
34
+ targetHandle?: string | null;
35
+ }