@flowdrop/flowdrop 2.0.0-beta.1 → 2.0.0-beta.3

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 (149) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/MIGRATION-2.0.md +173 -3
  3. package/dist/api/enhanced-client.js +6 -11
  4. package/dist/components/App.svelte +22 -45
  5. package/dist/components/App.svelte.d.ts +2 -7
  6. package/dist/components/CanvasIconButton.svelte +76 -0
  7. package/dist/components/CanvasIconButton.svelte.d.ts +18 -0
  8. package/dist/components/ConfigForm.svelte +6 -21
  9. package/dist/components/ConfigPanel.svelte +4 -3
  10. package/dist/components/LogoWordmark.svelte +113 -0
  11. package/dist/components/LogoWordmark.svelte.d.ts +26 -0
  12. package/dist/components/Navbar.svelte +8 -59
  13. package/dist/components/NodeSidebar.svelte +4 -11
  14. package/dist/components/NodeSwapPicker.svelte +0 -2
  15. package/dist/components/PipelineStatus.svelte +6 -1
  16. package/dist/components/PipelineStatus.svelte.d.ts +3 -0
  17. package/dist/components/PortMappingRow.svelte +0 -2
  18. package/dist/components/SchemaForm.svelte +4 -14
  19. package/dist/components/SettingsModal.svelte +0 -5
  20. package/dist/components/SettingsPanel.svelte +2 -6
  21. package/dist/components/ThemeToggle.svelte +0 -5
  22. package/dist/components/UniversalNode.svelte +32 -1
  23. package/dist/components/WorkflowEditor.svelte +66 -52
  24. package/dist/components/WorkflowEditor.svelte.d.ts +21 -0
  25. package/dist/components/chat/AIChatPanel.svelte +7 -2
  26. package/dist/components/console/ConsoleAutocomplete.svelte +1 -1
  27. package/dist/components/console/ConsoleOutput.svelte +2 -2
  28. package/dist/components/form/FormArray.svelte +0 -16
  29. package/dist/components/form/FormAutocomplete.svelte +18 -15
  30. package/dist/components/form/FormCheckboxGroup.svelte +0 -4
  31. package/dist/components/form/FormCodeEditor.svelte +9 -7
  32. package/dist/components/form/FormFieldLight.svelte +33 -4
  33. package/dist/components/form/FormFieldLight.svelte.d.ts +12 -0
  34. package/dist/components/form/FormMarkdownEditor.svelte +8 -5
  35. package/dist/components/form/FormNumberField.svelte +0 -4
  36. package/dist/components/form/FormRangeField.svelte +1 -20
  37. package/dist/components/form/FormSelect.svelte +10 -6
  38. package/dist/components/form/FormTemplateEditor.svelte +6 -4
  39. package/dist/components/form/FormTextField.svelte +10 -6
  40. package/dist/components/form/FormTextarea.svelte +10 -6
  41. package/dist/components/form/FormToggle.svelte +0 -4
  42. package/dist/components/form/FormUISchemaRenderer.svelte +3 -1
  43. package/dist/components/icons/CommandLineIcon.svelte +15 -0
  44. package/dist/components/icons/CommandLineIcon.svelte.d.ts +26 -0
  45. package/dist/components/icons/MenuIcon.svelte +4 -0
  46. package/dist/components/icons/MenuIcon.svelte.d.ts +26 -0
  47. package/dist/components/icons/MenuOpenIcon.svelte +6 -0
  48. package/dist/components/icons/MenuOpenIcon.svelte.d.ts +26 -0
  49. package/dist/components/interrupt/ChoicePrompt.svelte +0 -10
  50. package/dist/components/interrupt/ConfirmationPrompt.svelte +0 -5
  51. package/dist/components/interrupt/InterruptBubble.svelte +11 -12
  52. package/dist/components/interrupt/ReviewPrompt.svelte +0 -20
  53. package/dist/components/interrupt/TextInputPrompt.svelte +0 -6
  54. package/dist/components/layouts/MainLayout.svelte +4 -5
  55. package/dist/components/nodes/AtomNode.svelte +46 -34
  56. package/dist/components/nodes/GatewayNode.svelte +91 -99
  57. package/dist/components/nodes/IdeaNode.svelte +62 -90
  58. package/dist/components/nodes/NodeConfigButton.svelte +86 -0
  59. package/dist/components/nodes/NodeConfigButton.svelte.d.ts +15 -0
  60. package/dist/components/nodes/NotesNode.svelte +70 -81
  61. package/dist/components/nodes/SimpleNode.svelte +28 -78
  62. package/dist/components/nodes/SquareNode.svelte +79 -109
  63. package/dist/components/nodes/TerminalNode.svelte +28 -86
  64. package/dist/components/nodes/ToolNode.svelte +82 -95
  65. package/dist/components/nodes/WorkflowNode.svelte +91 -100
  66. package/dist/components/playground/ChatInput.svelte +0 -1
  67. package/dist/components/playground/InputCollector.svelte +0 -2
  68. package/dist/components/playground/PipelineKanbanView.svelte +4 -2
  69. package/dist/components/playground/PipelineKanbanView.svelte.d.ts +2 -0
  70. package/dist/components/playground/PipelinePanel.svelte +20 -3
  71. package/dist/components/playground/PipelinePanel.svelte.d.ts +2 -0
  72. package/dist/components/playground/PipelineTableView.svelte +4 -2
  73. package/dist/components/playground/PipelineTableView.svelte.d.ts +2 -0
  74. package/dist/components/playground/Playground.svelte +76 -25
  75. package/dist/components/playground/Playground.svelte.d.ts +3 -0
  76. package/dist/components/playground/PlaygroundApp.svelte +6 -1
  77. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  78. package/dist/components/playground/PlaygroundModal.svelte +5 -0
  79. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  80. package/dist/components/playground/PlaygroundStudio.svelte +7 -6
  81. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  82. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +2 -1
  83. package/dist/components/playground/pipelineViewUtils.svelte.js +2 -2
  84. package/dist/config/endpoints.d.ts +23 -0
  85. package/dist/config/endpoints.js +28 -0
  86. package/dist/core/index.d.ts +1 -2
  87. package/dist/core/index.js +2 -6
  88. package/dist/display/index.d.ts +6 -1
  89. package/dist/display/index.js +9 -1
  90. package/dist/editor/index.d.ts +1 -1
  91. package/dist/editor/index.js +1 -1
  92. package/dist/form/full.d.ts +2 -1
  93. package/dist/form/full.js +4 -1
  94. package/dist/form/index.d.ts +0 -1
  95. package/dist/form/index.js +3 -2
  96. package/dist/helpers/workflowEditorHelper.d.ts +4 -2
  97. package/dist/helpers/workflowEditorHelper.js +4 -3
  98. package/dist/playground/index.d.ts +2 -2
  99. package/dist/playground/index.js +2 -2
  100. package/dist/playground/mount.d.ts +19 -5
  101. package/dist/playground/mount.js +16 -8
  102. package/dist/registry/builtinNodeTypes.d.ts +53 -0
  103. package/dist/registry/builtinNodeTypes.js +67 -0
  104. package/dist/registry/builtinNodes.d.ts +2 -39
  105. package/dist/registry/builtinNodes.js +6 -53
  106. package/dist/services/agentSpecExecutionService.d.ts +0 -2
  107. package/dist/services/agentSpecExecutionService.js +0 -2
  108. package/dist/services/apiVariableService.js +12 -26
  109. package/dist/services/categoriesApi.js +3 -6
  110. package/dist/services/chatService.d.ts +4 -3
  111. package/dist/services/chatService.js +13 -18
  112. package/dist/services/interruptService.d.ts +7 -6
  113. package/dist/services/interruptService.js +19 -21
  114. package/dist/services/playgroundService.d.ts +9 -8
  115. package/dist/services/playgroundService.js +23 -25
  116. package/dist/services/portConfigApi.js +3 -6
  117. package/dist/services/settingsService.d.ts +9 -4
  118. package/dist/services/settingsService.js +23 -12
  119. package/dist/skins/drafter.d.ts +30 -0
  120. package/dist/skins/drafter.js +185 -0
  121. package/dist/skins/index.d.ts +2 -1
  122. package/dist/skins/index.js +4 -2
  123. package/dist/stores/apiContext.d.ts +11 -0
  124. package/dist/stores/apiContext.js +15 -0
  125. package/dist/stores/categoriesStore.svelte.js +0 -1
  126. package/dist/stores/playgroundStore.svelte.js +0 -2
  127. package/dist/styles/base.css +38 -9
  128. package/dist/styles/tokens.css +54 -2
  129. package/dist/svelte-app.d.ts +6 -0
  130. package/dist/svelte-app.js +4 -2
  131. package/dist/themes/drafter.d.ts +2 -0
  132. package/dist/themes/drafter.js +15 -0
  133. package/dist/themes/index.d.ts +2 -1
  134. package/dist/themes/index.js +8 -2
  135. package/dist/types/auth.d.ts +9 -51
  136. package/dist/types/auth.js +4 -54
  137. package/dist/types/events.d.ts +18 -0
  138. package/dist/types/events.js +2 -1
  139. package/dist/types/index.d.ts +4 -2
  140. package/dist/types/index.js +0 -1
  141. package/dist/types/settings.d.ts +1 -1
  142. package/dist/types/settings.js +1 -1
  143. package/dist/types/skin.d.ts +1 -1
  144. package/dist/types/theme.d.ts +16 -2
  145. package/dist/utils/edgeStyling.js +9 -5
  146. package/dist/utils/fetchWithAuth.d.ts +36 -15
  147. package/dist/utils/fetchWithAuth.js +53 -23
  148. package/dist/utils/nodeTypes.js +1 -1
  149. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,73 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [2.0.0-beta.3] - 2026-06-12
