@flowdrop/flowdrop 1.15.0 → 2.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/CHANGELOG.md +508 -0
  2. package/MIGRATION-2.0.md +629 -0
  3. package/README.md +23 -23
  4. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  5. package/dist/adapters/WorkflowAdapter.js +14 -8
  6. package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
  7. package/dist/api/enhanced-client.js +6 -11
  8. package/dist/chat/batchFeedback.d.ts +39 -0
  9. package/dist/chat/batchFeedback.js +51 -0
  10. package/dist/commands/executor.js +15 -1
  11. package/dist/commands/storeIntegration.svelte.d.ts +4 -1
  12. package/dist/commands/storeIntegration.svelte.js +26 -21
  13. package/dist/commands/types.d.ts +2 -0
  14. package/dist/components/App.svelte +163 -192
  15. package/dist/components/App.svelte.d.ts +47 -8
  16. package/dist/components/ConfigForm.svelte +77 -49
  17. package/dist/components/ConfigModal.svelte +7 -2
  18. package/dist/components/ConnectionLine.svelte +4 -2
  19. package/dist/components/Navbar.svelte +61 -1
  20. package/dist/components/NodeSidebar.svelte +27 -45
  21. package/dist/components/NodeStatusOverlay.svelte +94 -6
  22. package/dist/components/NodeSwapPicker.svelte +10 -8
  23. package/dist/components/PipelineStatus.svelte +22 -68
  24. package/dist/components/PipelineStatus.svelte.d.ts +3 -0
  25. package/dist/components/PortCoordinateTracker.svelte +5 -6
  26. package/dist/components/SchemaForm.stories.svelte +1 -3
  27. package/dist/components/SchemaForm.svelte +22 -27
  28. package/dist/components/SchemaForm.svelte.d.ts +0 -8
  29. package/dist/components/SettingsModal.svelte +8 -3
  30. package/dist/components/SettingsPanel.svelte +20 -4
  31. package/dist/components/SwapMappingEditor.svelte +67 -49
  32. package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
  33. package/dist/components/UniversalNode.svelte +9 -7
  34. package/dist/components/WorkflowEditor.svelte +121 -111
  35. package/dist/components/WorkflowEditor.svelte.d.ts +21 -10
  36. package/dist/components/chat/AIChatPanel.svelte +98 -89
  37. package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
  38. package/dist/components/chat/CommandPreview.svelte +2 -1
  39. package/dist/components/console/CommandConsole.svelte +7 -5
  40. package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
  41. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
  42. package/dist/components/console/ConsoleInput.svelte +15 -6
  43. package/dist/components/console/ConsoleOutput.svelte +2 -1
  44. package/dist/components/form/FormArray.svelte +5 -9
  45. package/dist/components/form/FormArray.svelte.d.ts +2 -1
  46. package/dist/components/form/FormAutocomplete.svelte +16 -15
  47. package/dist/components/form/FormField.svelte +4 -2
  48. package/dist/components/form/FormFieldLight.svelte +34 -3
  49. package/dist/components/form/FormFieldLight.svelte.d.ts +12 -0
  50. package/dist/components/form/FormMarkdownEditor.svelte +9 -4
  51. package/dist/components/form/FormRangeField.svelte +1 -0
  52. package/dist/components/form/FormTemplateEditor.svelte +11 -3
  53. package/dist/components/form/FormToggle.svelte +5 -12
  54. package/dist/components/form/FormToggle.svelte.d.ts +4 -2
  55. package/dist/components/form/FormUISchemaRenderer.svelte +3 -1
  56. package/dist/components/form/templateAutocomplete.js +1 -5
  57. package/dist/components/form/types.d.ts +1 -14
  58. package/dist/components/interrupt/FormPrompt.svelte +3 -2
  59. package/dist/components/interrupt/InterruptBubble.svelte +25 -17
  60. package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
  61. package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
  62. package/dist/components/layouts/MainLayout.svelte +20 -13
  63. package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
  64. package/dist/components/nodes/AtomNode.svelte +17 -5
  65. package/dist/components/nodes/GatewayNode.svelte +19 -10
  66. package/dist/components/nodes/IdeaNode.svelte +7 -0
  67. package/dist/components/nodes/SimpleNode.svelte +11 -6
  68. package/dist/components/nodes/SquareNode.svelte +15 -8
  69. package/dist/components/nodes/TerminalNode.svelte +9 -4
  70. package/dist/components/nodes/ToolNode.svelte +7 -1
  71. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  72. package/dist/components/playground/ChatInput.svelte +11 -14
  73. package/dist/components/playground/ChatPanel.svelte +6 -49
  74. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  75. package/dist/components/playground/ControlPanel.svelte +134 -123
  76. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  77. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  78. package/dist/components/playground/InputCollector.svelte +11 -9
  79. package/dist/components/playground/MessageStream.svelte +17 -23
  80. package/dist/components/playground/PipelineKanbanView.svelte +69 -8
  81. package/dist/components/playground/PipelineKanbanView.svelte.d.ts +2 -0
  82. package/dist/components/playground/PipelinePanel.svelte +31 -8
  83. package/dist/components/playground/PipelinePanel.svelte.d.ts +2 -0
  84. package/dist/components/playground/PipelineTableView.svelte +188 -44
  85. package/dist/components/playground/PipelineTableView.svelte.d.ts +2 -0
  86. package/dist/components/playground/Playground.svelte +154 -105
  87. package/dist/components/playground/Playground.svelte.d.ts +5 -0
  88. package/dist/components/playground/PlaygroundApp.svelte +11 -1
  89. package/dist/components/playground/PlaygroundApp.svelte.d.ts +6 -0
  90. package/dist/components/playground/PlaygroundModal.svelte +18 -3
  91. package/dist/components/playground/PlaygroundModal.svelte.d.ts +6 -0
  92. package/dist/components/playground/PlaygroundStudio.svelte +40 -32
  93. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +6 -0
  94. package/dist/components/playground/SessionManager.svelte +9 -12
  95. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +30 -1
  96. package/dist/components/playground/pipelineViewUtils.svelte.js +40 -3
  97. package/dist/config/endpoints.d.ts +23 -7
  98. package/dist/config/endpoints.js +30 -10
  99. package/dist/core/index.d.ts +5 -6
  100. package/dist/core/index.js +8 -12
  101. package/dist/display/index.d.ts +6 -3
  102. package/dist/display/index.js +7 -5
  103. package/dist/editor/index.d.ts +20 -21
  104. package/dist/editor/index.js +26 -36
  105. package/dist/form/code.d.ts +25 -15
  106. package/dist/form/code.js +44 -41
  107. package/dist/form/fieldRegistry.d.ts +17 -13
  108. package/dist/form/fieldRegistry.js +32 -12
  109. package/dist/form/full.d.ts +19 -14
  110. package/dist/form/full.js +26 -28
  111. package/dist/form/index.d.ts +3 -4
  112. package/dist/form/index.js +6 -5
  113. package/dist/form/markdown.d.ts +13 -8
  114. package/dist/form/markdown.js +22 -23
  115. package/dist/helpers/proximityConnect.d.ts +3 -2
  116. package/dist/helpers/proximityConnect.js +2 -5
  117. package/dist/helpers/workflowEditorHelper.d.ts +14 -5
  118. package/dist/helpers/workflowEditorHelper.js +28 -25
  119. package/dist/index.d.ts +28 -24
  120. package/dist/index.js +27 -50
  121. package/dist/messages/defaults.d.ts +2 -5
  122. package/dist/messages/defaults.js +3 -6
  123. package/dist/messages/index.d.ts +0 -1
  124. package/dist/messages/index.js +0 -1
  125. package/dist/mocks/app-forms.d.ts +6 -2
  126. package/dist/mocks/app-forms.js +11 -4
  127. package/dist/openapi/v1/openapi.yaml +3 -3
  128. package/dist/playground/index.d.ts +4 -5
  129. package/dist/playground/index.js +4 -32
  130. package/dist/playground/mount.d.ts +25 -0
  131. package/dist/playground/mount.js +50 -20
  132. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  133. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  134. package/dist/registry/builtinFormats.d.ts +9 -18
  135. package/dist/registry/builtinFormats.js +9 -39
  136. package/dist/registry/builtinNodeTypes.d.ts +53 -0
  137. package/dist/registry/builtinNodeTypes.js +67 -0
  138. package/dist/registry/builtinNodes.d.ts +2 -64
  139. package/dist/registry/builtinNodes.js +7 -103
  140. package/dist/registry/index.d.ts +3 -4
  141. package/dist/registry/index.js +4 -6
  142. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  143. package/dist/registry/nodeComponentRegistry.js +235 -17
  144. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  145. package/dist/registry/workflowFormatRegistry.js +24 -8
  146. package/dist/{schema → schemas}/index.d.ts +2 -2
  147. package/dist/{schema → schemas}/index.js +2 -2
  148. package/dist/schemas/v1/workflow.schema.json +3 -3
  149. package/dist/services/agentSpecExecutionService.d.ts +0 -2
  150. package/dist/services/agentSpecExecutionService.js +0 -3
  151. package/dist/services/apiVariableService.d.ts +2 -1
  152. package/dist/services/apiVariableService.js +16 -47
  153. package/dist/services/autoSaveService.d.ts +7 -0
  154. package/dist/services/autoSaveService.js +6 -4
  155. package/dist/services/categoriesApi.js +3 -6
  156. package/dist/services/chatService.d.ts +9 -4
  157. package/dist/services/chatService.js +23 -28
  158. package/dist/services/draftStorage.d.ts +129 -13
  159. package/dist/services/draftStorage.js +185 -37
  160. package/dist/services/dynamicSchemaService.d.ts +2 -1
  161. package/dist/services/dynamicSchemaService.js +5 -22
  162. package/dist/services/globalSave.d.ts +13 -12
  163. package/dist/services/globalSave.js +29 -51
  164. package/dist/services/historyService.d.ts +9 -3
  165. package/dist/services/historyService.js +9 -3
  166. package/dist/services/interruptService.d.ts +15 -9
  167. package/dist/services/interruptService.js +35 -37
  168. package/dist/services/nodeExecutionService.d.ts +18 -3
  169. package/dist/services/nodeExecutionService.js +71 -45
  170. package/dist/services/playgroundService.d.ts +16 -10
  171. package/dist/services/playgroundService.js +42 -43
  172. package/dist/services/portConfigApi.js +3 -6
  173. package/dist/services/settingsService.d.ts +9 -4
  174. package/dist/services/settingsService.js +23 -12
  175. package/dist/services/variableService.d.ts +2 -1
  176. package/dist/services/variableService.js +2 -2
  177. package/dist/services/workflowStorage.js +6 -6
  178. package/dist/stores/apiContext.d.ts +56 -0
  179. package/dist/stores/apiContext.js +80 -0
  180. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  181. package/dist/stores/categoriesStore.svelte.js +69 -64
  182. package/dist/stores/getInstance.svelte.d.ts +39 -0
  183. package/dist/stores/getInstance.svelte.js +65 -0
  184. package/dist/stores/historyStore.svelte.d.ts +77 -93
  185. package/dist/stores/historyStore.svelte.js +134 -160
  186. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  187. package/dist/stores/instanceContainer.svelte.js +114 -0
  188. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  189. package/dist/stores/interruptStore.svelte.js +253 -226
  190. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  191. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  192. package/dist/stores/playgroundStore.svelte.d.ts +169 -222
  193. package/dist/stores/playgroundStore.svelte.js +513 -580
  194. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  195. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  196. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  197. package/dist/stores/settingsStore.svelte.js +47 -12
  198. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  199. package/dist/stores/workflowStore.svelte.js +449 -501
  200. package/dist/stories/EdgeDecorator.svelte +5 -2
  201. package/dist/stories/NodeDecorator.svelte +5 -3
  202. package/dist/svelte-app.d.ts +60 -10
  203. package/dist/svelte-app.js +159 -54
  204. package/dist/types/auth.d.ts +9 -51
  205. package/dist/types/auth.js +4 -54
  206. package/dist/types/events.d.ts +6 -3
  207. package/dist/types/index.d.ts +37 -5
  208. package/dist/types/index.js +0 -1
  209. package/dist/types/navbar.d.ts +7 -0
  210. package/dist/types/playground.d.ts +18 -3
  211. package/dist/types/settings.d.ts +13 -0
  212. package/dist/types/settings.js +1 -0
  213. package/dist/utils/colors.d.ts +47 -21
  214. package/dist/utils/colors.js +69 -68
  215. package/dist/utils/connections.d.ts +9 -15
  216. package/dist/utils/connections.js +13 -32
  217. package/dist/utils/duration.d.ts +13 -0
  218. package/dist/utils/duration.js +45 -0
  219. package/dist/utils/edgeStyling.js +9 -5
  220. package/dist/utils/fetchWithAuth.d.ts +36 -15
  221. package/dist/utils/fetchWithAuth.js +53 -23
  222. package/dist/utils/icons.d.ts +5 -2
  223. package/dist/utils/icons.js +6 -5
  224. package/dist/utils/nodeSwap.d.ts +6 -2
  225. package/dist/utils/nodeSwap.js +62 -126
  226. package/dist/utils/nodeTypes.d.ts +17 -8
  227. package/dist/utils/nodeTypes.js +27 -20
  228. package/dist/utils/performanceUtils.js +7 -0
  229. package/package.json +7 -5
  230. package/dist/messages/deprecation.d.ts +0 -20
  231. package/dist/messages/deprecation.js +0 -33
  232. package/dist/registry/plugin.d.ts +0 -215
  233. package/dist/registry/plugin.js +0 -249
  234. package/dist/services/api.d.ts +0 -129
  235. package/dist/services/api.js +0 -217
