@flowdrop/flowdrop 2.0.0-beta.2 → 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 (85) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/MIGRATION-2.0.md +13 -0
  3. package/dist/components/App.svelte +21 -45
  4. package/dist/components/App.svelte.d.ts +2 -7
  5. package/dist/components/CanvasIconButton.svelte +76 -0
  6. package/dist/components/CanvasIconButton.svelte.d.ts +18 -0
  7. package/dist/components/ConfigForm.svelte +0 -19
  8. package/dist/components/ConfigPanel.svelte +4 -3
  9. package/dist/components/LogoWordmark.svelte +113 -0
  10. package/dist/components/LogoWordmark.svelte.d.ts +26 -0
  11. package/dist/components/Navbar.svelte +8 -59
  12. package/dist/components/NodeSidebar.svelte +4 -11
  13. package/dist/components/NodeSwapPicker.svelte +0 -2
  14. package/dist/components/PortMappingRow.svelte +0 -2
  15. package/dist/components/SchemaForm.svelte +0 -12
  16. package/dist/components/SettingsModal.svelte +0 -5
  17. package/dist/components/SettingsPanel.svelte +2 -6
  18. package/dist/components/ThemeToggle.svelte +0 -5
  19. package/dist/components/UniversalNode.svelte +32 -1
  20. package/dist/components/WorkflowEditor.svelte +62 -51
  21. package/dist/components/WorkflowEditor.svelte.d.ts +18 -0
  22. package/dist/components/chat/AIChatPanel.svelte +1 -1
  23. package/dist/components/console/ConsoleAutocomplete.svelte +1 -1
  24. package/dist/components/console/ConsoleOutput.svelte +2 -2
  25. package/dist/components/form/FormArray.svelte +0 -16
  26. package/dist/components/form/FormAutocomplete.svelte +10 -6
  27. package/dist/components/form/FormCheckboxGroup.svelte +0 -4
  28. package/dist/components/form/FormCodeEditor.svelte +9 -7
  29. package/dist/components/form/FormFieldLight.svelte +3 -3
  30. package/dist/components/form/FormMarkdownEditor.svelte +8 -5
  31. package/dist/components/form/FormNumberField.svelte +0 -4
  32. package/dist/components/form/FormRangeField.svelte +1 -20
  33. package/dist/components/form/FormSelect.svelte +10 -6
  34. package/dist/components/form/FormTemplateEditor.svelte +6 -4
  35. package/dist/components/form/FormTextField.svelte +10 -6
  36. package/dist/components/form/FormTextarea.svelte +10 -6
  37. package/dist/components/form/FormToggle.svelte +0 -4
  38. package/dist/components/icons/CommandLineIcon.svelte +15 -0
  39. package/dist/components/icons/CommandLineIcon.svelte.d.ts +26 -0
  40. package/dist/components/icons/MenuIcon.svelte +4 -0
  41. package/dist/components/icons/MenuIcon.svelte.d.ts +26 -0
  42. package/dist/components/icons/MenuOpenIcon.svelte +6 -0
  43. package/dist/components/icons/MenuOpenIcon.svelte.d.ts +26 -0
  44. package/dist/components/interrupt/ChoicePrompt.svelte +0 -10
  45. package/dist/components/interrupt/ConfirmationPrompt.svelte +0 -5
  46. package/dist/components/interrupt/InterruptBubble.svelte +0 -10
  47. package/dist/components/interrupt/ReviewPrompt.svelte +0 -20
  48. package/dist/components/interrupt/TextInputPrompt.svelte +0 -6
  49. package/dist/components/layouts/MainLayout.svelte +4 -5
  50. package/dist/components/nodes/AtomNode.svelte +46 -34
  51. package/dist/components/nodes/GatewayNode.svelte +91 -99
  52. package/dist/components/nodes/IdeaNode.svelte +62 -90
  53. package/dist/components/nodes/NodeConfigButton.svelte +86 -0
  54. package/dist/components/nodes/NodeConfigButton.svelte.d.ts +15 -0
  55. package/dist/components/nodes/NotesNode.svelte +70 -81
  56. package/dist/components/nodes/SimpleNode.svelte +28 -78
  57. package/dist/components/nodes/SquareNode.svelte +79 -109
  58. package/dist/components/nodes/TerminalNode.svelte +28 -86
  59. package/dist/components/nodes/ToolNode.svelte +82 -95
  60. package/dist/components/nodes/WorkflowNode.svelte +91 -100
  61. package/dist/components/playground/ChatInput.svelte +0 -1
  62. package/dist/components/playground/InputCollector.svelte +0 -2
  63. package/dist/components/playground/PlaygroundApp.svelte +1 -1
  64. package/dist/components/playground/PlaygroundStudio.svelte +0 -5
  65. package/dist/playground/mount.d.ts +9 -5
  66. package/dist/playground/mount.js +9 -5
  67. package/dist/skins/drafter.d.ts +30 -0
  68. package/dist/skins/drafter.js +185 -0
  69. package/dist/skins/index.d.ts +2 -1
  70. package/dist/skins/index.js +4 -2
  71. package/dist/styles/base.css +38 -9
  72. package/dist/styles/tokens.css +54 -2
  73. package/dist/svelte-app.d.ts +6 -0
  74. package/dist/svelte-app.js +3 -2
  75. package/dist/themes/drafter.d.ts +2 -0
  76. package/dist/themes/drafter.js +15 -0
  77. package/dist/themes/index.d.ts +2 -1
  78. package/dist/themes/index.js +8 -2
  79. package/dist/types/events.d.ts +18 -0
  80. package/dist/types/events.js +2 -1
  81. package/dist/types/settings.d.ts +1 -1
  82. package/dist/types/settings.js +1 -1
  83. package/dist/types/skin.d.ts +1 -1
  84. package/dist/types/theme.d.ts +16 -2
  85. package/package.json +1 -1
