@d34dman/flowdrop 0.0.52 → 0.0.54

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 (40) hide show
  1. package/dist/components/App.svelte +9 -6
  2. package/dist/components/ConfigForm.svelte +1 -0
  3. package/dist/components/NodeSidebar.svelte +0 -2
  4. package/dist/components/PortCoordinateTracker.svelte +58 -0
  5. package/dist/components/PortCoordinateTracker.svelte.d.ts +12 -0
  6. package/dist/components/SettingsPanel.svelte +1 -2
  7. package/dist/components/ThemeToggle.svelte +1 -1
  8. package/dist/components/WorkflowEditor.svelte +44 -11
  9. package/dist/components/form/FormAutocomplete.svelte +3 -12
  10. package/dist/components/form/FormField.svelte +1 -1
  11. package/dist/components/form/FormFieldLight.svelte +1 -1
  12. package/dist/components/nodes/SimpleNode.svelte +0 -6
  13. package/dist/components/nodes/SquareNode.svelte +0 -4
  14. package/dist/components/nodes/TerminalNode.svelte +45 -9
  15. package/dist/components/nodes/TerminalNode.svelte.d.ts +2 -1
  16. package/dist/components/nodes/ToolNode.svelte +17 -11
  17. package/dist/components/nodes/WorkflowNode.svelte +0 -10
  18. package/dist/core/index.d.ts +2 -2
  19. package/dist/core/index.js +1 -1
  20. package/dist/editor/index.d.ts +1 -0
  21. package/dist/editor/index.js +2 -0
  22. package/dist/helpers/proximityConnect.d.ts +19 -3
  23. package/dist/helpers/proximityConnect.js +109 -7
  24. package/dist/playground/index.d.ts +1 -1
  25. package/dist/playground/index.js +1 -1
  26. package/dist/services/portConfigApi.js +0 -11
  27. package/dist/settings/index.d.ts +2 -1
  28. package/dist/settings/index.js +2 -1
  29. package/dist/stores/interruptStore.d.ts +8 -30
  30. package/dist/stores/interruptStore.js +7 -76
  31. package/dist/stores/portCoordinateStore.d.ts +60 -0
  32. package/dist/stores/portCoordinateStore.js +186 -0
  33. package/dist/stores/settingsStore.d.ts +44 -2
  34. package/dist/stores/settingsStore.js +37 -15
  35. package/dist/types/index.d.ts +22 -0
  36. package/dist/utils/fetchWithAuth.d.ts +25 -0
  37. package/dist/utils/fetchWithAuth.js +34 -0
  38. package/package.json +1 -1
  39. package/dist/stores/themeStore.d.ts +0 -68
  40. package/dist/stores/themeStore.js +0 -213
@@ -36,6 +36,8 @@
36
36
  import { apiToasts, dismissToast } from '../services/toastService.js';
37
37
  import { initAutoSave } from '../services/autoSaveService.js';
38
38
  import { uiSettings } from '../stores/settingsStore.js';
39
+ import { initializePortCompatibility } from '../utils/connections.js';
40
+ import { DEFAULT_PORT_CONFIG } from '../config/defaultPortConfig.js';
39
41
 
