@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.
- package/CHANGELOG.md +475 -0
- package/MIGRATION-2.0.md +472 -0
- package/README.md +23 -23
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +14 -8
- package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
- package/dist/chat/batchFeedback.d.ts +39 -0
- package/dist/chat/batchFeedback.js +51 -0
- package/dist/commands/executor.js +15 -1
- package/dist/commands/storeIntegration.svelte.d.ts +4 -1
- package/dist/commands/storeIntegration.svelte.js +26 -21
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/App.svelte +162 -192
- package/dist/components/App.svelte.d.ts +47 -8
- package/dist/components/ConfigForm.svelte +110 -66
- package/dist/components/ConfigModal.svelte +7 -2
- package/dist/components/ConnectionLine.svelte +4 -2
- package/dist/components/Navbar.svelte +61 -1
- package/dist/components/NodeSidebar.svelte +27 -45
- package/dist/components/NodeStatusOverlay.svelte +94 -6
- package/dist/components/NodeSwapPicker.svelte +10 -8
- package/dist/components/PipelineStatus.svelte +16 -67
- package/dist/components/PortCoordinateTracker.svelte +5 -6
- package/dist/components/SchemaForm.stories.svelte +1 -3
- package/dist/components/SchemaForm.svelte +45 -40
- package/dist/components/SchemaForm.svelte.d.ts +0 -8
- package/dist/components/SettingsModal.svelte +8 -3
- package/dist/components/SettingsPanel.svelte +20 -4
- package/dist/components/SwapMappingEditor.svelte +67 -49
- package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
- package/dist/components/UniversalNode.svelte +9 -7
- package/dist/components/WorkflowEditor.svelte +118 -111
- package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
- package/dist/components/chat/AIChatPanel.svelte +93 -89
- package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
- package/dist/components/chat/CommandPreview.svelte +2 -1
- package/dist/components/console/CommandConsole.svelte +7 -5
- package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
- package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
- package/dist/components/console/ConsoleInput.svelte +15 -6
- package/dist/components/console/ConsoleOutput.svelte +2 -1
- package/dist/components/form/FormArray.svelte +5 -9
- package/dist/components/form/FormArray.svelte.d.ts +2 -1
- package/dist/components/form/FormAutocomplete.svelte +29 -13
- package/dist/components/form/FormField.svelte +4 -2
- package/dist/components/form/FormFieldLight.svelte +4 -2
- package/dist/components/form/FormMarkdownEditor.svelte +9 -4
- package/dist/components/form/FormRangeField.svelte +1 -0
- package/dist/components/form/FormTemplateEditor.svelte +11 -3
- package/dist/components/form/FormToggle.svelte +5 -12
- package/dist/components/form/FormToggle.svelte.d.ts +4 -2
- package/dist/components/form/templateAutocomplete.js +1 -5
- package/dist/components/form/types.d.ts +1 -14
- package/dist/components/interrupt/FormPrompt.svelte +3 -2
- package/dist/components/interrupt/InterruptBubble.svelte +16 -17
- package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
- package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
- package/dist/components/layouts/MainLayout.svelte +20 -13
- package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
- package/dist/components/nodes/AtomNode.svelte +292 -0
- package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
- package/dist/components/nodes/GatewayNode.svelte +19 -10
- package/dist/components/nodes/IdeaNode.svelte +7 -0
- package/dist/components/nodes/SimpleNode.svelte +11 -6
- package/dist/components/nodes/SquareNode.svelte +15 -8
- package/dist/components/nodes/TerminalNode.svelte +9 -4
- package/dist/components/nodes/ToolNode.svelte +7 -1
- package/dist/components/nodes/WorkflowNode.svelte +16 -7
- package/dist/components/playground/ChatInput.svelte +11 -14
- package/dist/components/playground/ChatPanel.svelte +6 -49
- package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
- package/dist/components/playground/ControlPanel.svelte +134 -123
- package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
- package/dist/components/playground/ExecutionLogs.svelte +11 -9
- package/dist/components/playground/InputCollector.svelte +11 -9
- package/dist/components/playground/MessageStream.svelte +17 -23
- package/dist/components/playground/PipelineKanbanView.svelte +65 -6
- package/dist/components/playground/PipelinePanel.svelte +11 -5
- package/dist/components/playground/PipelineTableView.svelte +186 -44
- package/dist/components/playground/Playground.svelte +95 -92
- package/dist/components/playground/Playground.svelte.d.ts +2 -0
- package/dist/components/playground/PlaygroundApp.svelte +6 -1
- package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundModal.svelte +13 -3
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundStudio.svelte +34 -32
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
- package/dist/components/playground/SessionManager.svelte +9 -12
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
- package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
- package/dist/config/endpoints.d.ts +0 -7
- package/dist/config/endpoints.js +2 -10
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +6 -6
- package/dist/display/index.d.ts +0 -2
- package/dist/display/index.js +0 -6
- package/dist/editor/index.d.ts +19 -20
- package/dist/editor/index.js +25 -35
- package/dist/form/code.d.ts +25 -15
- package/dist/form/code.js +44 -41
- package/dist/form/fieldRegistry.d.ts +17 -13
- package/dist/form/fieldRegistry.js +32 -12
- package/dist/form/full.d.ts +17 -13
- package/dist/form/full.js +22 -27
- package/dist/form/index.d.ts +3 -3
- package/dist/form/index.js +3 -3
- package/dist/form/markdown.d.ts +13 -8
- package/dist/form/markdown.js +22 -23
- package/dist/helpers/proximityConnect.d.ts +7 -3
- package/dist/helpers/proximityConnect.js +19 -6
- package/dist/helpers/workflowEditorHelper.d.ts +12 -5
- package/dist/helpers/workflowEditorHelper.js +27 -25
- package/dist/index.d.ts +28 -24
- package/dist/index.js +27 -50
- package/dist/messages/defaults.d.ts +2 -5
- package/dist/messages/defaults.js +3 -6
- package/dist/messages/index.d.ts +0 -1
- package/dist/messages/index.js +0 -1
- package/dist/mocks/app-forms.d.ts +6 -2
- package/dist/mocks/app-forms.js +11 -4
- package/dist/openapi/v1/openapi.yaml +227 -164
- package/dist/playground/index.d.ts +2 -3
- package/dist/playground/index.js +2 -30
- package/dist/playground/mount.d.ts +15 -0
- package/dist/playground/mount.js +46 -20
- package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
- package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
- package/dist/registry/builtinFormats.d.ts +9 -18
- package/dist/registry/builtinFormats.js +9 -39
- package/dist/registry/builtinNodes.d.ts +1 -26
- package/dist/registry/builtinNodes.js +14 -50
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.js +4 -6
- package/dist/registry/nodeComponentRegistry.d.ts +182 -15
- package/dist/registry/nodeComponentRegistry.js +235 -17
- package/dist/registry/workflowFormatRegistry.d.ts +14 -9
- package/dist/registry/workflowFormatRegistry.js +24 -8
- package/dist/{schema → schemas}/index.d.ts +2 -2
- package/dist/{schema → schemas}/index.js +2 -2
- package/dist/schemas/v1/workflow.schema.json +53 -6
- package/dist/services/agentSpecExecutionService.js +0 -1
- package/dist/services/apiVariableService.d.ts +2 -1
- package/dist/services/apiVariableService.js +5 -22
- package/dist/services/autoSaveService.d.ts +7 -0
- package/dist/services/autoSaveService.js +6 -4
- package/dist/services/chatService.d.ts +8 -4
- package/dist/services/chatService.js +15 -15
- package/dist/services/draftStorage.d.ts +129 -13
- package/dist/services/draftStorage.js +185 -37
- package/dist/services/dynamicSchemaService.d.ts +2 -1
- package/dist/services/dynamicSchemaService.js +5 -22
- package/dist/services/globalSave.d.ts +13 -12
- package/dist/services/globalSave.js +29 -51
- package/dist/services/historyService.d.ts +9 -3
- package/dist/services/historyService.js +9 -3
- package/dist/services/interruptService.d.ts +14 -9
- package/dist/services/interruptService.js +27 -27
- package/dist/services/nodeExecutionService.d.ts +18 -3
- package/dist/services/nodeExecutionService.js +71 -45
- package/dist/services/playgroundService.d.ts +14 -9
- package/dist/services/playgroundService.js +31 -30
- package/dist/services/variableService.d.ts +2 -1
- package/dist/services/variableService.js +2 -2
- package/dist/services/workflowStorage.js +6 -6
- package/dist/stores/apiContext.d.ts +45 -0
- package/dist/stores/apiContext.js +65 -0
- package/dist/stores/categoriesStore.svelte.d.ts +28 -23
- package/dist/stores/categoriesStore.svelte.js +70 -64
- package/dist/stores/getInstance.svelte.d.ts +39 -0
- package/dist/stores/getInstance.svelte.js +65 -0
- package/dist/stores/historyStore.svelte.d.ts +77 -93
- package/dist/stores/historyStore.svelte.js +134 -160
- package/dist/stores/instanceContainer.svelte.d.ts +111 -0
- package/dist/stores/instanceContainer.svelte.js +114 -0
- package/dist/stores/interruptStore.svelte.d.ts +112 -82
- package/dist/stores/interruptStore.svelte.js +253 -226
- package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
- package/dist/stores/pipelinePanelStore.svelte.js +61 -14
- package/dist/stores/playgroundStore.svelte.d.ts +169 -216
- package/dist/stores/playgroundStore.svelte.js +515 -572
- package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
- package/dist/stores/portCoordinateStore.svelte.js +109 -98
- package/dist/stores/settingsStore.svelte.d.ts +4 -1
- package/dist/stores/settingsStore.svelte.js +47 -12
- package/dist/stores/workflowStore.svelte.d.ts +178 -213
- package/dist/stores/workflowStore.svelte.js +449 -501
- package/dist/stories/EdgeDecorator.svelte +5 -2
- package/dist/stories/NodeDecorator.svelte +5 -3
- package/dist/svelte-app.d.ts +60 -10
- package/dist/svelte-app.js +157 -53
- package/dist/types/events.d.ts +6 -3
- package/dist/types/index.d.ts +71 -6
- package/dist/types/navbar.d.ts +7 -0
- package/dist/types/playground.d.ts +18 -3
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.js +1 -0
- package/dist/utils/colors.d.ts +47 -21
- package/dist/utils/colors.js +69 -68
- package/dist/utils/connections.d.ts +9 -15
- package/dist/utils/connections.js +13 -32
- package/dist/utils/duration.d.ts +13 -0
- package/dist/utils/duration.js +45 -0
- package/dist/utils/formMerge.d.ts +36 -0
- package/dist/utils/formMerge.js +70 -0
- package/dist/utils/icons.d.ts +5 -2
- package/dist/utils/icons.js +6 -5
- package/dist/utils/nodeSwap.d.ts +6 -2
- package/dist/utils/nodeSwap.js +62 -126
- package/dist/utils/nodeTypes.d.ts +17 -8
- package/dist/utils/nodeTypes.js +27 -19
- package/dist/utils/performanceUtils.js +7 -0
- package/package.json +6 -5
- package/dist/messages/deprecation.d.ts +0 -20
- package/dist/messages/deprecation.js +0 -33
- package/dist/registry/plugin.d.ts +0 -215
- package/dist/registry/plugin.js +0 -249
- package/dist/services/api.d.ts +0 -129
- package/dist/services/api.js +0 -217
|
@@ -4,6 +4,7 @@ import type { EndpointConfig } from '../config/endpoints.js';
|
|
|
4
4
|
import type { AuthProvider } from '../types/auth.js';
|
|
5
5
|
import type { FlowDropEventHandlers, FlowDropFeatures } from '../types/events.js';
|
|
6
6
|
import type { FlowDropTheme, FlowDropThemeName } from '../types/theme.js';
|
|
7
|
+
import type { FlowDropInstance } from '../stores/instanceContainer.svelte.js';
|
|
7
8
|
import type { SettingsCategory } from '../types/settings.js';
|
|
8
9
|
import type { MessagesOverride } from '../messages/index.js';
|
|
9
10
|
/**
|
|
@@ -22,14 +23,33 @@ interface Props {
|
|
|
22
23
|
showNavbar?: boolean;
|
|
23
24
|
/** Disable the node sidebar */
|
|
24
25
|
disableSidebar?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Editor interaction mode. Replaces the former `readOnly` + `lockWorkflow`
|
|
28
|
+
* boolean pair (2.0 breaking change).
|
|
29
|
+
*
|
|
30
|
+
* | mode | node drag / connect / select | proximity-connect | node swap | bottom console panel + toggle |
|
|
31
|
+
* |--------------|------------------------------|-------------------|-----------|-------------------------------|
|
|
32
|
+
* | `'edit'` | enabled | enabled | enabled | available |
|
|
33
|
+
* | `'readonly'` | disabled | disabled | disabled | hidden |
|
|
34
|
+
* | `'locked'` | disabled | disabled | disabled | hidden |
|
|
35
|
+
*
|
|
36
|
+
* In 1.x `readOnly` and `lockWorkflow` gated the exact same set of
|
|
37
|
+
* interactions and were always combined as `!readOnly && !lockWorkflow`,
|
|
38
|
+
* so any combination of the two booleans collapsed to "edit" (both false)
|
|
39
|
+
* or "disabled" (either true). `'readonly'` and `'locked'` therefore behave
|
|
40
|
+
* identically today; the two names are kept as distinct intents so future
|
|
41
|
+
* versions can differentiate them without another breaking change.
|
|
42
|
+
*
|
|
43
|
+
* Migration: `readOnly` → `mode="readonly"`; `lockWorkflow` →
|
|
44
|
+
* `mode="locked"`; both `false` (or unset) → `mode="edit"` (the default).
|
|
45
|
+
*
|
|
46
|
+
* @default 'edit'
|
|
47
|
+
*/
|
|
48
|
+
mode?: 'edit' | 'readonly' | 'locked';
|
|
31
49
|
/** Pipeline ID for fetching node execution info */
|
|
32
50
|
pipelineId?: string;
|
|
51
|
+
/** Increments to force a refresh of pipeline node status from the server */
|
|
52
|
+
refreshTrigger?: number;
|
|
33
53
|
/** Custom navbar title */
|
|
34
54
|
navbarTitle?: string;
|
|
35
55
|
/** Custom navbar actions */
|
|
@@ -42,14 +62,31 @@ interface Props {
|
|
|
42
62
|
}>;
|
|
43
63
|
/** Show settings gear icon in navbar */
|
|
44
64
|
showSettings?: boolean;
|
|
65
|
+
/** Show the "Connected" status indicator in the navbar (default: true) */
|
|
66
|
+
showStatus?: boolean;
|
|
45
67
|
/** API base URL */
|
|
46
68
|
apiBaseUrl?: string;
|
|
47
69
|
/** Endpoint configuration */
|
|
48
70
|
endpointConfig?: EndpointConfig;
|
|
49
71
|
/** Authentication provider */
|
|
50
72
|
authProvider?: AuthProvider;
|
|
51
|
-
/**
|
|
52
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Called before save — return false to cancel. Forwarded to the save
|
|
75
|
+
* pipeline. (Flattened from the former `eventHandlers` object in 2.0.)
|
|
76
|
+
*/
|
|
77
|
+
onBeforeSave?: FlowDropEventHandlers['onBeforeSave'];
|
|
78
|
+
/** Called after a successful save. */
|
|
79
|
+
onAfterSave?: FlowDropEventHandlers['onAfterSave'];
|
|
80
|
+
/** Called when a save fails. */
|
|
81
|
+
onSaveError?: FlowDropEventHandlers['onSaveError'];
|
|
82
|
+
/** Called on any API error — return true to suppress the default toast. */
|
|
83
|
+
onApiError?: FlowDropEventHandlers['onApiError'];
|
|
84
|
+
/** Called after a workflow is loaded/imported. */
|
|
85
|
+
onWorkflowLoad?: FlowDropEventHandlers['onWorkflowLoad'];
|
|
86
|
+
/** Called before a node swap — return false to cancel. */
|
|
87
|
+
onBeforeSwap?: FlowDropEventHandlers['onBeforeSwap'];
|
|
88
|
+
/** Called after a node swap is applied. */
|
|
89
|
+
onAfterSwap?: FlowDropEventHandlers['onAfterSwap'];
|
|
53
90
|
/** Feature configuration */
|
|
54
91
|
features?: FlowDropFeatures;
|
|
55
92
|
/** Visual theme — named built-in ('default' | 'minimal') or custom theme object */
|
|
@@ -76,6 +113,8 @@ interface Props {
|
|
|
76
113
|
* function call you'd rather not invoke unless the prop is actually read.
|
|
77
114
|
*/
|
|
78
115
|
messages?: MessagesOverride | (() => MessagesOverride);
|
|
116
|
+
/** Per-instance state container (created by mount functions). Defaults to the page-default instance. */
|
|
117
|
+
instance?: FlowDropInstance;
|
|
79
118
|
}
|
|
80
119
|
declare const App: import("svelte").Component<Props, {}, "">;
|
|
81
120
|
type App = ReturnType<typeof App>;
|
|
@@ -43,10 +43,12 @@
|
|
|
43
43
|
type DynamicSchemaResult
|
|
44
44
|
} from '../services/dynamicSchemaService.js';
|
|
45
45
|
import { globalSaveWorkflow } from '../services/globalSave.js';
|
|
46
|
+
import { provideInstance } from '../stores/getInstance.svelte.js';
|
|
46
47
|
import { getAvailableVariables } from '../services/variableService.js';
|
|
47
48
|
import { logger } from '../utils/logger.js';
|
|
48
49
|
import { getDataTypeColorToken, getPortBackgroundColor } from '../utils/colors.js';
|
|
49
50
|
import { applyPortOrder } from '../utils/portUtils.js';
|
|
51
|
+
import { mergeWithDefaults, cascadeClearAutocompleteDependents } from '../utils/formMerge.js';
|
|
50
52
|
|
|
51
53
|
interface Props {
|
|
52
54
|
/** Optional workflow node (if provided, schema and values are derived from it) */
|
|
@@ -104,6 +106,17 @@
|
|
|
104
106
|
onCancel
|
|
105
107
|
}: Props = $props();
|
|
106
108
|
|
|
109
|
+
// Resolve the active instance for endpoint configuration (dynamic schema fetch).
|
|
110
|
+
// ConfigForm is a standalone-capable container (exported from /editor and able
|
|
111
|
+
// to render bare, e.g. with a direct `schema`/`values`). Self-provide so its
|
|
112
|
+
// leaf <FormField>s resolve and SSR doesn't throw. provideInstance() reuses an
|
|
113
|
+
// ancestor's context instance when nested in <App>/<WorkflowEditor>, returns
|
|
114
|
+
// the shared page-default in the browser, and creates a fresh per-render
|
|
115
|
+
// instance on the server (no cross-request leakage); no destroy here for the
|
|
116
|
+
// same reasons as SchemaForm (shared/default in the browser, no SSR teardown).
|
|
117
|
+
const fd = provideInstance();
|
|
118
|
+
const checker = fd.portCompatibility;
|
|
119
|
+
|
|
107
120
|
// Set context for child components (e.g., FormAutocomplete)
|
|
108
121
|
// Use getter functions to ensure child components always get the current prop value,
|
|
109
122
|
// even if the prop changes after initial mount
|
|
@@ -187,17 +200,30 @@
|
|
|
187
200
|
const initialConfig = $derived(values ?? node?.data.config ?? {});
|
|
188
201
|
|
|
189
202
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
203
|
+
* User edits to config — only keys the user has touched since the current
|
|
204
|
+
* schema was loaded. configValues is derived from props + edits, so children
|
|
205
|
+
* mount with the correct values already in place (no parent→child race
|
|
206
|
+
* during the initial flush). Never assign to configValues directly.
|
|
192
207
|
*/
|
|
193
|
-
let
|
|
208
|
+
let edits = $state<Record<string, unknown>>({});
|
|
209
|
+
|
|
210
|
+
const configValues = $derived(mergeWithDefaults(configSchema, initialConfig, edits));
|
|
211
|
+
|
|
194
212
|
setContext<() => Record<string, unknown>>('flowdrop:getFormValues', () => configValues);
|
|
195
213
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
214
|
+
// Drop edits when the schema reference changes — covers dynamic-schema load
|
|
215
|
+
// (configSchema flips from undefined → loaded) and "different node opened"
|
|
216
|
+
// (node prop change → metadata.configSchema reference change). Identity
|
|
217
|
+
// comparison only — value churn in `initialConfig` preserves in-flight edits.
|
|
218
|
+
// capturing the initial derived reference is intentional; later changes are picked up by the effect below
|
|
219
|
+
// svelte-ignore state_referenced_locally
|
|
220
|
+
let prevSchemaRef = configSchema;
|
|
221
|
+
$effect.pre(() => {
|
|
222
|
+
if (configSchema !== prevSchemaRef) {
|
|
223
|
+
prevSchemaRef = configSchema;
|
|
224
|
+
edits = {};
|
|
225
|
+
}
|
|
226
|
+
});
|
|
201
227
|
|
|
202
228
|
/**
|
|
203
229
|
* Flag to track if workflow save is in progress
|
|
@@ -215,6 +241,38 @@
|
|
|
215
241
|
return { ...typeDefaults, ...instanceOverrides };
|
|
216
242
|
});
|
|
217
243
|
|
|
244
|
+
/**
|
|
245
|
+
* UI Extension values for display settings.
|
|
246
|
+
* Writable derived: recomputes from the node when it changes (covering both
|
|
247
|
+
* "different node opened" and the post-save round-trip through node.data),
|
|
248
|
+
* while local port-management edits overwrite it wholesale via reassignment.
|
|
249
|
+
* NOTE: the derived value is not a deep $state proxy — update it only by
|
|
250
|
+
* reassigning the whole object, never by mutating a property.
|
|
251
|
+
*/
|
|
252
|
+
let uiExtensionValues = $derived.by<NodeUIExtensions>(() => ({
|
|
253
|
+
hideUnconnectedHandles: initialUIExtensions.hideUnconnectedHandles ?? false,
|
|
254
|
+
portOrder: initialUIExtensions.portOrder
|
|
255
|
+
? {
|
|
256
|
+
inputs: initialUIExtensions.portOrder.inputs
|
|
257
|
+
? [...initialUIExtensions.portOrder.inputs]
|
|
258
|
+
: undefined,
|
|
259
|
+
outputs: initialUIExtensions.portOrder.outputs
|
|
260
|
+
? [...initialUIExtensions.portOrder.outputs]
|
|
261
|
+
: undefined
|
|
262
|
+
}
|
|
263
|
+
: undefined,
|
|
264
|
+
hiddenPorts: initialUIExtensions.hiddenPorts
|
|
265
|
+
? {
|
|
266
|
+
inputs: initialUIExtensions.hiddenPorts.inputs
|
|
267
|
+
? [...initialUIExtensions.hiddenPorts.inputs]
|
|
268
|
+
: undefined,
|
|
269
|
+
outputs: initialUIExtensions.hiddenPorts.outputs
|
|
270
|
+
? [...initialUIExtensions.hiddenPorts.outputs]
|
|
271
|
+
: undefined
|
|
272
|
+
}
|
|
273
|
+
: undefined
|
|
274
|
+
}));
|
|
275
|
+
|
|
218
276
|
/**
|
|
219
277
|
* Fetch dynamic schema when needed
|
|
220
278
|
*/
|
|
@@ -226,6 +284,7 @@
|
|
|
226
284
|
|
|
227
285
|
try {
|
|
228
286
|
const result: DynamicSchemaResult = await fetchDynamicSchema(
|
|
287
|
+
fd.api.config,
|
|
229
288
|
configEditOptions.dynamicSchema,
|
|
230
289
|
node,
|
|
231
290
|
workflowId
|
|
@@ -294,51 +353,6 @@
|
|
|
294
353
|
}
|
|
295
354
|
});
|
|
296
355
|
|
|
297
|
-
/**
|
|
298
|
-
* Initialize config values when node/schema changes
|
|
299
|
-
*/
|
|
300
|
-
$effect(() => {
|
|
301
|
-
if (configSchema?.properties) {
|
|
302
|
-
const mergedConfig: Record<string, unknown> = {};
|
|
303
|
-
Object.entries(configSchema.properties).forEach(([key, field]) => {
|
|
304
|
-
const fieldConfig = field as Record<string, unknown>;
|
|
305
|
-
// Use existing value if available, otherwise use default
|
|
306
|
-
mergedConfig[key] =
|
|
307
|
-
initialConfig[key] !== undefined ? initialConfig[key] : fieldConfig.default;
|
|
308
|
-
});
|
|
309
|
-
configValues = mergedConfig;
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Initialize UI extension values when node changes
|
|
315
|
-
*/
|
|
316
|
-
$effect(() => {
|
|
317
|
-
uiExtensionValues = {
|
|
318
|
-
hideUnconnectedHandles: initialUIExtensions.hideUnconnectedHandles ?? false,
|
|
319
|
-
portOrder: initialUIExtensions.portOrder
|
|
320
|
-
? {
|
|
321
|
-
inputs: initialUIExtensions.portOrder.inputs
|
|
322
|
-
? [...initialUIExtensions.portOrder.inputs]
|
|
323
|
-
: undefined,
|
|
324
|
-
outputs: initialUIExtensions.portOrder.outputs
|
|
325
|
-
? [...initialUIExtensions.portOrder.outputs]
|
|
326
|
-
: undefined
|
|
327
|
-
}
|
|
328
|
-
: undefined,
|
|
329
|
-
hiddenPorts: initialUIExtensions.hiddenPorts
|
|
330
|
-
? {
|
|
331
|
-
inputs: initialUIExtensions.hiddenPorts.inputs
|
|
332
|
-
? [...initialUIExtensions.hiddenPorts.inputs]
|
|
333
|
-
: undefined,
|
|
334
|
-
outputs: initialUIExtensions.hiddenPorts.outputs
|
|
335
|
-
? [...initialUIExtensions.hiddenPorts.outputs]
|
|
336
|
-
: undefined
|
|
337
|
-
}
|
|
338
|
-
: undefined
|
|
339
|
-
};
|
|
340
|
-
});
|
|
341
|
-
|
|
342
356
|
/**
|
|
343
357
|
* All input ports in current display order for the port management UI.
|
|
344
358
|
* Combines static metadata inputs + dynamic config inputs, sorted by portOrder.
|
|
@@ -376,9 +390,12 @@
|
|
|
376
390
|
if (newIdx < 0 || newIdx >= list.length) return;
|
|
377
391
|
const newOrder = list.map((p) => p.id);
|
|
378
392
|
[newOrder[idx], newOrder[newIdx]] = [newOrder[newIdx], newOrder[idx]];
|
|
379
|
-
uiExtensionValues
|
|
380
|
-
...uiExtensionValues
|
|
381
|
-
|
|
393
|
+
uiExtensionValues = {
|
|
394
|
+
...uiExtensionValues,
|
|
395
|
+
portOrder: {
|
|
396
|
+
...uiExtensionValues.portOrder,
|
|
397
|
+
[direction]: newOrder
|
|
398
|
+
}
|
|
382
399
|
};
|
|
383
400
|
handleFormBlur();
|
|
384
401
|
}
|
|
@@ -390,9 +407,12 @@
|
|
|
390
407
|
const current = uiExtensionValues.hiddenPorts?.[direction] ?? [];
|
|
391
408
|
const isHidden = current.includes(portId);
|
|
392
409
|
const next = isHidden ? current.filter((id) => id !== portId) : [...current, portId];
|
|
393
|
-
uiExtensionValues
|
|
394
|
-
...uiExtensionValues
|
|
395
|
-
|
|
410
|
+
uiExtensionValues = {
|
|
411
|
+
...uiExtensionValues,
|
|
412
|
+
hiddenPorts: {
|
|
413
|
+
...uiExtensionValues.hiddenPorts,
|
|
414
|
+
[direction]: next.length > 0 ? next : undefined
|
|
415
|
+
}
|
|
396
416
|
};
|
|
397
417
|
handleFormBlur();
|
|
398
418
|
}
|
|
@@ -405,8 +425,11 @@
|
|
|
405
425
|
const hidden = { ...uiExtensionValues.hiddenPorts };
|
|
406
426
|
delete order[direction];
|
|
407
427
|
delete hidden[direction];
|
|
408
|
-
uiExtensionValues
|
|
409
|
-
|
|
428
|
+
uiExtensionValues = {
|
|
429
|
+
...uiExtensionValues,
|
|
430
|
+
portOrder: Object.keys(order).length > 0 ? order : undefined,
|
|
431
|
+
hiddenPorts: Object.keys(hidden).length > 0 ? hidden : undefined
|
|
432
|
+
};
|
|
410
433
|
handleFormBlur();
|
|
411
434
|
}
|
|
412
435
|
|
|
@@ -419,10 +442,22 @@
|
|
|
419
442
|
}
|
|
420
443
|
|
|
421
444
|
/**
|
|
422
|
-
* Handle field value changes from FormField components
|
|
445
|
+
* Handle field value changes from FormField components.
|
|
446
|
+
*
|
|
447
|
+
* When a field changes, also clear any sibling autocomplete that declared
|
|
448
|
+
* this field in its `autocomplete.params` map — its previous value was
|
|
449
|
+
* computed against the old dependency value and is now stale. The cascade
|
|
450
|
+
* runs only on user-driven edits via this codepath; undo/redo and external
|
|
451
|
+
* config replacement flow through `initialConfig` and don't trigger it (#33).
|
|
423
452
|
*/
|
|
424
453
|
function handleFieldChange(key: string, value: unknown): void {
|
|
425
|
-
configValues[key]
|
|
454
|
+
const previous = configValues[key];
|
|
455
|
+
edits[key] = value;
|
|
456
|
+
if (previous === value) return;
|
|
457
|
+
const dependents = cascadeClearAutocompleteDependents(configSchema, key);
|
|
458
|
+
for (const [depKey, depValue] of Object.entries(dependents)) {
|
|
459
|
+
edits[depKey] = depValue;
|
|
460
|
+
}
|
|
426
461
|
}
|
|
427
462
|
|
|
428
463
|
/**
|
|
@@ -434,6 +469,11 @@
|
|
|
434
469
|
if (onChange) {
|
|
435
470
|
const extensions = showUIExtensions && node ? uiExtensionValues : undefined;
|
|
436
471
|
onChange({ ...configValues }, extensions);
|
|
472
|
+
// Discharge the edits buffer at the commit boundary. Subsequent prop
|
|
473
|
+
// changes (parent absorbing the commit, undo/redo, collaboration) then
|
|
474
|
+
// flow through `initialConfig` cleanly instead of being shadowed by a
|
|
475
|
+
// stale local edit.
|
|
476
|
+
edits = {};
|
|
437
477
|
}
|
|
438
478
|
}
|
|
439
479
|
|
|
@@ -731,7 +771,7 @@
|
|
|
731
771
|
offLabel="Visible"
|
|
732
772
|
ariaDescribedBy="ext-hideUnconnectedHandles-description"
|
|
733
773
|
onChange={(val) => {
|
|
734
|
-
uiExtensionValues
|
|
774
|
+
uiExtensionValues = { ...uiExtensionValues, hideUnconnectedHandles: val };
|
|
735
775
|
handleFormBlur();
|
|
736
776
|
}}
|
|
737
777
|
/>
|
|
@@ -767,11 +807,13 @@
|
|
|
767
807
|
<span
|
|
768
808
|
class="config-form__port-order-badge"
|
|
769
809
|
style="background-color:{getPortBackgroundColor(
|
|
810
|
+
checker,
|
|
770
811
|
port.dataType,
|
|
771
812
|
15
|
|
772
813
|
)};color:{getDataTypeColorToken(
|
|
814
|
+
checker,
|
|
773
815
|
port.dataType
|
|
774
|
-
)};border:1px solid {getPortBackgroundColor(port.dataType, 30)}"
|
|
816
|
+
)};border:1px solid {getPortBackgroundColor(checker, port.dataType, 30)}"
|
|
775
817
|
>
|
|
776
818
|
{port.dataType}
|
|
777
819
|
</span>
|
|
@@ -843,11 +885,13 @@
|
|
|
843
885
|
<span
|
|
844
886
|
class="config-form__port-order-badge"
|
|
845
887
|
style="background-color:{getPortBackgroundColor(
|
|
888
|
+
checker,
|
|
846
889
|
port.dataType,
|
|
847
890
|
15
|
|
848
891
|
)};color:{getDataTypeColorToken(
|
|
892
|
+
checker,
|
|
849
893
|
port.dataType
|
|
850
|
-
)};border:1px solid {getPortBackgroundColor(port.dataType, 30)}"
|
|
894
|
+
)};border:1px solid {getPortBackgroundColor(checker, port.dataType, 30)}"
|
|
851
895
|
>
|
|
852
896
|
{port.dataType}
|
|
853
897
|
</span>
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
let { onClose, onSave, onCancel, ...props }: Props = $props();
|
|
17
|
+
|
|
18
|
+
// Unique per component instance so two FlowDrop editors on one page
|
|
19
|
+
// don't render colliding DOM ids (a11y).
|
|
20
|
+
const uid = $props.id();
|
|
21
|
+
const titleId = `${uid}-config-modal-title`;
|
|
17
22
|
let localConfigValues = $derived.by(() => ({ ...props.configValues }));
|
|
18
23
|
|
|
19
24
|
function handleCancel() {
|
|
@@ -47,14 +52,14 @@
|
|
|
47
52
|
onkeydown={handleKeydown}
|
|
48
53
|
role="dialog"
|
|
49
54
|
aria-modal="true"
|
|
50
|
-
aria-labelledby=
|
|
55
|
+
aria-labelledby={titleId}
|
|
51
56
|
tabindex="-1"
|
|
52
57
|
>
|
|
53
58
|
<!-- Modal Container -->
|
|
54
59
|
<div class="config-modal">
|
|
55
60
|
<!-- Modal Header -->
|
|
56
61
|
<div class="config-modal__header">
|
|
57
|
-
<h2 id=
|
|
62
|
+
<h2 id={titleId} class="config-modal__title">
|
|
58
63
|
Configure: {props.nodeLabel}
|
|
59
64
|
</h2>
|
|
60
65
|
<button
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getDataTypeColor } from '../utils/colors';
|
|
3
|
+
import { getInstance } from '../stores/getInstance.svelte.js';
|
|
3
4
|
import { useConnection } from '@xyflow/svelte';
|
|
4
5
|
|
|
5
6
|
const connection = useConnection();
|
|
7
|
+
const checker = getInstance().portCompatibility;
|
|
6
8
|
|
|
7
9
|
let path: string | null = $derived.by(() => {
|
|
8
10
|
if (connection.current.inProgress) {
|
|
@@ -18,7 +20,7 @@
|
|
|
18
20
|
fill="none"
|
|
19
21
|
stroke-width={1.5}
|
|
20
22
|
class="animated"
|
|
21
|
-
stroke={getDataTypeColor(connection.current.fromHandle.id ?? '')}
|
|
23
|
+
stroke={getDataTypeColor(checker, connection.current.fromHandle.id ?? '')}
|
|
22
24
|
d={path}
|
|
23
25
|
/>
|
|
24
26
|
<circle
|
|
@@ -26,7 +28,7 @@
|
|
|
26
28
|
cy={connection.current.to.y}
|
|
27
29
|
fill="#fff"
|
|
28
30
|
r={3}
|
|
29
|
-
stroke={getDataTypeColor(connection.current.fromHandle.id ?? '')}
|
|
31
|
+
stroke={getDataTypeColor(checker, connection.current.fromHandle.id ?? '')}
|
|
30
32
|
stroke-width={1.5}
|
|
31
33
|
/>
|
|
32
34
|
{/if}
|
|
@@ -59,6 +59,25 @@
|
|
|
59
59
|
// Hoist the navigation branch — six reads in the template.
|
|
60
60
|
const nav = $derived(m().navigation);
|
|
61
61
|
|
|
62
|
+
// Flyout structure: actions after the first split into ungrouped (rendered
|
|
63
|
+
// flat at the top) and groups (rendered as labeled sections). Group order
|
|
64
|
+
// follows first occurrence in the source array.
|
|
65
|
+
const dropdownActions = $derived(primaryActions.slice(1));
|
|
66
|
+
const ungroupedActions = $derived(dropdownActions.filter((a) => !a.group));
|
|
67
|
+
const groupedActions = $derived.by(() => {
|
|
68
|
+
const groups = new Map<string, NavbarAction[]>();
|
|
69
|
+
for (const action of dropdownActions) {
|
|
70
|
+
if (!action.group) continue;
|
|
71
|
+
let bucket = groups.get(action.group);
|
|
72
|
+
if (!bucket) {
|
|
73
|
+
bucket = [];
|
|
74
|
+
groups.set(action.group, bucket);
|
|
75
|
+
}
|
|
76
|
+
bucket.push(action);
|
|
77
|
+
}
|
|
78
|
+
return Array.from(groups, ([label, items]) => ({ label, items }));
|
|
79
|
+
});
|
|
80
|
+
|
|
62
81
|
// Close dropdown when clicking outside
|
|
63
82
|
function handleClickOutside(event: MouseEvent) {
|
|
64
83
|
const target = event.target as HTMLElement;
|
|
@@ -203,7 +222,7 @@
|
|
|
203
222
|
|
|
204
223
|
{#if isDropdownOpen}
|
|
205
224
|
<div class="flowdrop-navbar__dropdown-menu">
|
|
206
|
-
{#each
|
|
225
|
+
{#each ungroupedActions as action (action.label)}
|
|
207
226
|
<a
|
|
208
227
|
href={action.href}
|
|
209
228
|
class="flowdrop-navbar__dropdown-item"
|
|
@@ -223,6 +242,32 @@
|
|
|
223
242
|
{/if}
|
|
224
243
|
</a>
|
|
225
244
|
{/each}
|
|
245
|
+
{#each groupedActions as group, groupIndex (group.label)}
|
|
246
|
+
{#if groupIndex > 0 || ungroupedActions.length > 0}
|
|
247
|
+
<div class="flowdrop-navbar__dropdown-divider" role="separator"></div>
|
|
248
|
+
{/if}
|
|
249
|
+
<div class="flowdrop-navbar__dropdown-group-header">{group.label}</div>
|
|
250
|
+
{#each group.items as action (action.label)}
|
|
251
|
+
<a
|
|
252
|
+
href={action.href}
|
|
253
|
+
class="flowdrop-navbar__dropdown-item"
|
|
254
|
+
onclick={(e) => {
|
|
255
|
+
action.onclick?.(e);
|
|
256
|
+
isDropdownOpen = false;
|
|
257
|
+
}}
|
|
258
|
+
target={action.external ? '_blank' : undefined}
|
|
259
|
+
rel={action.external ? 'noopener noreferrer' : undefined}
|
|
260
|
+
>
|
|
261
|
+
{#if action.icon}
|
|
262
|
+
<Icon icon={action.icon} class="w-4 h-4" />
|
|
263
|
+
{/if}
|
|
264
|
+
<span>{action.label}</span>
|
|
265
|
+
{#if action.external}
|
|
266
|
+
<Icon icon="mdi:open-in-new" class="w-3 h-3" />
|
|
267
|
+
{/if}
|
|
268
|
+
</a>
|
|
269
|
+
{/each}
|
|
270
|
+
{/each}
|
|
226
271
|
</div>
|
|
227
272
|
{/if}
|
|
228
273
|
</div>
|
|
@@ -568,6 +613,21 @@
|
|
|
568
613
|
border-bottom: none;
|
|
569
614
|
}
|
|
570
615
|
|
|
616
|
+
.flowdrop-navbar__dropdown-divider {
|
|
617
|
+
height: 1px;
|
|
618
|
+
background-color: var(--fd-border);
|
|
619
|
+
margin: 0.25rem 0;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.flowdrop-navbar__dropdown-group-header {
|
|
623
|
+
padding: 0.5rem 1rem 0.25rem;
|
|
624
|
+
font-size: var(--fd-text-xs);
|
|
625
|
+
font-weight: 600;
|
|
626
|
+
text-transform: uppercase;
|
|
627
|
+
letter-spacing: 0.04em;
|
|
628
|
+
color: var(--fd-muted-foreground);
|
|
629
|
+
}
|
|
630
|
+
|
|
571
631
|
.flowdrop-navbar__action {
|
|
572
632
|
display: flex;
|
|
573
633
|
align-items: center;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import Icon from '@iconify/svelte';
|
|
11
11
|
import { getNodeIcon, getCategoryIcon } from '../utils/icons.js';
|
|
12
12
|
import { getCategoryColorToken } from '../utils/colors.js';
|
|
13
|
-
import {
|
|
13
|
+
import { getInstance } from '../stores/getInstance.svelte.js';
|
|
14
14
|
import { getUiSettings } from '../stores/settingsStore.svelte.js';
|
|
15
15
|
import { extractConfigDefaults } from '../utils/nodeIds.js';
|
|
16
16
|
import { m } from '../messages/index.js';
|
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
let props: Props = $props();
|
|
29
|
+
const fd = getInstance();
|
|
29
30
|
let searchInput = $state('');
|
|
30
|
-
//
|
|
31
|
+
// initial default, user selects interactively
|
|
32
|
+
// svelte-ignore state_referenced_locally
|
|
31
33
|
let selectedCategory = $state(props.selectedCategory || 'all');
|
|
32
34
|
|
|
33
35
|
/**
|
|
@@ -140,26 +142,12 @@
|
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
|
|
143
|
-
/**
|
|
144
|
-
* Handle search input change
|
|
145
|
-
*/
|
|
146
|
-
function handleSearchChange(): void {
|
|
147
|
-
// Search is handled reactively through the derived filteredNodes
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Handle node click
|
|
152
|
-
*/
|
|
153
|
-
function handleNodeClick(nodeType: NodeMetadata): void {
|
|
154
|
-
// Handle node click - could be used for preview or configuration
|
|
155
|
-
}
|
|
156
|
-
|
|
157
145
|
/**
|
|
158
146
|
* Get category display name from the categories store.
|
|
159
147
|
* Falls back to auto-capitalizing the category machine name.
|
|
160
148
|
*/
|
|
161
149
|
function getCategoryDisplayName(category: NodeCategory): string {
|
|
162
|
-
return
|
|
150
|
+
return fd.categories.getLabel(category);
|
|
163
151
|
}
|
|
164
152
|
</script>
|
|
165
153
|
|
|
@@ -180,7 +168,6 @@
|
|
|
180
168
|
placeholder={m().layout.searchComponents}
|
|
181
169
|
class="flowdrop-input flowdrop-join__item flowdrop-w--full"
|
|
182
170
|
bind:value={searchInput}
|
|
183
|
-
oninput={handleSearchChange}
|
|
184
171
|
/>
|
|
185
172
|
</div>
|
|
186
173
|
<button class="flowdrop-btn flowdrop-join__item" aria-label={m().layout.searchComponents}>
|
|
@@ -273,9 +260,12 @@
|
|
|
273
260
|
<!-- Node Type Icon with Squircle Background -->
|
|
274
261
|
<span
|
|
275
262
|
class="flowdrop-node-icon"
|
|
276
|
-
style="--_icon-color: {getCategoryColorToken(
|
|
263
|
+
style="--_icon-color: {getCategoryColorToken(
|
|
264
|
+
fd.categories,
|
|
265
|
+
nodeType.category
|
|
266
|
+
)}"
|
|
277
267
|
>
|
|
278
|
-
<Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
|
|
268
|
+
<Icon icon={getNodeIcon(fd.categories, nodeType.icon, nodeType.category)} />
|
|
279
269
|
</span>
|
|
280
270
|
|
|
281
271
|
<!-- Node Type Info - Icon and Title only -->
|
|
@@ -309,25 +299,20 @@
|
|
|
309
299
|
<div class="fd-sidebar-flat-category">
|
|
310
300
|
{getCategoryDisplayName(category).toUpperCase()}
|
|
311
301
|
</div>
|
|
312
|
-
<div class="fd-sidebar-flat-list">
|
|
302
|
+
<div class="fd-sidebar-flat-list" role="list">
|
|
313
303
|
{#each categoryNodes as nodeType (nodeType.id)}
|
|
314
304
|
<div
|
|
315
305
|
class="fd-sidebar-flat-item"
|
|
306
|
+
role="listitem"
|
|
316
307
|
draggable="true"
|
|
317
308
|
ondragstart={(e) => handleNodeDragStart(e, nodeType)}
|
|
318
|
-
onclick={() => handleNodeClick(nodeType)}
|
|
319
|
-
role="button"
|
|
320
|
-
tabindex="0"
|
|
321
|
-
onkeydown={(e) => {
|
|
322
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
323
|
-
e.preventDefault();
|
|
324
|
-
handleNodeClick(nodeType);
|
|
325
|
-
}
|
|
326
|
-
}}
|
|
327
309
|
>
|
|
328
310
|
<span
|
|
329
311
|
class="fd-sidebar-flat-dot"
|
|
330
|
-
style="background: {getCategoryColorToken(
|
|
312
|
+
style="background: {getCategoryColorToken(
|
|
313
|
+
fd.categories,
|
|
314
|
+
nodeType.category
|
|
315
|
+
)}"
|
|
331
316
|
></span>
|
|
332
317
|
<span class="fd-sidebar-flat-name">{nodeType.name}</span>
|
|
333
318
|
</div>
|
|
@@ -343,9 +328,9 @@
|
|
|
343
328
|
<div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
|
|
344
329
|
<span
|
|
345
330
|
class="flowdrop-node-icon"
|
|
346
|
-
style="--_icon-color: {getCategoryColorToken(category)}"
|
|
331
|
+
style="--_icon-color: {getCategoryColorToken(fd.categories, category)}"
|
|
347
332
|
>
|
|
348
|
-
<Icon icon={getCategoryIcon(category)} />
|
|
333
|
+
<Icon icon={getCategoryIcon(fd.categories, category)} />
|
|
349
334
|
</span>
|
|
350
335
|
<span>{getCategoryDisplayName(category)}</span>
|
|
351
336
|
</div>
|
|
@@ -354,30 +339,27 @@
|
|
|
354
339
|
</div>
|
|
355
340
|
</summary>
|
|
356
341
|
<div class="flowdrop-details__content">
|
|
357
|
-
<div class="flowdrop-node-list">
|
|
342
|
+
<div class="flowdrop-node-list" role="list">
|
|
358
343
|
{#each categoryNodes as nodeType (nodeType.id)}
|
|
359
344
|
<div
|
|
360
345
|
class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
|
|
346
|
+
role="listitem"
|
|
361
347
|
draggable="true"
|
|
362
348
|
ondragstart={(e) => handleNodeDragStart(e, nodeType)}
|
|
363
|
-
onclick={() => handleNodeClick(nodeType)}
|
|
364
|
-
role="button"
|
|
365
|
-
tabindex="0"
|
|
366
|
-
onkeydown={(e) => {
|
|
367
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
368
|
-
e.preventDefault();
|
|
369
|
-
handleNodeClick(nodeType);
|
|
370
|
-
}
|
|
371
|
-
}}
|
|
372
349
|
>
|
|
373
350
|
<div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
|
|
374
351
|
<div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
|
|
375
352
|
<!-- Node Type Icon with Squircle Background -->
|
|
376
353
|
<span
|
|
377
354
|
class="flowdrop-node-icon"
|
|
378
|
-
style="--_icon-color: {getCategoryColorToken(
|
|
355
|
+
style="--_icon-color: {getCategoryColorToken(
|
|
356
|
+
fd.categories,
|
|
357
|
+
nodeType.category
|
|
358
|
+
)}"
|
|
379
359
|
>
|
|
380
|
-
<Icon
|
|
360
|
+
<Icon
|
|
361
|
+
icon={getNodeIcon(fd.categories, nodeType.icon, nodeType.category)}
|
|
362
|
+
/>
|
|
381
363
|
</span>
|
|
382
364
|
|
|
383
365
|
<!-- Node Type Info - Icon and Title only -->
|