@@ -313,9 +313,7 @@
313
313
  }
314
314
 
315
315
  .swap-picker__input:focus {
316
- outline: none;
317
316
  border-color: var(--fd-ring);
318
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
319
317
  }
320
318
 
321
319
  .swap-picker__input::placeholder {
@@ -136,9 +136,7 @@
136
136
  }
137
137
 
138
138
  .port-mapping-row__select:focus {
139
- outline: none;
140
139
  border-color: var(--fd-ring);
141
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
142
140
  }
143
141
 
144
142
  .port-mapping-row__select--dropped {
@@ -473,11 +473,6 @@
473
473
  color: var(--fd-foreground);
474
474
  }
475
475
 
476
- .schema-form__button--secondary:focus-visible {
477
- outline: none;
478
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
479
- }
480
-
481
476
  .schema-form__button--primary {
482
477
  background: linear-gradient(135deg, var(--fd-primary) 0%, var(--fd-primary-hover) 100%);
483
478
  color: var(--fd-primary-foreground);
@@ -498,13 +493,6 @@
498
493
  transform: translateY(0);
499
494
  }
500
495
 
501
- .schema-form__button--primary:focus-visible {
502
- outline: none;
503
- box-shadow:
504
- 0 0 0 3px rgba(59, 130, 246, 0.4),
505
- 0 4px 12px rgba(59, 130, 246, 0.35);
506
- }
507
-
508
496
  .schema-form__button-spinner {
509
497
  width: 1rem;
510
498
  height: 1rem;
@@ -259,11 +259,6 @@
259
259
  color: var(--fd-foreground);
260
260
  }
261
261
 
262
- .flowdrop-settings-modal__close:focus {
263
- outline: none;
264
- box-shadow: 0 0 0 2px var(--fd-ring);
265
- }
266
-
267
262
  /* Content */
268
263
  .flowdrop-settings-modal__content {
269
264
  flex: 1;
@@ -198,7 +198,8 @@
198
198
  description: 'Visual style and layout of the editor',
199
199
  oneOf: [
200
200
  { const: 'default', title: 'Default' },
201
- { const: 'minimal', title: 'Minimal' }
201
+ { const: 'minimal', title: 'Minimal' },
202
+ { const: 'drafter', title: 'Drafter' }
202
203
  ],
203
204
  default: 'default'
204
205
  }
@@ -538,11 +539,6 @@
538
539
  color: var(--fd-primary-foreground);
539
540
  }
540
541
 
541
- .flowdrop-settings-panel__tab:focus {
542
- outline: none;
543
- box-shadow: 0 0 0 2px var(--fd-ring);
544
- }
545
-
546
542
  :global(.flowdrop-settings-panel__tab-icon) {
547
543
  font-size: var(--fd-text-base);
548
544
  }