@@ -11,60 +11,66 @@
11
11
  * Coordinates are derived from SvelteFlow's InternalNode.internals.handleBounds
12
12
  * which SvelteFlow already maintains for all node types. This avoids replicating
13
13
  * CSS positioning logic and stays automatically accurate.
14
- */
15
- import type { WorkflowNode as WorkflowNodeType, PortCoordinate, PortCoordinateMap } from '../types/index.js';
16
- import type { InternalNode } from '@xyflow/svelte';
17
- /**
18
- * Rebuild coordinates for ALL nodes from SvelteFlow internals.
19
- * Call on initial workflow load (after render) and after bulk changes.
20
- *
21
- * @param nodes - All workflow nodes
22
- * @param getInternalNode - SvelteFlow's getInternalNode function
23
- */
24
- export declare function rebuildAllPortCoordinates(nodes: WorkflowNodeType[], getInternalNode: (id: string) => InternalNode | undefined): void;
25
- /**
26
- * Update coordinates for a single node (efficient for drag updates).
27
- * Only recomputes ports for the specified node.
28
- *
29
- * @param node - The workflow node to update
30
- * @param getInternalNode - SvelteFlow's getInternalNode function
31
- */
32
- export declare function updateNodePortCoordinates(node: WorkflowNodeType, getInternalNode: (id: string) => InternalNode | undefined): void;
33
- /**
34
- * Remove all coordinates for a node (on node delete).
35
- *
36
- * @param nodeId - ID of the node to remove
37
- */
38
- export declare function removeNodePortCoordinates(nodeId: string): void;
39
- /**
40
- * Clear all port coordinates (lifecycle cleanup).
41
- */
42
- export declare function clearPortCoordinates(): void;
43
- /**
44
- * Get coordinates for a specific handle.
45
14
  *
46
- * @param handleId - The handle ID to look up
47
- * @returns The port coordinate or undefined if not found
48
- */
49
- export declare function getPortCoordinate(handleId: string): PortCoordinate | undefined;
50
- /**
51
- * Get all coordinates for a specific node.
15
+ * The reactive state lives in the {@link PortCoordinateStore} class — one per
16
+ * FlowDrop instance, resolved in components via `getInstance().portCoordinates`.
52
17
  *
53
- * @param nodeId - The node ID to look up
54
- * @returns Array of port coordinates for the node
18
+ * @module stores/portCoordinateStore
55
19
  */