11
+
12
+ 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.
13
+
14
+ ### Breaking Changes
15
+
16
+ - **`mountPlaygroundApp` / `<PlaygroundApp>` no longer render the navbar by default.** `showNavbar` now defaults to `false`, matching `mountFlowDropApp` / `<App>` — so every mount path is navbar-off by default and embedding FlowDrop into a page that already has its own header never double-stacks a header. Pass `showNavbar: true` to opt back into the FlowDrop navbar (logo, branding, settings modal) when FlowDrop owns the whole page.
17
+
18
+ ### Added
19
+
20
+ - **Drafter blueprint theme.** A third built-in editor theme (`'drafter'`), shipping light + dark variants: a blueprint aesthetic with a soft mint canvas, a subtle emerald line grid, and translucent green-tinted flat nodes. Data-type port colors and category icon colors are left untouched so they keep popping against the muted draft surface. Selectable from the Settings → UI Theme picker; the `FlowDropSkinName` / theme / skin name unions gain `'drafter'`.
21
+ - **Per-theme canvas grid.** A new `FlowDropGridVariant` type (`'dots' | 'lines' | 'cross'`) and `themeConfig.canvas.grid` config let any theme drive the editor's background grid (previously hardcoded to dots). The grid pattern color is the `--fd-grid-pattern-color` token, whose default matches xyflow's dot color, so the default/minimal themes render unchanged. `resolveTheme` now merges `config.canvas` alongside `config.sidebar`.
22
+ - **`features.builtinEditors` opt-out.** The built-in markdown/code/template editors are batteries-included in the full editor by default. Pass `features: { builtinEditors: false }` to `mountFlowDropApp` (or `builtinEditors: false` to `mountWorkflowEditor` / `<WorkflowEditor>`) to skip registering them — keeping the textarea fallback or leaving room to register your own field components via `instance.fields.register(...)`. The corrected fallback hint now points at the registry-scoped API (`registerMarkdownEditorField(instance.fields)`).
23
+ - **FlowDrop wordmark logo in the navbar.** The header now renders the FlowDrop wordmark in place of the plain text title.
24
+
25
+ ### Changed
26
+
27
+ - **The default theme preference is now `'light'` (was `'dark'`).** `DEFAULT_THEME_SETTINGS.preference` was hardcoded to `'dark'`, so any embed with no persisted theme choice rendered dark. Embeds now default to light; hosts that want dark by default should set the preference explicitly.
28
+ - **Editor / node / chrome surface tokens are scoped per surface,** refining how themes (including Drafter) tint the canvas, nodes, and surrounding chrome independently.
29
+
30
+ ### Fixed
31
+
32
+ - **Nodes are a single tab stop again.** The node config (gear) button sat in the tab order, so leaving a node required a second `Tab` press. The gear button is removed from the tab order (it stays mouse/Enter-activatable), and node focus defers to xyflow's own node focus — Tab now moves one node per press. `nodesFocusable` redundancy was removed to eliminate the duplicate stop.
33
+ - **The full editor renders markdown/code/config editors out of the box again.** The beta.2 light-entry split made the heavy form editors opt-in everywhere, which inadvertently left `<WorkflowEditor>` / `<App>` / the playground showing the "Editor component not registered" textarea fallback for node config fields with `format: 'markdown' | 'code' | 'template'`. The full editor now registers the built-in editors on its own instance's field registry on mount. Registration uses a dynamic `import()`, so the chunks stay code-split (loaded lazily) and the light `/editor` static bundle is unaffected — the bundle guard still passes. The standalone `@flowdrop/flowdrop/form` primitive is unchanged and remains opt-in.
34
+ - **`<App navbarActions>` accepts the full `NavbarAction` shape.** The component prop was typed with an inline subset that silently dropped `external` and `group`, even though the underlying `<Navbar>` (and the `mountFlowDropApp` option) already supported them. It now uses the canonical `NavbarAction` type, so external-link and grouped actions type-check through the component as they always did through the mount API.
35
+ - **The node header no longer shows a top-accent line.**
36
+
37
+ ### Changed (internal — no API change)
38
+
39
+ - **One centralized focus ring across the whole component library.** New `--fd-ring-width` / `--fd-ring-offset` tokens and a single `.flowdrop-root :focus-visible` rule in `base.css` replace ~200 lines of component-local focus CSS (mismatched outlines, hardcoded `rgba(59,130,246)` box-shadow glows, `color-mix` rings) removed across 41 files. CodeMirror editors in `overflow:hidden` containers use a `:focus-within` ring (outline paints outside the box and would clip); range-input thumbs ring on the parent.
40
+ - **Extracted shared `NodeConfigButton` and `CanvasIconButton` components,** replacing per-node config-button and per-canvas floating-button duplication (including an inline-SVG cog) with one keyboard-correct implementation each.
41
+ - **Node geometry converted from `rem` to `px` and snapped to a 20px grid** across all eight node types — header, port rows (fixed 60px), icon sizes, paddings, gaps, and font sizes — so nodes stay aligned to the canvas coordinate grid regardless of the host's root font-size.
42
+ - **The code / markdown / template editors now rest on the shared input surface** instead of carrying their own background.
43
+
44
+ ## [2.0.0-beta.2] - 2026-06-09
45
+
46
+ Second 2.0 beta, published under the npm `beta` dist-tag (`npm install @flowdrop/flowdrop@beta`). This release tightens the 2.0 package boundaries, finishes `AuthProvider` propagation across the editor and playground runtime surfaces, and adds a regression guard for light-entry bundle size.
47
+
48
+ ### Breaking Changes
49
+
50
+ - **Heavy dependencies no longer leak into the light entries.** Three exports moved so that `@flowdrop/flowdrop/core` and the light `@flowdrop/flowdrop/form` never statically pull CodeMirror, `marked`, DOMPurify, or `@xyflow/svelte` (enforced by a new CI bundle guard, see below):
51
+ - `sanitizeHtml` moved from `@flowdrop/flowdrop/core` to `@flowdrop/flowdrop/display` (it is DOMPurify-backed; `/core` is the zero-heavy-dep entry).
52
+ - `FormFieldFull` moved from `@flowdrop/flowdrop/form` to `@flowdrop/flowdrop/form/full` — it statically bundles every editor (CodeMirror), so keeping it in the light `/form` entry pulled CodeMirror into every `SchemaForm` import. The `FormField` exported from `/form` remains the registry-based light factory.
53
+ - Service singleton _instances_ (`playgroundService`, `interruptService` from `/playground`; `nodeExecutionService` from `/editor`; `agentSpecExecutionService` from `/core`) are no longer exported — they were constructed eagerly at import time, pulling the service and its dependencies into the entry's static graph. The classes (`PlaygroundService`, `InterruptService`, `NodeExecutionService`, `AgentSpecExecutionService`) remain; call `getInstance()` for the shared instance. See [MIGRATION-2.0.md section 4](./MIGRATION-2.0.md#keeping-heavy-dependencies-out-of-the-light-entries).
54
+
55
+ ### Changed (internal — no API change)
56
+
57
+ - `SchemaForm`, `ConfigForm`, and the UISchema renderer now use the registry-based light `FormFieldLight` internally, so importing `SchemaForm` no longer statically pulls CodeMirror (heavy editors stay opt-in via `/form/code` and `/form/markdown`).
58
+ - Built-in node **type metadata** (aliases, `resolveBuiltinAlias`, `isBuiltinType`) split into a component-free `registry/builtinNodeTypes` module so `/core`'s node-type utilities no longer drag in the node components (and transitively `marked`/DOMPurify).
59
+ - `utils/edgeStyling` and the `ConnectionLineType` reference in `types/index.ts` use type-only imports of `@xyflow/svelte`, removing the runtime `@xyflow/svelte` dependency from the headless command DSL reachable from `/core`.
60
+
61
+ ### Added
62
+
63
+ - **Bundle-size guard (`pnpm run check:bundle`)** and a CI workflow that walk each published entry's static import graph and fail if a heavy dependency (CodeMirror, `@xyflow/svelte`, `marked`, DOMPurify) becomes statically reachable from a light entry (`/core`, `/form`, `/editor`). Dynamic `import()` — how `/form/code` lazy-loads CodeMirror — is intentionally ignored.
64
+ - **500-node editor render benchmark.** The e2e performance suite now includes a large trigger-chain workflow fixture so regressions in large-canvas rendering show up before release.
65
+
66
+ ### Fixed
67
+
68
+ - **`AuthProvider` now reaches all documented runtime surfaces.** Playground, chat, interrupt, settings, editor, and pipeline requests now consistently route through the configured provider, including standalone playground mounts and the editor/pipeline views that previously dropped auth headers on secondary requests.
69
+ - **Theme skin tokens clear when switching back to the default skin.** Returning from a custom/base skin to the default theme no longer leaves stale CSS custom properties behind.
70
+
71
+ ### Changed
72
+
73
+ - **`AuthProvider` request handling is centralized.** Services now share the `fetchWithAuth` path, and the public provider interface is slimmer while preserving bearer, API-key, custom-header, and callback authentication behavior.
74
+
8
75
  ## [2.0.0-beta.1] - 2026-06-07
