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