@@ -135,11 +135,6 @@
135
135
  border-color: var(--fd-border-strong);
136
136
  }
137
137
 
138
- .flowdrop-theme-toggle:focus {
139
- outline: none;
140
- box-shadow: 0 0 0 2px var(--fd-ring);
141
- }
142
-
143
138
  .flowdrop-theme-toggle:active {
144
139
  transform: scale(0.98);
145
140
  }
@@ -17,6 +17,9 @@
17
17
 
18
18
  const fd = getInstance();
19
19
 
20
+ // Element ref used only to reach xyflow's node wrapper (our ancestor).
21
+ let universalNodeEl: HTMLDivElement;
22
+
20
23
  let {
21
24
  data,
22
25
  selected = false
@@ -61,6 +64,34 @@
61
64
  shouldShowNodeStatus(executionInfo) && resolvedComponentName !== 'note'
62
65
  );
63
66
 
67
+ // Keyboard activation lives on xyflow's node wrapper — the single focusable,
68
+ // arrow-movable element SvelteFlow manages. Because the wrapper is our
69
+ // ancestor, its keydown events never bubble down into our markup, so we bind
70
+ // directly to it. Enter/Space opens the node's config, mirroring the
71
+ // double-click (and single-click for square/atom) mouse paths. Node selection
72
+ // and arrow-key movement remain SvelteFlow's job.
73
+ $effect(() => {
74
+ const wrapper = universalNodeEl?.closest<HTMLElement>('.svelte-flow__node');
75
+ if (!wrapper) return;
76
+
77
+ function onWrapperKeydown(event: KeyboardEvent): void {
78
+ // Only when the node itself is focused — not an inner field (e.g. an
79
+ // editable note) — so we don't hijack typing.
80
+ if (event.target !== wrapper) return;
81
+ if (event.key === 'Enter' || event.key === ' ') {
82
+ event.preventDefault();
83
+ data.onConfigOpen?.({
84
+ id: data.nodeId ?? 'unknown',
85
+ type: resolvedComponentName,
86
+ data
87
+ });
88
+ }
89
+ }
90
+
91
+ wrapper.addEventListener('keydown', onWrapperKeydown);
92
+ return () => wrapper.removeEventListener('keydown', onWrapperKeydown);
93
+ });
94
+
64
95
  /**
65
96
  * Get the node component for the given type from the registry.
66
97
  *
@@ -129,7 +160,7 @@
129
160
  }
130
161
  </script>
131
162
 
132
- <div class="universal-node">
163
+ <div class="universal-node" bind:this={universalNodeEl}>
133
164
  <!-- Render the node component dynamically (Svelte 5 dynamic component syntax) -->
134
165
  {#if nodeComponent}
135
166
  <!-- Svelte 5 dynamic component limitation; reactivity maintained via $derived -->
@@ -26,7 +26,7 @@
26
26
  import CanvasController from './CanvasController.svelte';
27
27
  import FlowDropZone from './FlowDropZone.svelte';
28
28
  import EdgeRefresher from './EdgeRefresher.svelte';
29
- import { tick, untrack } from 'svelte';
29
+ import { tick, untrack, onMount } from 'svelte';
30
30
  import type { EndpointConfig } from '../config/endpoints.js';
31
31
  import type { AuthProvider } from '../types/auth.js';
32
32
  import ConnectionLine from './ConnectionLine.svelte';
@@ -34,6 +34,7 @@
34
34
  import { m } from '../messages/index.js';
35
35
  import { provideInstance } from '../stores/getInstance.svelte.js';
36
36
  import type { FlowDropInstance } from '../stores/instanceContainer.svelte.js';
37
+ import type { FlowDropGridVariant } from '../types/theme.js';
37
38
  import UniversalNode from './UniversalNode.svelte';
38
39
  import {
39
40
  EdgeStylingHelper,
@@ -56,7 +57,8 @@
56
57
  import { logger } from '../utils/logger.js';
57
58
  import { validateWorkflowData } from '../utils/validation.js';
58
59
  import { createEditorStateMachine } from '../stores/editorStateMachine.svelte.js';
59
- import Icon from '@iconify/svelte';
60
+ import CanvasIconButton from './CanvasIconButton.svelte';
61
+ import CommandLineIcon from './icons/CommandLineIcon.svelte';
60
62
  import { DEV } from 'esm-env';
61
63
 
62
64
  interface Props {
@@ -85,6 +87,23 @@
85
87
  onToggleConsole?: () => void;
86
88
  /** Per-instance state container (created by mount functions). Defaults to the page-default instance. */
