@foresthubai/workflow-builder 0.4.4 → 0.4.5
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/dist/BuilderLayout.d.ts.map +1 -1
- package/dist/BuilderLayout.js +52 -61
- package/dist/BuilderLayout.js.map +1 -1
- package/dist/Canvas.d.ts.map +1 -1
- package/dist/Canvas.js +10 -3
- package/dist/Canvas.js.map +1 -1
- package/dist/CanvasEditor.d.ts.map +1 -1
- package/dist/CanvasEditor.js.map +1 -1
- package/dist/CanvasTabsToolbar.d.ts.map +1 -1
- package/dist/CanvasTabsToolbar.js +3 -0
- package/dist/CanvasTabsToolbar.js.map +1 -1
- package/dist/RightConfigPanel.d.ts.map +1 -1
- package/dist/RightConfigPanel.js +3 -0
- package/dist/RightConfigPanel.js.map +1 -1
- package/dist/WorkflowBuilder.d.ts +7 -13
- package/dist/WorkflowBuilder.d.ts.map +1 -1
- package/dist/WorkflowBuilder.js +25 -13
- package/dist/WorkflowBuilder.js.map +1 -1
- package/dist/cn.d.ts.map +1 -1
- package/dist/cn.js +3 -0
- package/dist/cn.js.map +1 -1
- package/dist/components/ui/add-button.d.ts.map +1 -1
- package/dist/components/ui/add-button.js +3 -0
- package/dist/components/ui/add-button.js.map +1 -1
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.js +3 -0
- package/dist/components/ui/alert-dialog.js.map +1 -1
- package/dist/components/ui/alert.d.ts.map +1 -1
- package/dist/components/ui/alert.js +3 -0
- package/dist/components/ui/alert.js.map +1 -1
- package/dist/components/ui/badge.d.ts.map +1 -1
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +3 -0
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +3 -0
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/ui/checkbox.d.ts.map +1 -1
- package/dist/components/ui/checkbox.js +3 -0
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/collapsible.js +3 -0
- package/dist/components/ui/collapsible.js.map +1 -1
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +3 -0
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/delete-button.d.ts.map +1 -1
- package/dist/components/ui/delete-button.js +3 -0
- package/dist/components/ui/delete-button.js.map +1 -1
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +3 -0
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.js +3 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/input.js +3 -0
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/label.d.ts.map +1 -1
- package/dist/components/ui/label.js +3 -0
- package/dist/components/ui/label.js.map +1 -1
- package/dist/components/ui/readonly-banner.d.ts.map +1 -1
- package/dist/components/ui/readonly-banner.js +3 -0
- package/dist/components/ui/readonly-banner.js.map +1 -1
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/resizable.js +3 -0
- package/dist/components/ui/resizable.js.map +1 -1
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js +3 -0
- package/dist/components/ui/scroll-area.js.map +1 -1
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/select.js +3 -0
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/separator.d.ts.map +1 -1
- package/dist/components/ui/separator.js +3 -0
- package/dist/components/ui/separator.js.map +1 -1
- package/dist/components/ui/switch.d.ts.map +1 -1
- package/dist/components/ui/switch.js +3 -0
- package/dist/components/ui/switch.js.map +1 -1
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/textarea.js +3 -0
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/components/ui/toast.d.ts.map +1 -1
- package/dist/components/ui/toast.js +3 -0
- package/dist/components/ui/toast.js.map +1 -1
- package/dist/components/ui/toaster.d.ts.map +1 -1
- package/dist/components/ui/toaster.js +3 -0
- package/dist/components/ui/toaster.js.map +1 -1
- package/dist/components/ui/toggle-group.d.ts.map +1 -1
- package/dist/components/ui/toggle-group.js +3 -0
- package/dist/components/ui/toggle-group.js.map +1 -1
- package/dist/components/ui/toggle.d.ts.map +1 -1
- package/dist/components/ui/toggle.js +3 -0
- package/dist/components/ui/toggle.js.map +1 -1
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/tooltip.js +3 -0
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/dialogs/NodePickerDialog.d.ts.map +1 -1
- package/dist/dialogs/NodePickerDialog.js +3 -0
- package/dist/dialogs/NodePickerDialog.js.map +1 -1
- package/dist/dialogs/ValidationDialog.d.ts.map +1 -1
- package/dist/dialogs/ValidationDialog.js +3 -0
- package/dist/dialogs/ValidationDialog.js.map +1 -1
- package/dist/graph/BaseNode.d.ts.map +1 -1
- package/dist/graph/BaseNode.js +11 -6
- package/dist/graph/BaseNode.js.map +1 -1
- package/dist/graph/CustomEdge.d.ts.map +1 -1
- package/dist/graph/CustomEdge.js +4 -1
- package/dist/graph/CustomEdge.js.map +1 -1
- package/dist/graph/CustomNode.d.ts.map +1 -1
- package/dist/graph/CustomNode.js +3 -0
- package/dist/graph/CustomNode.js.map +1 -1
- package/dist/graph/FunctionCallNode.d.ts.map +1 -1
- package/dist/graph/FunctionCallNode.js.map +1 -1
- package/dist/graph/PortHandle.d.ts.map +1 -1
- package/dist/graph/PortHandle.js.map +1 -1
- package/dist/graph/reactFlowRegistry.d.ts.map +1 -1
- package/dist/graph/reactFlowRegistry.js +3 -0
- package/dist/graph/reactFlowRegistry.js.map +1 -1
- package/dist/hooks/use-toast.d.ts.map +1 -1
- package/dist/hooks/use-toast.js +3 -0
- package/dist/hooks/use-toast.js.map +1 -1
- package/dist/hooks/useAvailableVariables.d.ts.map +1 -1
- package/dist/hooks/useAvailableVariables.js +3 -0
- package/dist/hooks/useAvailableVariables.js.map +1 -1
- package/dist/hooks/useCanvasHistory.d.ts.map +1 -1
- package/dist/hooks/useCanvasHistory.js +3 -0
- package/dist/hooks/useCanvasHistory.js.map +1 -1
- package/dist/hooks/useCanvasTabs.d.ts.map +1 -1
- package/dist/hooks/useCanvasTabs.js +3 -0
- package/dist/hooks/useCanvasTabs.js.map +1 -1
- package/dist/hooks/useFunctionDiagnosticsSync.d.ts.map +1 -1
- package/dist/hooks/useFunctionDiagnosticsSync.js +3 -0
- package/dist/hooks/useFunctionDiagnosticsSync.js.map +1 -1
- package/dist/hooks/useFunctionRegistry.d.ts.map +1 -1
- package/dist/hooks/useFunctionRegistry.js +3 -0
- package/dist/hooks/useFunctionRegistry.js.map +1 -1
- package/dist/hooks/useFunctions.d.ts.map +1 -1
- package/dist/hooks/useFunctions.js +3 -0
- package/dist/hooks/useFunctions.js.map +1 -1
- package/dist/hooks/useGraph.d.ts.map +1 -1
- package/dist/hooks/useGraph.js +3 -0
- package/dist/hooks/useGraph.js.map +1 -1
- package/dist/hooks/useNodeDefinitions.d.ts.map +1 -1
- package/dist/hooks/useNodeDefinitions.js +3 -0
- package/dist/hooks/useNodeDefinitions.js.map +1 -1
- package/dist/hooks/useParamErrors.d.ts.map +1 -1
- package/dist/hooks/useParamErrors.js +3 -0
- package/dist/hooks/useParamErrors.js.map +1 -1
- package/dist/hooks/useResolvedTheme.d.ts.map +1 -1
- package/dist/hooks/useResolvedTheme.js +3 -0
- package/dist/hooks/useResolvedTheme.js.map +1 -1
- package/dist/hooks/useResourceDiagnosticsSync.d.ts.map +1 -1
- package/dist/hooks/useResourceDiagnosticsSync.js +3 -0
- package/dist/hooks/useResourceDiagnosticsSync.js.map +1 -1
- package/dist/hooks/useSuppressThemeTransition.d.ts.map +1 -1
- package/dist/hooks/useSuppressThemeTransition.js +3 -0
- package/dist/hooks/useSuppressThemeTransition.js.map +1 -1
- package/dist/hooks/useWorkflowSerialization.d.ts.map +1 -1
- package/dist/hooks/useWorkflowSerialization.js +3 -0
- package/dist/hooks/useWorkflowSerialization.js.map +1 -1
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/inputs/ExpressionInput.d.ts.map +1 -1
- package/dist/inputs/ExpressionInput.js +3 -0
- package/dist/inputs/ExpressionInput.js.map +1 -1
- package/dist/inputs/ParameterEditor.d.ts.map +1 -1
- package/dist/inputs/ParameterEditor.js +3 -0
- package/dist/inputs/ParameterEditor.js.map +1 -1
- package/dist/inputs/PortSection.d.ts.map +1 -1
- package/dist/inputs/PortSection.js.map +1 -1
- package/dist/mode.d.ts +11 -0
- package/dist/mode.d.ts.map +1 -0
- package/dist/mode.js +8 -0
- package/dist/mode.js.map +1 -0
- package/dist/panels/BuilderSidebar.d.ts.map +1 -1
- package/dist/panels/BuilderSidebar.js +3 -0
- package/dist/panels/BuilderSidebar.js.map +1 -1
- package/dist/panels/ChannelConfigPanel.d.ts.map +1 -1
- package/dist/panels/ChannelConfigPanel.js +3 -0
- package/dist/panels/ChannelConfigPanel.js.map +1 -1
- package/dist/panels/ChannelsPanel.d.ts.map +1 -1
- package/dist/panels/ChannelsPanel.js +3 -0
- package/dist/panels/ChannelsPanel.js.map +1 -1
- package/dist/panels/DebugConsolePanel.d.ts.map +1 -1
- package/dist/panels/DebugConsolePanel.js +3 -0
- package/dist/panels/DebugConsolePanel.js.map +1 -1
- package/dist/panels/DebugContextPanel.d.ts.map +1 -1
- package/dist/panels/DebugContextPanel.js +3 -0
- package/dist/panels/DebugContextPanel.js.map +1 -1
- package/dist/panels/DebugExternalIOPanel.d.ts.map +1 -1
- package/dist/panels/DebugExternalIOPanel.js +3 -0
- package/dist/panels/DebugExternalIOPanel.js.map +1 -1
- package/dist/panels/DiagnosticsPanel.d.ts.map +1 -1
- package/dist/panels/DiagnosticsPanel.js +3 -0
- package/dist/panels/DiagnosticsPanel.js.map +1 -1
- package/dist/panels/EdgeConfigPanel.d.ts.map +1 -1
- package/dist/panels/EdgeConfigPanel.js +4 -1
- package/dist/panels/EdgeConfigPanel.js.map +1 -1
- package/dist/panels/FunctionConfigPanel.d.ts.map +1 -1
- package/dist/panels/FunctionConfigPanel.js +4 -1
- package/dist/panels/FunctionConfigPanel.js.map +1 -1
- package/dist/panels/FunctionListPanel.d.ts.map +1 -1
- package/dist/panels/FunctionListPanel.js +3 -0
- package/dist/panels/FunctionListPanel.js.map +1 -1
- package/dist/panels/MemoryConfigPanel.d.ts.map +1 -1
- package/dist/panels/MemoryConfigPanel.js +3 -0
- package/dist/panels/MemoryConfigPanel.js.map +1 -1
- package/dist/panels/MemoryPanel.d.ts.map +1 -1
- package/dist/panels/MemoryPanel.js +3 -0
- package/dist/panels/MemoryPanel.js.map +1 -1
- package/dist/panels/ModelConfigPanel.d.ts.map +1 -1
- package/dist/panels/ModelConfigPanel.js +3 -0
- package/dist/panels/ModelConfigPanel.js.map +1 -1
- package/dist/panels/ModelsPanel.d.ts.map +1 -1
- package/dist/panels/ModelsPanel.js +3 -0
- package/dist/panels/ModelsPanel.js.map +1 -1
- package/dist/panels/NodeConfigPanel.d.ts.map +1 -1
- package/dist/panels/NodeConfigPanel.js +4 -1
- package/dist/panels/NodeConfigPanel.js.map +1 -1
- package/dist/panels/NodeLibrary.d.ts.map +1 -1
- package/dist/panels/NodeLibrary.js +4 -1
- package/dist/panels/NodeLibrary.js.map +1 -1
- package/dist/panels/ResourceConfigPanel.d.ts.map +1 -1
- package/dist/panels/ResourceConfigPanel.js +4 -1
- package/dist/panels/ResourceConfigPanel.js.map +1 -1
- package/dist/panels/ResourceListPanel.d.ts.map +1 -1
- package/dist/panels/ResourceListPanel.js +4 -1
- package/dist/panels/ResourceListPanel.js.map +1 -1
- package/dist/panels/VariableConfigPanel.d.ts.map +1 -1
- package/dist/panels/VariableConfigPanel.js +4 -1
- package/dist/panels/VariableConfigPanel.js.map +1 -1
- package/dist/panels/VariablesPanel.d.ts.map +1 -1
- package/dist/panels/VariablesPanel.js +4 -1
- package/dist/panels/VariablesPanel.js.map +1 -1
- package/dist/stores/canvasStore.d.ts.map +1 -1
- package/dist/stores/canvasStore.js +3 -0
- package/dist/stores/canvasStore.js.map +1 -1
- package/dist/stores/debugStore.d.ts.map +1 -1
- package/dist/stores/debugStore.js +3 -0
- package/dist/stores/debugStore.js.map +1 -1
- package/dist/stores/diagnosticsStore.d.ts.map +1 -1
- package/dist/stores/diagnosticsStore.js +3 -0
- package/dist/stores/diagnosticsStore.js.map +1 -1
- package/dist/stores/editorStore.d.ts +1 -1
- package/dist/stores/editorStore.d.ts.map +1 -1
- package/dist/stores/editorStore.js +3 -0
- package/dist/stores/editorStore.js.map +1 -1
- package/dist/utils/categoryConstants.d.ts.map +1 -1
- package/dist/utils/categoryConstants.js +3 -0
- package/dist/utils/categoryConstants.js.map +1 -1
- package/dist/utils/channelOperations.d.ts.map +1 -1
- package/dist/utils/channelOperations.js +3 -0
- package/dist/utils/channelOperations.js.map +1 -1
- package/dist/utils/connectionRules.d.ts +3 -1
- package/dist/utils/connectionRules.d.ts.map +1 -1
- package/dist/utils/connectionRules.js +11 -16
- package/dist/utils/connectionRules.js.map +1 -1
- package/dist/utils/functionOperations.d.ts.map +1 -1
- package/dist/utils/functionOperations.js +3 -0
- package/dist/utils/functionOperations.js.map +1 -1
- package/dist/utils/graphOperations.d.ts.map +1 -1
- package/dist/utils/graphOperations.js +3 -0
- package/dist/utils/graphOperations.js.map +1 -1
- package/dist/utils/history.d.ts.map +1 -1
- package/dist/utils/history.js +4 -1
- package/dist/utils/history.js.map +1 -1
- package/dist/utils/memoryOperations.d.ts.map +1 -1
- package/dist/utils/memoryOperations.js +3 -0
- package/dist/utils/memoryOperations.js.map +1 -1
- package/dist/utils/migrateFunctionNodes.d.ts.map +1 -1
- package/dist/utils/migrateFunctionNodes.js +3 -0
- package/dist/utils/migrateFunctionNodes.js.map +1 -1
- package/dist/utils/modelOperations.d.ts.map +1 -1
- package/dist/utils/modelOperations.js +3 -0
- package/dist/utils/modelOperations.js.map +1 -1
- package/dist/utils/paramDisplay.d.ts.map +1 -1
- package/dist/utils/paramDisplay.js +3 -0
- package/dist/utils/paramDisplay.js.map +1 -1
- package/dist/utils/resourceHelpers.d.ts.map +1 -1
- package/dist/utils/resourceHelpers.js +3 -0
- package/dist/utils/resourceHelpers.js.map +1 -1
- package/dist/utils/translation.d.ts.map +1 -1
- package/dist/utils/translation.js +3 -0
- package/dist/utils/translation.js.map +1 -1
- package/dist/utils/variableOperations.d.ts.map +1 -1
- package/dist/utils/variableOperations.js +3 -0
- package/dist/utils/variableOperations.js.map +1 -1
- package/package.json +2 -2
- package/src/BuilderLayout.tsx +54 -61
- package/src/Canvas.tsx +12 -3
- package/src/CanvasEditor.tsx +4 -0
- package/src/CanvasTabsToolbar.tsx +4 -0
- package/src/RightConfigPanel.tsx +4 -0
- package/src/WorkflowBuilder.tsx +33 -18
- package/src/cn.ts +4 -0
- package/src/components/ui/add-button.tsx +4 -0
- package/src/components/ui/alert-dialog.tsx +4 -0
- package/src/components/ui/alert.tsx +4 -0
- package/src/components/ui/badge.tsx +4 -0
- package/src/components/ui/button.tsx +4 -0
- package/src/components/ui/card.tsx +4 -0
- package/src/components/ui/checkbox.tsx +4 -0
- package/src/components/ui/collapsible.tsx +4 -0
- package/src/components/ui/command.tsx +4 -0
- package/src/components/ui/delete-button.tsx +4 -0
- package/src/components/ui/dialog.tsx +4 -0
- package/src/components/ui/dropdown-menu.tsx +4 -0
- package/src/components/ui/input.tsx +4 -0
- package/src/components/ui/label.tsx +4 -0
- package/src/components/ui/readonly-banner.tsx +4 -0
- package/src/components/ui/resizable.tsx +4 -0
- package/src/components/ui/scroll-area.tsx +4 -0
- package/src/components/ui/select.tsx +4 -0
- package/src/components/ui/separator.tsx +4 -0
- package/src/components/ui/switch.tsx +4 -0
- package/src/components/ui/textarea.tsx +4 -0
- package/src/components/ui/toast.tsx +4 -0
- package/src/components/ui/toaster.tsx +4 -0
- package/src/components/ui/toggle-group.tsx +4 -0
- package/src/components/ui/toggle.tsx +4 -0
- package/src/components/ui/tooltip.tsx +4 -0
- package/src/dialogs/NodePickerDialog.tsx +4 -0
- package/src/dialogs/ValidationDialog.tsx +4 -0
- package/src/graph/BaseNode.tsx +12 -6
- package/src/graph/CustomEdge.tsx +5 -1
- package/src/graph/CustomNode.tsx +4 -0
- package/src/graph/FunctionCallNode.tsx +4 -0
- package/src/graph/PortHandle.tsx +4 -0
- package/src/graph/reactFlowRegistry.ts +4 -0
- package/src/hooks/use-toast.ts +4 -0
- package/src/hooks/useAvailableVariables.ts +4 -0
- package/src/hooks/useCanvasHistory.ts +4 -0
- package/src/hooks/useCanvasTabs.ts +4 -0
- package/src/hooks/useFunctionDiagnosticsSync.ts +4 -0
- package/src/hooks/useFunctionRegistry.ts +4 -0
- package/src/hooks/useFunctions.ts +4 -0
- package/src/hooks/useGraph.ts +4 -0
- package/src/hooks/useNodeDefinitions.ts +4 -0
- package/src/hooks/useParamErrors.ts +4 -0
- package/src/hooks/useResolvedTheme.ts +4 -0
- package/src/hooks/useResourceDiagnosticsSync.ts +4 -0
- package/src/hooks/useSuppressThemeTransition.ts +4 -0
- package/src/hooks/useWorkflowSerialization.ts +4 -0
- package/src/i18n/index.ts +4 -0
- package/src/index.ts +5 -1
- package/src/inputs/ExpressionInput.tsx +4 -0
- package/src/inputs/ParameterEditor.tsx +4 -0
- package/src/inputs/PortSection.tsx +4 -0
- package/src/mode.ts +15 -0
- package/src/panels/BuilderSidebar.tsx +4 -0
- package/src/panels/ChannelConfigPanel.tsx +4 -0
- package/src/panels/ChannelsPanel.tsx +4 -0
- package/src/panels/DebugConsolePanel.tsx +4 -0
- package/src/panels/DebugContextPanel.tsx +4 -0
- package/src/panels/DebugExternalIOPanel.tsx +4 -0
- package/src/panels/DiagnosticsPanel.tsx +4 -0
- package/src/panels/EdgeConfigPanel.tsx +5 -1
- package/src/panels/FunctionConfigPanel.tsx +5 -1
- package/src/panels/FunctionListPanel.tsx +4 -0
- package/src/panels/MemoryConfigPanel.tsx +4 -0
- package/src/panels/MemoryPanel.tsx +4 -0
- package/src/panels/ModelConfigPanel.tsx +4 -0
- package/src/panels/ModelsPanel.tsx +4 -0
- package/src/panels/NodeConfigPanel.tsx +5 -1
- package/src/panels/NodeLibrary.tsx +5 -1
- package/src/panels/ResourceConfigPanel.tsx +5 -1
- package/src/panels/ResourceListPanel.tsx +5 -1
- package/src/panels/VariableConfigPanel.tsx +5 -1
- package/src/panels/VariablesPanel.tsx +5 -1
- package/src/stores/canvasStore.test.ts +4 -0
- package/src/stores/canvasStore.ts +4 -0
- package/src/stores/debugStore.ts +4 -0
- package/src/stores/diagnosticsStore.ts +4 -0
- package/src/stores/editorStore.ts +5 -1
- package/src/utils/categoryConstants.ts +4 -0
- package/src/utils/channelOperations.ts +4 -0
- package/src/utils/connectionRules.ts +12 -9
- package/src/utils/functionOperations.ts +4 -0
- package/src/utils/graphOperations.ts +4 -0
- package/src/utils/history.test.ts +92 -0
- package/src/utils/history.ts +5 -1
- package/src/utils/memoryOperations.ts +4 -0
- package/src/utils/migrateFunctionNodes.ts +4 -0
- package/src/utils/modelOperations.ts +4 -0
- package/src/utils/paramDisplay.ts +4 -0
- package/src/utils/resourceHelpers.ts +4 -0
- package/src/utils/translation.ts +4 -0
- package/src/utils/variableOperations.ts +4 -0
- package/tailwind-preset.ts +4 -0
package/src/BuilderLayout.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import type { FunctionDeclaration } from "@foresthubai/workflow-core/function";
|
|
2
6
|
import type { NodeDefinition } from "@foresthubai/workflow-core/node";
|
|
3
7
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -16,7 +20,7 @@ import { DebugConsolePanel } from "./panels/DebugConsolePanel";
|
|
|
16
20
|
import type { CanvasTab } from "./hooks/useCanvasTabs";
|
|
17
21
|
import { getOrCreateCanvasStore, MAIN_CANVAS_ID } from "./stores/canvasStore";
|
|
18
22
|
import { useEditorStore } from "./stores/editorStore";
|
|
19
|
-
import { isReadOnly } from "./
|
|
23
|
+
import { isReadOnly } from "./mode";
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Chrome composer. Stable across canvas switches — only the {@link CanvasEditor}
|
|
@@ -196,69 +200,55 @@ export const BuilderLayout = ({
|
|
|
196
200
|
[graph],
|
|
197
201
|
);
|
|
198
202
|
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
if (readOnly) {
|
|
221
|
-
if (event.key === "Escape") clearSelection();
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
if ((event.ctrlKey || event.metaKey) && event.key === "z" && !event.shiftKey) {
|
|
225
|
-
event.preventDefault();
|
|
226
|
-
if (canUndo()) {
|
|
227
|
-
clearSelection();
|
|
228
|
-
undo();
|
|
229
|
-
}
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
if ((event.ctrlKey || event.metaKey) && (event.key === "y" || (event.key === "z" && event.shiftKey))) {
|
|
233
|
-
event.preventDefault();
|
|
234
|
-
if (canRedo()) {
|
|
235
|
-
clearSelection();
|
|
236
|
-
redo();
|
|
237
|
-
}
|
|
238
|
-
return;
|
|
203
|
+
// Keyboard handlers — undo/redo/copy/paste/delete/escape. Attached to the
|
|
204
|
+
// builder's root element (focusable via tabIndex), NOT to document: an
|
|
205
|
+
// embedded component must not steal Ctrl+Z/Delete from the host page. The
|
|
206
|
+
// handler fires only while focus is inside the builder, which clicking the
|
|
207
|
+
// canvas or any panel establishes.
|
|
208
|
+
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
209
|
+
const target = event.target as HTMLElement;
|
|
210
|
+
if (target?.tagName === "INPUT" || target?.tagName === "TEXTAREA" || target?.isContentEditable) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (readOnly) {
|
|
214
|
+
if (event.key === "Escape") clearSelection();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "z" && !event.shiftKey) {
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
if (canUndo()) {
|
|
220
|
+
clearSelection();
|
|
221
|
+
undo();
|
|
239
222
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if ((event.ctrlKey || event.metaKey) && (event.key === "y" || (event.key === "z" && event.shiftKey))) {
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
if (canRedo()) {
|
|
228
|
+
clearSelection();
|
|
229
|
+
redo();
|
|
246
230
|
}
|
|
247
|
-
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "c") {
|
|
234
|
+
if (selection.kind === "graph" && selection.nodeIds.length > 0) {
|
|
248
235
|
event.preventDefault();
|
|
249
|
-
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (event.key === "Delete" || event.key === "Backspace") {
|
|
253
|
-
deleteSelected();
|
|
254
|
-
}
|
|
255
|
-
if (event.key === "Escape") {
|
|
256
|
-
clearSelection();
|
|
236
|
+
graph.copySelection(selection.nodeIds);
|
|
257
237
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "v") {
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
handlePaste();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (event.key === "Delete" || event.key === "Backspace") {
|
|
246
|
+
deleteSelected();
|
|
247
|
+
}
|
|
248
|
+
if (event.key === "Escape") {
|
|
249
|
+
clearSelection();
|
|
250
|
+
}
|
|
251
|
+
};
|
|
262
252
|
|
|
263
253
|
const isFunctionCanvas = activeCanvasId !== MAIN_CANVAS_ID;
|
|
264
254
|
|
|
@@ -292,7 +282,10 @@ export const BuilderLayout = ({
|
|
|
292
282
|
);
|
|
293
283
|
|
|
294
284
|
return (
|
|
295
|
-
|
|
285
|
+
// tabIndex makes the root focusable so clicks anywhere inside land focus
|
|
286
|
+
// here (nearest focusable ancestor) and the scoped key handler receives
|
|
287
|
+
// shortcuts; outline-none suppresses the focus ring this would add.
|
|
288
|
+
<div className="h-full bg-canvas-bg flex flex-col outline-none" tabIndex={-1} onKeyDown={handleKeyDown}>
|
|
296
289
|
<div className="flex-1 flex overflow-hidden">
|
|
297
290
|
<BuilderSidebar
|
|
298
291
|
canvasId={activeCanvasId}
|
package/src/Canvas.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import {
|
|
2
6
|
applyEdgeChanges,
|
|
3
7
|
applyNodeChanges,
|
|
@@ -24,7 +28,7 @@ import { isValidConnection as validateConnection } from "./utils/connectionRules
|
|
|
24
28
|
import { getOrCreateCanvasStore } from "./stores/canvasStore";
|
|
25
29
|
import { useEditorStore } from "./stores/editorStore";
|
|
26
30
|
import { nodeTypes, edgeTypes } from "./graph/reactFlowRegistry";
|
|
27
|
-
import { isReadOnly } from "./
|
|
31
|
+
import { isReadOnly } from "./mode";
|
|
28
32
|
|
|
29
33
|
interface CanvasProps {
|
|
30
34
|
canvasId: string;
|
|
@@ -55,10 +59,15 @@ const CanvasArea = ({
|
|
|
55
59
|
const { screenToFlowPosition, getViewport, fitView } = useReactFlow();
|
|
56
60
|
const nodesInitialized = useNodesInitialized();
|
|
57
61
|
|
|
62
|
+
// Measure THIS canvas's container, never document.querySelector(".react-flow"):
|
|
63
|
+
// that grabs the first match on the page and breaks with two mounted builders
|
|
64
|
+
// or a host app that uses React Flow itself.
|
|
65
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
66
|
+
|
|
58
67
|
// Expose viewport center calculation to parent via ref so new nodes can be dropped there.
|
|
59
68
|
useEffect(() => {
|
|
60
69
|
viewportCenterRef.current = () => {
|
|
61
|
-
const container =
|
|
70
|
+
const container = containerRef.current;
|
|
62
71
|
if (!container) return { x: 250, y: 100 };
|
|
63
72
|
const { width, height } = container.getBoundingClientRect();
|
|
64
73
|
const { x, y, zoom } = getViewport();
|
|
@@ -190,7 +199,7 @@ const CanvasArea = ({
|
|
|
190
199
|
// - display:none removes layout boxes, so nodes never measure and the fit can't compute.
|
|
191
200
|
// opacity applies to the whole subtree as a group (children can't override it) yet keeps
|
|
192
201
|
// layout intact, so measurement/fitView work while it's invisible.
|
|
193
|
-
<div className="w-full h-full" style={{ opacity: ready ? 1 : 0 }}>
|
|
202
|
+
<div ref={containerRef} className="w-full h-full" style={{ opacity: ready ? 1 : 0 }}>
|
|
194
203
|
<ReactFlow
|
|
195
204
|
nodes={nodes}
|
|
196
205
|
edges={edges}
|
package/src/CanvasEditor.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import type { Dispatch, MutableRefObject, SetStateAction } from "react";
|
|
2
6
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
7
|
import type { Connection, Node, OnSelectionChangeFunc } from "@xyflow/react";
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import { ScrollArea } from "./components/ui/scroll-area";
|
|
2
6
|
import { cn } from "./cn";
|
|
3
7
|
import { FunctionSquare, Workflow, X } from "lucide-react";
|
package/src/RightConfigPanel.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import { useCallback, useMemo, type ReactNode } from "react";
|
|
2
6
|
import type { NodeData, NodeDefinition } from "@foresthubai/workflow-core/node";
|
|
3
7
|
import type { EdgeData, EdgeType } from "@foresthubai/workflow-core/edge";
|
package/src/WorkflowBuilder.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import type { ApiWorkflow } from "@foresthubai/workflow-core/workflow";
|
|
2
6
|
import type { ModelInfo } from "@foresthubai/workflow-core/model";
|
|
3
7
|
import {
|
|
@@ -33,18 +37,7 @@ import {
|
|
|
33
37
|
import { useDebugStore, type DebugSessionPhase } from "./stores/debugStore";
|
|
34
38
|
import { useEditorStore } from "./stores/editorStore";
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
export type BuilderMode = { type: "edit" } | { type: "preview" } | { type: "debug" };
|
|
38
|
-
|
|
39
|
-
/** True when canvas mutations should be blocked (preview or debug). */
|
|
40
|
-
export function isReadOnly(mode: BuilderMode): boolean {
|
|
41
|
-
return mode.type !== "edit";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Type guard for preview mode. */
|
|
45
|
-
export function isPreview(mode: BuilderMode): boolean {
|
|
46
|
-
return mode.type === "preview";
|
|
47
|
-
}
|
|
40
|
+
import type { BuilderMode } from "./mode";
|
|
48
41
|
|
|
49
42
|
// ============================================================================
|
|
50
43
|
// Public contract
|
|
@@ -74,7 +67,12 @@ export interface WorkflowBuilderProps {
|
|
|
74
67
|
onDebugStep?: (nodeId?: string) => void;
|
|
75
68
|
|
|
76
69
|
// ── Lifecycle events ──
|
|
77
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Fires after any USER domain-state mutation. Pull current state via
|
|
72
|
+
* handle.exportWorkflow(). Programmatic loads (handle.loadWorkflow /
|
|
73
|
+
* handle.clear) do NOT fire this — hosts can reset their dirty flag right
|
|
74
|
+
* after those calls without guarding against echoes.
|
|
75
|
+
*/
|
|
78
76
|
onChange?: () => void;
|
|
79
77
|
/**
|
|
80
78
|
* Undo/redo availability for the ACTIVE canvas changed — on history mutation,
|
|
@@ -239,6 +237,13 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
|
|
|
239
237
|
onChangeRef.current = onChange;
|
|
240
238
|
onHistoryChangeRef.current = onHistoryChange;
|
|
241
239
|
|
|
240
|
+
// onChange means "the USER changed the document". Programmatic loads
|
|
241
|
+
// (handle.loadWorkflow / handle.clear) mutate the same stores, so their
|
|
242
|
+
// subscription callbacks — which zustand runs synchronously inside the
|
|
243
|
+
// store writes — are muted while this flag is up. Without it every host
|
|
244
|
+
// sees phantom onChange events on load and has to guard its dirty flag.
|
|
245
|
+
const suppressChangeRef = useRef(false);
|
|
246
|
+
|
|
242
247
|
// onChange fires on any domain change. For canvas content we watch the
|
|
243
248
|
// history middleware's `mutationCount`, which bumps on checkpoints AND
|
|
244
249
|
// undo/redo but never on selection/drag (those go through setNodes without a
|
|
@@ -256,8 +261,8 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
|
|
|
256
261
|
let prev = store.getState().mutationCount;
|
|
257
262
|
const unsub = store.subscribe((state) => {
|
|
258
263
|
if (state.mutationCount !== prev) {
|
|
259
|
-
prev = state.mutationCount;
|
|
260
|
-
onChangeRef.current?.();
|
|
264
|
+
prev = state.mutationCount; // always track, even when muted
|
|
265
|
+
if (!suppressChangeRef.current) onChangeRef.current?.();
|
|
261
266
|
}
|
|
262
267
|
});
|
|
263
268
|
subs.push(unsub);
|
|
@@ -285,8 +290,8 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
|
|
|
285
290
|
let prevEditorCount = useEditorStore.getState().mutationCount;
|
|
286
291
|
const unsubEditor = useEditorStore.subscribe((state) => {
|
|
287
292
|
if (state.mutationCount !== prevEditorCount) {
|
|
288
|
-
prevEditorCount = state.mutationCount;
|
|
289
|
-
onChangeRef.current?.();
|
|
293
|
+
prevEditorCount = state.mutationCount; // always track, even when muted
|
|
294
|
+
if (!suppressChangeRef.current) onChangeRef.current?.();
|
|
290
295
|
}
|
|
291
296
|
});
|
|
292
297
|
subs.push(unsubEditor);
|
|
@@ -347,14 +352,24 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
|
|
|
347
352
|
ref,
|
|
348
353
|
(): WorkflowBuilderHandle => ({
|
|
349
354
|
loadWorkflow: (workflow) => {
|
|
355
|
+
suppressChangeRef.current = true;
|
|
350
356
|
try {
|
|
351
357
|
importProject(workflow);
|
|
352
358
|
} catch (e) {
|
|
353
359
|
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
360
|
+
} finally {
|
|
361
|
+
suppressChangeRef.current = false;
|
|
354
362
|
}
|
|
355
363
|
},
|
|
356
364
|
exportWorkflow: () => exportProject(),
|
|
357
|
-
clear: () =>
|
|
365
|
+
clear: () => {
|
|
366
|
+
suppressChangeRef.current = true;
|
|
367
|
+
try {
|
|
368
|
+
clearProject();
|
|
369
|
+
} finally {
|
|
370
|
+
suppressChangeRef.current = false;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
358
373
|
setMode: (mode) => useEditorStore.getState().setBuilderMode(mode),
|
|
359
374
|
getMode: () => useEditorStore.getState().builderMode,
|
|
360
375
|
validate: runValidate,
|
package/src/cn.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react";
|
|
2
6
|
import { Plus, type LucideIcon } from "lucide-react";
|
|
3
7
|
import { Button, type ButtonProps } from "./button";
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import { Slot } from "@radix-ui/react-slot"
|
|
3
7
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
3
7
|
import { Check } from "lucide-react"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
|
|
2
6
|
|
|
3
7
|
const Collapsible = CollapsiblePrimitive.Root
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import { type DialogProps } from "@radix-ui/react-dialog"
|
|
3
7
|
import { Command as CommandPrimitive } from "cmdk"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react";
|
|
2
6
|
import { Trash2 } from "lucide-react";
|
|
3
7
|
import { Button, type ButtonProps } from "./button";
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
3
7
|
import { X } from "lucide-react"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
3
7
|
import { Check, ChevronRight, Circle } from "lucide-react"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
3
7
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as SelectPrimitive from "@radix-ui/react-select"
|
|
3
7
|
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as ToastPrimitives from "@radix-ui/react-toast"
|
|
3
7
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
|
|
3
7
|
import { type VariantProps } from "class-variance-authority"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import * as React from "react"
|
|
2
6
|
import * as TogglePrimitive from "@radix-ui/react-toggle"
|
|
3
7
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import { useEffect, useState } from "react";
|
|
2
6
|
import { useTranslation } from "react-i18next";
|
|
3
7
|
import { NodeDefinition, NodeCategory } from "@foresthubai/workflow-core/node";
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import { useState } from "react";
|
|
2
6
|
import { useTranslation } from "react-i18next";
|
|
3
7
|
import { AlertCircle, AlertTriangle, ChevronDown } from "lucide-react";
|
package/src/graph/BaseNode.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 ForestHub. All rights reserved.
|
|
3
|
+
// For commercial licensing, contact root@foresthub.ai
|
|
4
|
+
|
|
1
5
|
import { Badge } from "../components/ui/badge";
|
|
2
6
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../components/ui/tooltip";
|
|
3
7
|
import {
|
|
@@ -16,7 +20,7 @@ import { getOrCreateCanvasStore } from "../stores/canvasStore";
|
|
|
16
20
|
import { useDebugStore } from "../stores/debugStore";
|
|
17
21
|
import { useDiagnosticsStore } from "../stores/diagnosticsStore";
|
|
18
22
|
import { useEditorStore } from "../stores/editorStore";
|
|
19
|
-
import { isReadOnly } from "../
|
|
23
|
+
import { isReadOnly } from "../mode";
|
|
20
24
|
import { categoryIcons } from "../utils/categoryConstants";
|
|
21
25
|
import { computeNodeDiagnostics } from "@foresthubai/workflow-core/diagnostics";
|
|
22
26
|
import {
|
|
@@ -63,10 +67,12 @@ export const BaseNode = memo(
|
|
|
63
67
|
// Get active canvas ID with imperative access (no subscription), since it doesn't change for a node
|
|
64
68
|
const activeCanvasId = useEditorStore.getState().activeCanvasId;
|
|
65
69
|
|
|
66
|
-
// Get necessary canvas store data
|
|
70
|
+
// Get necessary canvas store data.
|
|
71
|
+
// BaseNode must NEVER subscribe to s.nodes: dragging replaces the nodes array
|
|
72
|
+
// every frame, so a nodes subscription re-renders all N nodes per frame (O(N²)).
|
|
73
|
+
// Subscribing to s.edges is fine — edges keep their identity during a drag.
|
|
67
74
|
const canvasStore = getOrCreateCanvasStore(activeCanvasId);
|
|
68
75
|
const edges = canvasStore((s) => s.edges);
|
|
69
|
-
const nodes = canvasStore((s) => s.nodes);
|
|
70
76
|
const channels = useEditorStore((s) => s.channels);
|
|
71
77
|
const memory = useEditorStore((s) => s.memory);
|
|
72
78
|
const models = useEditorStore((s) => s.models);
|
|
@@ -103,7 +109,7 @@ export const BaseNode = memo(
|
|
|
103
109
|
}, [availableModels, models]);
|
|
104
110
|
|
|
105
111
|
// Get port definitions using centralized dispatcher
|
|
106
|
-
const portDefinitions = getPorts(nodeData);
|
|
112
|
+
const portDefinitions = useMemo(() => getPorts(nodeData), [nodeData]);
|
|
107
113
|
|
|
108
114
|
// Separate ports by type for positioning
|
|
109
115
|
const { executionInputs, toolInputs, executionOutputs, toolOutputs } = useMemo(() => {
|
|
@@ -508,7 +514,7 @@ export const BaseNode = memo(
|
|
|
508
514
|
|
|
509
515
|
{/* Execution output handles (right) — disabled when tool input is connected */}
|
|
510
516
|
{executionOutputs.map((port, index) => {
|
|
511
|
-
const canAccept = !isPreview && !usedAsToolInput && canPortAcceptEdge(
|
|
517
|
+
const canAccept = !isPreview && !usedAsToolInput && canPortAcceptEdge(nodeData, port.id, edges);
|
|
512
518
|
return (
|
|
513
519
|
<PortHandle
|
|
514
520
|
key={port.id}
|
|
@@ -533,7 +539,7 @@ export const BaseNode = memo(
|
|
|
533
539
|
|
|
534
540
|
{/* Tool output handles (bottom) */}
|
|
535
541
|
{toolOutputs.map((port, index) => {
|
|
536
|
-
const canAccept = !isPreview && canPortAcceptEdge(
|
|
542
|
+
const canAccept = !isPreview && canPortAcceptEdge(nodeData, port.id, edges);
|
|
537
543
|
return (
|
|
538
544
|
<PortHandle
|
|
539
545
|
key={port.id}
|