@flowdrop/flowdrop 1.14.0 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/CHANGELOG.md +475 -0
  2. package/MIGRATION-2.0.md +472 -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/chat/batchFeedback.d.ts +39 -0
  8. package/dist/chat/batchFeedback.js +51 -0
  9. package/dist/commands/executor.js +15 -1
  10. package/dist/commands/storeIntegration.svelte.d.ts +4 -1
  11. package/dist/commands/storeIntegration.svelte.js +26 -21
  12. package/dist/commands/types.d.ts +2 -0
  13. package/dist/components/App.svelte +162 -192
  14. package/dist/components/App.svelte.d.ts +47 -8
  15. package/dist/components/ConfigForm.svelte +110 -66
  16. package/dist/components/ConfigModal.svelte +7 -2
  17. package/dist/components/ConnectionLine.svelte +4 -2
  18. package/dist/components/Navbar.svelte +61 -1
  19. package/dist/components/NodeSidebar.svelte +27 -45
  20. package/dist/components/NodeStatusOverlay.svelte +94 -6
  21. package/dist/components/NodeSwapPicker.svelte +10 -8
  22. package/dist/components/PipelineStatus.svelte +16 -67
  23. package/dist/components/PortCoordinateTracker.svelte +5 -6
  24. package/dist/components/SchemaForm.stories.svelte +1 -3
  25. package/dist/components/SchemaForm.svelte +45 -40
  26. package/dist/components/SchemaForm.svelte.d.ts +0 -8
  27. package/dist/components/SettingsModal.svelte +8 -3
  28. package/dist/components/SettingsPanel.svelte +20 -4
  29. package/dist/components/SwapMappingEditor.svelte +67 -49
  30. package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
  31. package/dist/components/UniversalNode.svelte +9 -7
  32. package/dist/components/WorkflowEditor.svelte +118 -111
  33. package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
  34. package/dist/components/chat/AIChatPanel.svelte +93 -89
  35. package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
  36. package/dist/components/chat/CommandPreview.svelte +2 -1
  37. package/dist/components/console/CommandConsole.svelte +7 -5
  38. package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
  39. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
  40. package/dist/components/console/ConsoleInput.svelte +15 -6
  41. package/dist/components/console/ConsoleOutput.svelte +2 -1
  42. package/dist/components/form/FormArray.svelte +5 -9
  43. package/dist/components/form/FormArray.svelte.d.ts +2 -1
  44. package/dist/components/form/FormAutocomplete.svelte +29 -13
  45. package/dist/components/form/FormField.svelte +4 -2
  46. package/dist/components/form/FormFieldLight.svelte +4 -2
  47. package/dist/components/form/FormMarkdownEditor.svelte +9 -4
  48. package/dist/components/form/FormRangeField.svelte +1 -0
  49. package/dist/components/form/FormTemplateEditor.svelte +11 -3
  50. package/dist/components/form/FormToggle.svelte +5 -12
  51. package/dist/components/form/FormToggle.svelte.d.ts +4 -2
  52. package/dist/components/form/templateAutocomplete.js +1 -5
  53. package/dist/components/form/types.d.ts +1 -14
  54. package/dist/components/interrupt/FormPrompt.svelte +3 -2
  55. package/dist/components/interrupt/InterruptBubble.svelte +16 -17
  56. package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
  57. package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
  58. package/dist/components/layouts/MainLayout.svelte +20 -13
  59. package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
  60. package/dist/components/nodes/AtomNode.svelte +292 -0
  61. package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
  62. package/dist/components/nodes/GatewayNode.svelte +19 -10
  63. package/dist/components/nodes/IdeaNode.svelte +7 -0
  64. package/dist/components/nodes/SimpleNode.svelte +11 -6
  65. package/dist/components/nodes/SquareNode.svelte +15 -8
  66. package/dist/components/nodes/TerminalNode.svelte +9 -4
  67. package/dist/components/nodes/ToolNode.svelte +7 -1
  68. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  69. package/dist/components/playground/ChatInput.svelte +11 -14
  70. package/dist/components/playground/ChatPanel.svelte +6 -49
  71. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  72. package/dist/components/playground/ControlPanel.svelte +134 -123
  73. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  74. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  75. package/dist/components/playground/InputCollector.svelte +11 -9
  76. package/dist/components/playground/MessageStream.svelte +17 -23
  77. package/dist/components/playground/PipelineKanbanView.svelte +65 -6
  78. package/dist/components/playground/PipelinePanel.svelte +11 -5
  79. package/dist/components/playground/PipelineTableView.svelte +186 -44
  80. package/dist/components/playground/Playground.svelte +95 -92
  81. package/dist/components/playground/Playground.svelte.d.ts +2 -0
  82. package/dist/components/playground/PlaygroundApp.svelte +6 -1
  83. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  84. package/dist/components/playground/PlaygroundModal.svelte +13 -3
  85. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  86. package/dist/components/playground/PlaygroundStudio.svelte +34 -32
  87. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  88. package/dist/components/playground/SessionManager.svelte +9 -12
  89. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
  90. package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
  91. package/dist/config/endpoints.d.ts +0 -7
  92. package/dist/config/endpoints.js +2 -10
  93. package/dist/core/index.d.ts +4 -4
  94. package/dist/core/index.js +6 -6
  95. package/dist/display/index.d.ts +0 -2
  96. package/dist/display/index.js +0 -6
  97. package/dist/editor/index.d.ts +19 -20
  98. package/dist/editor/index.js +25 -35
  99. package/dist/form/code.d.ts +25 -15
  100. package/dist/form/code.js +44 -41
  101. package/dist/form/fieldRegistry.d.ts +17 -13
  102. package/dist/form/fieldRegistry.js +32 -12
  103. package/dist/form/full.d.ts +17 -13
  104. package/dist/form/full.js +22 -27
  105. package/dist/form/index.d.ts +3 -3
  106. package/dist/form/index.js +3 -3
  107. package/dist/form/markdown.d.ts +13 -8
  108. package/dist/form/markdown.js +22 -23
  109. package/dist/helpers/proximityConnect.d.ts +7 -3
  110. package/dist/helpers/proximityConnect.js +19 -6
  111. package/dist/helpers/workflowEditorHelper.d.ts +12 -5
  112. package/dist/helpers/workflowEditorHelper.js +27 -25
  113. package/dist/index.d.ts +28 -24
  114. package/dist/index.js +27 -50
  115. package/dist/messages/defaults.d.ts +2 -5
  116. package/dist/messages/defaults.js +3 -6
  117. package/dist/messages/index.d.ts +0 -1
  118. package/dist/messages/index.js +0 -1
  119. package/dist/mocks/app-forms.d.ts +6 -2
  120. package/dist/mocks/app-forms.js +11 -4
  121. package/dist/openapi/v1/openapi.yaml +227 -164
  122. package/dist/playground/index.d.ts +2 -3
  123. package/dist/playground/index.js +2 -30
  124. package/dist/playground/mount.d.ts +15 -0
  125. package/dist/playground/mount.js +46 -20
  126. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  127. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  128. package/dist/registry/builtinFormats.d.ts +9 -18
  129. package/dist/registry/builtinFormats.js +9 -39
  130. package/dist/registry/builtinNodes.d.ts +1 -26
  131. package/dist/registry/builtinNodes.js +14 -50
  132. package/dist/registry/index.d.ts +3 -4
  133. package/dist/registry/index.js +4 -6
  134. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  135. package/dist/registry/nodeComponentRegistry.js +235 -17
  136. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  137. package/dist/registry/workflowFormatRegistry.js +24 -8
  138. package/dist/{schema → schemas}/index.d.ts +2 -2
  139. package/dist/{schema → schemas}/index.js +2 -2
  140. package/dist/schemas/v1/workflow.schema.json +53 -6
  141. package/dist/services/agentSpecExecutionService.js +0 -1
  142. package/dist/services/apiVariableService.d.ts +2 -1
  143. package/dist/services/apiVariableService.js +5 -22
  144. package/dist/services/autoSaveService.d.ts +7 -0
  145. package/dist/services/autoSaveService.js +6 -4
  146. package/dist/services/chatService.d.ts +8 -4
  147. package/dist/services/chatService.js +15 -15
  148. package/dist/services/draftStorage.d.ts +129 -13
  149. package/dist/services/draftStorage.js +185 -37
  150. package/dist/services/dynamicSchemaService.d.ts +2 -1
  151. package/dist/services/dynamicSchemaService.js +5 -22
  152. package/dist/services/globalSave.d.ts +13 -12
  153. package/dist/services/globalSave.js +29 -51
  154. package/dist/services/historyService.d.ts +9 -3
  155. package/dist/services/historyService.js +9 -3
  156. package/dist/services/interruptService.d.ts +14 -9
  157. package/dist/services/interruptService.js +27 -27
  158. package/dist/services/nodeExecutionService.d.ts +18 -3
  159. package/dist/services/nodeExecutionService.js +71 -45
  160. package/dist/services/playgroundService.d.ts +14 -9
  161. package/dist/services/playgroundService.js +31 -30
  162. package/dist/services/variableService.d.ts +2 -1
  163. package/dist/services/variableService.js +2 -2
  164. package/dist/services/workflowStorage.js +6 -6
  165. package/dist/stores/apiContext.d.ts +45 -0
  166. package/dist/stores/apiContext.js +65 -0
  167. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  168. package/dist/stores/categoriesStore.svelte.js +70 -64
  169. package/dist/stores/getInstance.svelte.d.ts +39 -0
  170. package/dist/stores/getInstance.svelte.js +65 -0
  171. package/dist/stores/historyStore.svelte.d.ts +77 -93
  172. package/dist/stores/historyStore.svelte.js +134 -160
  173. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  174. package/dist/stores/instanceContainer.svelte.js +114 -0
  175. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  176. package/dist/stores/interruptStore.svelte.js +253 -226
  177. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  178. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  179. package/dist/stores/playgroundStore.svelte.d.ts +169 -216
  180. package/dist/stores/playgroundStore.svelte.js +515 -572
  181. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  182. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  183. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  184. package/dist/stores/settingsStore.svelte.js +47 -12
  185. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  186. package/dist/stores/workflowStore.svelte.js +449 -501
  187. package/dist/stories/EdgeDecorator.svelte +5 -2
  188. package/dist/stories/NodeDecorator.svelte +5 -3
  189. package/dist/svelte-app.d.ts +60 -10
  190. package/dist/svelte-app.js +157 -53
  191. package/dist/types/events.d.ts +6 -3
  192. package/dist/types/index.d.ts +71 -6
  193. package/dist/types/navbar.d.ts +7 -0
  194. package/dist/types/playground.d.ts +18 -3
  195. package/dist/types/settings.d.ts +13 -0
  196. package/dist/types/settings.js +1 -0
  197. package/dist/utils/colors.d.ts +47 -21
  198. package/dist/utils/colors.js +69 -68
  199. package/dist/utils/connections.d.ts +9 -15
  200. package/dist/utils/connections.js +13 -32
  201. package/dist/utils/duration.d.ts +13 -0
  202. package/dist/utils/duration.js +45 -0
  203. package/dist/utils/formMerge.d.ts +36 -0
  204. package/dist/utils/formMerge.js +70 -0
  205. package/dist/utils/icons.d.ts +5 -2
  206. package/dist/utils/icons.js +6 -5
  207. package/dist/utils/nodeSwap.d.ts +6 -2
  208. package/dist/utils/nodeSwap.js +62 -126
  209. package/dist/utils/nodeTypes.d.ts +17 -8
  210. package/dist/utils/nodeTypes.js +27 -19
  211. package/dist/utils/performanceUtils.js +7 -0
  212. package/package.json +6 -5
  213. package/dist/messages/deprecation.d.ts +0 -20
  214. package/dist/messages/deprecation.js +0 -33
  215. package/dist/registry/plugin.d.ts +0 -215
  216. package/dist/registry/plugin.js +0 -249
  217. package/dist/services/api.d.ts +0 -129
  218. package/dist/services/api.js +0 -217