87
89
  instance?: FlowDropInstance;
90
+ /**
91
+ * Canvas background grid pattern. Supplied by App from the active theme's
92
+ * `config.canvas.grid` default; any theme can opt into 'lines' / 'cross'.
93
+ * Color is driven separately by the `--fd-grid-pattern-color` token.
94
+ * @default 'dots'
95
+ */
96
+ gridVariant?: FlowDropGridVariant;
97
+ /**
98
+ * Register the built-in heavy form editors (markdown / code / template)
99
+ * on this instance's field registry. Batteries-included by default — node
100
+ * config fields with `format: 'markdown' | 'code' | 'template'` render real
101
+ * CodeMirror editors. The chunks are code-split (loaded lazily here), so
102
+ * the `/editor` static bundle stays light. Set `false` to keep the textarea
103
+ * fallback or register your own field components. See `features.builtinEditors`.
104
+ * @default true
105
+ */
106
+ builtinEditors?: boolean;
88
107
  }
89
108
 
90
109
  let props: Props = $props();
@@ -95,10 +114,36 @@
95
114
  // svelte-ignore state_referenced_locally
96
115
  const fd = provideInstance(props.instance);
97
116
 
117
+ // Batteries-included: register the built-in heavy form editors (markdown /
118
+ // code / template) on this instance's field registry so node config fields
119
+ // render real editors out of the box. Dynamic import is deliberate — the
120
+ // bundle guard only inspects *static* imports, and `form/markdown`/`form/code`
121
+ // statically re-export their CodeMirror components, so a static import here
122
+ // would leak CodeMirror into the light `/editor` entry. Importing lazily on
123
+ // mount keeps the static graph clean while the chunks load on demand.
124
+ // register*Field is idempotent per registry, so re-mounts are safe.
125
+ onMount(() => {
126
+ if (props.builtinEditors === false) return;
127
+ void (async () => {
128
+ const [code, markdown] = await Promise.all([
129
+ import('../form/code.js'),
130
+ import('../form/markdown.js')
131
+ ]);
132
+ code.registerCodeEditorField(fd.fields);
133
+ code.registerTemplateEditorField(fd.fields);
134
+ markdown.registerMarkdownEditorField(fd.fields);
135
+ })();
136
+ });
137
+
98
138
  // `mode` is the public API; the canvas only needs to know whether editing is
99
139
  // enabled. 'readonly' and 'locked' both disable interaction identically.
100
140
  const canvasEditable = $derived((props.mode ?? 'edit') === 'edit');
101
141
 
142
+ // Canvas grid pattern, supplied by the active theme's config.canvas.grid
143
+ // (App passes it down). BackgroundVariant's enum values are the same strings
144
+ // ('dots' | 'lines' | 'cross'), so the theme config maps straight through.
145
+ const gridVariant = $derived((props.gridVariant ?? 'dots') as BackgroundVariant);
146
+
102
147
  // ---------------------------------------------------------------------------
103
148
  // Editor State Machine
104
149
  // Centralizes reactive guards — replaces scattered boolean flags
@@ -875,23 +920,26 @@
875
920
  >
876
921
  <Controls />