40
42
  /**
41
43
  * Configuration props for runtime customization
@@ -177,9 +179,6 @@
177
179
  // WorkflowEditor reference for save functionality
178
180
  let workflowEditorRef: WorkflowEditor | null = null;
179
181
 
180
- // Removed currentWorkflowState - no longer needed
181
- // The global store ($workflowStore) serves as the single source of truth
182
-
183
182
  /**
184
183
  * Fetch node types from the server
185
184
  *
@@ -383,9 +382,6 @@
383
382
  }
384
383
  }
385
384
 
386
- // Removed handleWorkflowChange function - no longer needed
387
- // The global store serves as the single source of truth and is already reactive
388
-
389
385
  /**
390
386
  * Save workflow - exposed API function
391
387
  *
@@ -591,6 +587,11 @@
591
587
  onMount(() => {
592
588
  (async () => {
593
589
  await initializeApiEndpoints();
590
+
591
+ // Ensure port compatibility checker is initialized (needed for proximity connect, etc.)
592
+ // mountFlowDropApp initializes this before mounting, but SvelteKit routes need it here.
593
+ initializePortCompatibility(DEFAULT_PORT_CONFIG);
594
+
594
595
  await fetchNodeTypes();
595
596
 
596
597
  // Initialize the workflow store if we have an initial workflow
@@ -738,6 +739,7 @@
738
739
  onClose={() => (isWorkflowSettingsOpen = false)}
739
740
  >
740
741
  <ConfigForm
742
+ {authProvider}
741
743
  schema={workflowConfigSchema}
742
744
  values={workflowConfigValues}
743
745
  showUIExtensions={false}
@@ -765,6 +767,7 @@
765
767
  onClose={closeConfigSidebar}
766
768
  >
767
769
  <ConfigForm
770
+ {authProvider}
768
771
  node={currentNode}
769
772
  workflowId={$workflowStore?.id}
770
773
  workflowNodes={$workflowStore?.nodes}
@@ -600,6 +600,7 @@
600
600
  ariaDescribedBy="ext-hideUnconnectedHandles-description"
601
601
  onChange={(val) => {
602
602
  uiExtensionValues.hideUnconnectedHandles = val;
603
+ handleFormBlur();
603
604
  }}
604
605
  />
605
606
  </FormFieldWrapper>
@@ -494,8 +494,6 @@
494
494
  align-items: center;
495
495
  }
496
496
 
497
- /* Close button styles removed - no longer needed */
498
-
499
497
  .flowdrop-sidebar__title h2 {
500
498
  font-size: 1rem;
501
499
  font-weight: 600;
@@ -0,0 +1,58 @@
1
+ <!--
2
+ Port Coordinate Tracker Component
3
+ Bridge component that exposes SvelteFlow's getInternalNode to the parent.
4
+ Must be rendered inside SvelteFlowProvider context.
5
+
6
+ Uses the same pattern as EdgeRefresher - a renderless component that hooks
7
+ into the SvelteFlow context.
8
+ -->
9
+
10
+ <script lang="ts">
11
+ import { useSvelteFlow, type InternalNode } from '@xyflow/svelte';
12
+ import type { WorkflowNode as WorkflowNodeType } from '../types/index.js';
13
+ import {
14
+ rebuildAllPortCoordinates,
15
+ updateNodePortCoordinates
16
+ } from '../stores/portCoordinateStore.js';
17
+
18
+ interface Props {
19
+ /** Node to update coordinates for (e.g., during drag). Set to null when not dragging. */
20
+ nodeToUpdate: WorkflowNodeType | null;
21
+ /** Set to trigger a full rebuild of all port coordinates */
22
+ rebuildTrigger: number;
23
+ /** All workflow nodes - used for full rebuild */
24
+ nodes: WorkflowNodeType[];
25
+ }
26
+
27
+ let { nodeToUpdate, rebuildTrigger, nodes }: Props = $props();
28
+
29
+ const { getInternalNode } = useSvelteFlow();
30
+
31
+ // Cast the getInternalNode function for our use
32
+ const getInternal = getInternalNode as (id: string) => InternalNode | undefined;
33
+
34
+ /**
35
+ * Rebuild all port coordinates when rebuildTrigger changes.
36
+ * Debounced to batch rapid position updates (e.g., animated auto-layout,
37
+ * magnetic child nodes following a parent drag).
38
+ */
39
+ $effect(() => {
40
+ const _trigger = rebuildTrigger;
41
+ if (_trigger > 0) {
42
+ const timeout = setTimeout(() => {
43
+ rebuildAllPortCoordinates(nodes, getInternal);
44
+ }, 150);
45
+ return () => clearTimeout(timeout);
46
+ }
47
+ });
48
+
49
+ /**
50
+ * Update a single node's coordinates when nodeToUpdate changes.
51
+ * This is used during drag for efficient per-node updates.
52
+ */
53
+ $effect(() => {
54
+ if (nodeToUpdate) {
55
+ updateNodePortCoordinates(nodeToUpdate, getInternal);
56
+ }
57
+ });
58
+ </script>
@@ -0,0 +1,12 @@
1
+ import type { WorkflowNode as WorkflowNodeType } from '../types/index.js';
2
+ interface Props {
3
+ /** Node to update coordinates for (e.g., during drag). Set to null when not dragging. */
4
+ nodeToUpdate: WorkflowNodeType | null;
5
+ /** Set to trigger a full rebuild of all port coordinates */
6
+ rebuildTrigger: number;
7
+ /** All workflow nodes - used for full rebuild */
8
+ nodes: WorkflowNodeType[];
9
+ }
10
+ declare const PortCoordinateTracker: import("svelte").Component<Props, {}, "">;
11
+ type PortCoordinateTracker = ReturnType<typeof PortCoordinateTracker>;
12
+ export default PortCoordinateTracker;
@@ -144,8 +144,7 @@
144
144
  proximityConnect: {
145
145
  type: 'boolean',
146
146
  title: 'Proximity Connect',
147
- description:
148
- 'Auto-connect compatible ports when dragging nodes near each other',
147
+ description: 'Auto-connect compatible ports when dragging nodes near each other',
149
148
  default: false
150
149
  },
151
150
  proximityConnectDistance: {
@@ -7,7 +7,7 @@
7
7
 
8
8
  <script lang="ts">
9
9
  import Icon from '@iconify/svelte';
10
- import { theme, resolvedTheme, cycleTheme } from '../stores/themeStore.js';
10
+ import { theme, resolvedTheme, cycleTheme } from '../stores/settingsStore.js';
11
11
  import type { ThemePreference } from '../types/settings.js';
12
12
 
13
13
  /**
@@ -16,8 +16,7 @@
16
16
  type ColorMode
17
17
  } from '@xyflow/svelte';
18
18
  import '@xyflow/svelte/dist/style.css';
19
- import { resolvedTheme } from '../stores/themeStore.js';
20
- import { editorSettings, behaviorSettings } from '../stores/settingsStore.js';
19
+ import { resolvedTheme, editorSettings, behaviorSettings } from '../stores/settingsStore.js';
21
20
  import type {
22
21
  WorkflowNode as WorkflowNodeType,
23
22
  NodeMetadata,
@@ -47,10 +46,11 @@
47
46
  ProximityConnectHelper,
48
47
  type ProximityEdgeCandidate
49
48
  } from '../helpers/proximityConnect.js';
49
+ import PortCoordinateTracker from './PortCoordinateTracker.svelte';
50
+ import { getPortCoordinateSnapshot } from '../stores/portCoordinateStore.js';
50
51
 
51
52
  interface Props {
52
53
  nodes?: NodeMetadata[];
53
- // workflow?: Workflow; // Removed - use global store directly
54
54
  endpointConfig?: EndpointConfig;
55
55
  height?: string | number;
56
56
  width?: string | number;
@@ -77,6 +77,10 @@
77
77
  // Proximity connect state
78
78
  let currentProximityCandidates = $state<ProximityEdgeCandidate[]>([]);
79
79
 
80
+ // Port coordinate tracker state
81
+ let portCoordNodeToUpdate = $state<WorkflowNodeType | null>(null);
82
+ let portCoordRebuildTrigger = $state(0);
83
+
80
84
  // Track the workflow ID we're currently editing to detect workflow switches
81
85
  let currentWorkflowId: string | null = null;
82
86
 
@@ -179,6 +183,14 @@
179
183
  );
180
184
  flowEdges = styledEdges;
181
185
 
186
+ // Trigger port coordinate rebuild after workflow load
187
+ // (PortCoordinateTracker will wait for SvelteFlow to render before reading handleBounds)
188
+ // Note: Using Date.now() instead of ++ to avoid reading the old value,
189
+ // which would make this effect depend on portCoordRebuildTrigger and loop.
190
+ if ($editorSettings.proximityConnect) {
191
+ portCoordRebuildTrigger = Date.now();
192
+ }
193
+
182
194
  // Only load execution info if we have a pipelineId (pipeline status mode)
183
195
  // and if the workflow or pipeline has changed
184
196
  const workflowChanged = currentWorkflow.id !== previousWorkflowId;
@@ -348,7 +360,8 @@
348
360
 
349
361
  /**
350
362
  * Handle node drag - compute proximity connect preview edges
351
- * Called continuously during drag if proximity connect is enabled
363
+ * Called continuously during drag if proximity connect is enabled.
364
+ * Uses port-to-port distance via the port coordinate store.
352
365
  */
353
366
  function handleNodeDrag({
354
367
  targetNode
@@ -362,19 +375,31 @@
362
375
  flowEdges = ProximityConnectHelper.removePreviewEdges(flowEdges);
363
376
  currentProximityCandidates = [];
364
377
  }
378
+ portCoordNodeToUpdate = null;
365
379
  return;
366
380
  }
367
381
 
382
+ // Update the dragged node's port coordinates (position changed during drag)
383
+ portCoordNodeToUpdate = targetNode;
384
+
368
385
  // Remove previous preview edges
369
386
  const baseEdges = ProximityConnectHelper.removePreviewEdges(flowEdges);
370
387
 
371
- // Find the best compatible edge with nearby nodes
372
- const candidates = ProximityConnectHelper.findCompatibleEdges(
373
- targetNode,
374
- flowNodes,
375
- baseEdges,
376
- $editorSettings.proximityConnectDistance
377
- );
388
+ // Find the best compatible edge using port-to-port distance
389
+ const portCoordinates = getPortCoordinateSnapshot();
390
+ const candidates = portCoordinates.size > 0
391
+ ? ProximityConnectHelper.findCompatibleEdgesByPortCoordinates(
392
+ targetNode.id,
393
+ portCoordinates,
394
+ baseEdges,
395
+ $editorSettings.proximityConnectDistance
396
+ )
397
+ : ProximityConnectHelper.findCompatibleEdges(
398
+ targetNode,
399
+ flowNodes,
400
+ baseEdges,
401
+ $editorSettings.proximityConnectDistance
402
+ );
378
403
 
379
404
  // Create preview edges
380
405
  const previews = ProximityConnectHelper.createPreviewEdges(candidates);
@@ -392,6 +417,7 @@
392
417
  */
393
418
  function handleNodeDragStop(): void {
394
419
  isDraggingNode = false;
420
+ portCoordNodeToUpdate = null;
395
421
 
396
422
  // Finalize proximity connect if there are candidates
397
423
  if ($editorSettings.proximityConnect && currentProximityCandidates.length > 0) {
@@ -700,6 +726,13 @@
700
726
  <!-- EdgeRefresher component - handles updateNodeInternals calls -->
701
727
  <EdgeRefresher {nodeIdToRefresh} onRefreshComplete={handleEdgeRefreshComplete} />
702
728
 
729
+ <!-- Port Coordinate Tracker - maintains port positions for proximity connect -->
730
+ <PortCoordinateTracker
731
+ nodeToUpdate={portCoordNodeToUpdate}
732
+ rebuildTrigger={portCoordRebuildTrigger}
733
+ nodes={flowNodes}
734
+ />
735
+
703
736
  <div class="flowdrop-workflow-editor">
704
737
  <!-- Main Editor Area -->
705
738
  <div class="flowdrop-workflow-editor__main">
@@ -25,6 +25,7 @@
25
25
  import Icon from '@iconify/svelte';
26
26
  import type { AutocompleteConfig, AuthProvider } from '../../types/index.js';
27
27
  import type { FieldOption } from './types.js';
28
+ import { buildFetchHeaders } from '../../utils/fetchWithAuth.js';
28
29
 
29
30
  /**
30
31
  * Props interface for FormAutocomplete component
@@ -192,18 +193,8 @@
192
193
  abortController = new AbortController();
193
194
 
194
195
  try {
195
- // Build headers with authentication
196
- const headers: Record<string, string> = {
197
- Accept: 'application/json',
198
- 'Content-Type': 'application/json'
199
- };
200
-
201
- // Add auth headers if provider is available (call getter to get current value)
202
- const authProvider = getAuthProvider?.();
203
- if (authProvider) {
204
- const authHeaders = await authProvider.getAuthHeaders();
205
- Object.assign(headers, authHeaders);
206
- }
196
+ // Build headers with authentication (call getter to get current value)
197
+ const headers = await buildFetchHeaders(getAuthProvider?.());
207
198
 
208
199
  // Fetch with timeout
209
200
  const timeoutId = setTimeout(() => {
@@ -44,7 +44,7 @@
44
44
  import type { FieldSchema } from './types.js';
45
45
  import { getSchemaOptions } from './types.js';
46
46
  import type { WorkflowNode, WorkflowEdge, AuthProvider } from '../../types/index.js';
47
- import { resolvedTheme } from '../../stores/themeStore.js';
47
+ import { resolvedTheme } from '../../stores/settingsStore.js';
48
48
 
49
49
  interface Props {
50
50
  /** Unique key/id for the field */
@@ -43,7 +43,7 @@
43
43
  import FormCheckboxGroup from './FormCheckboxGroup.svelte';
44
44
  import FormArray from './FormArray.svelte';
45
45
  import { resolveFieldComponent } from '../../form/fieldRegistry.js';
46
- import { resolvedTheme } from '../../stores/themeStore.js';
46
+ import { resolvedTheme } from '../../stores/settingsStore.js';
47
47
  import type { FieldSchema } from './types.js';
48
48
  import { getSchemaOptions } from './types.js';
49
49
 
@@ -42,8 +42,6 @@
42
42
  return instanceOverride ?? typeDefault;
43
43
  });
44
44
 
45
- // Removed local config state - now using global ConfigSidebar
46
-
47
45
  // Prioritize metadata icon over config icon for simple nodes (metadata is the node definition)
48
46
  let nodeIcon = $derived(
49
47
  (props.data.metadata?.icon as string) || (props.data.config?.icon as string) || 'mdi:square'
@@ -265,8 +263,6 @@
265
263
  />
266
264
  {/each}
267
265
 
268
- <!-- ConfigSidebar removed - now using global ConfigSidebar in WorkflowEditor -->
269
-
270
266
  <style>
271
267
  .flowdrop-simple-node {
272
268
  position: relative;
@@ -379,8 +375,6 @@
379
375
  color: var(--fd-node-icon);
380
376
  }
381
377
 
382
- /* Label styling removed - now using header title */
383
-
384
378
  .flowdrop-simple-node__processing {
385
379
  position: absolute;
386
380
  top: 4px;
@@ -43,8 +43,6 @@
43
43
  return instanceOverride ?? typeDefault;
44
44
  });
45
45
 
46
- // Removed local config state - now using global ConfigSidebar
47
-
48
46
  /**
49
47
  * Get icon using the same resolution as WorkflowNode
50
48
  * Uses getNodeIcon utility with category fallback
@@ -333,8 +331,6 @@
333
331
  color: var(--fd-node-icon);
334
332
  }
335
333
 
336
- /* Label styling removed - now using header title */
337
-
338
334
  .flowdrop-square-node__processing {
339
335
  position: absolute;
340
336
  top: 4px;
@@ -10,11 +10,12 @@
10
10
 
11
11
  <script lang="ts">
12
12
  import { Position, Handle } from '@xyflow/svelte';
13
- import type { ConfigValues, NodeMetadata } from '../../types/index.js';
13
+ import type { ConfigValues, NodeMetadata, NodeExtensions, NodePort } from '../../types/index.js';
14
14
  import Icon from '@iconify/svelte';
15
15
  import { getDataTypeColor, getCategoryColorToken } from '../../utils/colors.js';
16
16
  import { getNodeIcon } from '../../utils/icons.js';
17
17
  import { getCircleHandlePosition } from '../../utils/handlePositioning.js';
18
+ import { connectedHandles } from '../../stores/workflowStore.js';
18
19
 
19
20
  /**
20
21
  * Terminal node variant types
@@ -70,6 +71,7 @@
70
71
  config: ConfigValues;
71
72
  metadata: NodeMetadata;
72
73
  nodeId?: string;
74
+ extensions?: NodeExtensions;
73
75
  onConfigOpen?: (node: {
74
76
  id: string;
75
77
  type: string;
@@ -133,6 +135,30 @@
133
135
  */
134
136
  let variantConfig = $derived(VARIANT_CONFIGS[variant]);
135
137
 
138
+ /**
139
+ * Get the hideUnconnectedHandles setting from extensions
140
+ * Merges node type defaults with instance overrides
141
+ */
142
+ const hideUnconnectedHandles = $derived(() => {
143
+ const typeDefault = props.data.metadata?.extensions?.ui?.hideUnconnectedHandles ?? false;
144
+ const instanceOverride = props.data.extensions?.ui?.hideUnconnectedHandles;
145
+ return instanceOverride ?? typeDefault;
146
+ });
147
+
148
+ /**
149
+ * Check if a port should be visible based on connection state and settings
150
+ */
151
+ function isPortVisible(port: NodePort, type: 'input' | 'output'): boolean {
152
+ if (!hideUnconnectedHandles()) {
153
+ return true;
154
+ }
155
+ if (port.required) {
156
+ return true;
157
+ }
158
+ const handleId = `${props.data.nodeId}-${type}-${port.id}`;
159
+ return $connectedHandles.has(handleId);
160
+ }
161
+
136
162
  /**
137
163
  * Get icon using the same resolution as WorkflowNode
138
164
  * Uses getNodeIcon utility with category fallback, or variant default
@@ -239,14 +265,24 @@
239
265
  );
240
266
 
241
267
  /**
242
- * Determine if we should show inputs based on ports
268
+ * Visible input ports filtered by hideUnconnectedHandles setting
269
+ */
270
+ let visibleInputPorts = $derived(inputPorts.filter((port) => isPortVisible(port, 'input')));
271
+
272
+ /**
273
+ * Visible output ports filtered by hideUnconnectedHandles setting
274
+ */
275
+ let visibleOutputPorts = $derived(outputPorts.filter((port) => isPortVisible(port, 'output')));
276
+
277
+ /**
278
+ * Determine if we should show inputs based on visible ports
243
279
  */
244
- let showInputs = $derived(inputPorts.length > 0);
280
+ let showInputs = $derived(visibleInputPorts.length > 0);
245
281
 
246
282
  /**
247
- * Determine if we should show outputs based on ports
283
+ * Determine if we should show outputs based on visible ports
248
284
  */
249
- let showOutputs = $derived(outputPorts.length > 0);
285
+ let showOutputs = $derived(visibleOutputPorts.length > 0);
250
286
 
251
287
  /**
252
288
  * Handle configuration sidebar - using global ConfigSidebar
@@ -317,8 +353,8 @@
317
353
  <div class="flowdrop-terminal-node__circle-wrapper">
318
354
  <!-- Input Handles (for end/exit variants) -->
319
355
  {#if showInputs}
320
- {#each inputPorts as port, index (port.id)}
321
- {@const pos = getCircleHandlePosition(index, inputPorts.length, 'left')}
356
+ {#each visibleInputPorts as port, index (`${port.id}-${visibleInputPorts.length}`)}
357
+ {@const pos = getCircleHandlePosition(index, visibleInputPorts.length, 'left')}
322
358
  <Handle
323
359
  type="target"
324
360
  position={Position.Left}
@@ -339,8 +375,8 @@
339
375
 
340
376
  <!-- Output Handles (for start variant) -->
341
377
  {#if showOutputs}
342
- {#each outputPorts as port, index (port.id)}
343
- {@const pos = getCircleHandlePosition(index, outputPorts.length, 'right')}
378
+ {#each visibleOutputPorts as port, index (`${port.id}-${visibleOutputPorts.length}`)}
379
+ {@const pos = getCircleHandlePosition(index, visibleOutputPorts.length, 'right')}
344
380
  <Handle
345
381
  type="source"
346
382
  position={Position.Right}
@@ -1,10 +1,11 @@
1
- import type { ConfigValues, NodeMetadata } from '../../types/index.js';
1
+ import type { ConfigValues, NodeMetadata, NodeExtensions } from '../../types/index.js';
2
2
  type $$ComponentProps = {
3
3
  data: {
4
4
  label: string;
5
5
  config: ConfigValues;
6
6
  metadata: NodeMetadata;
7
7
  nodeId?: string;
8
+ extensions?: NodeExtensions;
8
9
  onConfigOpen?: (node: {
9
10
  id: string;
10
11
  type: string;
@@ -67,9 +67,7 @@
67
67
  * This allows users to customize the badge text per-instance via config.
68
68
  */
69
69
  const displayBadge = $derived(
70
- (props.data.config?.instanceBadge as string) ||
71
- (props.data.metadata?.badge as string) ||
72
- 'TOOL'
70
+ (props.data.config?.instanceBadge as string) || (props.data.metadata?.badge as string) || 'TOOL'
73
71
  );
74
72
 
75
73
  /**
@@ -96,20 +94,28 @@
96
94
  */
97
95
  let nodeStyle = $derived(`--fd-tool-node-color: ${toolColor}`);
98
96
 
99
- // Check for tool interface ports in metadata
97
+ /**
98
+ * Configurable port dataType to expose on this tool node.
99
+ * Defaults to 'tool', but can be overridden via metadata.portDataType
100
+ * to show a different port type (e.g., 'trigger') when the node is
101
+ * repurposed with a custom badge.
102
+ */
103
+ let portDataType = $derived((props.data.metadata?.portDataType as string) || 'tool');
104
+
105
+ // Check for matching interface ports in metadata
100
106
  let hasToolInputPort = $derived(
101
- props.data.metadata?.inputs?.some((port) => port.dataType === 'tool') || false
107
+ props.data.metadata?.inputs?.some((port) => port.dataType === portDataType) || false
102
108
  );
103
109
  let hasToolOutputPort = $derived(
104
- props.data.metadata?.outputs?.some((port) => port.dataType === 'tool') || false
110
+ props.data.metadata?.outputs?.some((port) => port.dataType === portDataType) || false
105
111
  );
106
112
 
107
- // Get the actual tool ports for proper handle generation
113
+ // Get the actual matching ports for proper handle generation
108
114
  let toolInputPort = $derived(
109
- props.data.metadata?.inputs?.find((port) => port.dataType === 'tool')
115
+ props.data.metadata?.inputs?.find((port) => port.dataType === portDataType)
110
116
  );
111
117
  let toolOutputPort = $derived(
112
- props.data.metadata?.outputs?.find((port) => port.dataType === 'tool')
118
+ props.data.metadata?.outputs?.find((port) => port.dataType === portDataType)
113
119
  );
114
120
 
115
121
  /**
@@ -159,7 +165,7 @@
159
165
  position={Position.Left}
160
166
  id={`${props.data.nodeId}-input-${toolInputPort.id}`}
161
167
  style="top: 40px; transform: translateY(-50%); margin-left: -10px; --fd-handle-fill: {getDataTypeColor(
162
- 'tool'
168
+ portDataType
163
169
  )}; --fd-handle-border-color: var(--fd-handle-border);"
164
170
  />
165
171
  {/if}
@@ -232,7 +238,7 @@
232
238
  position={Position.Right}
233
239
  id={`${props.data.nodeId}-output-${toolOutputPort.id}`}
234
240
  style="top: 40px; transform: translateY(-50%); margin-right: -10px; --fd-handle-fill: {getDataTypeColor(
235
- 'tool'
241
+ portDataType
236
242
  )}; --fd-handle-border-color: var(--fd-handle-border);"
237
243
  />
238
244
  {/if}
@@ -124,11 +124,6 @@
124
124
  allOutputPorts.filter((port) => isPortVisible(port, 'output'))
125
125
  );
126
126
 
127
- /**
128
- * Handle configuration value changes - now handled by global ConfigSidebar
129
- */
130
- // Removed local config handling - now using global ConfigSidebar
131
-
132
127
  /**
133
128
  * Handle node click - only handle selection, no config opening
134
129
  */
@@ -201,7 +196,6 @@
201
196
 
202
197
  <!-- Status Indicators -->
203
198
  <div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
204
- <!-- Status indicators removed - using outer NodeStatusOverlay instead -->
205
199
  </div>
206
200
  </div>
207
201
  <!-- Node Description - line-height 20px so header grows in steps of 10 -->
@@ -326,8 +320,6 @@
326
320
  </button>
327
321
  </div>
328
322
 
329
- <!-- ConfigSidebar removed - now using global ConfigSidebar in WorkflowEditor -->
330
-
331
323
  <style>
332
324
  .flowdrop-workflow-node {
333
325
  position: relative;
@@ -436,8 +428,6 @@
436
428
  line-height: 1;
437
429
  }
438
430
 
439
- /* Status indicator styles removed - using outer NodeStatusOverlay instead */
440
-
441
431
  @keyframes pulse {
442
432
  0%,
443
433
  100% {
@@ -40,5 +40,5 @@ export { isFieldOptionArray, normalizeOptions } from '../components/form/types.j
40
40
  export { DEFAULT_PORT_CONFIG } from '../config/defaultPortConfig.js';
41
41
  export { defaultEndpointConfig, createEndpointConfig } from '../config/endpoints.js';
42
42
  export * from '../adapters/WorkflowAdapter.js';
43
- export type { ThemePreference, ResolvedTheme } from '../stores/themeStore.js';
44
- export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/themeStore.js';
43
+ export type { ThemePreference, ResolvedTheme } from '../stores/settingsStore.js';
44
+ export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/settingsStore.js';
@@ -51,4 +51,4 @@ export { defaultEndpointConfig, createEndpointConfig } from '../config/endpoints
51
51
  // Adapters
52
52
  // ============================================================================
53
53
  export * from '../adapters/WorkflowAdapter.js';
54
- export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/themeStore.js';
54
+ export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/settingsStore.js';
@@ -64,6 +64,7 @@ export { mountWorkflowEditor, mountFlowDropApp, unmountFlowDropApp } from '../sv
64
64
  export { nodeComponentRegistry, createNamespacedType, parseNamespacedType, BUILTIN_NODE_COMPONENTS, BUILTIN_NODE_TYPES, FLOWDROP_SOURCE, registerBuiltinNodes, areBuiltinsRegistered, isBuiltinType, getBuiltinTypes, resolveBuiltinAlias, registerFlowDropPlugin, unregisterFlowDropPlugin, registerCustomNode, createPlugin, isValidNamespace, getRegisteredPlugins, getPluginNodeCount } from '../registry/index.js';
65
65
  export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from '../helpers/workflowEditorHelper.js';
66
66
  export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged, connectedHandles, isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange, setHistoryEnabled, isHistoryEnabled, setRestoringFromHistory } from '../stores/workflowStore.js';
67
+ export { portCoordinateStore, rebuildAllPortCoordinates, updateNodePortCoordinates, removeNodePortCoordinates, getPortCoordinate, getNodePortCoordinates, getPortCoordinateSnapshot } from '../stores/portCoordinateStore.js';
67
68
  export { historyStateStore, canUndo, canRedo, historyActions, setOnRestoreCallback, historyService, HistoryService } from '../stores/historyStore.js';
68
69
  export type { HistoryEntry, HistoryState, PushOptions } from '../stores/historyStore.js';
69
70
  export * from '../services/api.js';
@@ -101,6 +101,8 @@ export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes
101
101
  isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange,
102
102
  // History control
103
103
  setHistoryEnabled, isHistoryEnabled, setRestoringFromHistory } from '../stores/workflowStore.js';
104
+ // Port Coordinate Store
105
+ export { portCoordinateStore, rebuildAllPortCoordinates, updateNodePortCoordinates, removeNodePortCoordinates, getPortCoordinate, getNodePortCoordinates, getPortCoordinateSnapshot } from '../stores/portCoordinateStore.js';
104
106
  // History Store and Service
105
107
  export { historyStateStore, canUndo, canRedo, historyActions, setOnRestoreCallback, historyService, HistoryService } from '../stores/historyStore.js';
106
108
  // ============================================================================