9
76
 
10
77
  Pre-release of 2.0.0, published under the npm `beta` dist-tag (`npm install @flowdrop/flowdrop@beta`). `latest` remains 1.15.0 until 2.0.0 GA. See [MIGRATION-2.0.md](./MIGRATION-2.0.md) for the upgrade guide.
package/MIGRATION-2.0.md CHANGED
@@ -80,6 +80,52 @@ authProvider)` for you. Services that previously read the singleton (playground,
80
80
  interrupt, chat, node-execution, dynamic-schema, variable) now take the endpoint
81
81
  config as their first argument — pass `fd.api.config`.
82
82
 
83
+ These services also accept the instance's `AuthProvider` as an **optional
84
+ trailing argument** so every request they make is authenticated consistently
85
+ with `fd.api.client` (the typed workflow/node API). The built-in components and
86
+ mount helpers pass `fd.api.authProvider` for you — no action is needed for the
87
+ normal mounted flow. Only direct callers of the service singletons need to
88
+ forward it:
89
+
90
+ ```js
91
+ import { playgroundService } from '@flowdrop/flowdrop/playground';
92
+
93
+ // 2.0 — authenticate playground requests by forwarding the provider
94
+ await playgroundService.listSessions(fd.api.config, workflowId, undefined, fd.api.authProvider);
95
+ await playgroundService.sendMessage(fd.api.config, sessionId, text, undefined, fd.api.authProvider);
96
+ ```
97
+
98
+ The settings-sync entry points gained the same optional provider:
99
+ `setSettingsEndpointConfig(config, authProvider?)` and
100
+ `createSettingsService(config, authProvider?)` — pass an `AuthProvider` if your
101
+ backend's preferences endpoint requires auth.
102
+
103
+ The standalone playground mounts — `mountPlayground`, `mountPlaygroundStudio`,
104
+ and `mountPlaygroundApp` — accept an `authProvider` option that they wire into
105
+ `fd.api` for you (the editor's built-in playground already inherits the
106
+ provider from `mountFlowDropApp`). Pass it so playground requests (sessions,
107
+ messages, polling, interrupts) carry your auth/CSRF headers:
108
+
109
+ ```js
110
+ mountPlayground(el, {
111
+ workflowId,
112
+ endpointConfig: createEndpointConfig('/api/flowdrop'),
113
+ authProvider: new CallbackAuthProvider({ getToken: () => session.getCsrfToken() })
114
+ });
115
+ ```
116
+
117
+ The same plumbing was extended to the editor and pipeline surfaces, which
118
+ previously built unauthenticated API clients from `endpointConfig` alone:
119
+
120
+ - The exported `<WorkflowEditor>` component accepts an `authProvider` prop
121
+ (threaded for you by `mountFlowDropApp` / `mountWorkflowEditor` / `<App>`).
122
+ Pass it when using `<WorkflowEditor>` directly.
123
+ - `<PipelineStatus>` accepts an `authProvider` prop (used when it builds its own
124
+ client from `endpointConfig` / `baseUrl`; ignored when you pass `apiClient`).
125
+ - `PipelineViewProps` — the contract for custom pipeline views registered via
126
+ `pipelineViews` — gained an optional `authProvider`, so custom views can build
127
+ authenticated clients the same way the built-in graph/kanban/table views do.
128
+
83
129
  ### `EndpointConfig.auth` is removed — use an `AuthProvider`
84
130
 
85
131
  The `auth` block on `EndpointConfig` (and its header-injection branch) is gone.
@@ -108,6 +154,65 @@ mountFlowDropApp(el, {
108
154
  | `{ type: 'api_key', apiKey }` | `new StaticAuthProvider({ type: 'api_key', apiKey })` |
109
155
  | `{ type: 'custom', headers }` | `new StaticAuthProvider({ type: 'custom', headers })` |
110
156
 
157
+ `StaticAuthProvider`'s `api_key` type now accepts an optional `apiKeyHeader` to
158
+ override the header name (defaults to `X-API-Key`):
159
+
160
+ ```js
161
+ new StaticAuthProvider({ type: 'api_key', apiKey: KEY, apiKeyHeader: 'X-Tenant-Key' });
162
+ ```
163
+
164
+ ### `AuthProvider.isAuthenticated()` is removed
165
+
166
+ The `isAuthenticated()` method has been dropped from the `AuthProvider`
167
+ interface and all built-in providers. It was never consulted by the library —
168
+ request authentication is driven entirely by `getAuthHeaders()` and the optional
169
+ `onUnauthorized()` / `onForbidden()` hooks. If you implemented a **custom**
170
+ `AuthProvider`, you can delete the method; no replacement is needed.
171
+
172
+ ```ts
173
+ // 1.x — required
174
+ const provider: AuthProvider = {
175
+ getAuthHeaders: async () => ({ Authorization: `Bearer ${token}` }),
176
+ isAuthenticated: () => Boolean(token) // ← remove this
177
+ };
178
+
179
+ // 2.0
180
+ const provider: AuthProvider = {
181
+ getAuthHeaders: async () => ({ Authorization: `Bearer ${token}` })
182
+ };
183
+ ```
184
+
185
+ ### Auth refresh (`401`) now applies to every request
186
+
187
+ Previously only the typed workflow/node API (`fd.api.client`) refreshed and
188
+ retried on `401`. The per-instance services (playground, chat, interrupt,
189
+ settings, port config, categories) and form autocomplete attached auth headers
190
+ but did **not** invoke `onUnauthorized()`. They now all route through one
191
+ authenticated-fetch path, so a configured `onUnauthorized()` fires — and the
192
+ request retries once with a refreshed token — uniformly across the library.
193
+
194
+ This is not a source change for consumers, but if your `onUnauthorized()` had
195
+ side effects (analytics, redirects) it may now be called from request paths
196
+ where it previously was not. Make it idempotent.
197
+
198
+ ### Swap the auth provider at runtime — `fd.api.setAuthProvider()`
199
+
200
+ The `AuthProvider` is still supplied at mount time, but you no longer have to
201
+ remount to change it (e.g. on login/logout). `ApiContext` gained
202
+ `setAuthProvider()`, which updates the live client and is picked up by services
203
+ on their next request:
204
+
205
+ ```js
206
+ import { getInstance } from '@flowdrop/flowdrop/editor';
207
+ const fd = getInstance();
208
+
209
+ // after the user logs in / refreshes their session
210
+ fd.api.setAuthProvider(new StaticAuthProvider({ type: 'bearer', token: newToken }));
211
+
212
+ // on logout
213
+ fd.api.setAuthProvider(new NoAuthProvider());
214
+ ```
215
+
111
216
  ### Instance-scoped port compatibility
112
217
 
113
218
  `initializePortCompatibility()`, `getPortCompatibilityChecker()`, and
@@ -190,6 +295,19 @@ import { getInstance } from '@flowdrop/flowdrop/editor';
190
295
  registerCodeEditorField(getInstance().fields);
191
296
  ```