56
- export declare function getNodePortCoordinates(nodeId: string): PortCoordinate[];
57
- /**
58
- * Get the current snapshot of all port coordinates.
59
- * Returns the reactive SvelteMap directly.
60
- *
61
- * @returns Current port coordinate map
62
- */
63
- export declare function getPortCoordinateSnapshot(): PortCoordinateMap;
20
+ import type { WorkflowNode as WorkflowNodeType, PortCoordinate, PortCoordinateMap } from '../types/index.js';
21
+ import type { InternalNode } from '@xyflow/svelte';
64
22
  /**
65
- * Get the reactive port coordinates state.
66
- * Useful for components that need to reactively read the coordinates.
23
+ * Per-instance absolute port coordinates, keyed by handleId.
67
24
  *
68
- * @returns The reactive port coordinate map
25
+ * A single SvelteMap mutated in place: per-key reads (getPortCoordinate)
26
+ * only react to the keys they touch, and Svelte coalesces synchronous
27
+ * mutations into one flush, so bulk updates stay cheap.
69
28
  */
70
- export declare function getPortCoordinates(): PortCoordinateMap;
29
+ export declare class PortCoordinateStore {
30
+ #private;
31
+ /**
32
+ * Rebuild coordinates for ALL nodes from SvelteFlow internals.
33
+ * Call on initial workflow load (after render) and after bulk changes.
34
+ *
35
+ * @param nodes - All workflow nodes
36
+ * @param getInternalNode - SvelteFlow's getInternalNode function
37
+ */
38
+ rebuildAll(nodes: WorkflowNodeType[], getInternalNode: (id: string) => InternalNode | undefined): void;
39
+ /**
40
+ * Update coordinates for a single node (efficient for drag updates).
41
+ * Only recomputes ports for the specified node.
42
+ *
43
+ * @param node - The workflow node to update
44
+ * @param getInternalNode - SvelteFlow's getInternalNode function
45
+ */
46
+ updateNode(node: WorkflowNodeType, getInternalNode: (id: string) => InternalNode | undefined): void;
47
+ /**
48
+ * Remove all coordinates for a node (on node delete).
49
+ *
50
+ * @param nodeId - ID of the node to remove
51
+ */
52
+ removeNode(nodeId: string): void;
53
+ /**
54
+ * Clear all port coordinates (lifecycle cleanup).
55
+ */
56
+ clear(): void;
57
+ /**
58
+ * Get coordinates for a specific handle.
59
+ *
60
+ * @param handleId - The handle ID to look up
61
+ * @returns The port coordinate or undefined if not found
62
+ */
63
+ get(handleId: string): PortCoordinate | undefined;
64
+ /**
65
+ * Get all coordinates for a specific node.
66
+ *
67
+ * @param nodeId - The node ID to look up
68
+ * @returns Array of port coordinates for the node
69
+ */
70
+ getForNode(nodeId: string): PortCoordinate[];
71
+ /**
72
+ * The reactive port coordinate map.
73
+ * Returns the reactive SvelteMap directly.
74
+ */
75
+ get coordinates(): PortCoordinateMap;
76
+ }
@@ -11,12 +11,15 @@
11
11
  * Coordinates are derived from SvelteFlow's InternalNode.internals.handleBounds