@@ -0,0 +1,292 @@
1
+ <!--
2
+ Atom Node Component
3
+ Minimalist, label-only node for "supplies a value" atoms (Constant now, Cast later).
4
+ Renders as a pill that hugs its content with handles driven by the node's ports.
5
+
6
+ The body text and the output port's dataType are both driven by config via
7
+ `extensions.ui.atom` (see AtomUIConfig). This component owns no domain semantics —
8
+ meaning comes entirely from the node's NodeMetadata.
9
+ -->
10
+
11
+ <script lang="ts">
12
+ import { Position, Handle } from '@xyflow/svelte';
13
+ import type {
14
+ ConfigValues,
15
+ ConfigSchema,
16
+ NodeMetadata,
17
+ NodeExtensions,
18
+ NodePort,
19
+ AtomUIConfig,
20
+ WorkflowNode as WorkflowNodeType
21
+ } from '../../types/index.js';
22
+ import { getDataTypeColor } from '../../utils/colors.js';
23
+ import { getInstance } from '../../stores/getInstance.svelte.js';
24
+ import { applyPortOrder, isPortVisible } from '../../utils/portUtils.js';
25
+ import { ProximityConnectHelper } from '../../helpers/proximityConnect.js';
26
+
27
+ interface AtomNodeData {
28
+ label: string;
29
+ config: ConfigValues;
30
+ metadata: NodeMetadata;
31
+ nodeId?: string;
32
+ extensions?: NodeExtensions;
33
+ onConfigOpen?: (node: {
34
+ id: string;
35
+ type: string;
36
+ data: { label: string; config: ConfigValues; metadata: NodeMetadata };
37
+ }) => void;
38
+ }
39
+
40
+ interface Props {
41
+ data: AtomNodeData;
42
+ selected?: boolean;
43
+ isProcessing?: boolean;
44
+ isError?: boolean;
45
+ }
46
+
47
+ let { data, selected, isProcessing, isError }: Props = $props();
48
+
49
+ const fd = getInstance();
50
+ const checker = fd.portCompatibility;
51
+
52
+ const nodeId = $derived(data.nodeId ?? 'unknown');
53
+ const nodeType = $derived(data.metadata?.type ?? 'atom');
54
+
55
+ // Instance extensions override node-type defaults.
56
+ const atomCfg = $derived<AtomUIConfig>(
57
+ data.extensions?.ui?.atom ?? data.metadata?.extensions?.ui?.atom ?? {}
58
+ );
59
+ const hideUnconnectedHandles = $derived(
60
+ data.extensions?.ui?.hideUnconnectedHandles ??
61
+ data.metadata?.extensions?.ui?.hideUnconnectedHandles ??
62
+ false
63
+ );
64
+ const hiddenPorts = $derived(
65
+ data.extensions?.ui?.hiddenPorts ?? data.metadata?.extensions?.ui?.hiddenPorts ?? {}
66
+ );
67
+ const portOrder = $derived(
68
+ data.extensions?.ui?.portOrder ?? data.metadata?.extensions?.ui?.portOrder ?? {}
69
+ );
70
+
71
+ // Optional, server-driven accent. When unset the inline custom property is
72
+ // omitted, so CSS falls back to the neutral border — uncolored atoms are
73
+ // unchanged. Mirrors ToolNode: node definition (metadata) wins over instance.
74
+ const atomColor = $derived(data.metadata?.color ?? (data.config?.color as string | undefined));
75
+ const isRect = $derived(atomCfg.shape === 'rectangle');
76
+
77
+ // Only the dynamic bits live inline; max-width and accent both optional.
78
+ const nodeStyle = $derived(
79
+ [
80
+ atomCfg.maxWidth ? `max-width: ${atomCfg.maxWidth}px` : '',
81
+ atomColor ? `--fd-atom-node-color: ${atomColor}` : ''
82
+ ]
83
+ .filter(Boolean)
84
+ .join('; ')
85
+ );
86
+
87
+ // The node slice getAllPorts needs — keeps port resolution in one place,
88
+ // shared with proximity-connect and coordinate/validation logic. No cast: the
89
+ // helper's signature documents that `type` + `data` are all it reads.
90
+ const nodeLike = $derived<Pick<WorkflowNodeType, 'type' | 'data'>>({ type: nodeType, data });
91
+
92
+ const inPorts = $derived(
93
+ applyPortOrder(ProximityConnectHelper.getAllPorts(nodeLike, 'input'), portOrder.inputs).filter(
94
+ (p: NodePort) =>
95
+ isPortVisible(
96
+ p,
97
+ 'input',
98
+ hiddenPorts,
99
+ hideUnconnectedHandles,
100
+ fd.workflow.connectedHandles,
101
+ nodeId
102
+ )
103
+ )
104
+ );
105
+ const outPorts = $derived(
106
+ applyPortOrder(
107
+ ProximityConnectHelper.getAllPorts(nodeLike, 'output'),
108
+ portOrder.outputs
109
+ ).filter((p: NodePort) =>
110
+ isPortVisible(
111
+ p,
112
+ 'output',
113
+ hiddenPorts,
114
+ hideUnconnectedHandles,
115
+ fd.workflow.connectedHandles,
116
+ nodeId
117
+ )
118
+ )
119
+ );
120
+
121
+ /** Friendly label for a value, using the field's oneOf titles when present. */
122
+ function resolveDisplay(schema: ConfigSchema | undefined, key: string, raw: unknown): string {
123
+ const prop = schema?.properties?.[key] as
124
+ | { oneOf?: Array<{ const: unknown; title?: string }> }
125
+ | undefined;
126
+ const match = prop?.oneOf?.find((o) => o.const === raw);
127
+ if (match?.title) return match.title;
128
+ if (typeof raw === 'boolean') return raw ? 'true' : 'false';
129
+ return String(raw);
130
+ }
131
+
132
+ // Body text: config[valueKey] (label-resolved), else node label. When neither
133
+ // resolves, fall back to the placeholder and render it dimmed.
134
+ const display = $derived.by(() => {
135
+ const key = atomCfg.valueKey;
136
+ const raw = key ? data.config?.[key] : undefined;
137
+ if (key && raw !== undefined && raw !== null && raw !== '') {
138
+ return { text: resolveDisplay(data.metadata?.configSchema, key, raw), empty: false };
139
+ }
140
+ if (data.label) return { text: data.label, empty: false };
141
+ return { text: atomCfg.placeholder ?? '', empty: true };
142
+ });
143
+
144
+ // Pill height is content-driven (~28px), so fixed px offsets don't fit.
145
+ // Distribute handles as a % of node height: 50% for one port, evenly otherwise.
146
+ function portTopPct(index: number, count: number): number {
147
+ return ((index + 1) / (count + 1)) * 100;
148
+ }
149
+
150
+ function openConfig(): void {
151
+ data.onConfigOpen?.({ id: nodeId, type: nodeType, data });
152
+ }
153
+ function handleKeydown(event: KeyboardEvent): void {
154
+ if (event.key === 'Enter' || event.key === ' ') {
155
+ event.preventDefault();
156
+ openConfig();
157
+ }
158
+ }
159
+ </script>
160
+
161
+ {#each inPorts as port, index (port.id)}
162
+ <Handle
163
+ type="target"
164
+ position={Position.Left}
165
+ id={`${nodeId}-input-${port.id}`}
166
+ style="--fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
167
+ checker,
168
+ port.dataType
169
+ )}); top: {portTopPct(index, inPorts.length)}%;"
170
+ />
171
+ {/each}
172
+
173
+ <div
174
+ class="flowdrop-atom-node"
175
+ class:flowdrop-atom-node--selected={selected}
176
+ class:flowdrop-atom-node--processing={isProcessing}
177
+ class:flowdrop-atom-node--error={isError}
178
+ class:flowdrop-atom-node--empty={display.empty}
179
+ class:flowdrop-atom-node--rect={isRect}
180
+ style={nodeStyle}
181
+ onclick={openConfig}
182
+ onkeydown={handleKeydown}
183
+ role="button"
184
+ tabindex="0"
185
+ >
186
+ {#if atomCfg.prefix && !display.empty}
187
+ <span class="flowdrop-atom-node__prefix" aria-hidden="true">{atomCfg.prefix}</span>
188
+ {/if}
189
+ <span class="flowdrop-atom-node__body" title={display.text}>{display.text}</span>
190
+ </div>
191
+
192
+ {#each outPorts as port, index (port.id)}
193
+ <Handle
194
+ type="source"
195
+ position={Position.Right}
196
+ id={`${nodeId}-output-${port.id}`}
197
+ style="--fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
198
+ checker,
199
+ port.dataType
200
+ )}); top: {portTopPct(index, outPorts.length)}%;"
201
+ />
202
+ {/each}
203
+
204
+ <style>
205
+ .flowdrop-atom-node {
206
+ position: relative;
207
+ display: inline-flex;
208
+ align-items: center;
209
+ width: fit-content;
210
+ min-width: 2rem;
211
+ min-height: 28px;
212
+ padding: 2px var(--fd-space-sm);
213
+ background-color: var(--fd-card);
214
+ /* --fd-atom-node-color is set inline only when the server provides a color;
215
+ otherwise it falls back to the neutral border token. */
216
+ border: 1.5px solid var(--fd-atom-node-color, var(--fd-node-border));
217
+ border-radius: 999px;
218
+ box-shadow: var(--fd-shadow-sm);
219
+ color: var(--fd-foreground);
220
+ cursor: pointer;
221
+ transition:
222
+ box-shadow var(--fd-transition-fast),
223
+ border-color var(--fd-transition-fast);
224
+ z-index: 10;
225
+ }
226
+
227
+ .flowdrop-atom-node--rect {
228
+ border-radius: var(--fd-radius-md);
229
+ }
230
+
231
+ .flowdrop-atom-node:hover {
232
+ box-shadow: var(--fd-shadow-md);
233
+ border-color: var(--fd-atom-node-color, var(--fd-node-border-hover));
234
+ }
235
+
236
+ .flowdrop-atom-node--selected {
237
+ box-shadow:
238
+ 0 0 0 2px color-mix(in srgb, var(--fd-atom-node-color, var(--fd-primary)) 30%, transparent),
239
+ var(--fd-shadow-md);
240
+ border-color: var(--fd-atom-node-color, var(--fd-primary));
241
+ }
242
+
243
+ .flowdrop-atom-node:focus-visible {
244
+ outline: 2px solid var(--fd-ring);
245
+ outline-offset: 2px;
246
+ }
247
+
248
+ .flowdrop-atom-node--processing {
249
+ opacity: 0.7;
250
+ }
251
+
252
+ .flowdrop-atom-node--error {
253
+ border-color: var(--fd-error) !important;
254
+ background-color: var(--fd-error-muted) !important;
255
+ }
256
+
257
+ .flowdrop-atom-node--empty .flowdrop-atom-node__body {
258
+ color: var(--fd-muted-foreground);
259
+ font-style: italic;
260
+ }
261
+
262
+ .flowdrop-atom-node__prefix {
263
+ flex-shrink: 0;
264
+ margin-right: 2px;
265
+ color: var(--fd-muted-foreground);
266
+ font-size: var(--fd-text-sm);
267
+ line-height: 1.2;
268
+ }
269
+
270
+ .flowdrop-atom-node__body {
271
+ /* min-width:0 lets the body ellipsize as a flex sibling of the prefix */
272
+ min-width: 0;
273
+ font-size: var(--fd-text-sm);
274
+ line-height: 1.2;
275
+ white-space: nowrap;
276
+ overflow: hidden;
277
+ text-overflow: ellipsis;
278
+ }
279
+
280
+ /* `top` is set inline (dynamic, must beat svelte-flow defaults); transform and
281
+ stacking live here so the hover rule can compose instead of fighting !important. */
282
+ :global(.svelte-flow__node-atom .svelte-flow__handle) {
283
+ --fd-handle-border-color: var(--fd-handle-border);
284
+ transform: translateY(-50%);
285
+ z-index: 20 !important;
286
+ pointer-events: auto !important;
287
+ }
288
+
289
+ :global(.svelte-flow__node-atom .svelte-flow__handle:hover) {
290
+ transform: translateY(-50%) scale(1.2);
291
+ }
292
+ </style>
@@ -0,0 +1,26 @@
1
+ import type { ConfigValues, NodeMetadata, NodeExtensions } from '../../types/index.js';
2
+ interface AtomNodeData {
3
+ label: string;
4
+ config: ConfigValues;
5
+ metadata: NodeMetadata;
6
+ nodeId?: string;
7
+ extensions?: NodeExtensions;
8
+ onConfigOpen?: (node: {
9
+ id: string;
10
+ type: string;
11
+ data: {
12
+ label: string;
13
+ config: ConfigValues;
14
+ metadata: NodeMetadata;
15
+ };
16
+ }) => void;
17
+ }
18
+ interface Props {
19
+ data: AtomNodeData;
20
+ selected?: boolean;
21
+ isProcessing?: boolean;
22
+ isError?: boolean;
23
+ }
24
+ declare const AtomNode: import("svelte").Component<Props, {}, "">;
25
+ type AtomNode = ReturnType<typeof AtomNode>;
26
+ export default AtomNode;
@@ -18,7 +18,7 @@
18
18
  getCategoryColorToken,