192
297
 
298
+ **You only need this for the standalone `@flowdrop/flowdrop/form` primitive.**
299
+ The full editor is batteries-included: `<WorkflowEditor>` / `<App>` and the
300
+ playground register the built-in markdown, code, and template editors on their
301
+ own instance's `fields` registry on mount, so node config fields with
302
+ `format: 'markdown' | 'code' | 'template'` render real editors out of the box —
303
+ no host call required. Registration is a dynamic `import()`, so the CodeMirror /
304
+ `marked` chunks stay code-split (the light `/editor` static bundle is
305
+ unaffected) and load lazily on first use. To strip them, pass
306
+ `features: { builtinEditors: false }` to `mountFlowDropApp` (or
307
+ `builtinEditors: false` to `mountWorkflowEditor` / `<WorkflowEditor>`) and either
308
+ accept the textarea fallback or register your own field components via
309
+ `instance.fields.register(...)`.
310
+
193
311
  Category color/icon helpers likewise take a `CategoriesStore` as an explicit
194
312
  parameter — the last global-instance fallback in `utils` is gone.
195
313
 
@@ -206,16 +324,16 @@ barrel no longer re-exports `marked`.
206
324
 
207
325
  - **Playground exports** (`Playground`, `PlaygroundModal`, `ChatPanel`,
208
326
  `SessionManager`, `InputCollector`, `ExecutionLogs`, `MessageBubble`,
209
- `PlaygroundService`, `playgroundService`, `PlaygroundStore`) are removed from
327
+ `PlaygroundService`, `PlaygroundStore`) are removed from
210
328
  `@flowdrop/flowdrop/editor`. Import them from `@flowdrop/flowdrop/playground`