12
12
  * which SvelteFlow already maintains for all node types. This avoids replicating
13
13
  * CSS positioning logic and stays automatically accurate.
14
+ *
15
+ * The reactive state lives in the {@link PortCoordinateStore} class — one per
16
+ * FlowDrop instance, resolved in components via `getInstance().portCoordinates`.
17
+ *
18
+ * @module stores/portCoordinateStore
14
19
  */
15
20
  import { SvelteMap } from 'svelte/reactivity';
16
21
  import { untrack } from 'svelte';
17
22
  import { ProximityConnectHelper } from '../helpers/proximityConnect.js';
18
- /** Reactive state holding all port absolute coordinates, keyed by handleId */
19
- let coordinates = $state(new SvelteMap());
20
23
  /**
21
24
  * Parse a handle ID to extract nodeId, direction, and portId.
22
25
  * Handle ID format: ${nodeId}-${direction}-${portId}
@@ -92,111 +95,119 @@ function computeNodePortCoordinates(node, internalNode) {
92
95
  }
93
96
  return result;
94
97
  }
98
+ // =========================================================================
99
+ // PortCoordinateStore (per-instance reactive state)
100
+ // =========================================================================
95
101
  /**
96
- * Rebuild coordinates for ALL nodes from SvelteFlow internals.
97
- * Call on initial workflow load (after render) and after bulk changes.
102
+ * Per-instance absolute port coordinates, keyed by handleId.
98
103
  *
99
- * @param nodes - All workflow nodes
100
- * @param getInternalNode - SvelteFlow's getInternalNode function
104
+ * A single SvelteMap mutated in place: per-key reads (getPortCoordinate)
105
+ * only react to the keys they touch, and Svelte coalesces synchronous
106
+ * mutations into one flush, so bulk updates stay cheap.
101
107
  */