19
19
  getPortBackgroundColor
20
20
  } from '../../utils/colors.js';
21
- import { getConnectedHandles } from '../../stores/workflowStore.svelte.js';
21
+ import { getInstance } from '../../stores/getInstance.svelte.js';
22
22
  import { m } from '../../messages/index.js';
23
23
 
24
24
  interface Props {
@@ -31,6 +31,9 @@
31
31
 
32
32
  let props: Props = $props();
33
33
 
34
+ const fd = getInstance();
35
+ const checker = fd.portCompatibility;
36
+
34
37
  // Hoist the graph branch — three reads in the template, two inside
35
38
  // {#each port} / {#each branch} loops. One getter walk per render.
36
39
  const graph = $derived(m().nodes.graph);
@@ -80,7 +83,7 @@
80
83
 
81
84
  // Check if port is connected
82
85
  const handleId = `${props.data.nodeId}-${type}-${port.id}`;
83
- return getConnectedHandles().has(handleId);
86
+ return fd.workflow.connectedHandles.has(handleId);
84
87
  }
85
88
 
86
89
  /**
@@ -103,7 +106,7 @@
103
106
 
104
107
  // Check if branch output is connected
105
108
  const handleId = `${props.data.nodeId}-output-${branchName}`;
106
- return getConnectedHandles().has(handleId);
109
+ return fd.workflow.connectedHandles.has(handleId);
107
110
  }
108
111
 
109
112
  // Gateway-specific data - branches are calculated at runtime from config
@@ -173,10 +176,10 @@
173
176
  <!-- Node Icon with Squircle Background -->
174
177
  <div
175
178
  class="flowdrop-workflow-node__icon-wrapper"
176
- style="--_icon-color: {getCategoryColorToken(props.data.metadata.category)}"
179
+ style="--_icon-color: {getCategoryColorToken(fd.categories, props.data.metadata.category)}"
177
180
  >
178
181
  <Icon
179
- icon={getNodeIcon(props.data.metadata.icon, props.data.metadata.category)}
182
+ icon={getNodeIcon(fd.categories, props.data.metadata.icon, props.data.metadata.category)}
180
183
  class="flowdrop-workflow-node__icon"
181
184
  />
182
185
  </div>
@@ -208,6 +211,7 @@
208
211
  id={`${props.data.nodeId}-input-${port.id}`}
209
212
  class="flowdrop-workflow-node__handle"
210
213
  style="top: 50%; transform: translateY(-50%); --fd-handle-fill: {getDataTypeColorToken(
214
+ checker,
211
215
  port.dataType
212
216
  )}; --fd-handle-border-color: var(--fd-handle-border);"
213
217
  tabindex={-1}
@@ -220,11 +224,13 @@
220
224
  <span
221
225
  class="flowdrop-badge flowdrop-badge--sm"
222
226
  style="background-color: {getPortBackgroundColor(
227
+ checker,
223
228
  port.dataType,
224
229
  15
225
230
  )}; color: {getDataTypeColorToken(
231
+ checker,
226
232
  port.dataType
227
- )}; border: 1px solid {getPortBackgroundColor(port.dataType, 30)};"
233
+ )}; border: 1px solid {getPortBackgroundColor(checker, port.dataType, 30)};"
228
234
  >
229
235
  {port.dataType}
230
236
  </span>
@@ -250,7 +256,7 @@
250
256
  {#if visibleBranches.length > 0}
251
257
  <div class="flowdrop-workflow-node__ports">
252
258
  <div class="flowdrop-workflow-node__ports-list">
253
- {#each visibleBranches as branch, branchIndex (branch.name)}
259
+ {#each visibleBranches as branch (branch.name)}
254
260
  {@const isActive = isBranchActive(branch.name)}
255
261
  <div class="flowdrop-workflow-node__port">
256
262
  <!-- Port Info: padding lives here so handle position is simple -->
@@ -261,7 +267,7 @@
261
267
  class="flowdrop-flex flowdrop-gap--2 flowdrop-justify--end flowdrop-items--center"
262
268
  >
263
269
  {#if isActive}
264
- <span style="color: {getDataTypeColorToken('trigger')};">
270
+ <span style="color: {getDataTypeColorToken(checker, 'trigger')};">
265
271
  <Icon icon="mdi:check-circle" />
266
272
  </span>
267
273
  {/if}
@@ -274,11 +280,13 @@
274
280
  <span
275
281
  class="flowdrop-badge flowdrop-badge--sm"
276
282
  style="background-color: {getPortBackgroundColor(
283
+ checker,
277
284
  'trigger',
278
285
  15
279
286
  )}; color: {getDataTypeColorToken(
287
+ checker,
280
288
  'trigger'
281
- )}; border: 1px solid {getPortBackgroundColor('trigger', 30)};"
289
+ )}; border: 1px solid {getPortBackgroundColor(checker, 'trigger', 30)};"
282
290
  >
283
291
  trigger
284
292
  </span>
@@ -292,8 +300,9 @@
292
300
  id={`${props.data.nodeId}-output-${branch.name}`}
293
301
  class={`flowdrop-workflow-node__handle ${isActive ? 'flowdrop-workflow-node__handle--active' : ''}`}
294
302
  style="top: 50%; transform: translateY(-50%); --fd-handle-fill: {isActive
295
- ? getDataTypeColorToken('trigger')
303
+ ? getDataTypeColorToken(checker, 'trigger')
296
304
  : getDataTypeColorToken(
305
+ checker,
297
306
  'trigger'
298
307
  )}; --fd-handle-border-color: var(--fd-handle-border);"
299
308
  tabindex={-1}
@@ -11,6 +11,7 @@
11
11
  import type { ConfigValues, NodeMetadata } from '../../types/index.js';
12
12
  import Icon from '@iconify/svelte';
13
13
  import { getDataTypeColor } from '../../utils/colors.js';
14
+ import { getInstance } from '../../stores/getInstance.svelte.js';
14
15
  import { m } from '../../messages/index.js';
15
16
 
16
17
  /**
@@ -34,6 +35,8 @@
34
35
  isError?: boolean;
35
36
  }>();
36
37
 
38
+ const checker = getInstance().portCompatibility;
39
+
37
40
  /**
38
41
  * Instance-specific title override from config.
39
42
  * Falls back to the original label if not set.
@@ -151,6 +154,7 @@
151
154
  type="target"
152
155
  position={Position.Left}
153
156
  style="--fd-handle-fill: {getDataTypeColor(
157
+ checker,
154
158
  IDEA_DATA_TYPE
155
159
  )}; --fd-handle-border-color: var(--fd-handle-border); top: 40px; transform: translateY(-50%); z-index: 30;"
156
160
  id={`${props.data.nodeId}-input-left`}
@@ -163,6 +167,7 @@
163
167
  type="target"
164
168
  position={Position.Top}
165
169
  style="--fd-handle-fill: {getDataTypeColor(
170
+ checker,
166
171
  IDEA_DATA_TYPE
167
172
  )}; --fd-handle-border-color: var(--fd-handle-border); left: 150px; transform: translateX(-50%); z-index: 30;"
168
173
  id={`${props.data.nodeId}-input-top`}
@@ -215,6 +220,7 @@
215
220
  type="source"
216
221
  position={Position.Right}
217
222
  style="--fd-handle-fill: {getDataTypeColor(
223
+ checker,
218
224
  IDEA_DATA_TYPE
219
225
  )}; --fd-handle-border-color: var(--fd-handle-border); top: 40px; transform: translateY(-50%); z-index: 30;"
220
226
  id={`${props.data.nodeId}-output-right`}
@@ -227,6 +233,7 @@
227
233
  type="source"
228
234
  position={Position.Bottom}
229
235
  style="--fd-handle-fill: {getDataTypeColor(
236
+ checker,
230
237
  IDEA_DATA_TYPE
231
238
  )}; --fd-handle-border-color: var(--fd-handle-border); left: 150px; transform: translateX(-50%); z-index: 30;"
232
239
  id={`${props.data.nodeId}-output-bottom`}
@@ -21,7 +21,7 @@
21
21
  import { dynamicPortToNodePort } from '../../types/index.js';
22
22
  import Icon from '@iconify/svelte';
23
23
  import { getDataTypeColor, getCategoryColorToken } from '../../utils/colors.js';
24
- import { getConnectedHandles } from '../../stores/workflowStore.svelte.js';
24
+ import { getInstance } from '../../stores/getInstance.svelte.js';
25
25
  import { applyPortOrder, getPortTop, isPortVisible } from '../../utils/portUtils.js';
26
26
  import CogIcon from '../icons/CogIcon.svelte';
27
27
  import AlertCircleIcon from '../icons/AlertCircleIcon.svelte';
@@ -44,6 +44,9 @@
44
44
  isError?: boolean;
45
45
  }>();
46
46
 
47
+ const fd = getInstance();
48
+ const checker = fd.portCompatibility;
49
+
47
50
  /**
48
51
  * Get UI extension settings from extensions, merging node type defaults with instance overrides.
49
52
  */