211
329
  instead.
212
330
 
213
331
  ```js
214
332
  // 1.x
215
- import { Playground, playgroundService } from '@flowdrop/flowdrop/editor';
333
+ import { Playground, PlaygroundStore } from '@flowdrop/flowdrop/editor';
216
334
 
217
335
  // 2.0
218
- import { Playground, playgroundService } from '@flowdrop/flowdrop/playground';
336
+ import { Playground, PlaygroundStore } from '@flowdrop/flowdrop/playground';
219
337
  ```
220
338
 
221
339
  - **`marked`** is no longer re-exported from `@flowdrop/flowdrop/display`. If you
@@ -284,6 +402,58 @@ This also tightens tree-shaking: importing `App` and a couple of types from the
284
402
  main entry no longer pulls the form, display, playground, and settings barrels
285
403
  into your bundle.
286
404
 
405
+ ### Keeping heavy dependencies out of the light entries
406
+
407
+ A few more exports moved so that the lightweight entries (`/core`, the light
408
+ `/form`) never statically pull a heavy dependency (CodeMirror, `marked`,
409
+ DOMPurify, `@xyflow/svelte`). These are enforced by a bundle guard in CI, so
410
+ they cannot silently regress.
411
+
412
+ - **`sanitizeHtml` moved from `@flowdrop/flowdrop/core` to
413
+ `@flowdrop/flowdrop/display`.** It is DOMPurify-backed, and `/core` is the
414
+ "zero heavy dependencies" entry, so it now lives alongside `MarkdownDisplay`.
415
+
416
+ ```js
417
+ // before
418
+ import { sanitizeHtml } from '@flowdrop/flowdrop/core';
419
+ // 2.0
420
+ import { sanitizeHtml } from '@flowdrop/flowdrop/display';
421
+ ```
422
+
423
+ - **`FormFieldFull` moved from `@flowdrop/flowdrop/form` to
424
+ `@flowdrop/flowdrop/form/full`.** `FormFieldFull` statically bundles every
425
+ editor (including CodeMirror); keeping it in the light `/form` entry pulled
426
+ CodeMirror into every `SchemaForm` import. The default `FormField` exported
427
+ from `/form` is the registry-based light field factory — register heavy
428
+ editors via `/form/code` / `/form/markdown` (unchanged). Use `/form/full`
429
+ only if you specifically want every editor statically bundled.
430
+
431
+ ```js
432
+ // before
433
+ import { FormFieldFull } from '@flowdrop/flowdrop/form';
434
+ // 2.0
435
+ import { FormFieldFull } from '@flowdrop/flowdrop/form/full';
436
+ ```
437
+
438
+ - **Service singleton _instances_ are no longer exported.**
439
+ `playgroundService` and `interruptService` (`@flowdrop/flowdrop/playground`),
440
+ `nodeExecutionService` (`@flowdrop/flowdrop/editor`), and
441
+ `agentSpecExecutionService` (`@flowdrop/flowdrop/core`) are removed. They were
442
+ module-level instances constructed at import time, which forced the service
443
+ (and its dependencies) to be built the moment the entry was imported. The
444
+ **classes** remain exported (`PlaygroundService`, `InterruptService`,
445
+ `NodeExecutionService`, `AgentSpecExecutionService`). Most apps never touched
446
+ these directly (use `fd.playground` / `fd.interrupts`); if you did, call
447
+ `getInstance()` to obtain the shared instance:
448
+
449
+ ```js
450
+ // before
451
+ import { playgroundService } from '@flowdrop/flowdrop/playground';
452
+ // 2.0
453
+ import { PlaygroundService } from '@flowdrop/flowdrop/playground';
454
+ const playgroundService = PlaygroundService.getInstance();
455
+ ```
456
+
287
457
  ## 5. Workflow `metadata` is required and `version` → `schemaVersion`