102
- export function rebuildAllPortCoordinates(nodes, getInternalNode) {
103
- const map = new SvelteMap();
104
- for (const node of nodes) {
108
+ export class PortCoordinateStore {
109
+ /**
110
+ * Reactive state holding all port absolute coordinates, keyed by handleId.
111
+ */
112
+ #coordinates = new SvelteMap();
113
+ /**
114
+ * Rebuild coordinates for ALL nodes from SvelteFlow internals.
115
+ * Call on initial workflow load (after render) and after bulk changes.
116
+ *
117
+ * @param nodes - All workflow nodes
118
+ * @param getInternalNode - SvelteFlow's getInternalNode function
119
+ */
120
+ rebuildAll(nodes, getInternalNode) {
121
+ this.#coordinates.clear();
122
+ for (const node of nodes) {
123
+ const internalNode = getInternalNode(node.id);
124
+ if (!internalNode)
125
+ continue;
126
+ const coords = computeNodePortCoordinates(node, internalNode);
127
+ for (const coord of coords) {
128
+ this.#coordinates.set(coord.handleId, coord);
129
+ }
130
+ }
131
+ }
132
+ /**
133
+ * Update coordinates for a single node (efficient for drag updates).
134
+ * Only recomputes ports for the specified node.
135
+ *
136
+ * @param node - The workflow node to update
137
+ * @param getInternalNode - SvelteFlow's getInternalNode function
138
+ */
139
+ updateNode(node, getInternalNode) {
105
140
  const internalNode = getInternalNode(node.id);
106
141
  if (!internalNode)
107
- continue;
108
- const coords = computeNodePortCoordinates(node, internalNode);
109
- for (const coord of coords) {
110
- map.set(coord.handleId, coord);
142
+ return;
143
+ // Collect this node's stale keys without tracking the read (callers run
144
+ // inside $effect), then mutate in place — only the touched keys notify.
145
+ const stale = [];
146
+ untrack(() => {
147
+ for (const [key, coord] of this.#coordinates) {
148
+ if (coord.nodeId === node.id)
149
+ stale.push(key);
150
+ }
151
+ });
152
+ for (const key of stale) {
153
+ this.#coordinates.delete(key);
111
154
  }
112
- }
113
- coordinates = map;
114
- }
115
- /**
116
- * Update coordinates for a single node (efficient for drag updates).
117
- * Only recomputes ports for the specified node.
118
- *
119
- * @param node - The workflow node to update
120
- * @param getInternalNode - SvelteFlow's getInternalNode function
121
- */
122
- export function updateNodePortCoordinates(node, getInternalNode) {
123
- const internalNode = getInternalNode(node.id);
124
- if (!internalNode)
125
- return;
126
- // Build a new map with all entries except this node's, then add recomputed entries.
127
- // Single assignment fires one reactive notification instead of N deletes + M sets.
128
- const newMap = new SvelteMap();
129
- untrack(() => {
130
- for (const [key, coord] of coordinates) {
131
- if (coord.nodeId !== node.id)
132
- newMap.set(key, coord);
155
+ for (const coord of computeNodePortCoordinates(node, internalNode)) {
156
+ this.#coordinates.set(coord.handleId, coord);
133
157
  }
134
- });
135
- for (const coord of computeNodePortCoordinates(node, internalNode)) {
136
- newMap.set(coord.handleId, coord);
137
158
  }
138
- coordinates = newMap;
139
- }
140
- /**
141
- * Remove all coordinates for a node (on node delete).
142
- *
143
- * @param nodeId - ID of the node to remove
144
- */
145
- export function removeNodePortCoordinates(nodeId) {
146
- const newMap = new SvelteMap();
147
- untrack(() => {
148
- for (const [key, coord] of coordinates) {
149
- if (coord.nodeId !== nodeId)
150
- newMap.set(key, coord);
159
+ /**
160
+ * Remove all coordinates for a node (on node delete).
161
+ *
162
+ * @param nodeId - ID of the node to remove
163
+ */
164
+ removeNode(nodeId) {
165
+ const stale = [];
166
+ untrack(() => {
167
+ for (const [key, coord] of this.#coordinates) {
168
+ if (coord.nodeId === nodeId)
169
+ stale.push(key);
170
+ }
171
+ });
172
+ for (const key of stale) {
173
+ this.#coordinates.delete(key);
151
174
  }
152
- });
153
- coordinates = newMap;
154
- }
155
- /**
156
- * Clear all port coordinates (lifecycle cleanup).
157
- */
158
- export function clearPortCoordinates() {
159
- coordinates = new SvelteMap();
160
- }
161
- /**
162
- * Get coordinates for a specific handle.
163
- *
164
- * @param handleId - The handle ID to look up
165
- * @returns The port coordinate or undefined if not found
166
- */
167
- export function getPortCoordinate(handleId) {
168
- return coordinates.get(handleId);
169
- }
170
- /**
171
- * Get all coordinates for a specific node.
172
- *
173
- * @param nodeId - The node ID to look up
174
- * @returns Array of port coordinates for the node
175
- */
176
- export function getNodePortCoordinates(nodeId) {
177
- const result = [];
178
- for (const coord of coordinates.values()) {
179
- if (coord.nodeId === nodeId) {
180
- result.push(coord);
175
+ }
176
+ /**
177
+ * Clear all port coordinates (lifecycle cleanup).
178
+ */
179
+ clear() {
180
+ this.#coordinates.clear();
181
+ }
182
+ /**
183
+ * Get coordinates for a specific handle.
184
+ *
185
+ * @param handleId - The handle ID to look up
186
+ * @returns The port coordinate or undefined if not found
187
+ */
188
+ get(handleId) {
189
+ return this.#coordinates.get(handleId);
190
+ }
191
+ /**
192
+ * Get all coordinates for a specific node.
193
+ *
194
+ * @param nodeId - The node ID to look up
195
+ * @returns Array of port coordinates for the node
196
+ */
197
+ getForNode(nodeId) {
198
+ const result = [];
199
+ for (const coord of this.#coordinates.values()) {
200
+ if (coord.nodeId === nodeId) {
201
+ result.push(coord);
202
+ }
181
203
  }
204
+ return result;
205
+ }
206
+ /**
207
+ * The reactive port coordinate map.
208
+ * Returns the reactive SvelteMap directly.
209
+ */
210
+ get coordinates() {
211
+ return this.#coordinates;
182
212
  }
183
- return result;
184
- }
185
- /**
186
- * Get the current snapshot of all port coordinates.
187
- * Returns the reactive SvelteMap directly.
188
- *
189
- * @returns Current port coordinate map
190
- */
191
- export function getPortCoordinateSnapshot() {
192
- return coordinates;
193
- }
194
- /**
195
- * Get the reactive port coordinates state.
196
- * Useful for components that need to reactively read the coordinates.
197
- *
198
- * @returns The reactive port coordinate map
199
- */
200
- export function getPortCoordinates() {
201
- return coordinates;
202
213
  }
@@ -99,11 +99,14 @@ export declare function cycleTheme(): void;
99
99
  export declare function cleanupThemeSubscription(): void;
100
100
  /**
101
101
  * Initialize the theme system
102
- * Should be called once on app startup
102
+ * Called on app startup; safe to call more than once (idempotent) the
103
+ * mount functions call it before mounting and <App> calls it on mount so
104
+ * direct component embeds get the persisted theme applied too.
103
105
  *
104
106
  * This function:
105
107
  * 1. Applies the current resolved theme to the document
106
108
  * 2. Sets up reactivity to apply theme changes
109
+ * 3. Subscribes to the system color-scheme so 'auto' tracks it live
107
110
  *
108
111
  * Note: In Svelte 5, we use $effect for reactivity. Since $effect can only
109
112
  * be used in component context or $effect.root, we use $effect.root here
@@ -27,20 +27,18 @@ const changeListeners = new Set();
27
27
  // localStorage Persistence
28
28
  // =========================================================================
29
29
  /**
30
- * Load settings from localStorage
30
+ * Load the raw persisted settings snapshot from localStorage
31
31
  *
32
- * @returns Saved settings or null if not found/invalid
32
+ * @returns The parsed partial snapshot, or null if not found/invalid
33
33
  */
34
- function loadFromStorage() {
34
+ function loadRawFromStorage() {
35
35
  if (typeof window === 'undefined') {
36
36
  return null;
37
37
  }
38
38
  try {
39
39
  const saved = localStorage.getItem(SETTINGS_STORAGE_KEY);
40
40
  if (saved) {
41
- const parsed = JSON.parse(saved);
42
- // Deep merge with defaults to handle missing properties
43
- return deepMergeSettings(DEFAULT_SETTINGS, parsed);
41
+ return JSON.parse(saved);
44
42
  }
45
43
  }
46
44
  catch (error) {
@@ -48,6 +46,19 @@ function loadFromStorage() {
48
46
  }
49
47
  return null;
50
48
  }
49
+ /**
50
+ * Load settings from localStorage
51
+ *
52
+ * @returns Saved settings merged over defaults, or null if not found/invalid
53
+ */
54
+ function loadFromStorage() {
55
+ const raw = loadRawFromStorage();
56
+ if (!raw) {
57
+ return null;
58
+ }
59
+ // Deep merge with defaults to handle missing properties
60
+ return deepMergeSettings(DEFAULT_SETTINGS, raw);
61
+ }
51
62
  /**
52
63
  * Save settings to localStorage
53
64
  *
@@ -377,11 +388,14 @@ export function cleanupThemeSubscription() {
377
388
  }
378
389
  /**
379
390
  * Initialize the theme system
380
- * Should be called once on app startup
391
+ * Called on app startup; safe to call more than once (idempotent) the
392
+ * mount functions call it before mounting and <App> calls it on mount so
393
+ * direct component embeds get the persisted theme applied too.
381
394
  *
382
395
  * This function:
383
396
  * 1. Applies the current resolved theme to the document
384
397
  * 2. Sets up reactivity to apply theme changes
398
+ * 3. Subscribes to the system color-scheme so 'auto' tracks it live
385
399
  *
386
400
  * Note: In Svelte 5, we use $effect for reactivity. Since $effect can only
387
401
  * be used in component context or $effect.root, we use $effect.root here
@@ -390,14 +404,25 @@ export function cleanupThemeSubscription() {
390
404
  export function initializeTheme() {
391
405
  const resolved = getResolvedTheme();
392
406
  applyTheme(resolved);
407
+ // Already wired (theme is page-global) — don't stack effect roots or
408
+ // media-query listeners on repeated mounts.
409
+ if (themeEffectCleanup) {
410
+ return;
411
+ }
412
+ // Track the system preference so 'auto' reacts to OS theme changes.
413
+ const stopThemeListener = initThemeListener();
393
414
  // Create a standalone reactive root to watch for theme changes.
394
415
  // $effect.root returns a cleanup function.
395
- themeEffectCleanup = $effect.root(() => {
416
+ const stopEffectRoot = $effect.root(() => {
396
417
  $effect(() => {
397
418
  const currentResolved = getResolvedTheme();
398
419
  applyTheme(currentResolved);
399
420
  });
400
421
  });
422
+ themeEffectCleanup = () => {
423
+ stopThemeListener();
424
+ stopEffectRoot();
425
+ };
401
426
  }
402
427
  /**
403
428
  * Check if theme system is initialized
@@ -519,15 +544,25 @@ export function onSettingsChange(callback) {
519
544
  * @param options - Initialization options
520
545
  */
521
546
  export async function initializeSettings(options) {
522
- // Apply custom defaults if provided
547
+ // Apply custom defaults if provided.
548
+ //
549
+ // Precedence (lowest to highest): library DEFAULT_SETTINGS < host
550
+ // `defaults` < the user's persisted snapshot. Host defaults SEED values
551
+ // the user has never saved — they must not override a returning user's
552
+ // persisted choices, and they are not eagerly written to storage
553
+ // (storage stays user-driven, written by updateSettings on real
554
+ // changes). Before 2.0.0 this merged host defaults OVER the persisted
555
+ // snapshot and re-saved it, which reset e.g. the user's dark-mode
556
+ // preference on every page load for hosts passing a `settings` mount
557
+ // option.
523
558
  if (options?.defaults) {
524
- const currentSettings = getSettings();
525
- const withDefaults = deepMergeSettings(currentSettings, options.defaults);
559
+ const seeded = deepMergeSettings(DEFAULT_SETTINGS, options.defaults);
560
+ const raw = loadRawFromStorage();
561
+ const withDefaults = raw ? deepMergeSettings(seeded, raw) : seeded;
526
562
  storeState = {
527
563
  ...storeState,
528
564
  settings: withDefaults
529
565
  };
530
- saveToStorage(withDefaults);
531
566
  }
532
567
  // Initialize theme system
533
568
  initializeTheme();