@@ -141,7 +144,7 @@
141
144
  'input',
142
145
  hiddenPorts,
143
146
  hideUnconnectedHandles,
144
- getConnectedHandles(),
147
+ fd.workflow.connectedHandles,
145
148
  props.data.nodeId
146
149
  )
147
150
  )
@@ -160,7 +163,7 @@
160
163
  'output',
161
164
  hiddenPorts,
162
165
  hideUnconnectedHandles,
163
- getConnectedHandles(),
166
+ fd.workflow.connectedHandles,
164
167
  props.data.nodeId
165
168
  )
166
169
  )
@@ -178,11 +181,12 @@
178
181
  </script>
179
182
 
180
183
  <!-- Input Handles: 1 port centered at 40px; N ports at 20px start, 40px gap -->
181
- {#each visibleInputPorts as port, index}
184
+ {#each visibleInputPorts as port, index (port.id)}
182
185
  <Handle
183
186
  type="target"
184
187
  position={Position.Left}
185
188
  style="--fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
189
+ checker,
186
190
  port.dataType
187
191
  )}); --fd-handle-border-color: var(--fd-handle-border); top: {getPortTop(
188
192
  index,
@@ -214,7 +218,7 @@
214
218
  <!-- Node Icon (circle dot) — visibility controlled by --fd-node-circle-display -->
215
219
  <span
216
220
  class="flowdrop-simple-node__color-dot"
217
- style="background: {getCategoryColorToken(props.data.metadata?.category)}"
221
+ style="background: {getCategoryColorToken(fd.categories, props.data.metadata?.category)}"
218
222
  ></span>
219
223
 
220
224
  <!-- Node Title -->
@@ -256,11 +260,12 @@
256
260
  </div>
257
261
 
258
262
  <!-- Output Handles: 1 port centered at 40px; N ports at 20px start, 40px gap -->
259
- {#each visibleOutputPorts as port, index}
263
+ {#each visibleOutputPorts as port, index (port.id)}
260
264
  <Handle
261
265
  type="source"
262
266
  position={Position.Right}
263
267
  style="--fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
268
+ checker,
264
269
  port.dataType
265
270
  )}); --fd-handle-border-color: var(--fd-handle-border); top: {getPortTop(
266
271
  index,
@@ -22,7 +22,7 @@
22
22
  import Icon from '@iconify/svelte';
23
23
  import { getDataTypeColor, getCategoryColorToken } from '../../utils/colors.js';
24
24
  import { getNodeIcon } from '../../utils/icons.js';
25
- import { getConnectedHandles } from '../../stores/workflowStore.svelte.js';
25
+ import { getInstance } from '../../stores/getInstance.svelte.js';
26
26
  import { applyPortOrder, getPortTop, isPortVisible } from '../../utils/portUtils.js';
27
27
  import CogIcon from '../icons/CogIcon.svelte';
28
28
  import AlertCircleIcon from '../icons/AlertCircleIcon.svelte';
@@ -45,6 +45,9 @@
45
45
  isError?: boolean;
46
46
  }>();
47
47
 
48
+ const fd = getInstance();
49
+ const checker = fd.portCompatibility;
50
+
48
51
  /**
49
52
  * Get UI extension settings from extensions, merging node type defaults with instance overrides.
50
53
  */
@@ -66,13 +69,15 @@
66
69
  * Get icon using the same resolution as WorkflowNode
67
70
  * Uses getNodeIcon utility with category fallback
68
71
  */
69
- let squareIcon = $derived(getNodeIcon(props.data.metadata?.icon, props.data.metadata?.category));
72
+ let squareIcon = $derived(
73
+ getNodeIcon(fd.categories, props.data.metadata?.icon, props.data.metadata?.category)
74
+ );
70
75
 
71
76
  /**
72
77
  * Get icon color using category-based color tokens for consistency
73
78
  * Falls back to primary color if category not available
74
79
  */
75
- let squareColor = $derived(getCategoryColorToken(props.data.metadata?.category));
80
+ let squareColor = $derived(getCategoryColorToken(fd.categories, props.data.metadata?.category));
76
81
 
77
82
  // Handle configuration sidebar - now using global ConfigSidebar
78
83
  function openConfigSidebar(): void {
@@ -129,7 +134,7 @@
129
134
  'input',
130
135
  hiddenPorts,
131
136
  hideUnconnectedHandles,
132
- getConnectedHandles(),
137
+ fd.workflow.connectedHandles,
133
138
  props.data.nodeId
134
139
  )
135
140
  )
@@ -148,7 +153,7 @@
148
153
  'output',
149
154
  hiddenPorts,
150
155
  hideUnconnectedHandles,
151
- getConnectedHandles(),
156
+ fd.workflow.connectedHandles,
152
157
  props.data.nodeId
153
158
  )