288
458
 
289
459
  The workflow document's `metadata` object is now **required** on the `Workflow`
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @module api/enhanced-client
7
7
  */
8
- import { buildEndpointUrl, getEndpointMethod, getEndpointHeaders } from '../config/endpoints.js';
8
+ import { buildEndpointUrl, getEndpointMethod, getRequestHeaders } from '../config/endpoints.js';
9
9
  import { NoAuthProvider } from '../types/auth.js';
10
10
  import { getApiSettings } from '../stores/settingsStore.svelte.js';
11
11
  import { logger } from '../utils/logger.js';
@@ -56,15 +56,12 @@ export class EnhancedFlowDropApiClient {
56
56
  async request(endpointKey, endpointPath, params, options = {}, operation = 'API request') {
57
57
  const url = buildEndpointUrl(this.config, endpointPath, params);
58
58
  const method = options.method ?? getEndpointMethod(this.config, endpointKey);
59
- const configHeaders = getEndpointHeaders(this.config, endpointKey);
60
59
  // Get user settings for timeout and retry
61
60
  const userApiSettings = getApiSettings();
62
- // Get auth headers from provider
63
- const authHeaders = await this.authProvider.getAuthHeaders();
64
- // Merge headers: config headers < auth headers < request-specific headers
61
+ // Merge headers via the shared path: static endpoint headers < auth headers
62
+ // < request-specific headers.
65
63
  const headers = {
66
- ...configHeaders,
67
- ...authHeaders,
64
+ ...(await getRequestHeaders(this.config, endpointKey, this.authProvider)),
68
65
  ...options.headers
69
66
  };
70
67
  // Create AbortController for timeout
@@ -93,11 +90,9 @@ export class EnhancedFlowDropApiClient {
93
90
  if (this.authProvider.onUnauthorized) {
94
91
  const refreshed = await this.authProvider.onUnauthorized();
95
92
  if (refreshed && attempt < maxAttempts) {
96
- // Get new auth headers and retry
97
- const newAuthHeaders = await this.authProvider.getAuthHeaders();
93
+ // Rebuild headers via the shared path to pick up the fresh token.
98
94
  fetchConfig.headers = {
99
- ...configHeaders,
100
- ...newAuthHeaders,
95
+ ...(await getRequestHeaders(this.config, endpointKey, this.authProvider)),
101
96
  ...options.headers
102
97
  };
103
98
  continue; // Retry with new headers
@@ -9,7 +9,9 @@
9
9
  import MainLayout from './layouts/MainLayout.svelte';
10
10
  import WorkflowEditor from './WorkflowEditor.svelte';
11
11
  import NodeSidebar from './NodeSidebar.svelte';
12
- import Icon from '@iconify/svelte';
12
+ import CanvasIconButton from './CanvasIconButton.svelte';
13
+ import MenuIcon from './icons/MenuIcon.svelte';
14
+ import MenuOpenIcon from './icons/MenuOpenIcon.svelte';
13
15
  import ConfigForm from './ConfigForm.svelte';
14
16
  import ConfigPanel from './ConfigPanel.svelte';
15
17
  import CommandConsole from './console/CommandConsole.svelte';
@@ -18,6 +20,7 @@
18
20
  import NodeSwapPicker from './NodeSwapPicker.svelte';
19
21
  import SwapMappingEditor from './SwapMappingEditor.svelte';
20
22
  import Navbar from './Navbar.svelte';
23
+ import type { NavbarAction } from '../types/navbar.js';
21
24
  import type { NodeMetadata, Workflow, WorkflowNode, ConfigSchema } from '../types/index.js';
22
25
  import type { InteractiveSwapState, SwapEventContext } from '../utils/nodeSwap.js';
23
26
  import {
@@ -98,13 +101,7 @@
98
101
  /** Custom navbar title */
99
102
  navbarTitle?: string;
100
103
  /** Custom navbar actions */
101
- navbarActions?: Array<{
102
- label: string;
103
- href: string;
104
- icon?: string;
105
- variant?: 'primary' | 'secondary' | 'outline';
106
- onclick?: (event: Event) => void;
107
- }>;
104
+ navbarActions?: NavbarAction[];
108
105
  /** Show settings gear icon in navbar */
109
106
  showSettings?: boolean;
110
107
  /** Show the "Connected" status indicator in the navbar (default: true) */
@@ -1278,27 +1275,34 @@
1278
1275
  >
1279
1276
  <!-- Floating sidebar toggle — always visible on the canvas top-left -->
1280
1277
  {#if !disableSidebar}
1281
- <button
1278
+ <CanvasIconButton
1282
1279
  class="flowdrop-sidebar-fab"
1283
- onclick={toggleSidebar}
1284
- aria-label={isSidebarCollapsed
1285
- ? mergedMessages.layout.expandSidebar
1286
- : mergedMessages.layout.collapseSidebar}
1287
- title={isSidebarCollapsed
1280
+ label={isSidebarCollapsed
1288
1281
  ? mergedMessages.layout.expandSidebar
1289
1282
  : mergedMessages.layout.collapseSidebar}
1283
+ active={!isSidebarCollapsed}
1284
+ onclick={toggleSidebar}
1290
1285
  >
1291
- <Icon icon={isSidebarCollapsed ? 'mdi:menu' : 'mdi:menu-open'} />
1292
- </button>
1286
+ {#snippet icon()}
1287
+ {#if isSidebarCollapsed}
1288
+ <MenuIcon />
1289
+ {:else}
1290
+ <MenuOpenIcon />
1291
+ {/if}
1292
+ {/snippet}
1293
+ </CanvasIconButton>
1293
1294
  {/if}
1294
1295
 
1295
1296
  <WorkflowEditor
1296
1297
  bind:this={workflowEditorRef}
1297
1298
  endpointConfig={endpointConfig ?? undefined}
1299
+ {authProvider}
1298
1300
  {openConfigSidebar}
1299
1301
  {mode}
1300
1302
  {pipelineId}
1301
1303
  {refreshTrigger}
1304
+ builtinEditors={features.builtinEditors}
1305
+ gridVariant={themeConfig?.canvas?.grid ?? 'dots'}
1302
1306
  consoleOpen={getUiSettings().consoleOpen}
1303
1307
  onToggleConsole={toggleConsole}
1304
1308
  />
@@ -1411,38 +1415,11 @@
1411
1415
  font-weight: 500;
1412
1416
  }
1413
1417
 
1414
- /* Floating sidebar toggle button */
1415
- .flowdrop-sidebar-fab {
1416
- position: absolute;
1418
+ /* Floating sidebar toggle button — placement only; visuals live in CanvasIconButton */
1419
+ :global(.flowdrop-sidebar-fab) {
1417
1420
  top: 12px;
1418
1421
  left: 12px;
1419
1422
  z-index: 50;
1420
- display: flex;
1421
- align-items: center;
1422
- justify-content: center;
1423
- width: 2.25rem;
1424
- height: 2.25rem;
1425
- border: 1px solid var(--fd-border);
1426
- border-radius: var(--fd-radius-md);
1427
- background-color: var(--fd-background);
1428
- color: var(--fd-muted-foreground);
1429
- cursor: pointer;
1430
- box-shadow: var(--fd-shadow-md);
1431
- transition:
1432
- color var(--fd-transition-fast),
1433
- background-color var(--fd-transition-fast),
1434
- box-shadow var(--fd-transition-fast);
1435
- }
1436
-
1437
- .flowdrop-sidebar-fab:hover {
1438
- color: var(--fd-foreground);
1439
- background-color: var(--fd-subtle);
1440
- box-shadow: var(--fd-shadow-lg);
1441
- }
1442
-
1443
- .flowdrop-sidebar-fab:focus {
1444
- outline: none;
1445
- box-shadow: 0 0 0 2px var(--fd-ring);
1446
1423
  }
1447
1424
 
1448
1425
  /* Main editor area */
@@ -1,3 +1,4 @@
1
+ import type { NavbarAction } from '../types/navbar.js';
1
2
  import type { NodeMetadata, Workflow, ConfigSchema } from '../types/index.js';
2
3
  import type { SwapStrategy } from '../utils/nodeSwap.js';
3
4
  import type { EndpointConfig } from '../config/endpoints.js';
@@ -53,13 +54,7 @@ interface Props {
53
54
  /** Custom navbar title */
54
55
  navbarTitle?: string;
55
56
  /** Custom navbar actions */
56
- navbarActions?: Array<{
57
- label: string;
58
- href: string;
59
- icon?: string;
60
- variant?: 'primary' | 'secondary' | 'outline';
61
- onclick?: (event: Event) => void;
62
- }>;
57
+ navbarActions?: NavbarAction[];
63
58
  /** Show settings gear icon in navbar */
64
59
  showSettings?: boolean;
65
60
  /** Show the "Connected" status indicator in the navbar (default: true) */
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ /**
6
+ * The icon to render — an inline-SVG snippet (offline-safe). Pass a local
7
+ * icon component from `$lib/components/icons/`, not a network-fetched one.
8
+ */
9
+ icon: Snippet;
10
+ /** Accessible label — drives both aria-label and the hover title. */
11
+ label: string;
12
+ /** Renders the active/toggled-on visual state (e.g. the panel it controls is open). */
13
+ active?: boolean;
14
+ onclick: () => void;
15
+ /** Caller-supplied class for positioning (top/left/bottom/z-index). */
16
+ class?: string;
17
+ }
18
+
19
+ let { icon, label, active = false, onclick, class: klass = '' }: Props = $props();
20
+ </script>
21
+
22
+ <button
23
+ class="flowdrop-canvas-btn {klass}"
24
+ class:flowdrop-canvas-btn--active={active}
25
+ {onclick}
26
+ aria-label={label}
27
+ title={label}
28
+ type="button"
29
+ >
30
+ {@render icon()}
31
+ </button>
32
+
33
+ <style>
34
+ .flowdrop-canvas-btn {
35
+ position: absolute;
36
+ display: flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+ width: 2.25rem;
40
+ height: 2.25rem;
41
+ border: 1px solid var(--fd-border);
42
+ border-radius: var(--fd-radius-md);
43
+ background-color: var(--fd-background);
44
+ color: var(--fd-muted-foreground);
45
+ cursor: pointer;
46
+ box-shadow: var(--fd-shadow-md);
47
+ transition:
48
+ color var(--fd-transition-fast),
49
+ background-color var(--fd-transition-fast),
50
+ box-shadow var(--fd-transition-fast),
51
+ border-color var(--fd-transition-fast);
52
+ }
53
+
54
+ /* Size whatever inline SVG the caller renders. */
55
+ .flowdrop-canvas-btn :global(svg) {
56
+ width: 18px;
57
+ height: 18px;
58
+ }
59
+
60
+ .flowdrop-canvas-btn:hover {
61
+ color: var(--fd-foreground);
62
+ background-color: var(--fd-subtle);
63
+ box-shadow: var(--fd-shadow-lg);
64
+ }
65
+
66
+ .flowdrop-canvas-btn--active {
67
+ color: var(--fd-primary);
68
+ background-color: var(--fd-primary-muted);
69
+ border-color: var(--fd-primary);
70
+ }
71
+
72
+ .flowdrop-canvas-btn--active:hover {
73
+ color: var(--fd-primary);
74
+ background-color: var(--fd-primary-muted);
75
+ }
76
+ </style>
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /**
4
+ * The icon to render — an inline-SVG snippet (offline-safe). Pass a local
5
+ * icon component from `$lib/components/icons/`, not a network-fetched one.
6
+ */
7
+ icon: Snippet;
8
+ /** Accessible label — drives both aria-label and the hover title. */
9
+ label: string;
10
+ /** Renders the active/toggled-on visual state (e.g. the panel it controls is open). */
11
+ active?: boolean;
12
+ onclick: () => void;
13
+ /** Caller-supplied class for positioning (top/left/bottom/z-index). */
14
+ class?: string;
15
+ }
16
+ declare const CanvasIconButton: import("svelte").Component<Props, {}, "">;
17
+ type CanvasIconButton = ReturnType<typeof CanvasIconButton>;
18
+ export default CanvasIconButton;
@@ -32,9 +32,13 @@
32
32
  } from '../types/index.js';
33
33
  import { dynamicPortToNodePort } from '../types/index.js';
34
34
  import type { UISchemaElement } from '../types/uischema.js';
35
- import { FormField, FormFieldWrapper, FormToggle } from './form/index.js';
35
+ // Import the light, registry-based field factory and light fields directly
36
+ // (not via the form barrel, which aggregates the heavy CodeMirror editors).
37
+ import FormField from './form/FormFieldLight.svelte';
38
+ import FormFieldWrapper from './form/FormFieldWrapper.svelte';
39
+ import FormToggle from './form/FormToggle.svelte';
36
40
  import FormUISchemaRenderer from './form/FormUISchemaRenderer.svelte';
37
- import type { FieldSchema } from './form/index.js';
41
+ import type { FieldSchema } from './form/types.js';
38
42
  import {
39
43
  getEffectiveConfigEditOptions,
40
44
  fetchDynamicSchema,
@@ -1069,11 +1073,6 @@
1069
1073
  color: var(--fd-foreground);
1070
1074
  }
1071
1075
 
1072
- .config-form__button--secondary:focus-visible {
1073
- outline: none;
1074
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
1075
- }
1076
-
1077
1076
  .config-form__button--primary {
1078
1077
  background: linear-gradient(135deg, var(--fd-primary) 0%, var(--fd-primary-hover) 100%);
1079
1078
  color: var(--fd-primary-foreground);
@@ -1094,13 +1093,6 @@
1094
1093
  transform: translateY(0);
1095
1094
  }
1096
1095
 
1097
- .config-form__button--primary:focus-visible {
1098
- outline: none;
1099
- box-shadow:
1100
- 0 0 0 3px rgba(59, 130, 246, 0.4),
1101
- 0 4px 12px rgba(59, 130, 246, 0.35);
1102
- }
1103
-
1104
1096
  /* ============================================
1105
1097
  UI EXTENSIONS SECTION
1106
1098
  ============================================ */
@@ -1555,11 +1547,4 @@
1555
1547
  .config-form__button--external:active {
1556
1548
  transform: translateY(0);
1557
1549
  }
1558
-
1559
- .config-form__button--external:focus-visible {
1560
- outline: none;
1561
- box-shadow:
1562
- 0 0 0 3px rgba(99, 102, 241, 0.4),
1563
- 0 4px 12px rgba(99, 102, 241, 0.35);
1564
- }
1565
1550
  </style>
@@ -110,7 +110,8 @@
110
110
  height: 100%;
111
111
  display: flex;
112
112
  flex-direction: column;
113
- background-color: var(--fd-background);
113
+ background-color: var(--fd-panel-bg);
114
+ backdrop-filter: var(--fd-panel-backdrop-filter);
114
115
  }
115
116
 
116
117
  .config-panel__header {
@@ -119,7 +120,7 @@
119
120
  align-items: center;
120
121
  padding: 0.875rem 1rem;
121
122
  border-bottom: 1px solid var(--fd-border);
122
- background-color: var(--fd-muted);
123
+ background-color: var(--fd-card);
123
124
  flex-shrink: 0;
124
125
  }
125
126
 
@@ -180,7 +181,7 @@
180
181
  .config-panel__details {
181
182
  padding: 0.75rem 1rem;
182
183
  border-bottom: 1px solid var(--fd-border-muted);
183
- background-color: var(--fd-muted);
184
+ background-color: var(--fd-card);
184
185
  flex-shrink: 0;
185
186
  }
186
187