@flowdrop/flowdrop 2.0.0-beta.3 → 2.0.0-beta.5
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 +38 -0
- package/README.md +5 -5
- package/dist/adapters/WorkflowAdapter.js +4 -5
- package/dist/adapters/agentspec/AgentSpecAdapter.js +3 -3
- package/dist/adapters/agentspec/defaultNodeTypes.js +9 -9
- package/dist/commands/executor.js +5 -6
- package/dist/commands/types.js +5 -5
- package/dist/components/App.svelte +19 -150
- package/dist/components/Button.stories.svelte +65 -0
- package/dist/components/Button.stories.svelte.d.ts +19 -0
- package/dist/components/Button.svelte +62 -0
- package/dist/components/Button.svelte.d.ts +24 -0
- package/dist/components/ConfigForm.svelte +4 -4
- package/dist/components/EditorStatusBar.stories.svelte +44 -0
- package/dist/components/EditorStatusBar.stories.svelte.d.ts +27 -0
- package/dist/components/EditorStatusBar.svelte +99 -0
- package/dist/components/EditorStatusBar.svelte.d.ts +15 -0
- package/dist/components/IconButton.svelte +80 -0
- package/dist/components/IconButton.svelte.d.ts +30 -0
- package/dist/components/Input.svelte +74 -0
- package/dist/components/Input.svelte.d.ts +17 -0
- package/dist/components/Navbar.svelte +9 -4
- package/dist/components/Navbar.svelte.d.ts +3 -0
- package/dist/components/NodeSidebar.svelte +17 -115
- package/dist/components/NodeSwapPicker.svelte +12 -28
- package/dist/components/Select.svelte +53 -0
- package/dist/components/Select.svelte.d.ts +15 -0
- package/dist/components/Textarea.svelte +39 -0
- package/dist/components/Textarea.svelte.d.ts +12 -0
- package/dist/components/ThemeToggle.svelte +15 -89
- package/dist/components/UniversalNode.svelte +5 -4
- package/dist/components/UniversalNode.svelte.d.ts +1 -1
- package/dist/components/console/ConsoleInput.svelte +3 -3
- package/dist/components/form/FormArray.svelte +37 -157
- package/dist/components/form/FormCheckboxGroup.svelte +1 -1
- package/dist/components/form/FormField.svelte +5 -44
- package/dist/components/form/FormFieldLight.svelte +5 -44
- package/dist/components/form/FormFieldset.svelte +1 -1
- package/dist/components/form/FormNumberField.svelte +4 -32
- package/dist/components/form/FormRangeField.svelte +17 -7
- package/dist/components/form/FormSelect.svelte +13 -79
- package/dist/components/form/FormTextField.svelte +3 -39
- package/dist/components/form/FormTextarea.svelte +4 -43
- package/dist/components/form/resolveFieldType.d.ts +24 -0
- package/dist/components/form/resolveFieldType.js +55 -0
- package/dist/components/icons/CloseIcon.svelte +6 -0
- package/dist/components/icons/CloseIcon.svelte.d.ts +26 -0
- package/dist/components/nodes/AtomNode.svelte +3 -3
- package/dist/components/nodes/AtomNode.svelte.d.ts +1 -1
- package/dist/components/nodes/GatewayNode.svelte +8 -11
- package/dist/components/nodes/GatewayNode.svelte.d.ts +1 -1
- package/dist/components/nodes/IdeaNode.svelte +10 -8
- package/dist/components/nodes/IdeaNode.svelte.d.ts +8 -4
- package/dist/components/nodes/NotesNode.svelte +6 -4
- package/dist/components/nodes/NotesNode.svelte.d.ts +8 -4
- package/dist/components/nodes/SimpleNode.svelte +10 -8
- package/dist/components/nodes/SimpleNode.svelte.d.ts +4 -4
- package/dist/components/nodes/SquareNode.svelte +10 -8
- package/dist/components/nodes/SquareNode.svelte.d.ts +4 -4
- package/dist/components/nodes/TerminalNode.svelte +10 -8
- package/dist/components/nodes/TerminalNode.svelte.d.ts +4 -4
- package/dist/components/nodes/ToolNode.svelte +10 -8
- package/dist/components/nodes/ToolNode.svelte.d.ts +6 -6
- package/dist/components/nodes/WorkflowNode.svelte +7 -10
- package/dist/components/nodes/WorkflowNode.svelte.d.ts +1 -1
- package/dist/components/playground/InputCollector.svelte +11 -46
- package/dist/components/playground/PipelineKanbanView.svelte +2 -2
- package/dist/components/playground/PipelineTableView.svelte +2 -2
- package/dist/helpers/workflowEditorHelper.js +4 -5
- package/dist/messages/index.d.ts +1 -1
- package/dist/messages/index.js +1 -1
- package/dist/openapi/v1/openapi.yaml +2 -2
- package/dist/registry/nodeComponentRegistry.d.ts +2 -1
- package/dist/services/dynamicSchemaService.d.ts +2 -2
- package/dist/services/dynamicSchemaService.js +8 -8
- package/dist/skins/drafter.js +41 -28
- package/dist/stores/playgroundStore.svelte.js +1 -1
- package/dist/stores/workflowStore.svelte.js +0 -18
- package/dist/styles/base.css +247 -5
- package/dist/styles/tokens.css +6 -0
- package/dist/svelte-app.js +68 -107
- package/dist/types/index.d.ts +6 -7
- package/dist/utils/connections.js +20 -56
- package/dist/utils/nodeIds.d.ts +1 -1
- package/dist/utils/nodeIds.js +1 -1
- package/dist/utils/nodeSwap.js +3 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0-beta.5] - 2026-06-20
|
|
11
|
+
|
|
12
|
+
Fifth 2.0 beta, published under the npm `beta` dist-tag (`npm install @flowdrop/flowdrop@beta`). `latest` remains 1.15.0 until 2.0.0 GA. This release is a node-identity cleanup: node components now derive their instance id from the canonical `id` prop SvelteFlow already passes, dropping the duplicate `data.nodeId` copy and its load-time healing, and the node-_type_ entity id is renamed `metadata.id` → `metadata.node_type_id` to disambiguate it from the instance id. Six node components were also moved off the type-erasing `$props<T>()` onto a typed `interface Props`, which surfaced and fixed latent config-access errors in `ToolNode`.
|
|
13
|
+
|
|
14
|
+
### Breaking Changes
|
|
15
|
+
|
|
16
|
+
- **Node-type entity id renamed `metadata.id` → `metadata.node_type_id`.** The node-type entity id collided conceptually with the node _instance_ id; it is now `metadata.node_type_id` to disambiguate the two. It is a server-owned wire field, so the library preserves the snake_case name as a non-opinionated passthrough — adapters, the command type-map, connection validation, and the dynamic-schema paths were all updated to match. Consumers reading `metadata.id` for the node type must read `metadata.node_type_id`.
|
|
17
|
+
- **Dropped the duplicate `data.nodeId` field.** Node components now derive their id from the canonical `id` prop that SvelteFlow already passes to every node, instead of a copy stored in `data.nodeId`. Handle ids, config-open callbacks, and status overlays read `id` directly. The field and its load-time "healing" are gone, so an intact graph can no longer look corrupted from a missing copy.
|
|
18
|
+
|
|
19
|
+
### Changed (internal — no API change)
|
|
20
|
+
|
|
21
|
+
- **Six node components typed properly.** The node components that used `$props<T>()` — which returns `any` and silently disabled type-checking — now declare an `interface Props` with `let props: Props = $props()`. This surfaced and fixed latent config-access errors in `ToolNode`.
|
|
22
|
+
|
|
23
|
+
## [2.0.0-beta.4] - 2026-06-13
|
|
24
|
+
|
|
25
|
+
Fourth 2.0 beta, published under the npm `beta` dist-tag (`npm install @flowdrop/flowdrop@beta`). `latest` remains 1.15.0 until 2.0.0 GA. This release is mostly an internal design-system consolidation pass — new shared `Button` / `IconButton` / `Input` / `Select` / `Textarea` primitives route every control through the `base.css` class system as the single source of truth, so themes (including Drafter) style fields and buttons consistently. User-visible changes are a navbar `end` snippet for embedding custom trailing controls, a new `--fd-control-radius` theming token, a range-slider fill fix, and opaque Drafter buttons. The archived Astro docs site is retired in favor of the Mintlify docs at `flowdrop.mintlify.app`.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **`end` snippet on the navbar.** A new `end` snippet prop renders custom trailing content in the navbar (before the settings gear), letting consuming apps inject their own controls — e.g. a theme toggle — without forking the navbar.
|
|
30
|
+
- **`--fd-control-radius` theming token.** Form controls (inputs, selects, textareas, the array action buttons, fieldset/checkbox-group/config-form borders) now read their corner radius from `--fd-control-radius`, so themes can tighten field corners independently of cards and panels. Drafter sets it to `2px` for its crisp blueprint look; other themes inherit the existing radius.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **Range slider fill now tracks the thumb center.** The colored fill measured 0–100% of the raw track width while the browser keeps the thumb inside the track, so the fill and thumb drifted apart — worst at the extremes and visible on load. The fill now mirrors the thumb's actual travel (`thumb/2` to `100% − thumb/2`) via a new `--fd-range-fill` calc, with the thumb size tokenized.
|
|
35
|
+
- **Drafter buttons are opaque and flat.** Several buttons in the Drafter skin (file-upload node config, canvas toggles) rendered with translucent backgrounds and read as see-through; they are now flat and opaque.
|
|
36
|
+
|
|
37
|
+
### Changed (internal — no API change)
|
|
38
|
+
|
|
39
|
+
- **Shared form-control and button primitives.** New typed `Button`, `IconButton`, `Input`, `Select`, and `Textarea` wrappers sit over the existing `.flowdrop-btn*` / `.flowdrop-input*` / `.flowdrop-btn--icon*` classes in `base.css`, making it the single source for control look (`:disabled` is the only muted state). `ThemeToggle`, the config form, sidebar search, the node swap picker, `FormArray` move/delete buttons, `InputCollector`, and the playground inputs were migrated onto them, dropping their hand-rolled, duplicated CSS. The primitives are internal (not exported), so their APIs aren't frozen before GA; public component prop APIs (e.g. `ThemeToggle`) are unchanged.
|
|
40
|
+
- **`EditorStatusBar` extracted onto the shared button system.** The endpoint-error banner — previously inline in `App.svelte` with its own button CSS and a centered `80rem` layout — is now an `EditorStatusBar` component that routes actions through the `Button` primitive, spans the editor chrome flush, and uses a proper `CloseIcon` for dismissal. Added Storybook stories for `Button` and `EditorStatusBar`.
|
|
41
|
+
- **Navbar control heights from one token.** Three hardcoded `height: 2.5rem` rules now use `var(--fd-size-btn-min)`, sharing a single source of truth with `.flowdrop-btn`.
|
|
42
|
+
- **Extracted shared logic flagged by the duplication review.** `connections.ts` gains a shared `detectCycles()` (used by both `hasCycles` / `hasInvalidCycles`); `svelte-app.ts` extracts a `configureInstance()` shared by `mountFlowDropApp` / `mountWorkflowEditor`; and `FormField` / `FormFieldLight` share a `resolveBaseFieldType()` for the basic-type switch (with a unit test pinning resolution order) while keeping the deliberate full/light rendering split for the bundle-guard contract. No behavioral change.
|
|
43
|
+
|
|
44
|
+
### Docs
|
|
45
|
+
|
|
46
|
+
- **Retired the archived Astro/Starlight docs site (`apps/docs`)** and its docker-publish workflow; the Mintlify site at `flowdrop.mintlify.app` is canonical. All live documentation links (README, AGENTS, quick-start, i18n guide, API README, dev playground navbar, docker-playground landing, interrupt-feature doc) were repointed there. `apps/api-docs` is kept only to back the `api:lint` / `api:bundle` tooling and CI. Corrected the local dev-server port reference to the recommended Express demo.
|
|
47
|
+
|
|
10
48
|
## [2.0.0-beta.3] - 2026-06-12
|
|
11
49
|
|
|
12
50
|
Third 2.0 beta, published under the npm `beta` dist-tag (`npm install @flowdrop/flowdrop@beta`). `latest` remains 1.15.0 until 2.0.0 GA. This release finishes the navbar/theme defaults pass started in beta.2 (navbar opt-in everywhere, light as the default theme, the FlowDrop wordmark in the header), adds the **Drafter** blueprint theme with per-theme canvas grids, and lands a keyboard-navigation and focus-ring overhaul alongside the 20px node-grid alignment.
|
package/README.md
CHANGED
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
<p align="center">
|
|
26
|
-
<a href="https://
|
|
26
|
+
<a href="https://flowdrop.mintlify.app/docs/quickstart">Quickstart</a> •
|
|
27
27
|
<a href="#features">Features</a> •
|
|
28
28
|
<a href="#integration">Integration</a> •
|
|
29
|
-
<a href="https://
|
|
29
|
+
<a href="https://flowdrop.mintlify.app">Docs</a>
|
|
30
30
|
</p>
|
|
31
31
|
|
|
32
32
|
<p align="center">
|
|
@@ -139,7 +139,7 @@ Every user-facing string flows through a typed `Messages` tree. Pass a callback
|
|
|
139
139
|
<App messages={() => ({ form: { schema: { save: 'Apply' } } })} />
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
Wire the callback to your i18n library (paraglide-js, sveltekit-i18n, etc.) — locale changes propagate automatically. See the [i18n & Custom Messages guide](https://
|
|
142
|
+
Wire the callback to your i18n library (paraglide-js, sveltekit-i18n, etc.) — locale changes propagate automatically. See the [i18n & Custom Messages guide](https://flowdrop.mintlify.app/guides/i18n) for the full shape and a paraglide-js worked example.
|
|
143
143
|
|
|
144
144
|
## Sub-Module Exports
|
|
145
145
|
|
|
@@ -283,8 +283,8 @@ Runtime configuration means you build once and deploy to staging, production, or
|
|
|
283
283
|
|
|
284
284
|
| Resource | Description |
|
|
285
285
|
| -------------------------------------------------------------------------------------------- | ------------------------ |
|
|
286
|
-
| [QUICK_START.md](https://
|
|
287
|
-
| [API Documentation](https://
|
|
286
|
+
| [QUICK_START.md](https://flowdrop.mintlify.app/docs/quickstart) | Get running in 5 minutes |
|
|
287
|
+
| [API Documentation](https://flowdrop.mintlify.app/api-reference/introduction) | REST API specification |
|
|
288
288
|
| [CHANGELOG.md](https://github.com/flowdrop-io/flowdrop/blob/main/libs/flowdrop/CHANGELOG.md) | Version history |
|
|
289
289
|
|
|
290
290
|
## Development
|
|
@@ -46,7 +46,7 @@ export class WorkflowAdapter {
|
|
|
46
46
|
* Add a node to a workflow
|
|
47
47
|
*/
|
|
48
48
|
addNode(workflow, nodeType, position, config) {
|
|
49
|
-
const metadata = this.nodeTypes.find((nt) => nt.
|
|
49
|
+
const metadata = this.nodeTypes.find((nt) => nt.node_type_id === nodeType);
|
|
50
50
|
if (!metadata) {
|
|
51
51
|
throw new Error(`Node type '${nodeType}' not found`);
|
|
52
52
|
}
|
|
@@ -248,7 +248,7 @@ export class WorkflowAdapter {
|
|
|
248
248
|
description: svelteFlowWorkflow.description,
|
|
249
249
|
nodes: svelteFlowWorkflow.nodes.map((node) => ({
|
|
250
250
|
id: node.id,
|
|
251
|
-
type: node.data.metadata.
|
|
251
|
+
type: node.data.metadata.node_type_id,
|
|
252
252
|
position: node.position,
|
|
253
253
|
data: {
|
|
254
254
|
label: node.data.label,
|
|
@@ -282,8 +282,7 @@ export class WorkflowAdapter {
|
|
|
282
282
|
data: {
|
|
283
283
|
label: node.data.label,
|
|
284
284
|
config: node.data.config,
|
|
285
|
-
metadata: node.data.metadata
|
|
286
|
-
nodeId: node.id
|
|
285
|
+
metadata: node.data.metadata
|
|
287
286
|
}
|
|
288
287
|
})),
|
|
289
288
|
edges: workflow.edges.map((edge) => ({
|
|
@@ -326,7 +325,7 @@ export class WorkflowAdapter {
|
|
|
326
325
|
const nodeTypeCounts = new Map();
|
|
327
326
|
cloned.nodes.forEach((node) => {
|
|
328
327
|
const oldId = node.id;
|
|
329
|
-
const nodeTypeId = node.data.metadata.
|
|
328
|
+
const nodeTypeId = node.data.metadata.node_type_id;
|
|
330
329
|
// Get the current count for this node type
|
|
331
330
|
const currentCount = nodeTypeCounts.get(nodeTypeId) || 0;
|
|
332
331
|
const newCount = currentCount + 1;
|
|
@@ -266,7 +266,7 @@ export class AgentSpecAdapter {
|
|
|
266
266
|
metadata: {
|
|
267
267
|
'flowdrop:position': node.position,
|
|
268
268
|
'flowdrop:node_id': node.id,
|
|
269
|
-
'flowdrop:node_type_id': node.data.metadata.
|
|
269
|
+
'flowdrop:node_type_id': node.data.metadata.node_type_id
|
|
270
270
|
}
|
|
271
271
|
};
|
|
272
272
|
// Add type-specific attributes from config
|
|
@@ -362,7 +362,7 @@ export class AgentSpecAdapter {
|
|
|
362
362
|
return ext;
|
|
363
363
|
}
|
|
364
364
|
// Infer from FlowDrop node type ID
|
|
365
|
-
const fromId = extractComponentType(node.data.metadata.
|
|
365
|
+
const fromId = extractComponentType(node.data.metadata.node_type_id);
|
|
366
366
|
if (fromId)
|
|
367
367
|
return fromId;
|
|
368
368
|
// Infer from FlowDrop visual type + category
|
|
@@ -404,7 +404,7 @@ export class AgentSpecAdapter {
|
|
|
404
404
|
const nodeTypeId = asNode.metadata?.['flowdrop:node_type_id'] ||
|
|
405
405
|
`${AGENTSPEC_NAMESPACE}.${asNode.component_type}`;
|
|
406
406
|
const metadata = {
|
|
407
|
-
|
|
407
|
+
node_type_id: nodeTypeId,
|
|
408
408
|
name: defaults.defaultName,
|
|
409
409
|
type: defaults.visualType,
|
|
410
410
|
description: asNode.description || defaults.defaultDescription,
|
|
@@ -30,7 +30,7 @@ function buildRegistry() {
|
|
|
30
30
|
registry.set('start_node', {
|
|
31
31
|
componentType: 'start_node',
|
|
32
32
|
metadata: {
|
|
33
|
-
|
|
33
|
+
node_type_id: `${AGENTSPEC_NS}.start_node`,
|
|
34
34
|
name: 'Start',
|
|
35
35
|
type: 'terminal',
|
|
36
36
|
description: 'Flow entry point. Defines the initial inputs for the flow.',
|
|
@@ -55,7 +55,7 @@ function buildRegistry() {
|
|
|
55
55
|
registry.set('end_node', {
|
|
56
56
|
componentType: 'end_node',
|
|
57
57
|
metadata: {
|
|
58
|
-
|
|
58
|
+
node_type_id: `${AGENTSPEC_NS}.end_node`,
|
|
59
59
|
name: 'End',
|
|
60
60
|
type: 'terminal',
|
|
61
61
|
description: 'Flow exit point. Defines the final outputs of the flow.',
|
|
@@ -80,7 +80,7 @@ function buildRegistry() {
|
|
|
80
80
|
registry.set('llm_node', {
|
|
81
81
|
componentType: 'llm_node',
|
|
82
82
|
metadata: {
|
|
83
|
-
|
|
83
|
+
node_type_id: `${AGENTSPEC_NS}.llm_node`,
|
|
84
84
|
name: 'LLM',
|
|
85
85
|
type: 'default',
|
|
86
86
|
description: 'Generate text using a large language model with configurable prompts.',
|
|
@@ -145,7 +145,7 @@ function buildRegistry() {
|
|
|
145
145
|
registry.set('branching_node', {
|
|
146
146
|
componentType: 'branching_node',
|
|
147
147
|
metadata: {
|
|
148
|
-
|
|
148
|
+
node_type_id: `${AGENTSPEC_NS}.branching_node`,
|
|
149
149
|
name: 'Branch',
|
|
150
150
|
type: 'gateway',
|
|
151
151
|
description: 'Route execution to different paths based on conditions.',
|
|
@@ -213,7 +213,7 @@ function buildRegistry() {
|
|
|
213
213
|
registry.set('tool_node', {
|
|
214
214
|
componentType: 'tool_node',
|
|
215
215
|
metadata: {
|
|
216
|
-
|
|
216
|
+
node_type_id: `${AGENTSPEC_NS}.tool_node`,
|
|
217
217
|
name: 'Tool',
|
|
218
218
|
type: 'tool',
|
|
219
219
|
description: 'Execute a tool function with inputs and receive outputs.',
|
|
@@ -271,7 +271,7 @@ function buildRegistry() {
|
|
|
271
271
|
registry.set('api_node', {
|
|
272
272
|
componentType: 'api_node',
|
|
273
273
|
metadata: {
|
|
274
|
-
|
|
274
|
+
node_type_id: `${AGENTSPEC_NS}.api_node`,
|
|
275
275
|
name: 'API Call',
|
|
276
276
|
type: 'default',
|
|
277
277
|
description: 'Make an HTTP API call with configurable endpoint, method, and headers.',
|
|
@@ -342,7 +342,7 @@ function buildRegistry() {
|
|
|
342
342
|
registry.set('agent_node', {
|
|
343
343
|
componentType: 'agent_node',
|
|
344
344
|
metadata: {
|
|
345
|
-
|
|
345
|
+
node_type_id: `${AGENTSPEC_NS}.agent_node`,
|
|
346
346
|
name: 'Agent',
|
|
347
347
|
type: 'default',
|
|
348
348
|
description: 'Run a multi-round agent conversation within the flow.',
|
|
@@ -400,7 +400,7 @@ function buildRegistry() {
|
|
|
400
400
|
registry.set('flow_node', {
|
|
401
401
|
componentType: 'flow_node',
|
|
402
402
|
metadata: {
|
|
403
|
-
|
|
403
|
+
node_type_id: `${AGENTSPEC_NS}.flow_node`,
|
|
404
404
|
name: 'Sub-Flow',
|
|
405
405
|
type: 'simple',
|
|
406
406
|
description: 'Execute another flow as a sub-routine within this flow.',
|
|
@@ -451,7 +451,7 @@ function buildRegistry() {
|
|
|
451
451
|
registry.set('map_node', {
|
|
452
452
|
componentType: 'map_node',
|
|
453
453
|
metadata: {
|
|
454
|
-
|
|
454
|
+
node_type_id: `${AGENTSPEC_NS}.map_node`,
|
|
455
455
|
name: 'Map',
|
|
456
456
|
type: 'default',
|
|
457
457
|
description: 'Apply a flow or operation to each item in a collection (map-reduce).',
|
|
@@ -76,7 +76,7 @@ function executeAddNode(command, context) {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
const position = command.position ?? computeAutoPosition(workflow.nodes);
|
|
79
|
-
const nodeId = generateNodeId(metadata.
|
|
79
|
+
const nodeId = generateNodeId(metadata.node_type_id, workflow.nodes);
|
|
80
80
|
const config = extractConfigDefaults(metadata.configSchema);
|
|
81
81
|
const node = {
|
|
82
82
|
id: nodeId,
|
|
@@ -86,8 +86,7 @@ function executeAddNode(command, context) {
|
|
|
86
86
|
data: {
|
|
87
87
|
label: metadata.name,
|
|
88
88
|
config,
|
|
89
|
-
metadata
|
|
90
|
-
nodeId
|
|
89
|
+
metadata
|
|
91
90
|
}
|
|
92
91
|
};
|
|
93
92
|
context.dispatch.addNode(node);
|
|
@@ -357,7 +356,7 @@ function executeInfo(command, context) {
|
|
|
357
356
|
const resultData = {
|
|
358
357
|
nodeId: shortId,
|
|
359
358
|
label: node.data.label ?? metadata?.name ?? '',
|
|
360
|
-
type: metadata?.
|
|
359
|
+
type: metadata?.node_type_id ? toShortTypeId(metadata.node_type_id) : '',
|
|
361
360
|
position: node.position,
|
|
362
361
|
config: node.data.config ?? {},
|
|
363
362
|
inputs,
|
|
@@ -578,7 +577,7 @@ function executeListNodes(context) {
|
|
|
578
577
|
const nodes = workflow.nodes.map((n) => ({
|
|
579
578
|
nodeId: toShortId(n.id),
|
|
580
579
|
label: n.data.label ?? n.data.metadata?.name ?? '',
|
|
581
|
-
type: n.data.metadata?.
|
|
580
|
+
type: n.data.metadata?.node_type_id ? toShortTypeId(n.data.metadata.node_type_id) : ''
|
|
582
581
|
}));
|
|
583
582
|
const resultData = { nodes };
|
|
584
583
|
return {
|
|
@@ -610,7 +609,7 @@ function executeListEdges(context) {
|
|
|
610
609
|
}
|
|
611
610
|
function executeListTypes(context) {
|
|
612
611
|
const types = context.nodeTypes.map((m) => ({
|
|
613
|
-
typeId: toShortTypeId(m.
|
|
612
|
+
typeId: toShortTypeId(m.node_type_id),
|
|
614
613
|
name: m.name,
|
|
615
614
|
category: m.category
|
|
616
615
|
}));
|
package/dist/commands/types.js
CHANGED
|
@@ -24,18 +24,18 @@ export function buildTypeMap(nodeTypes) {
|
|
|
24
24
|
const shortIdCounts = new Map();
|
|
25
25
|
// First pass: register full IDs and count short ID occurrences
|
|
26
26
|
for (const metadata of nodeTypes) {
|
|
27
|
-
map.set(metadata.
|
|
28
|
-
const dotIndex = metadata.
|
|
27
|
+
map.set(metadata.node_type_id, metadata);
|
|
28
|
+
const dotIndex = metadata.node_type_id.indexOf('.');
|
|
29
29
|
if (dotIndex !== -1) {
|
|
30
|
-
const shortId = metadata.
|
|
30
|
+
const shortId = metadata.node_type_id.substring(dotIndex + 1);
|
|
31
31
|
shortIdCounts.set(shortId, (shortIdCounts.get(shortId) ?? 0) + 1);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
// Second pass: register unique short IDs
|
|
35
35
|
for (const metadata of nodeTypes) {
|
|
36
|
-
const dotIndex = metadata.
|
|
36
|
+
const dotIndex = metadata.node_type_id.indexOf('.');
|
|
37
37
|
if (dotIndex !== -1) {
|
|
38
|
-
const shortId = metadata.
|
|
38
|
+
const shortId = metadata.node_type_id.substring(dotIndex + 1);
|
|
39
39
|
if (shortIdCounts.get(shortId) === 1) {
|
|
40
40
|
map.set(shortId, metadata);
|
|
41
41
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import WorkflowEditor from './WorkflowEditor.svelte';
|
|
11
11
|
import NodeSidebar from './NodeSidebar.svelte';
|
|
12
12
|
import CanvasIconButton from './CanvasIconButton.svelte';
|
|
13
|
+
import EditorStatusBar from './EditorStatusBar.svelte';
|
|
13
14
|
import MenuIcon from './icons/MenuIcon.svelte';
|
|
14
15
|
import MenuOpenIcon from './icons/MenuOpenIcon.svelte';
|
|
15
16
|
import ConfigForm from './ConfigForm.svelte';
|
|
@@ -411,8 +412,8 @@
|
|
|
411
412
|
if (propNodes && propNodes.length > 0) {
|
|
412
413
|
// Merge format-provided nodes with prop nodes (deduplicate by ID, props take priority)
|
|
413
414
|
const formatNodes = fd.formats.getAllFormatNodes();
|
|
414
|
-
const existingIds = new Set(propNodes.map((n) => n.
|
|
415
|
-
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.
|
|
415
|
+
const existingIds = new Set(propNodes.map((n) => n.node_type_id));
|
|
416
|
+
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.node_type_id));
|
|
416
417
|
nodes = [...propNodes, ...uniqueFormatNodes];
|
|
417
418
|
nodeTypesLoading = false;
|
|
418
419
|
return;
|
|
@@ -428,8 +429,8 @@
|
|
|
428
429
|
|
|
429
430
|
// Merge format-provided nodes with API nodes (deduplicate by ID, API takes priority)
|
|
430
431
|
const formatNodes = fd.formats.getAllFormatNodes();
|
|
431
|
-
const existingIds = new Set(fetchedNodes.map((n) => n.
|
|
432
|
-
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.
|
|
432
|
+
const existingIds = new Set(fetchedNodes.map((n) => n.node_type_id));
|
|
433
|
+
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.node_type_id));
|
|
433
434
|
nodes = [...fetchedNodes, ...uniqueFormatNodes];
|
|
434
435
|
error = null;
|
|
435
436
|
nodeTypesLoading = false;
|
|
@@ -1212,53 +1213,21 @@
|
|
|
1212
1213
|
{/snippet}
|
|
1213
1214
|
|
|
1214
1215
|
<!-- Main Content: Workflow Editor with Error Status -->
|
|
1215
|
-
<!-- Status Display: aria-live announces API errors dynamically without requiring focus -->
|
|
1216
1216
|
{#if error}
|
|
1217
|
-
<
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
<button
|
|
1232
|
-
class="flowdrop-btn flowdrop-btn--sm flowdrop-btn--outline"
|
|
1233
|
-
onclick={() => {
|
|
1234
|
-
const defaultUrl = '/api/flowdrop';
|
|
1235
|
-
const newUrl = prompt('Enter Backend API URL:', defaultUrl);
|
|
1236
|
-
if (newUrl) {
|
|
1237
|
-
configureApi(createEndpointConfig(newUrl));
|
|
1238
|
-
fetchNodeTypes();
|
|
1239
|
-
}
|
|
1240
|
-
}}
|
|
1241
|
-
type="button"
|
|
1242
|
-
>
|
|
1243
|
-
Set API URL
|
|
1244
|
-
</button>
|
|
1245
|
-
<button
|
|
1246
|
-
class="flowdrop-btn flowdrop-btn--sm flowdrop-btn--outline"
|
|
1247
|
-
onclick={testApiConnection}
|
|
1248
|
-
type="button"
|
|
1249
|
-
>
|
|
1250
|
-
Test API
|
|
1251
|
-
</button>
|
|
1252
|
-
<button
|
|
1253
|
-
class="flowdrop-btn flowdrop-btn--ghost flowdrop-btn--sm"
|
|
1254
|
-
onclick={() => (error = null)}
|
|
1255
|
-
type="button"
|
|
1256
|
-
>
|
|
1257
|
-
✕
|
|
1258
|
-
</button>
|
|
1259
|
-
</div>
|
|
1260
|
-
</div>
|
|
1261
|
-
</div>
|
|
1217
|
+
<EditorStatusBar
|
|
1218
|
+
{error}
|
|
1219
|
+
onRetry={retryLoad}
|
|
1220
|
+
onSetApiUrl={() => {
|
|
1221
|
+
const defaultUrl = '/api/flowdrop';
|
|
1222
|
+
const newUrl = prompt('Enter Backend API URL:', defaultUrl);
|
|
1223
|
+
if (newUrl) {
|
|
1224
|
+
configureApi(createEndpointConfig(newUrl));
|
|
1225
|
+
fetchNodeTypes();
|
|
1226
|
+
}
|
|
1227
|
+
}}
|
|
1228
|
+
onTestApi={testApiConnection}
|
|
1229
|
+
onDismiss={() => (error = null)}
|
|
1230
|
+
/>
|
|
1262
1231
|
{/if}
|
|
1263
1232
|
|
|
1264
1233
|
<!-- Main Editor Area -->
|
|
@@ -1314,106 +1283,6 @@
|
|
|
1314
1283
|
.flowdrop-root {
|
|
1315
1284
|
display: contents;
|
|
1316
1285
|
}
|
|
1317
|
-
/* Status bar styles */
|
|
1318
|
-
.flowdrop-status {
|
|
1319
|
-
background-color: var(--fd-info-muted);
|
|
1320
|
-
border-bottom: 1px solid var(--fd-info);
|
|
1321
|
-
padding: 1rem;
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
.flowdrop-status--error {
|
|
1325
|
-
background-color: var(--fd-error-muted);
|
|
1326
|
-
border-bottom: 1px solid var(--fd-error);
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
.flowdrop-status__content {
|
|
1330
|
-
max-width: 80rem;
|
|
1331
|
-
margin: 0 auto;
|
|
1332
|
-
display: flex;
|
|
1333
|
-
align-items: center;
|
|
1334
|
-
justify-content: space-between;
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
.flowdrop-status__indicator {
|
|
1338
|
-
width: 0.5rem;
|
|
1339
|
-
height: 0.5rem;
|
|
1340
|
-
border-radius: 50%;
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
.flowdrop-status__indicator--error {
|
|
1344
|
-
background-color: var(--fd-error);
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
/* Button styles */
|
|
1348
|
-
.flowdrop-btn {
|
|
1349
|
-
padding: 0.375rem 0.75rem;
|
|
1350
|
-
border-radius: var(--fd-radius-md);
|
|
1351
|
-
font-size: 0.75rem;
|
|
1352
|
-
font-weight: 500;
|
|
1353
|
-
cursor: pointer;
|
|
1354
|
-
border: 1px solid transparent;
|
|
1355
|
-
transition: all var(--fd-transition-fast);
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
.flowdrop-btn--sm {
|
|
1359
|
-
padding: 0.25rem 0.5rem;
|
|
1360
|
-
font-size: 0.625rem;
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
.flowdrop-btn--outline {
|
|
1364
|
-
background-color: transparent;
|
|
1365
|
-
border-color: var(--fd-border);
|
|
1366
|
-
color: var(--fd-foreground);
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
.flowdrop-btn--outline:hover {
|
|
1370
|
-
background-color: var(--fd-muted);
|
|
1371
|
-
border-color: var(--fd-border-strong);
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
.flowdrop-btn--primary {
|
|
1375
|
-
background-color: var(--fd-primary);
|
|
1376
|
-
border-color: var(--fd-primary);
|
|
1377
|
-
color: var(--fd-primary-foreground);
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
.flowdrop-btn--primary:hover {
|
|
1381
|
-
background-color: var(--fd-primary-hover);
|
|
1382
|
-
border-color: var(--fd-primary-hover);
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
.flowdrop-btn--ghost {
|
|
1386
|
-
background-color: transparent;
|
|
1387
|
-
border-color: transparent;
|
|
1388
|
-
color: var(--fd-muted-foreground);
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
.flowdrop-btn--ghost:hover {
|
|
1392
|
-
background-color: var(--fd-muted);
|
|
1393
|
-
color: var(--fd-foreground);
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
/* Utility classes */
|
|
1397
|
-
.flowdrop-flex {
|
|
1398
|
-
display: flex;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
.flowdrop-gap--2 {
|
|
1402
|
-
gap: 0.5rem;
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
.flowdrop-gap--3 {
|
|
1406
|
-
gap: 0.75rem;
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
.flowdrop-text--sm {
|
|
1410
|
-
font-size: 0.875rem;
|
|
1411
|
-
line-height: 1.25rem;
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
.flowdrop-font--medium {
|
|
1415
|
-
font-weight: 500;
|
|
1416
|
-
}
|
|
1417
1286
|
|
|
1418
1287
|
/* Floating sidebar toggle button — placement only; visuals live in CanvasIconButton */
|
|
1419
1288
|
:global(.flowdrop-sidebar-fab) {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { ComponentProps } from 'svelte';
|
|
3
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
4
|
+
import Button from './Button.svelte';
|
|
5
|
+
import { fn } from 'storybook/test';
|
|
6
|
+
|
|
7
|
+
const { Story } = defineMeta({
|
|
8
|
+
title: 'Display/Button',
|
|
9
|
+
component: Button,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: 'centered'
|
|
13
|
+
},
|
|
14
|
+
argTypes: {
|
|
15
|
+
variant: {
|
|
16
|
+
control: { type: 'select' },
|
|
17
|
+
options: ['primary', 'secondary', 'outline', 'ghost']
|
|
18
|
+
},
|
|
19
|
+
size: {
|
|
20
|
+
control: { type: 'select' },
|
|
21
|
+
options: ['sm', 'md', 'lg']
|
|
22
|
+
},
|
|
23
|
+
disabled: { control: 'boolean' }
|
|
24
|
+
},
|
|
25
|
+
args: {
|
|
26
|
+
variant: 'secondary',
|
|
27
|
+
size: 'md',
|
|
28
|
+
disabled: false,
|
|
29
|
+
onclick: fn()
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<!-- Arg-driven template so the Controls panel can tweak variant/size/disabled live. -->
|
|
35
|
+
{#snippet template(args: Omit<ComponentProps<typeof Button>, 'children'>)}
|
|
36
|
+
<Button {...args}>Button</Button>
|
|
37
|
+
{/snippet}
|
|
38
|
+
|
|
39
|
+
<Story name="Primary" args={{ variant: 'primary' }} {template} />
|
|
40
|
+
|
|
41
|
+
<Story name="Secondary" args={{ variant: 'secondary' }} {template} />
|
|
42
|
+
|
|
43
|
+
<Story name="Outline" args={{ variant: 'outline' }} {template} />
|
|
44
|
+
|
|
45
|
+
<Story name="Ghost" args={{ variant: 'ghost' }} {template} />
|
|
46
|
+
|
|
47
|
+
<Story name="Disabled" args={{ variant: 'primary', disabled: true }} {template} />
|
|
48
|
+
|
|
49
|
+
<!-- Static showcases comparing every variant / size side by side. -->
|
|
50
|
+
<Story name="Variants" asChild>
|
|
51
|
+
<div style="display: flex; gap: 0.75rem; align-items: center;">
|
|
52
|
+
<Button variant="primary">Primary</Button>
|
|
53
|
+
<Button variant="secondary">Secondary</Button>
|
|
54
|
+
<Button variant="outline">Outline</Button>
|
|
55
|
+
<Button variant="ghost">Ghost</Button>
|
|
56
|
+
</div>
|
|
57
|
+
</Story>
|
|
58
|
+
|
|
59
|
+
<Story name="Sizes" asChild>
|
|
60
|
+
<div style="display: flex; gap: 0.75rem; align-items: center;">
|
|
61
|
+
<Button variant="primary" size="sm">Small</Button>
|
|
62
|
+
<Button variant="primary" size="md">Medium</Button>
|
|
63
|
+
<Button variant="primary" size="lg">Large</Button>
|
|
64
|
+
</div>
|
|
65
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Button from './Button.svelte';
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const Button: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type Button = InstanceType<typeof Button>;
|
|
19
|
+
export default Button;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Button — typed wrapper over the shared `.flowdrop-btn` system (base.css).
|
|
3
|
+
|
|
4
|
+
All button styling (variants, sizes, the --fd-size-btn-min height token and the
|
|
5
|
+
centralized focus ring) lives in base.css. This component is the ergonomic,
|
|
6
|
+
type-safe entry point so callers pick `variant`/`size` instead of hand-writing
|
|
7
|
+
class strings — the single place new buttons should route through.
|
|
8
|
+
|
|
9
|
+
Internal for now (not exported from any public entry) so the API isn't frozen
|
|
10
|
+
before GA. Existing hand-rolled buttons migrate onto it incrementally.
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import type { Snippet } from 'svelte';
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
/** Visual style — maps to `.flowdrop-btn--{variant}` in base.css */
|
|
18
|
+
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
|
|
19
|
+
/** Size — `md` is the base `.flowdrop-btn`; `sm`/`lg` add a modifier */
|
|
20
|
+
size?: 'sm' | 'md' | 'lg';
|
|
21
|
+
/** Native button type */
|
|
22
|
+
type?: 'button' | 'submit' | 'reset';
|
|
23
|
+
/** Tooltip text */
|
|
24
|
+
title?: string;
|
|
25
|
+
/** Accessible label (use when the button is icon-only) */
|
|
26
|
+
ariaLabel?: string;
|
|
27
|
+
/** Disabled state */
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
/** Extra classes appended to the root button */
|
|
30
|
+
class?: string;
|
|
31
|
+
/** Click handler */
|
|
32
|
+
onclick?: (event: MouseEvent) => void;
|
|
33
|
+
/** Button contents (icon, label, or both) */
|
|
34
|
+
children: Snippet;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
variant = 'secondary',
|
|
39
|
+
size = 'md',
|
|
40
|
+
type = 'button',
|
|
41
|
+
title,
|
|
42
|
+
ariaLabel,
|
|
43
|
+
disabled = false,
|
|
44
|
+
class: className = '',
|
|
45
|
+
onclick,
|
|
46
|
+
children
|
|
47
|
+
}: Props = $props();
|
|
48
|
+
|
|
49
|
+
// 'md' is the unmodified base class; only 'sm'/'lg' need a size modifier.
|
|
50
|
+
const sizeClass = $derived(size === 'md' ? '' : `flowdrop-btn--${size}`);
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<button
|
|
54
|
+
class="flowdrop-btn flowdrop-btn--{variant} {sizeClass} {className}"
|
|
55
|
+
{type}
|
|
56
|
+
{title}
|
|
57
|
+
{disabled}
|
|
58
|
+
aria-label={ariaLabel}
|
|
59
|
+
{onclick}
|
|
60
|
+
>
|
|
61
|
+
{@render children()}
|
|
62
|
+
</button>
|