@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 @@
1
+ export * from './use-flow-data';
@@ -0,0 +1,8 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export function useDocumentTitle(page: string) {
4
+ useEffect(() => {
5
+ const capitalized = page.charAt(0).toUpperCase() + page.slice(1);
6
+ document.title = `invect | ${capitalized}`;
7
+ }, [page]);
8
+ }
@@ -0,0 +1,144 @@
1
+ import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
2
+ import {
3
+ useNodesState,
4
+ useEdgesState,
5
+ type Node,
6
+ type Edge,
7
+ type NodeChange,
8
+ type EdgeChange,
9
+ } from '@xyflow/react';
10
+ import { useFlowReactFlowData } from '../api/flows.api';
11
+ import { type ReactFlowData } from '@invect/core/types';
12
+
13
+ // Hook that uses the backend React Flow renderer service
14
+ export function useFlowData(
15
+ flowId: string,
16
+ flowVersion?: string,
17
+ flowRunId?: string,
18
+ ): {
19
+ flowData: ReactFlowData | undefined;
20
+ displayVersion: ReactFlowData['version'] | undefined;
21
+ loading: boolean;
22
+ error: string | null;
23
+ nodes: Node[];
24
+ setNodes: React.Dispatch<React.SetStateAction<Node[]>>;
25
+ onNodesChange: (changes: NodeChange<Node>[]) => void;
26
+ edges: Edge[];
27
+ setEdges: React.Dispatch<React.SetStateAction<Edge[]>>;
28
+ onEdgesChange: (changes: EdgeChange<Edge>[]) => void;
29
+ isDirty: boolean;
30
+ resetDirty: (savedVersionId?: string) => void;
31
+ queryError: string | null;
32
+ nodeMetadata: Record<string, { name: string; type?: string }>;
33
+ } {
34
+ const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
35
+ const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
36
+ const [isDirty, setIsDirty] = useState(false);
37
+ const [initialDataLoaded, setInitialDataLoaded] = useState(false);
38
+
39
+ // Track the last version we loaded to avoid re-applying same data after save
40
+ const lastLoadedVersionRef = useRef<string | null>(null);
41
+
42
+ // Use React Query to fetch flow data (backend already provides positioned nodes)
43
+ const {
44
+ data: flowData,
45
+ isLoading: loading,
46
+ error: queryError,
47
+ } = useFlowReactFlowData(flowId, { version: flowVersion, flowRunId });
48
+
49
+ // Apply backend data directly when it changes (backend provides positions)
50
+ // But skip if we just saved and the version hasn't actually changed
51
+ useEffect(() => {
52
+ if (!flowData?.nodes) {
53
+ setNodes([]);
54
+ setEdges([]);
55
+ lastLoadedVersionRef.current = null;
56
+ return;
57
+ }
58
+
59
+ // Use flowId:version as composite identifier to detect if this is truly new data
60
+ const versionInfo = flowData.version;
61
+ const currentVersionId = versionInfo ? `${versionInfo.flowId}:${versionInfo.version}` : null;
62
+
63
+ // When tracking a flow run, always update nodes to show execution status changes
64
+ // Only skip version check when NOT tracking execution
65
+ if (!flowRunId) {
66
+ // Skip if we've already loaded this exact version (prevents panel closing on save)
67
+ if (
68
+ currentVersionId &&
69
+ currentVersionId === lastLoadedVersionRef.current &&
70
+ initialDataLoaded
71
+ ) {
72
+ return;
73
+ }
74
+ }
75
+
76
+ // Set nodes and edges from backend (already positioned)
77
+ setNodes(flowData.nodes as Node[]);
78
+ setEdges(flowData.edges as Edge[]);
79
+
80
+ // Mark initial data as loaded and track the version
81
+ setInitialDataLoaded(true);
82
+ lastLoadedVersionRef.current = currentVersionId;
83
+ }, [flowData, flowRunId, setNodes, setEdges, initialDataLoaded]);
84
+
85
+ // Track local changes (dirty state) - only after initial data is loaded
86
+ useEffect(() => {
87
+ if (initialDataLoaded) {
88
+ setIsDirty(true);
89
+ }
90
+ }, [nodes, edges, initialDataLoaded]);
91
+
92
+ // Reset dirty state, optionally marking a newly saved version as already loaded
93
+ const resetDirty = useCallback((savedVersionId?: string) => {
94
+ setIsDirty(false);
95
+ // If we know the new version ID, mark it as loaded so we don't re-apply data on refetch
96
+ if (savedVersionId) {
97
+ lastLoadedVersionRef.current = savedVersionId;
98
+ }
99
+ }, []);
100
+
101
+ const nodeMetadata = useMemo(() => {
102
+ if (!flowData?.nodes) {
103
+ return {};
104
+ }
105
+
106
+ return flowData.nodes.reduce(
107
+ (acc: Record<string, { name: string; type?: string }>, node: Node) => {
108
+ acc[node.id] = {
109
+ name: ((node.data as Record<string, unknown>)?.display_name as string) || node.id,
110
+ type: node.type,
111
+ };
112
+ return acc;
113
+ },
114
+ {},
115
+ );
116
+ }, [flowData]);
117
+
118
+ return {
119
+ // Data
120
+ flowData,
121
+ displayVersion: flowData?.version,
122
+
123
+ // Loading states
124
+ loading,
125
+ error: queryError?.message || null,
126
+
127
+ // Nodes and edges
128
+ nodes,
129
+ setNodes,
130
+ onNodesChange,
131
+ edges,
132
+ setEdges,
133
+ onEdgesChange,
134
+
135
+ // Dirty state tracking
136
+ isDirty,
137
+ resetDirty,
138
+
139
+ queryError: queryError?.message || null,
140
+ nodeMetadata,
141
+ };
142
+ }
143
+
144
+ export type UseFlowDataResult = ReturnType<typeof useFlowData>;
@@ -0,0 +1,27 @@
1
+ import { useTheme } from '../contexts/ThemeProvider';
2
+
3
+ /**
4
+ * Returns the class name string for portal wrappers that need to be inside the
5
+ * `.invect` CSS scope.
6
+ *
7
+ * Radix portals render to `document.body`, which sits outside the main `.invect`
8
+ * container. Without a wrapper, portal content loses access to invect's CSS
9
+ * variables and the Tailwind `dark:` variant.
10
+ *
11
+ * Usage:
12
+ * ```tsx
13
+ * const portalClass = useInvectPortalClass();
14
+ * return (
15
+ * <SomePrimitive.Portal>
16
+ * <div className={portalClass}>
17
+ * {children}
18
+ * </div>
19
+ * </SomePrimitive.Portal>
20
+ * );
21
+ * ```
22
+ */
23
+ export function useInvectPortalClass(): string {
24
+ const { resolvedTheme } = useTheme();
25
+
26
+ return `invect ${resolvedTheme}`;
27
+ }
@@ -0,0 +1,2 @@
1
+ // Re-export from canonical location for backwards compatibility
2
+ export * from '../components/flow-editor/use-flow-editor';
package/src/index.ts ADDED
@@ -0,0 +1,70 @@
1
+ // Main component exports
2
+ export { Invect } from './Invect';
3
+ export { InvectShell } from './InvectShell';
4
+ export { InvectLoader } from './components/shared/InvectLoader';
5
+ export { InvectLogo } from './components/shared/InvectLogo';
6
+
7
+ // Export types for better TypeScript support
8
+ export type { InvectProps } from './Invect';
9
+ export type { InvectShellProps } from './InvectShell';
10
+ export type { InvectLoaderProps } from './components/shared/InvectLoader';
11
+ export type { InvectLogoProps } from './components/shared/InvectLogo';
12
+
13
+ // Plugin system types
14
+ export type {
15
+ InvectFrontendPlugin,
16
+ PluginSidebarContribution,
17
+ PluginRouteContribution,
18
+ PluginPanelTabContribution,
19
+ PluginHeaderActionContribution,
20
+ PanelTabProps,
21
+ HeaderActionProps,
22
+ PermissionContext,
23
+ } from './types/plugin.types';
24
+ export { usePluginRegistry } from './contexts/PluginRegistryContext';
25
+ export type { PluginRegistry } from './contexts/PluginRegistryContext';
26
+
27
+ // Export API context for advanced usage
28
+ export { ApiProvider, useApiClient, useApiBaseURL } from './contexts/ApiContext';
29
+ export type { ApiProviderProps } from './contexts/ApiContext';
30
+
31
+ // OAuth2 callback handler - needed for OAuth2 redirects
32
+ export { OAuth2CallbackHandler } from './components/credentials/OAuth2ConnectButton';
33
+
34
+ // Flow editor shell
35
+ export { FlowEditor } from './components/flow-editor/FlowEditor';
36
+
37
+ // Standard page layout for non-editor pages
38
+ export { PageLayout } from './components/PageLayout';
39
+ export type { PageLayoutProps } from './components/PageLayout';
40
+
41
+ // Zustand stores for state management
42
+ export * from './stores';
43
+
44
+ // React Query + Zustand hooks
45
+ export * from './api';
46
+
47
+ // UI primitives
48
+ export { TreeView, type TreeDataItem, type TreeRenderItemParams } from './components/ui/tree-view';
49
+ export {
50
+ Dialog,
51
+ DialogClose,
52
+ DialogContent,
53
+ DialogDescription,
54
+ DialogFooter,
55
+ DialogHeader,
56
+ DialogOverlay,
57
+ DialogPortal,
58
+ DialogTitle,
59
+ DialogTrigger,
60
+ } from './components/ui/dialog';
61
+ export {
62
+ DropdownMenu,
63
+ DropdownMenuContent,
64
+ DropdownMenuItem,
65
+ DropdownMenuLabel,
66
+ DropdownMenuRadioGroup,
67
+ DropdownMenuRadioItem,
68
+ DropdownMenuSeparator,
69
+ DropdownMenuTrigger,
70
+ } from './components/ui/dropdown-menu';
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,13 @@
1
+ declare module 'prettier/standalone' {
2
+ export function format(source: string, options: Record<string, unknown>): Promise<string>;
3
+ }
4
+
5
+ declare module 'prettier/plugins/babel' {
6
+ const plugin: Record<string, unknown>;
7
+ export default plugin;
8
+ }
9
+
10
+ declare module 'prettier/plugins/estree' {
11
+ const plugin: Record<string, unknown>;
12
+ export default plugin;
13
+ }
@@ -0,0 +1,27 @@
1
+ import { Link } from 'react-router';
2
+ import { FlowRunsTable } from '../components/flow-runs-table/FlowRunsTable';
3
+ import { PageLayout } from '../components/PageLayout';
4
+ import { useDocumentTitle } from '../hooks/use-document-title';
5
+
6
+ interface AllFlowRunsProps {
7
+ basePath?: string;
8
+ }
9
+
10
+ export const AllFlowRuns = ({ basePath = '' }: AllFlowRunsProps) => {
11
+ useDocumentTitle('flow runs');
12
+ return (
13
+ <PageLayout
14
+ title="Flow Runs"
15
+ actions={
16
+ <Link
17
+ to={basePath || '/'}
18
+ className="inline-flex items-center px-3 py-1.5 text-sm text-muted-foreground bg-card border border-border rounded hover:bg-muted transition-colors"
19
+ >
20
+ ← Back to Home
21
+ </Link>
22
+ }
23
+ >
24
+ <FlowRunsTable basePath={basePath} />
25
+ </PageLayout>
26
+ );
27
+ };
@@ -0,0 +1,362 @@
1
+ import React, { useState, useRef, useMemo } from 'react';
2
+ import { PageLayout } from '../components/PageLayout';
3
+ import { Link } from 'react-router';
4
+ import { Plus, Shield, Clock, AlertCircle, Search, Loader2, ChevronRight } from 'lucide-react';
5
+ import { Button } from '../components/ui/button';
6
+ import { Badge } from '../components/ui/badge';
7
+
8
+ import {
9
+ AlertDialog,
10
+ AlertDialogAction,
11
+ AlertDialogCancel,
12
+ AlertDialogContent,
13
+ AlertDialogDescription,
14
+ AlertDialogFooter,
15
+ AlertDialogHeader,
16
+ AlertDialogTitle,
17
+ } from '../components/ui/alert-dialog';
18
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../components/ui/tooltip';
19
+ import type { Credential, CreateCredentialInput } from '../api/types';
20
+ import { useDocumentTitle } from '../hooks/use-document-title';
21
+ import { CreateCredentialModal } from '../components/credentials/CreateCredentialModal';
22
+ import { CredentialDetailDialog } from '../components/credentials/CredentialDetailDialog';
23
+ import { ProviderIcon } from '../components/shared/ProviderIcon';
24
+ import { getCredentialBranding, getCredentialProviderLabel } from '../utils/credentialBranding';
25
+ import {
26
+ AUTH_TYPE_CONFIG,
27
+ formatDate,
28
+ formatFullDate,
29
+ isTokenExpired,
30
+ } from '../components/credentials/credential-utils';
31
+ import {
32
+ useCredentials,
33
+ useCreateCredential,
34
+ useUpdateCredential,
35
+ useDeleteCredential,
36
+ useTestCredential,
37
+ } from '../api/credentials.api';
38
+
39
+ export interface CredentialsProps {
40
+ basePath?: string;
41
+ }
42
+
43
+ export const Credentials: React.FC<CredentialsProps> = ({ basePath: _basePath = '/invect' }) => {
44
+ useDocumentTitle('credentials');
45
+ const containerRef = useRef<HTMLDivElement>(null);
46
+
47
+ // State
48
+ const [createModalOpen, setCreateModalOpen] = useState(false);
49
+ const [selectedCredential, setSelectedCredential] = useState<Credential | null>(null);
50
+ const [deletingCredential, setDeletingCredential] = useState<Credential | null>(null);
51
+ const [testingId, setTestingId] = useState<string | null>(null);
52
+ const [testResult, setTestResult] = useState<{ success: boolean; error?: string } | null>(null);
53
+ const [searchQuery, setSearchQuery] = useState('');
54
+ const [filterAuthType, setFilterAuthType] = useState<string>('all');
55
+
56
+ // Queries
57
+ const { data: credentials = [], isLoading, error } = useCredentials({ includeShared: true });
58
+ const createCredentialMutation = useCreateCredential();
59
+ const updateCredentialMutation = useUpdateCredential();
60
+ const deleteCredentialMutation = useDeleteCredential();
61
+ const testCredentialMutation = useTestCredential();
62
+
63
+ // Derived
64
+ const authTypes = useMemo(() => {
65
+ const types = new Set(credentials.map((c) => c.authType));
66
+ return Array.from(types).sort();
67
+ }, [credentials]);
68
+
69
+ const filteredCredentials = useMemo(() => {
70
+ let result = credentials;
71
+ if (searchQuery.trim()) {
72
+ const q = searchQuery.toLowerCase();
73
+ result = result.filter(
74
+ (c) =>
75
+ c.name.toLowerCase().includes(q) ||
76
+ c.description?.toLowerCase().includes(q) ||
77
+ c.authType.toLowerCase().includes(q) ||
78
+ c.config?.oauth2Provider?.toLowerCase().includes(q),
79
+ );
80
+ }
81
+ if (filterAuthType !== 'all') {
82
+ result = result.filter((c) => c.authType === filterAuthType);
83
+ }
84
+ return result;
85
+ }, [credentials, searchQuery, filterAuthType]);
86
+
87
+ const openDetail = (credential: Credential) => {
88
+ setSelectedCredential(credential);
89
+ setTestResult(null);
90
+ };
91
+
92
+ const closeDetail = () => {
93
+ setSelectedCredential(null);
94
+ setTestResult(null);
95
+ };
96
+
97
+ const handleTest = (id: string) => {
98
+ setTestingId(id);
99
+ setTestResult(null);
100
+ testCredentialMutation.mutate(id, {
101
+ onSuccess: (result) => {
102
+ setTestingId(null);
103
+ setTestResult(result);
104
+ },
105
+ onError: (mutationError) => {
106
+ setTestingId(null);
107
+ const message = mutationError instanceof Error ? mutationError.message : 'Unknown error';
108
+ setTestResult({ success: false, error: message });
109
+ },
110
+ });
111
+ };
112
+
113
+ return (
114
+ <PageLayout
115
+ ref={containerRef}
116
+ title="Credentials"
117
+ subtitle="Manage API keys, OAuth connections, and database credentials for your integrations."
118
+ actions={
119
+ <>
120
+ <Link
121
+ to={_basePath || '/'}
122
+ className="inline-flex items-center px-3 py-1.5 text-sm text-muted-foreground bg-card border border-border rounded-md hover:bg-muted transition-colors"
123
+ >
124
+ ← Back
125
+ </Link>
126
+ <Button onClick={() => setCreateModalOpen(true)}>
127
+ <Plus className="w-4 h-4 mr-2" />
128
+ New Credential
129
+ </Button>
130
+ </>
131
+ }
132
+ >
133
+ {/* Search & Filters */}
134
+ {credentials.length > 0 && (
135
+ <div className="flex flex-col gap-3 mb-4 sm:flex-row sm:items-center">
136
+ <div className="relative flex-1 max-w-sm">
137
+ <Search className="absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 pointer-events-none text-muted-foreground" />
138
+ <input
139
+ type="text"
140
+ placeholder="Search credentials…"
141
+ value={searchQuery}
142
+ onChange={(e) => setSearchQuery(e.target.value)}
143
+ className="w-full rounded-lg border border-border bg-transparent py-2 pl-9 pr-3 text-sm outline-none placeholder:text-muted-foreground focus:border-primary/50"
144
+ />
145
+ </div>
146
+ <div className="flex items-center gap-1.5 flex-wrap">
147
+ <button
148
+ onClick={() => setFilterAuthType('all')}
149
+ className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors ${
150
+ filterAuthType === 'all'
151
+ ? 'bg-foreground text-background border-foreground'
152
+ : 'bg-card text-muted-foreground border-border hover:border-foreground/30'
153
+ }`}
154
+ >
155
+ All ({credentials.length})
156
+ </button>
157
+ {authTypes.map((type) => {
158
+ const config = AUTH_TYPE_CONFIG[type];
159
+ const count = credentials.filter((c) => c.authType === type).length;
160
+ return (
161
+ <button
162
+ key={type}
163
+ onClick={() => setFilterAuthType(filterAuthType === type ? 'all' : type)}
164
+ className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors ${
165
+ filterAuthType === type
166
+ ? 'bg-foreground text-background border-foreground'
167
+ : 'bg-card text-muted-foreground border-border hover:border-foreground/30'
168
+ }`}
169
+ >
170
+ {config?.label ?? type} ({count})
171
+ </button>
172
+ );
173
+ })}
174
+ </div>
175
+ </div>
176
+ )}
177
+
178
+ {/* Content */}
179
+ <TooltipProvider>
180
+ {isLoading ? (
181
+ <div className="flex flex-col items-center justify-center py-20">
182
+ <Loader2 className="w-8 h-8 mb-3 text-muted-foreground animate-spin" />
183
+ <p className="text-sm text-muted-foreground">Loading credentials...</p>
184
+ </div>
185
+ ) : error ? (
186
+ <div className="flex flex-col items-center justify-center py-20">
187
+ <AlertCircle className="w-12 h-12 mb-3 text-destructive" />
188
+ <p className="text-sm text-destructive">
189
+ Error loading credentials: {error instanceof Error ? error.message : 'Unknown error'}
190
+ </p>
191
+ </div>
192
+ ) : credentials.length === 0 ? (
193
+ <div className="flex flex-col items-center justify-center py-20 border border-dashed rounded-xl border-border">
194
+ <div className="flex items-center justify-center w-16 h-16 mb-4 rounded-full bg-muted">
195
+ <Shield className="w-8 h-8 text-muted-foreground" />
196
+ </div>
197
+ <h3 className="mb-1 text-lg font-semibold text-foreground">No credentials yet</h3>
198
+ <p className="max-w-sm mb-6 text-sm text-center text-muted-foreground">
199
+ Add API keys, connect OAuth providers, or configure database credentials to power your
200
+ workflows.
201
+ </p>
202
+ <Button onClick={() => setCreateModalOpen(true)}>
203
+ <Plus className="w-4 h-4 mr-2" />
204
+ Create Credential
205
+ </Button>
206
+ </div>
207
+ ) : filteredCredentials.length === 0 ? (
208
+ <div className="flex flex-col items-center justify-center py-16">
209
+ <Search className="w-10 h-10 mb-3 text-muted-foreground" />
210
+ <p className="text-sm text-muted-foreground">No credentials match your search.</p>
211
+ </div>
212
+ ) : (
213
+ <div className="border border-border rounded-lg overflow-hidden bg-card divide-y divide-border">
214
+ {filteredCredentials.map((credential) => {
215
+ const iconInfo = getCredentialBranding(credential);
216
+ const providerLabel = getCredentialProviderLabel(credential);
217
+ const authConfig = AUTH_TYPE_CONFIG[credential.authType];
218
+ const expired = isTokenExpired(credential);
219
+
220
+ return (
221
+ <button
222
+ key={credential.id}
223
+ type="button"
224
+ onClick={() => openDetail(credential)}
225
+ className="w-full flex items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/50 focus-visible:bg-muted/50 focus-visible:outline-none"
226
+ >
227
+ {/* Icon */}
228
+ <div className="flex items-center justify-center w-9 h-9 rounded-lg bg-muted/60 shrink-0">
229
+ <ProviderIcon
230
+ providerId={iconInfo.providerId}
231
+ icon={iconInfo.icon}
232
+ className="w-5 h-5"
233
+ />
234
+ </div>
235
+
236
+ {/* Name + provider */}
237
+ <div className="flex-1 min-w-0">
238
+ <div className="flex items-center gap-2">
239
+ <span className="text-sm font-medium text-foreground truncate">
240
+ {credential.name}
241
+ </span>
242
+ <span
243
+ className={`inline-block w-1.5 h-1.5 rounded-full shrink-0 ${
244
+ expired
245
+ ? 'bg-warning'
246
+ : credential.isActive
247
+ ? 'bg-success'
248
+ : 'bg-muted-foreground/40'
249
+ }`}
250
+ />
251
+ {expired && (
252
+ <Badge
253
+ variant="secondary"
254
+ className="text-[10px] px-1.5 py-0 font-medium leading-4 bg-warning-muted text-warning"
255
+ >
256
+ Expired
257
+ </Badge>
258
+ )}
259
+ </div>
260
+ {providerLabel && (
261
+ <p className="text-xs text-muted-foreground truncate">{providerLabel}</p>
262
+ )}
263
+ </div>
264
+
265
+ {/* Auth badge */}
266
+ <Badge
267
+ variant="secondary"
268
+ className={`text-[10px] px-1.5 py-0 font-medium leading-5 shrink-0 ${authConfig?.color ?? 'bg-muted text-muted-foreground'}`}
269
+ >
270
+ {authConfig?.label ?? credential.authType}
271
+ </Badge>
272
+
273
+ {/* Last used */}
274
+ <Tooltip>
275
+ <TooltipTrigger asChild>
276
+ <span className="hidden sm:flex items-center gap-1 text-xs text-muted-foreground shrink-0 w-20 justify-end">
277
+ <Clock className="w-3 h-3" />
278
+ {formatDate(credential.lastUsedAt)}
279
+ </span>
280
+ </TooltipTrigger>
281
+ <TooltipContent>
282
+ Last used: {formatFullDate(credential.lastUsedAt)}
283
+ </TooltipContent>
284
+ </Tooltip>
285
+
286
+ <ChevronRight className="w-4 h-4 text-muted-foreground/50 shrink-0" />
287
+ </button>
288
+ );
289
+ })}
290
+ </div>
291
+ )}
292
+ </TooltipProvider>
293
+
294
+ {/* Detail Dialog */}
295
+ <CredentialDetailDialog
296
+ credential={selectedCredential}
297
+ onClose={closeDetail}
298
+ onDelete={(c) => setDeletingCredential(c)}
299
+ onTest={handleTest}
300
+ testingId={testingId}
301
+ testResult={testResult}
302
+ onUpdate={(id, data) => updateCredentialMutation.mutate({ id, data })}
303
+ isUpdating={updateCredentialMutation.isPending}
304
+ portalContainer={containerRef.current}
305
+ />
306
+
307
+ {/* Create modal */}
308
+ <CreateCredentialModal
309
+ open={createModalOpen}
310
+ onClose={() => setCreateModalOpen(false)}
311
+ onSubmit={(data: CreateCredentialInput) =>
312
+ createCredentialMutation.mutate(data, {
313
+ onSuccess: () => setCreateModalOpen(false),
314
+ })
315
+ }
316
+ isLoading={createCredentialMutation.isPending}
317
+ portalContainer={containerRef.current}
318
+ />
319
+
320
+ {/* Delete Confirmation */}
321
+ <AlertDialog
322
+ open={!!deletingCredential}
323
+ onOpenChange={(open) => !open && setDeletingCredential(null)}
324
+ >
325
+ <AlertDialogContent container={containerRef.current}>
326
+ <AlertDialogHeader>
327
+ <AlertDialogTitle>Delete Credential?</AlertDialogTitle>
328
+ <AlertDialogDescription>
329
+ Are you sure you want to delete "{deletingCredential?.name}"? This action cannot be
330
+ undone and may break workflows that use this credential.
331
+ </AlertDialogDescription>
332
+ </AlertDialogHeader>
333
+ <AlertDialogFooter>
334
+ <AlertDialogCancel onClick={() => setDeletingCredential(null)}>
335
+ Cancel
336
+ </AlertDialogCancel>
337
+ <AlertDialogAction
338
+ onClick={(e) => {
339
+ e.preventDefault();
340
+ if (deletingCredential) {
341
+ deleteCredentialMutation.mutate(deletingCredential.id, {
342
+ onSuccess: () => {
343
+ setDeletingCredential(null);
344
+ if (selectedCredential?.id === deletingCredential.id) {
345
+ closeDetail();
346
+ }
347
+ },
348
+ onError: () => setDeletingCredential(null),
349
+ });
350
+ }
351
+ }}
352
+ disabled={deleteCredentialMutation.isPending}
353
+ className="bg-destructive hover:bg-destructive/90"
354
+ >
355
+ {deleteCredentialMutation.isPending ? 'Deleting…' : 'Delete'}
356
+ </AlertDialogAction>
357
+ </AlertDialogFooter>
358
+ </AlertDialogContent>
359
+ </AlertDialog>
360
+ </PageLayout>
361
+ );
362
+ };