154
159
  )
@@ -167,11 +172,12 @@
167
172
  </script>
168
173
 
169
174
  <!-- Input Handles: 1 port centered at 40px; N ports at 20px start, 40px gap -->
170
- {#each visibleInputPorts as port, index}
175
+ {#each visibleInputPorts as port, index (port.id)}
171
176
  <Handle
172
177
  type="target"
173
178
  position={Position.Left}
174
179
  style="--fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
180
+ checker,
175
181
  port.dataType
176
182
  )}); --fd-handle-border-color: var(--fd-handle-border); top: {getPortTop(
177
183
  index,
@@ -203,7 +209,7 @@
203
209
  <!-- Circle dot — visibility controlled by --fd-node-circle-display -->
204
210
  <span
205
211
  class="flowdrop-square-node__color-dot"
206
- style="background: {getCategoryColorToken(props.data.metadata?.category)}"
212
+ style="background: {getCategoryColorToken(fd.categories, props.data.metadata?.category)}"
207
213
  ></span>
208
214
  </div>
209
215
 
@@ -232,11 +238,12 @@
232
238
  </div>
233
239
 
234
240
  <!-- Output Handles: 1 port centered at 40px; N ports at 20px start, 40px gap -->
235
- {#each visibleOutputPorts as port, index}
241
+ {#each visibleOutputPorts as port, index (port.id)}
236
242
  <Handle
237
243
  type="source"
238
244
  position={Position.Right}
239
245
  style="--fd-handle-fill: var(--fd-port-skin-color, {getDataTypeColor(
246
+ checker,
240
247
  port.dataType
241
248
  )}); --fd-handle-border-color: var(--fd-handle-border); top: {getPortTop(
242
249
  index,