@foresthubai/workflow-builder 0.4.3 → 0.4.4
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 +661 -661
- package/NOTICE +16 -16
- package/README.md +110 -110
- package/dist/components/ui/command.d.ts +2 -2
- package/dist/components/ui/input.d.ts +1 -1
- package/dist/components/ui/resizable.d.ts +1 -1
- package/dist/components/ui/textarea.d.ts +1 -1
- package/dist/graph/BaseNode.js +10 -10
- package/dist/graph/reactFlowRegistry.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/BuilderLayout.tsx +345 -345
- package/src/Canvas.tsx +261 -261
- package/src/CanvasEditor.tsx +142 -142
- package/src/CanvasTabsToolbar.tsx +176 -176
- package/src/RightConfigPanel.tsx +266 -266
- package/src/WorkflowBuilder.tsx +408 -408
- package/src/cn.ts +6 -6
- package/src/components/ui/add-button.tsx +39 -39
- package/src/components/ui/alert-dialog.tsx +141 -141
- package/src/components/ui/alert.tsx +59 -59
- package/src/components/ui/badge.tsx +36 -36
- package/src/components/ui/button.tsx +85 -85
- package/src/components/ui/card.tsx +79 -79
- package/src/components/ui/checkbox.tsx +28 -28
- package/src/components/ui/collapsible.tsx +9 -9
- package/src/components/ui/command.tsx +153 -153
- package/src/components/ui/delete-button.tsx +23 -23
- package/src/components/ui/dialog.tsx +125 -125
- package/src/components/ui/dropdown-menu.tsx +198 -198
- package/src/components/ui/input.tsx +55 -55
- package/src/components/ui/label.tsx +24 -24
- package/src/components/ui/readonly-banner.tsx +15 -15
- package/src/components/ui/resizable.tsx +43 -43
- package/src/components/ui/scroll-area.tsx +102 -102
- package/src/components/ui/select.tsx +160 -160
- package/src/components/ui/separator.tsx +29 -29
- package/src/components/ui/switch.tsx +27 -27
- package/src/components/ui/textarea.tsx +51 -51
- package/src/components/ui/toast.tsx +127 -127
- package/src/components/ui/toaster.tsx +33 -33
- package/src/components/ui/toggle-group.tsx +59 -59
- package/src/components/ui/toggle.tsx +43 -43
- package/src/components/ui/tooltip.tsx +32 -32
- package/src/dialogs/NodePickerDialog.tsx +84 -84
- package/src/dialogs/ValidationDialog.tsx +184 -184
- package/src/graph/BaseNode.tsx +557 -557
- package/src/graph/CustomEdge.tsx +185 -185
- package/src/graph/CustomNode.tsx +16 -16
- package/src/graph/FunctionCallNode.tsx +30 -30
- package/src/graph/PortHandle.tsx +189 -189
- package/src/graph/reactFlowRegistry.ts +26 -26
- package/src/hooks/use-toast.ts +125 -125
- package/src/hooks/useAvailableVariables.ts +20 -20
- package/src/hooks/useCanvasHistory.ts +22 -22
- package/src/hooks/useCanvasTabs.ts +168 -168
- package/src/hooks/useFunctionDiagnosticsSync.ts +40 -40
- package/src/hooks/useFunctionRegistry.ts +26 -26
- package/src/hooks/useFunctions.ts +44 -44
- package/src/hooks/useGraph.ts +161 -161
- package/src/hooks/useNodeDefinitions.ts +82 -82
- package/src/hooks/useParamErrors.ts +26 -26
- package/src/hooks/useResolvedTheme.ts +30 -30
- package/src/hooks/useResourceDiagnosticsSync.ts +58 -58
- package/src/hooks/useSuppressThemeTransition.ts +79 -79
- package/src/hooks/useWorkflowSerialization.ts +141 -141
- package/src/i18n/index.ts +53 -53
- package/src/i18n/locales/de.json +501 -501
- package/src/i18n/locales/en.json +557 -557
- package/src/index.ts +27 -27
- package/src/inputs/ExpressionInput.tsx +297 -297
- package/src/inputs/ParameterEditor.tsx +515 -515
- package/src/inputs/PortSection.tsx +144 -144
- package/src/panels/BuilderSidebar.tsx +301 -301
- package/src/panels/ChannelConfigPanel.tsx +49 -49
- package/src/panels/ChannelsPanel.tsx +28 -28
- package/src/panels/DebugConsolePanel.tsx +73 -73
- package/src/panels/DebugContextPanel.tsx +77 -77
- package/src/panels/DebugExternalIOPanel.tsx +180 -180
- package/src/panels/DiagnosticsPanel.tsx +170 -170
- package/src/panels/EdgeConfigPanel.tsx +104 -104
- package/src/panels/FunctionConfigPanel.tsx +179 -179
- package/src/panels/FunctionListPanel.tsx +45 -45
- package/src/panels/MemoryConfigPanel.tsx +55 -55
- package/src/panels/MemoryPanel.tsx +40 -40
- package/src/panels/ModelConfigPanel.tsx +41 -41
- package/src/panels/ModelsPanel.tsx +36 -36
- package/src/panels/NodeConfigPanel.tsx +630 -630
- package/src/panels/NodeLibrary.tsx +288 -288
- package/src/panels/ResourceConfigPanel.tsx +132 -132
- package/src/panels/ResourceListPanel.tsx +113 -113
- package/src/panels/VariableConfigPanel.tsx +161 -161
- package/src/panels/VariablesPanel.tsx +145 -145
- package/src/stores/canvasStore.test.ts +44 -44
- package/src/stores/canvasStore.ts +245 -245
- package/src/stores/debugStore.ts +74 -74
- package/src/stores/diagnosticsStore.ts +130 -130
- package/src/stores/editorStore.ts +190 -190
- package/src/styles/index.css +526 -526
- package/src/utils/categoryConstants.ts +26 -26
- package/src/utils/channelOperations.ts +86 -86
- package/src/utils/connectionRules.ts +137 -137
- package/src/utils/functionOperations.ts +179 -179
- package/src/utils/graphOperations.ts +550 -550
- package/src/utils/history.ts +207 -207
- package/src/utils/memoryOperations.ts +57 -57
- package/src/utils/migrateFunctionNodes.ts +107 -107
- package/src/utils/modelOperations.ts +55 -55
- package/src/utils/paramDisplay.ts +71 -71
- package/src/utils/resourceHelpers.ts +32 -32
- package/src/utils/translation.ts +28 -28
- package/src/utils/variableOperations.ts +75 -75
- package/tailwind-preset.ts +166 -166
- package/dist/lib/utils.d.ts +0 -3
- package/dist/lib/utils.d.ts.map +0 -1
- package/dist/lib/utils.js +0 -6
- package/dist/lib/utils.js.map +0 -1
- package/dist/toolbars/CanvasTabsToolbar.d.ts +0 -11
- package/dist/toolbars/CanvasTabsToolbar.d.ts.map +0 -1
- package/dist/toolbars/CanvasTabsToolbar.js +0 -101
- package/dist/toolbars/CanvasTabsToolbar.js.map +0 -1
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
2
|
-
import { serialize, deserialize, type ApiWorkflow, type Workflow, type Canvas } from "@foresthubai/workflow-core/workflow";
|
|
3
|
-
import { migrate } from "@foresthubai/workflow-core/migration";
|
|
4
|
-
import type { NodeData } from "@foresthubai/workflow-core/node";
|
|
5
|
-
import type { EdgeData } from "@foresthubai/workflow-core/edge";
|
|
6
|
-
import type { Channel } from "@foresthubai/workflow-core/channel";
|
|
7
|
-
import type { Memory } from "@foresthubai/workflow-core/memory";
|
|
8
|
-
import type { Model } from "@foresthubai/workflow-core/model";
|
|
9
|
-
import { Edge, Node } from "@xyflow/react";
|
|
10
|
-
import { clearAllCanvasStores, getOrCreateCanvasStore, getAllCanvasStores, notifyCanvasRegistryChange, MAIN_CANVAS_ID } from "../stores/canvasStore";
|
|
11
|
-
import { useEditorStore } from "../stores/editorStore";
|
|
12
|
-
import { getReactFlowType } from "../utils/graphOperations";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Store-bound wrapper around the headless `serialize`/`deserialize` in
|
|
16
|
-
* `@foresthubai/workflow-core/workflow`. All conversion logic lives in core;
|
|
17
|
-
* this hook only mediates Zustand I/O.
|
|
18
|
-
*
|
|
19
|
-
* Core sets each node's outer `type` to the domain node type (e.g.
|
|
20
|
-
* "Agent"); React Flow needs the *display* type — `getReactFlowType`
|
|
21
|
-
* handles that on import.
|
|
22
|
-
*/
|
|
23
|
-
export function useWorkflowSerialization() {
|
|
24
|
-
const importProject = useCallback((workflow: ApiWorkflow): void => {
|
|
25
|
-
// Migrate at the load boundary so an older saved document is brought current
|
|
26
|
-
// before deserialize ever sees it. A no-op on an already-current document.
|
|
27
|
-
const state = deserialize(migrate(workflow));
|
|
28
|
-
|
|
29
|
-
clearAllCanvasStores();
|
|
30
|
-
|
|
31
|
-
// Bodies → canvas stores. The function declarations are already in domain shape
|
|
32
|
-
// (FunctionDeclaration) on state.functions; the body is the canvas at the same id.
|
|
33
|
-
for (const [canvasId, canvas] of Object.entries(state.canvases)) {
|
|
34
|
-
const store = getOrCreateCanvasStore(canvasId);
|
|
35
|
-
const rfNodes: Node<NodeData>[] = canvas.nodes.map((n) => ({
|
|
36
|
-
id: n.id,
|
|
37
|
-
type: getReactFlowType(n.type),
|
|
38
|
-
position: n.position,
|
|
39
|
-
data: n,
|
|
40
|
-
}));
|
|
41
|
-
const rfEdges: Edge<EdgeData>[] = canvas.edges.map((e) => ({
|
|
42
|
-
id: e.id,
|
|
43
|
-
type: e.type,
|
|
44
|
-
source: e.source,
|
|
45
|
-
sourceHandle: e.sourceHandle,
|
|
46
|
-
target: e.target,
|
|
47
|
-
targetHandle: e.targetHandle,
|
|
48
|
-
...(e.data ? { data: e.data } : {}),
|
|
49
|
-
}));
|
|
50
|
-
store.getState().initialize(rfNodes, rfEdges);
|
|
51
|
-
// `initialize` rebuilds node-output vars from the nodes. Replace with the
|
|
52
|
-
// fully merged set core already computed (includes declared + fnarg).
|
|
53
|
-
store.getState().setVariables(() => canvas.variables);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Reconcile project-scoped resources to exactly what this workflow declares.
|
|
57
|
-
// The editor stores are module-level and outlive the previously loaded project,
|
|
58
|
-
// so a workflow that declares none of a given kind must still reset it —
|
|
59
|
-
// otherwise the prior project's channels/memory/models bleed in (the canvas
|
|
60
|
-
// stores get this for free via clearAllCanvasStores above, as does setFunctions).
|
|
61
|
-
const { channels, memory, models } = state;
|
|
62
|
-
useEditorStore.getState().setChannels(() => channels);
|
|
63
|
-
useEditorStore.getState().setMemory(() => memory);
|
|
64
|
-
useEditorStore.getState().setModels(() => models);
|
|
65
|
-
useEditorStore.getState().setFunctions(() => state.functions);
|
|
66
|
-
|
|
67
|
-
// The function canvas stores were created after clearAllCanvasStores' notify,
|
|
68
|
-
// so tell WorkflowBuilder to re-subscribe to the new set.
|
|
69
|
-
notifyCanvasRegistryChange();
|
|
70
|
-
}, []);
|
|
71
|
-
|
|
72
|
-
const exportProject = useCallback((): ApiWorkflow => {
|
|
73
|
-
return serialize(readStateFromStores());
|
|
74
|
-
}, []);
|
|
75
|
-
|
|
76
|
-
// Inverse of importProject: reset every (module-level) store to empty so a fresh
|
|
77
|
-
// builder never inherits the previously mounted project. Used by the clear() handle
|
|
78
|
-
// and on mount when no initialWorkflow is supplied.
|
|
79
|
-
const clearProject = useCallback((): void => {
|
|
80
|
-
clearAllCanvasStores(); // drops all canvas stores, re-seeds an empty main, notifies
|
|
81
|
-
const editor = useEditorStore.getState();
|
|
82
|
-
// A stale non-main activeCanvasId would lazily resurrect a phantom function body.
|
|
83
|
-
editor.setActiveCanvas(MAIN_CANVAS_ID);
|
|
84
|
-
// Project-scoped resources aren't touched by clearAllCanvasStores. Empty only when
|
|
85
|
-
// non-empty so an already-clean store doesn't bump mutationCount (as importProject).
|
|
86
|
-
editor.setChannels((c) => (Object.keys(c).length ? {} : c));
|
|
87
|
-
editor.setMemory((m) => (Object.keys(m).length ? {} : m));
|
|
88
|
-
editor.setModels((m) => (Object.keys(m).length ? {} : m));
|
|
89
|
-
editor.setFunctions((f) => (Object.keys(f).length ? {} : f));
|
|
90
|
-
editor.clearSelection();
|
|
91
|
-
}, []);
|
|
92
|
-
|
|
93
|
-
return { exportProject, importProject, clearProject };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Read the editor's live Zustand state into a {@link Workflow} literal.
|
|
98
|
-
* Peels the React Flow wrapper without recomputing anything; channels and
|
|
99
|
-
* memory files are unprefixed for the core shape.
|
|
100
|
-
*
|
|
101
|
-
* Exported so the imperative handle can pass live state to
|
|
102
|
-
* `validateWorkflowState` without a serialize/deserialize round-trip.
|
|
103
|
-
*/
|
|
104
|
-
export function readStateFromStores(): Workflow {
|
|
105
|
-
const canvases: Record<string, Canvas> = {};
|
|
106
|
-
for (const [id, store] of Object.entries(getAllCanvasStores())) {
|
|
107
|
-
const s = store.getState();
|
|
108
|
-
canvases[id] = {
|
|
109
|
-
nodes: s.nodes.map((n) => ({ ...n.data, position: n.position })),
|
|
110
|
-
edges: s.edges.map((e) => ({
|
|
111
|
-
id: e.id,
|
|
112
|
-
type: e.type,
|
|
113
|
-
source: e.source,
|
|
114
|
-
sourceHandle: e.sourceHandle,
|
|
115
|
-
target: e.target,
|
|
116
|
-
targetHandle: e.targetHandle,
|
|
117
|
-
...(e.data ? { data: e.data } : {}),
|
|
118
|
-
})),
|
|
119
|
-
variables: s.variables,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const channels: Record<string, Channel> = {};
|
|
124
|
-
for (const ch of Object.values(useEditorStore.getState().channels)) channels[ch.id] = ch;
|
|
125
|
-
|
|
126
|
-
const memory: Record<string, Memory> = {};
|
|
127
|
-
for (const m of Object.values(useEditorStore.getState().memory)) memory[m.id] = m;
|
|
128
|
-
|
|
129
|
-
const models: Record<string, Model> = {};
|
|
130
|
-
for (const m of Object.values(useEditorStore.getState().models)) models[m.id] = m;
|
|
131
|
-
|
|
132
|
-
// Function declarations are project-scoped and already in domain shape; the body
|
|
133
|
-
// for each is the canvas at the same id (above).
|
|
134
|
-
return {
|
|
135
|
-
canvases,
|
|
136
|
-
functions: useEditorStore.getState().functions,
|
|
137
|
-
channels,
|
|
138
|
-
memory,
|
|
139
|
-
models,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { serialize, deserialize, type ApiWorkflow, type Workflow, type Canvas } from "@foresthubai/workflow-core/workflow";
|
|
3
|
+
import { migrate } from "@foresthubai/workflow-core/migration";
|
|
4
|
+
import type { NodeData } from "@foresthubai/workflow-core/node";
|
|
5
|
+
import type { EdgeData } from "@foresthubai/workflow-core/edge";
|
|
6
|
+
import type { Channel } from "@foresthubai/workflow-core/channel";
|
|
7
|
+
import type { Memory } from "@foresthubai/workflow-core/memory";
|
|
8
|
+
import type { Model } from "@foresthubai/workflow-core/model";
|
|
9
|
+
import { Edge, Node } from "@xyflow/react";
|
|
10
|
+
import { clearAllCanvasStores, getOrCreateCanvasStore, getAllCanvasStores, notifyCanvasRegistryChange, MAIN_CANVAS_ID } from "../stores/canvasStore";
|
|
11
|
+
import { useEditorStore } from "../stores/editorStore";
|
|
12
|
+
import { getReactFlowType } from "../utils/graphOperations";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Store-bound wrapper around the headless `serialize`/`deserialize` in
|
|
16
|
+
* `@foresthubai/workflow-core/workflow`. All conversion logic lives in core;
|
|
17
|
+
* this hook only mediates Zustand I/O.
|
|
18
|
+
*
|
|
19
|
+
* Core sets each node's outer `type` to the domain node type (e.g.
|
|
20
|
+
* "Agent"); React Flow needs the *display* type — `getReactFlowType`
|
|
21
|
+
* handles that on import.
|
|
22
|
+
*/
|
|
23
|
+
export function useWorkflowSerialization() {
|
|
24
|
+
const importProject = useCallback((workflow: ApiWorkflow): void => {
|
|
25
|
+
// Migrate at the load boundary so an older saved document is brought current
|
|
26
|
+
// before deserialize ever sees it. A no-op on an already-current document.
|
|
27
|
+
const state = deserialize(migrate(workflow));
|
|
28
|
+
|
|
29
|
+
clearAllCanvasStores();
|
|
30
|
+
|
|
31
|
+
// Bodies → canvas stores. The function declarations are already in domain shape
|
|
32
|
+
// (FunctionDeclaration) on state.functions; the body is the canvas at the same id.
|
|
33
|
+
for (const [canvasId, canvas] of Object.entries(state.canvases)) {
|
|
34
|
+
const store = getOrCreateCanvasStore(canvasId);
|
|
35
|
+
const rfNodes: Node<NodeData>[] = canvas.nodes.map((n) => ({
|
|
36
|
+
id: n.id,
|
|
37
|
+
type: getReactFlowType(n.type),
|
|
38
|
+
position: n.position,
|
|
39
|
+
data: n,
|
|
40
|
+
}));
|
|
41
|
+
const rfEdges: Edge<EdgeData>[] = canvas.edges.map((e) => ({
|
|
42
|
+
id: e.id,
|
|
43
|
+
type: e.type,
|
|
44
|
+
source: e.source,
|
|
45
|
+
sourceHandle: e.sourceHandle,
|
|
46
|
+
target: e.target,
|
|
47
|
+
targetHandle: e.targetHandle,
|
|
48
|
+
...(e.data ? { data: e.data } : {}),
|
|
49
|
+
}));
|
|
50
|
+
store.getState().initialize(rfNodes, rfEdges);
|
|
51
|
+
// `initialize` rebuilds node-output vars from the nodes. Replace with the
|
|
52
|
+
// fully merged set core already computed (includes declared + fnarg).
|
|
53
|
+
store.getState().setVariables(() => canvas.variables);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Reconcile project-scoped resources to exactly what this workflow declares.
|
|
57
|
+
// The editor stores are module-level and outlive the previously loaded project,
|
|
58
|
+
// so a workflow that declares none of a given kind must still reset it —
|
|
59
|
+
// otherwise the prior project's channels/memory/models bleed in (the canvas
|
|
60
|
+
// stores get this for free via clearAllCanvasStores above, as does setFunctions).
|
|
61
|
+
const { channels, memory, models } = state;
|
|
62
|
+
useEditorStore.getState().setChannels(() => channels);
|
|
63
|
+
useEditorStore.getState().setMemory(() => memory);
|
|
64
|
+
useEditorStore.getState().setModels(() => models);
|
|
65
|
+
useEditorStore.getState().setFunctions(() => state.functions);
|
|
66
|
+
|
|
67
|
+
// The function canvas stores were created after clearAllCanvasStores' notify,
|
|
68
|
+
// so tell WorkflowBuilder to re-subscribe to the new set.
|
|
69
|
+
notifyCanvasRegistryChange();
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
const exportProject = useCallback((): ApiWorkflow => {
|
|
73
|
+
return serialize(readStateFromStores());
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
// Inverse of importProject: reset every (module-level) store to empty so a fresh
|
|
77
|
+
// builder never inherits the previously mounted project. Used by the clear() handle
|
|
78
|
+
// and on mount when no initialWorkflow is supplied.
|
|
79
|
+
const clearProject = useCallback((): void => {
|
|
80
|
+
clearAllCanvasStores(); // drops all canvas stores, re-seeds an empty main, notifies
|
|
81
|
+
const editor = useEditorStore.getState();
|
|
82
|
+
// A stale non-main activeCanvasId would lazily resurrect a phantom function body.
|
|
83
|
+
editor.setActiveCanvas(MAIN_CANVAS_ID);
|
|
84
|
+
// Project-scoped resources aren't touched by clearAllCanvasStores. Empty only when
|
|
85
|
+
// non-empty so an already-clean store doesn't bump mutationCount (as importProject).
|
|
86
|
+
editor.setChannels((c) => (Object.keys(c).length ? {} : c));
|
|
87
|
+
editor.setMemory((m) => (Object.keys(m).length ? {} : m));
|
|
88
|
+
editor.setModels((m) => (Object.keys(m).length ? {} : m));
|
|
89
|
+
editor.setFunctions((f) => (Object.keys(f).length ? {} : f));
|
|
90
|
+
editor.clearSelection();
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
return { exportProject, importProject, clearProject };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Read the editor's live Zustand state into a {@link Workflow} literal.
|
|
98
|
+
* Peels the React Flow wrapper without recomputing anything; channels and
|
|
99
|
+
* memory files are unprefixed for the core shape.
|
|
100
|
+
*
|
|
101
|
+
* Exported so the imperative handle can pass live state to
|
|
102
|
+
* `validateWorkflowState` without a serialize/deserialize round-trip.
|
|
103
|
+
*/
|
|
104
|
+
export function readStateFromStores(): Workflow {
|
|
105
|
+
const canvases: Record<string, Canvas> = {};
|
|
106
|
+
for (const [id, store] of Object.entries(getAllCanvasStores())) {
|
|
107
|
+
const s = store.getState();
|
|
108
|
+
canvases[id] = {
|
|
109
|
+
nodes: s.nodes.map((n) => ({ ...n.data, position: n.position })),
|
|
110
|
+
edges: s.edges.map((e) => ({
|
|
111
|
+
id: e.id,
|
|
112
|
+
type: e.type,
|
|
113
|
+
source: e.source,
|
|
114
|
+
sourceHandle: e.sourceHandle,
|
|
115
|
+
target: e.target,
|
|
116
|
+
targetHandle: e.targetHandle,
|
|
117
|
+
...(e.data ? { data: e.data } : {}),
|
|
118
|
+
})),
|
|
119
|
+
variables: s.variables,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const channels: Record<string, Channel> = {};
|
|
124
|
+
for (const ch of Object.values(useEditorStore.getState().channels)) channels[ch.id] = ch;
|
|
125
|
+
|
|
126
|
+
const memory: Record<string, Memory> = {};
|
|
127
|
+
for (const m of Object.values(useEditorStore.getState().memory)) memory[m.id] = m;
|
|
128
|
+
|
|
129
|
+
const models: Record<string, Model> = {};
|
|
130
|
+
for (const m of Object.values(useEditorStore.getState().models)) models[m.id] = m;
|
|
131
|
+
|
|
132
|
+
// Function declarations are project-scoped and already in domain shape; the body
|
|
133
|
+
// for each is the canvas at the same id (above).
|
|
134
|
+
return {
|
|
135
|
+
canvases,
|
|
136
|
+
functions: useEditorStore.getState().functions,
|
|
137
|
+
channels,
|
|
138
|
+
memory,
|
|
139
|
+
models,
|
|
140
|
+
};
|
|
141
|
+
}
|
package/src/i18n/index.ts
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import i18next, { type i18n as I18n } from "i18next";
|
|
2
|
-
|
|
3
|
-
import de from "./locales/de.json";
|
|
4
|
-
import en from "./locales/en.json";
|
|
5
|
-
|
|
6
|
-
const resources = {
|
|
7
|
-
de: {
|
|
8
|
-
translation: de,
|
|
9
|
-
},
|
|
10
|
-
en: {
|
|
11
|
-
translation: en,
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// A PRIVATE i18next instance, not the global default. This keeps the builder's
|
|
16
|
-
// translations isolated from any i18next the host app runs, so the two never
|
|
17
|
-
// collide. We deliberately do NOT use LanguageDetector: the host owns locale and
|
|
18
|
-
// drives it via the WorkflowBuilder `language` prop (see WorkflowBuilder.tsx),
|
|
19
|
-
// rather than the builder reading navigator/localStorage behind the host's back.
|
|
20
|
-
//
|
|
21
|
-
// We also deliberately do NOT `.use(initReactI18next)`. That helper's init()
|
|
22
|
-
// calls react-i18next's setI18n(), which overwrites react-i18next's library-wide
|
|
23
|
-
// DEFAULT instance (a single module-level pointer) with whichever instance
|
|
24
|
-
// initialized last. The builder is a component, not the app — clobbering that
|
|
25
|
-
// global would hijack the HOST's provider-less useTranslation() (its toolbar,
|
|
26
|
-
// etc.). Instead this instance stays fully private: WorkflowBuilder feeds it to
|
|
27
|
-
// its subtree via <I18nextProvider>, and useTranslation resolves the instance
|
|
28
|
-
// from that context, never from the global — so no global registration is needed.
|
|
29
|
-
// React options live on the instance (useTranslation merges instance.options.react
|
|
30
|
-
// ahead of react-i18next's globals), keeping behaviour independent of the host.
|
|
31
|
-
//
|
|
32
|
-
// Init is eager (module load) because non-React callers capture `i18n.t` at
|
|
33
|
-
// module-eval time (e.g. hooks/useNodeDefinitions.ts). Resources are bundled, so
|
|
34
|
-
// i18next loads them synchronously inside init() and `t` is usable immediately.
|
|
35
|
-
const i18n: I18n = i18next.createInstance();
|
|
36
|
-
|
|
37
|
-
void i18n.init({
|
|
38
|
-
resources,
|
|
39
|
-
fallbackLng: "en",
|
|
40
|
-
lng: "en",
|
|
41
|
-
|
|
42
|
-
interpolation: {
|
|
43
|
-
escapeValue: false, // not needed for react as it escapes by default
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
defaultNS: "translation",
|
|
47
|
-
|
|
48
|
-
// Own these so the builder doesn't inherit the host's react-i18next defaults.
|
|
49
|
-
// Resources are bundled synchronously, so there's nothing to suspend on.
|
|
50
|
-
react: { useSuspense: false, bindI18n: "languageChanged" },
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
export default i18n;
|
|
1
|
+
import i18next, { type i18n as I18n } from "i18next";
|
|
2
|
+
|
|
3
|
+
import de from "./locales/de.json";
|
|
4
|
+
import en from "./locales/en.json";
|
|
5
|
+
|
|
6
|
+
const resources = {
|
|
7
|
+
de: {
|
|
8
|
+
translation: de,
|
|
9
|
+
},
|
|
10
|
+
en: {
|
|
11
|
+
translation: en,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// A PRIVATE i18next instance, not the global default. This keeps the builder's
|
|
16
|
+
// translations isolated from any i18next the host app runs, so the two never
|
|
17
|
+
// collide. We deliberately do NOT use LanguageDetector: the host owns locale and
|
|
18
|
+
// drives it via the WorkflowBuilder `language` prop (see WorkflowBuilder.tsx),
|
|
19
|
+
// rather than the builder reading navigator/localStorage behind the host's back.
|
|
20
|
+
//
|
|
21
|
+
// We also deliberately do NOT `.use(initReactI18next)`. That helper's init()
|
|
22
|
+
// calls react-i18next's setI18n(), which overwrites react-i18next's library-wide
|
|
23
|
+
// DEFAULT instance (a single module-level pointer) with whichever instance
|
|
24
|
+
// initialized last. The builder is a component, not the app — clobbering that
|
|
25
|
+
// global would hijack the HOST's provider-less useTranslation() (its toolbar,
|
|
26
|
+
// etc.). Instead this instance stays fully private: WorkflowBuilder feeds it to
|
|
27
|
+
// its subtree via <I18nextProvider>, and useTranslation resolves the instance
|
|
28
|
+
// from that context, never from the global — so no global registration is needed.
|
|
29
|
+
// React options live on the instance (useTranslation merges instance.options.react
|
|
30
|
+
// ahead of react-i18next's globals), keeping behaviour independent of the host.
|
|
31
|
+
//
|
|
32
|
+
// Init is eager (module load) because non-React callers capture `i18n.t` at
|
|
33
|
+
// module-eval time (e.g. hooks/useNodeDefinitions.ts). Resources are bundled, so
|
|
34
|
+
// i18next loads them synchronously inside init() and `t` is usable immediately.
|
|
35
|
+
const i18n: I18n = i18next.createInstance();
|
|
36
|
+
|
|
37
|
+
void i18n.init({
|
|
38
|
+
resources,
|
|
39
|
+
fallbackLng: "en",
|
|
40
|
+
lng: "en",
|
|
41
|
+
|
|
42
|
+
interpolation: {
|
|
43
|
+
escapeValue: false, // not needed for react as it escapes by default
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
defaultNS: "translation",
|
|
47
|
+
|
|
48
|
+
// Own these so the builder doesn't inherit the host's react-i18next defaults.
|
|
49
|
+
// Resources are bundled synchronously, so there's nothing to suspend on.
|
|
50
|
+
react: { useSuspense: false, bindI18n: "languageChanged" },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export default i18n;
|