877
922
  {#if canvasEditable && props.onToggleConsole}
878
- <button
923
+ <CanvasIconButton
879
924
  class="flowdrop-console-toggle"
880
- class:flowdrop-console-toggle--active={props.consoleOpen}
925
+ label={m().layout.commandConsole}
926
+ active={props.consoleOpen}
881
927
  onclick={props.onToggleConsole}
882
- aria-label={m().layout.commandConsole}
883
- title={m().layout.commandConsole}
884
- type="button"
885
928
  >
886
- <Icon icon="heroicons:command-line" width="18" height="18" />
887
- </button>
929
+ {#snippet icon()}
930
+ <CommandLineIcon />
931
+ {/snippet}
932
+ </CanvasIconButton>
888
933
  {/if}
889
934
  <!-- Always render Background for consistent bg color in dark/light mode -->
890
935
  <Background
891
936
  gap={getEditorSettings().gridSize}
892
- bgColor="var(--fd-background)"
893
- variant={BackgroundVariant.Dots}
894
- patternColor={getEditorSettings().showGrid ? undefined : 'transparent'}
937
+ bgColor="var(--fd-canvas-bg)"
938
+ variant={gridVariant}
939
+ lineWidth={1}
940
+ patternColor={getEditorSettings().showGrid
941
+ ? 'var(--fd-grid-pattern-color)'
942
+ : 'transparent'}
895
943
  />
896
944
  {#if getEditorSettings().showMinimap}
897
945
  <MiniMap />
@@ -1006,48 +1054,11 @@
1006
1054
  justify-content: space-between;
1007
1055
  }
1008
1056
 
1009
- .flowdrop-console-toggle {
1010
- position: absolute;
1057
+ /* Console toggle — placement only; visuals live in CanvasIconButton */
1058
+ :global(.flowdrop-console-toggle) {
1011
1059
  bottom: 140px;
1012
1060
  left: 12px;
1013
1061
  z-index: 5;
1014
- display: flex;
1015
- align-items: center;
1016
- justify-content: center;
1017
- width: 2rem;
1018
- height: 2rem;
1019
- border: 1px solid var(--fd-border);
1020
- border-radius: var(--fd-radius-md);
1021
- background-color: var(--fd-background);
1022
- color: var(--fd-muted-foreground);
1023
- cursor: pointer;
1024
- box-shadow: var(--fd-shadow-sm);
1025
- transition:
1026
- color var(--fd-transition-fast),
1027
- background-color var(--fd-transition-fast),
1028
- box-shadow var(--fd-transition-fast);
1029
- }
1030
-
1031
- .flowdrop-console-toggle:hover {
1032
- color: var(--fd-foreground);
1033
- background-color: var(--fd-subtle);
1034
- box-shadow: var(--fd-shadow-md);
1035
- }
1036
-
1037
- .flowdrop-console-toggle:focus {
1038
- outline: none;
1039
- box-shadow: 0 0 0 2px var(--fd-ring);
1040
- }
1041
-
1042
- .flowdrop-console-toggle--active {
1043
- color: var(--fd-primary);
1044
- background-color: var(--fd-primary-muted);
1045
- border-color: var(--fd-primary);
1046
- }
1047
-
1048
- .flowdrop-console-toggle--active:hover {
1049
- color: var(--fd-primary);
1050
- background-color: var(--fd-primary-muted);
1051
1062
  }
1052
1063
 
1053
1064
  :global(.flowdrop-workflow-editor .svelte-flow__node:hover) {
@@ -3,6 +3,7 @@ import type { WorkflowNode as WorkflowNodeType } from '../types/index.js';
3
3
  import type { EndpointConfig } from '../config/endpoints.js';
4
4
  import type { AuthProvider } from '../types/auth.js';
5
5
  import type { FlowDropInstance } from '../stores/instanceContainer.svelte.js';
6
+ import type { FlowDropGridVariant } from '../types/theme.js';
6
7
  interface Props {
7
8
  endpointConfig?: EndpointConfig;
8
9
  /** Auth provider applied to this instance's API requests. */
@@ -27,6 +28,23 @@ interface Props {
27
28
  onToggleConsole?: () => void;
28
29
  /** Per-instance state container (created by mount functions). Defaults to the page-default instance. */
29
30
  instance?: FlowDropInstance;
31
+ /**
32
+ * Canvas background grid pattern. Supplied by App from the active theme's
33
+ * `config.canvas.grid` default; any theme can opt into 'lines' / 'cross'.
34
+ * Color is driven separately by the `--fd-grid-pattern-color` token.
35
+ * @default 'dots'
36
+ */
37
+ gridVariant?: FlowDropGridVariant;
38
+ /**
39
+ * Register the built-in heavy form editors (markdown / code / template)
40
+ * on this instance's field registry. Batteries-included by default — node
41
+ * config fields with `format: 'markdown' | 'code' | 'template'` render real
42
+ * CodeMirror editors. The chunks are code-split (loaded lazily here), so
43
+ * the `/editor` static bundle stays light. Set `false` to keep the textarea
44
+ * fallback or register your own field components. See `features.builtinEditors`.
45
+ * @default true
46
+ */
47
+ builtinEditors?: boolean;
30
48
  }
31
49
  declare const WorkflowEditor: import("svelte").Component<Props, {
32
50
  updateNodeData: (nodeId: string, dataUpdates: Partial<WorkflowNodeType["data"]>) => void;
@@ -552,7 +552,7 @@
552
552
 
553
553
  .ai-chat-panel__messages::-webkit-scrollbar-thumb {
554
554
  background: var(--fd-scrollbar-thumb);
555
- border-radius: 4px;
555
+ border-radius: var(--fd-scrollbar-radius);
556
556
  }
557
557
 
558
558
  .ai-chat-panel__messages::-webkit-scrollbar-thumb:hover {
@@ -98,7 +98,7 @@
98
98
 
99
99
  .console-autocomplete::-webkit-scrollbar-thumb {
100
100
  background: var(--fd-scrollbar-thumb);
101
- border-radius: 3px;
101
+ border-radius: var(--fd-scrollbar-radius);
102
102
  }
103
103
 
104
104
  .console-autocomplete__item {
@@ -68,12 +68,12 @@
68
68
 
69
69
  .console-output::-webkit-scrollbar-track {
70
70
  background: var(--fd-scrollbar-track);
71
- border-radius: 4px;
71
+ border-radius: var(--fd-scrollbar-radius);
72
72
  }
73
73
 
74
74
  .console-output::-webkit-scrollbar-thumb {
75
75
  background: var(--fd-scrollbar-thumb);
76
- border-radius: 4px;
76
+ border-radius: var(--fd-scrollbar-radius);
77
77
  }
78
78
 
79
79
  .console-output::-webkit-scrollbar-thumb:hover {
@@ -670,9 +670,7 @@
670
670
  }
671
671
 
672
672
  .form-array__item-toggle:focus-visible {
673
- outline: none;
674
673
  border-color: var(--fd-primary);
675
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
676
674
  }
677
675
 
678
676
  .form-array__item-toggle :global(svg) {
@@ -717,11 +715,6 @@
717
715
  height: 1rem;
718
716
  }
719
717
 
720
- .form-array__action-btn:focus-visible {
721
- outline: none;
722
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
723
- }
724
-
725
718
  .form-array__action-btn:disabled {
726
719
  opacity: 0.35;
727
720
  cursor: not-allowed;
@@ -800,9 +793,7 @@
800
793
  }
801
794
 
802
795
  .form-array__input:focus {
803
- outline: none;
804
796
  border-color: var(--fd-primary);
805
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
806
797
  }
807
798
 
808
799
  .form-array__input--number {
@@ -837,9 +828,7 @@
837
828
  }
838
829
 
839
830
  .form-array__select:focus {
840
- outline: none;
841
831
  border-color: var(--fd-primary);
842
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
843
832
  }
844
833
 
845
834
  /* ============================================
@@ -1002,11 +991,6 @@
1002
991
  color: var(--fd-success-hover);
1003
992
  }
1004
993
 
1005
- .form-array__add-btn:focus-visible {
1006
- outline: none;
1007
- box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.3);
1008
- }
1009
-
1010
994
  .form-array__add-btn:active:not(:disabled) {
1011
995
  background-color: var(--fd-success);
1012
996
  }
@@ -787,7 +787,6 @@
787
787
  }
788
788
 
789
789
  .form-autocomplete--disabled {
790
- opacity: 0.6;
791
790
  pointer-events: none;
792
791
  }
793
792
 
@@ -804,7 +803,7 @@
804
803
  font-size: var(--fd-text-sm);
805
804
  font-family: inherit;
806
805
  color: var(--fd-foreground);
807
- background-color: var(--fd-muted);
806
+ background-color: var(--fd-background);
808
807
  transition: all var(--fd-transition-normal);
809
808
  box-shadow: var(--fd-shadow-sm);
810
809
  cursor: text;
@@ -817,11 +816,16 @@
817
816
  }
818
817
 
819
818
  .form-autocomplete__field--focused {
820
- border-color: var(--fd-primary);
819
+ border-color: var(--fd-ring);
821
820
  background-color: var(--fd-background);
822
- box-shadow:
823
- 0 0 0 3px rgba(59, 130, 246, 0.12),
824
- var(--fd-shadow-sm);
821
+ }
822
+
823
+ .form-autocomplete--disabled .form-autocomplete__field {
824
+ background-color: var(--fd-muted);
825
+ border-color: var(--fd-border-muted);
826
+ color: var(--fd-muted-foreground);
827
+ cursor: not-allowed;
828
+ opacity: 1;
825
829
  }
826
830
 
827
831
  /* Multiple mode - textarea-like styling */
@@ -143,10 +143,6 @@
143
143
  transform: scale(1);
144
144
  }
145
145
 
146
- .form-checkbox__input:focus-visible + .form-checkbox__custom {
147
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
148
- }
149
-
150
146
  .form-checkbox__label {
151
147
  font-size: var(--fd-text-sm);
152
148
  color: var(--fd-foreground);
@@ -364,7 +364,9 @@
364
364
  border: 1px solid var(--fd-border);
365
365
  border-radius: var(--fd-radius-lg);
366
366
  overflow: hidden;
367
- background-color: var(--fd-muted);
367
+ /* Match plain form fields (FormTextField/Textarea/Select): rest on the
368
+ input surface, not the recessed --fd-muted. */
369
+ background-color: var(--fd-background);
368
370
  transition: all var(--fd-transition-normal);
369
371
  box-shadow: var(--fd-shadow-sm);
370
372
  }
@@ -374,12 +376,15 @@
374
376
  background-color: var(--fd-background);
375
377
  }
376
378
 
379
+ /* Compound widget: the focusable CodeMirror lives inside this container, so
380
+ the standard ring (centralized for simple elements) is drawn here via
381
+ :focus-within using the same --fd-ring tokens. Outline paints outside the
382
+ container's overflow, so it isn't clipped. */
377
383
  .form-code-editor__container:focus-within {
378
384
  border-color: var(--fd-primary);
379
385
  background-color: var(--fd-background);
380
- box-shadow:
381
- 0 0 0 3px rgba(59, 130, 246, 0.12),
382
- var(--fd-shadow-sm);
386
+ outline: var(--fd-ring-width) solid var(--fd-ring);
387
+ outline-offset: var(--fd-ring-offset);
383
388
  }
384
389
 
385
390
  .form-code-editor--error .form-code-editor__container {
@@ -388,9 +393,6 @@
388
393
 
389
394
  .form-code-editor--error .form-code-editor__container:focus-within {
390
395
  border-color: var(--fd-error);
391
- box-shadow:
392
- 0 0 0 3px rgba(239, 68, 68, 0.12),
393
- var(--fd-shadow-sm);
394
396
  }
395
397
 
396
398
  /* Dark theme overrides */
@@ -233,11 +233,11 @@
233
233
  function getEditorHint(editorType: string): string {
234
234
  switch (editorType) {
235
235
  case 'code-editor-fallback':
236
- return "Code editor requires: import { registerCodeEditorField } from '@flowdrop/flowdrop/form/code'; registerCodeEditorField();";
236
+ return "Code editor not registered. Register it on the instance's field registry: import { registerCodeEditorField } from '@flowdrop/flowdrop/form/code'; registerCodeEditorField(instance.fields);";
237
237
  case 'markdown-editor-fallback':
238
- return "Markdown editor requires: import { registerMarkdownEditorField } from '@flowdrop/flowdrop/form/markdown'; registerMarkdownEditorField();";
238
+ return "Markdown editor not registered. Register it on the instance's field registry: import { registerMarkdownEditorField } from '@flowdrop/flowdrop/form/markdown'; registerMarkdownEditorField(instance.fields);";
239
239
  case 'template-editor-fallback':
240
- return "Template editor requires: import { registerTemplateEditorField } from '@flowdrop/flowdrop/form/code'; registerTemplateEditorField();";
240
+ return "Template editor not registered. Register it on the instance's field registry: import { registerTemplateEditorField } from '@flowdrop/flowdrop/form/code'; registerTemplateEditorField(instance.fields);";
241
241
  default:
242
242
  return 'This field type requires additional registration.';
243
243
  }
@@ -641,7 +641,8 @@
641
641
  border: 1px solid var(--fd-border);
642
642
  border-radius: var(--fd-radius-lg);
643
643
  overflow: hidden;
644
- background-color: var(--fd-muted);
644
+ /* Match plain form fields: rest on the input surface, not --fd-muted. */
645
+ background-color: var(--fd-background);
645
646
  transition: border-color var(--fd-transition-normal);
646
647
  }
647
648
 
@@ -655,12 +656,13 @@
655
656
  border-color: var(--fd-border-strong);
656
657
  }
657
658
 
659
+ /* Compound widget: ring drawn on the body via :focus-within, using the
660
+ standard --fd-ring tokens (outline isn't clipped by the container overflow). */
658
661
  .form-markdown-editor__body:focus-within {
659
662
  border-color: var(--fd-primary);
660
663
  background-color: var(--fd-background);
661
- box-shadow:
662
- 0 0 0 3px var(--fd-primary-muted),
663
- var(--fd-shadow-sm);
664
+ outline: var(--fd-ring-width) solid var(--fd-ring);
665
+ outline-offset: var(--fd-ring-offset);
664
666
  }
665
667
 
666
668
  /* ── Status bar ────────────────────────────────── */
@@ -702,7 +704,8 @@
702
704
 
703
705
  .form-markdown-editor__body :global(.cm-editor) {
704
706
  height: var(--editor-height, 300px);
705
- background-color: var(--fd-muted) !important;
707
+ /* Match plain form fields: white input surface (overrides oneDark in dark mode). */
708
+ background-color: var(--fd-background) !important;
706
709
  color: var(--fd-foreground) !important;
707
710
  }
708
711
 
@@ -102,11 +102,7 @@
102
102
  }
103
103
 
104
104
  .form-number-field:focus {
105
- outline: none;
106
105
  border-color: var(--fd-primary);
107
106
  background-color: var(--fd-background);
108
- box-shadow:
109
- 0 0 0 3px rgba(59, 130, 246, 0.12),
110
- var(--fd-shadow-sm);
111
107
  }
112
108
  </style>
@@ -165,12 +165,6 @@
165
165
  0 2px 4px rgba(0, 0, 0, 0.1);
166
166
  }
167
167
 
168
- .form-range-field:focus::-webkit-slider-thumb {
169
- box-shadow:
170
- 0 0 0 3px rgba(59, 130, 246, 0.2),
171
- 0 4px 12px rgba(59, 130, 246, 0.35);
172
- }
173
-
174
168
  /* Firefox - Track */
175
169
  .form-range-field::-moz-range-track {
176
170
  height: 6px;
@@ -205,20 +199,7 @@
205
199
  0 2px 4px rgba(0, 0, 0, 0.1);
206
200
  }
207
201
 
208
- .form-range-field:focus::-moz-range-thumb {
209
- box-shadow:
210
- 0 0 0 3px rgba(59, 130, 246, 0.2),
211
- 0 4px 12px rgba(59, 130, 246, 0.35);
212
- }
213
-
214
- /* Focus styles */
215
- .form-range-field:focus {
216
- outline: none;
217
- }
218
-
219
- .form-range-field:focus-visible {
220
- outline: none;
221
- }
202
+ /* Focus ring is centralized in base.css (outline on the range input). */
222
203
 
223
204
  /* Value display row */
224
205
  .form-range-values {
@@ -85,7 +85,7 @@
85
85
  font-size: var(--fd-text-sm);
86
86
  font-family: inherit;
87
87
  color: var(--fd-foreground);
88
- background-color: var(--fd-muted);
88
+ background-color: var(--fd-background);
89
89
  transition: all var(--fd-transition-normal);
90
90
  box-shadow: var(--fd-shadow-sm);
91
91
  cursor: pointer;
@@ -98,12 +98,16 @@
98
98
  }
99
99
 
100
100
  .form-select:focus {
101
- outline: none;
102
- border-color: var(--fd-primary);
101
+ border-color: var(--fd-ring);
103
102
  background-color: var(--fd-background);
104
- box-shadow:
105
- 0 0 0 3px rgba(59, 130, 246, 0.12),
106
- var(--fd-shadow-sm);
103
+ }
104
+
105
+ .form-select:disabled {
106
+ background-color: var(--fd-muted);
107
+ border-color: var(--fd-border-muted);
108
+ color: var(--fd-muted-foreground);
109
+ cursor: not-allowed;
110
+ opacity: 1;
107
111
  }
108
112
 
109
113
  .form-select__icon {