@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.
Files changed (87) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +5 -5
  3. package/dist/adapters/WorkflowAdapter.js +4 -5
  4. package/dist/adapters/agentspec/AgentSpecAdapter.js +3 -3
  5. package/dist/adapters/agentspec/defaultNodeTypes.js +9 -9
  6. package/dist/commands/executor.js +5 -6
  7. package/dist/commands/types.js +5 -5
  8. package/dist/components/App.svelte +19 -150
  9. package/dist/components/Button.stories.svelte +65 -0
  10. package/dist/components/Button.stories.svelte.d.ts +19 -0
  11. package/dist/components/Button.svelte +62 -0
  12. package/dist/components/Button.svelte.d.ts +24 -0
  13. package/dist/components/ConfigForm.svelte +4 -4
  14. package/dist/components/EditorStatusBar.stories.svelte +44 -0
  15. package/dist/components/EditorStatusBar.stories.svelte.d.ts +27 -0
  16. package/dist/components/EditorStatusBar.svelte +99 -0
  17. package/dist/components/EditorStatusBar.svelte.d.ts +15 -0
  18. package/dist/components/IconButton.svelte +80 -0
  19. package/dist/components/IconButton.svelte.d.ts +30 -0
  20. package/dist/components/Input.svelte +74 -0
  21. package/dist/components/Input.svelte.d.ts +17 -0
  22. package/dist/components/Navbar.svelte +9 -4
  23. package/dist/components/Navbar.svelte.d.ts +3 -0
  24. package/dist/components/NodeSidebar.svelte +17 -115
  25. package/dist/components/NodeSwapPicker.svelte +12 -28
  26. package/dist/components/Select.svelte +53 -0
  27. package/dist/components/Select.svelte.d.ts +15 -0
  28. package/dist/components/Textarea.svelte +39 -0
  29. package/dist/components/Textarea.svelte.d.ts +12 -0
  30. package/dist/components/ThemeToggle.svelte +15 -89
  31. package/dist/components/UniversalNode.svelte +5 -4
  32. package/dist/components/UniversalNode.svelte.d.ts +1 -1
  33. package/dist/components/console/ConsoleInput.svelte +3 -3
  34. package/dist/components/form/FormArray.svelte +37 -157
  35. package/dist/components/form/FormCheckboxGroup.svelte +1 -1
  36. package/dist/components/form/FormField.svelte +5 -44
  37. package/dist/components/form/FormFieldLight.svelte +5 -44
  38. package/dist/components/form/FormFieldset.svelte +1 -1
  39. package/dist/components/form/FormNumberField.svelte +4 -32
  40. package/dist/components/form/FormRangeField.svelte +17 -7
  41. package/dist/components/form/FormSelect.svelte +13 -79
  42. package/dist/components/form/FormTextField.svelte +3 -39
  43. package/dist/components/form/FormTextarea.svelte +4 -43
  44. package/dist/components/form/resolveFieldType.d.ts +24 -0
  45. package/dist/components/form/resolveFieldType.js +55 -0
  46. package/dist/components/icons/CloseIcon.svelte +6 -0
  47. package/dist/components/icons/CloseIcon.svelte.d.ts +26 -0
  48. package/dist/components/nodes/AtomNode.svelte +3 -3
  49. package/dist/components/nodes/AtomNode.svelte.d.ts +1 -1
  50. package/dist/components/nodes/GatewayNode.svelte +8 -11
  51. package/dist/components/nodes/GatewayNode.svelte.d.ts +1 -1
  52. package/dist/components/nodes/IdeaNode.svelte +10 -8
  53. package/dist/components/nodes/IdeaNode.svelte.d.ts +8 -4
  54. package/dist/components/nodes/NotesNode.svelte +6 -4
  55. package/dist/components/nodes/NotesNode.svelte.d.ts +8 -4
  56. package/dist/components/nodes/SimpleNode.svelte +10 -8
  57. package/dist/components/nodes/SimpleNode.svelte.d.ts +4 -4
  58. package/dist/components/nodes/SquareNode.svelte +10 -8
  59. package/dist/components/nodes/SquareNode.svelte.d.ts +4 -4
  60. package/dist/components/nodes/TerminalNode.svelte +10 -8
  61. package/dist/components/nodes/TerminalNode.svelte.d.ts +4 -4
  62. package/dist/components/nodes/ToolNode.svelte +10 -8
  63. package/dist/components/nodes/ToolNode.svelte.d.ts +6 -6
  64. package/dist/components/nodes/WorkflowNode.svelte +7 -10
  65. package/dist/components/nodes/WorkflowNode.svelte.d.ts +1 -1
  66. package/dist/components/playground/InputCollector.svelte +11 -46
  67. package/dist/components/playground/PipelineKanbanView.svelte +2 -2
  68. package/dist/components/playground/PipelineTableView.svelte +2 -2
  69. package/dist/helpers/workflowEditorHelper.js +4 -5
  70. package/dist/messages/index.d.ts +1 -1
  71. package/dist/messages/index.js +1 -1
  72. package/dist/openapi/v1/openapi.yaml +2 -2
  73. package/dist/registry/nodeComponentRegistry.d.ts +2 -1
  74. package/dist/services/dynamicSchemaService.d.ts +2 -2
  75. package/dist/services/dynamicSchemaService.js +8 -8
  76. package/dist/skins/drafter.js +41 -28
  77. package/dist/stores/playgroundStore.svelte.js +1 -1
  78. package/dist/stores/workflowStore.svelte.js +0 -18
  79. package/dist/styles/base.css +247 -5
  80. package/dist/styles/tokens.css +6 -0
  81. package/dist/svelte-app.js +68 -107
  82. package/dist/types/index.d.ts +6 -7
  83. package/dist/utils/connections.js +20 -56
  84. package/dist/utils/nodeIds.d.ts +1 -1
  85. package/dist/utils/nodeIds.js +1 -1
  86. package/dist/utils/nodeSwap.js +3 -6
  87. package/package.json +1 -1
@@ -19,6 +19,10 @@
19
19
 
20
20
  <script lang="ts">
21
21
  import Icon from '@iconify/svelte';
22
+ import Input from '../Input.svelte';
23
+ import Select from '../Select.svelte';
24
+ import Textarea from '../Textarea.svelte';
25
+ import IconButton from '../IconButton.svelte';
22
26
  import type { FieldSchema } from './types.js';
23
27
  import { m } from '../../messages/index.js';
24
28
 
@@ -284,40 +288,37 @@
284
288
  <!-- Action buttons group -->
285
289
  <div class="form-array__actions">
286
290
  <!-- Move Up button -->
287
- <button
288
- type="button"
289
- class="form-array__action-btn form-array__action-btn--move"
291
+ <IconButton
292
+ variant="primary"
290
293
  onclick={() => moveItemUp(index)}
291
294
  disabled={index === 0 || disabled}
292
- aria-label={t.moveItemUp({ n: index + 1 })}
295
+ ariaLabel={t.moveItemUp({ n: index + 1 })}
293
296
  title={t.moveUp}
294
297
  >
295
298
  <Icon icon="heroicons:arrow-up" />
296
- </button>
299
+ </IconButton>
297
300
 
298
301
  <!-- Move Down button -->
299
- <button
300
- type="button"
301
- class="form-array__action-btn form-array__action-btn--move"
302
+ <IconButton
303
+ variant="primary"
302
304
  onclick={() => moveItemDown(index)}
303
305
  disabled={index === items.length - 1 || disabled}
304
- aria-label={t.moveItemDown({ n: index + 1 })}
306
+ ariaLabel={t.moveItemDown({ n: index + 1 })}
305
307
  title={t.moveDown}
306
308
  >
307
309
  <Icon icon="heroicons:arrow-down" />
308
- </button>
310
+ </IconButton>
309
311
 
310
312
  <!-- Delete button -->
311
- <button
312
- type="button"
313
- class="form-array__action-btn form-array__action-btn--delete"
313
+ <IconButton
314
+ variant="danger"
314
315
  onclick={() => removeItem(index)}
315
316
  disabled={!canRemoveItem || disabled}
316
- aria-label={t.deleteItem({ n: index + 1 })}
317
+ ariaLabel={t.deleteItem({ n: index + 1 })}
317
318
  title={t.delete}
318
319
  >
319
320
  <Icon icon="heroicons:trash" />
320
- </button>
321
+ </IconButton>
321
322
  </div>
322
323
  </div>
323
324
 
@@ -330,18 +331,16 @@
330
331
  <!-- Simple type: render inline input -->
331
332
  {#if itemSchema.type === 'string'}
332
333
  {#if itemSchema.format === 'multiline'}
333
- <textarea
334
- class="form-array__input form-array__textarea"
334
+ <Textarea
335
335
  value={String(item ?? '')}
336
336
  placeholder={itemSchema.placeholder ?? ''}
337
337
  rows={3}
338
338
  oninput={(e) => updateItem(index, e.currentTarget.value)}
339
339
  {disabled}
340
- ></textarea>
340
+ />
341
341
  {:else}
342
- <input
342
+ <Input
343
343
  type="text"
344
- class="form-array__input"
345
344
  value={String(item ?? '')}
346
345
  placeholder={itemSchema.placeholder ?? ''}
347
346
  oninput={(e) => updateItem(index, e.currentTarget.value)}
@@ -349,9 +348,9 @@
349
348
  />
350
349
  {/if}
351
350
  {:else if itemSchema.type === 'number' || itemSchema.type === 'integer'}
352
- <input
351
+ <Input
353
352
  type="number"
354
- class="form-array__input form-array__input--number"
353
+ class="flowdrop-input--numeric"
355
354
  value={item as number}
356
355
  placeholder={itemSchema.placeholder ?? ''}
357
356
  min={itemSchema.minimum}
@@ -380,8 +379,7 @@
380
379
  </label>
381
380
  {:else if itemSchema.enum}
382
381
  <!-- Enum: render select -->
383
- <select
384
- class="form-array__select"
382
+ <Select
385
383
  value={String(item ?? '')}
386
384
  onchange={(e) => updateItem(index, e.currentTarget.value)}
387
385
  {disabled}
@@ -389,12 +387,11 @@
389
387
  {#each itemSchema.enum as option (option)}
390
388
  <option value={String(option)}>{String(option)}</option>
391
389
  {/each}
392
- </select>
390
+ </Select>
393
391
  {:else}
394
392
  <!-- Fallback to text -->
395
- <input
393
+ <Input
396
394
  type="text"
397
- class="form-array__input"
398
395
  value={String(item ?? '')}
399
396
  placeholder={itemSchema.placeholder ?? ''}
400
397
  oninput={(e) => updateItem(index, e.currentTarget.value)}
@@ -425,9 +422,8 @@
425
422
 
426
423
  <div class="form-array__subform-input">
427
424
  {#if propFieldSchema.enum}
428
- <select
425
+ <Select
429
426
  id="{id}-{index}-{propKey}"
430
- class="form-array__select"
431
427
  value={String(propValue ?? '')}
432
428
  onchange={(e) =>
433
429
  updateObjectProperty(index, propKey, e.currentTarget.value)}
@@ -436,23 +432,21 @@
436
432
  {#each propFieldSchema.enum as option (option)}
437
433
  <option value={String(option)}>{String(option)}</option>
438
434
  {/each}
439
- </select>
435
+ </Select>
440
436
  {:else if propFieldSchema.type === 'string' && propFieldSchema.format === 'multiline'}
441
- <textarea
437
+ <Textarea
442
438
  id="{id}-{index}-{propKey}"
443
- class="form-array__input form-array__textarea"
444
439
  value={String(propValue ?? '')}
445
440
  placeholder={propFieldSchema.placeholder ?? ''}
446
441
  rows={3}
447
442
  oninput={(e) =>
448
443
  updateObjectProperty(index, propKey, e.currentTarget.value)}
449
444
  {disabled}
450
- ></textarea>
445
+ />
451
446
  {:else if propFieldSchema.type === 'string'}
452
- <input
447
+ <Input
453
448
  id="{id}-{index}-{propKey}"
454
449
  type="text"
455
- class="form-array__input"
456
450
  value={String(propValue ?? '')}
457
451
  placeholder={propFieldSchema.placeholder ?? ''}
458
452
  oninput={(e) =>
@@ -460,10 +454,10 @@
460
454
  {disabled}
461
455
  />
462
456
  {:else if propFieldSchema.type === 'number' || propFieldSchema.type === 'integer'}
463
- <input
457
+ <Input
464
458
  id="{id}-{index}-{propKey}"
465
459
  type="number"
466
- class="form-array__input form-array__input--number"
460
+ class="flowdrop-input--numeric"
467
461
  value={propValue as number}
468
462
  placeholder={propFieldSchema.placeholder ?? ''}
469
463
  min={propFieldSchema.minimum}
@@ -493,10 +487,9 @@
493
487
  </span>
494
488
  </label>
495
489
  {:else}
496
- <input
490
+ <Input
497
491
  id="{id}-{index}-{propKey}"
498
492
  type="text"
499
- class="form-array__input"
500
493
  value={String(propValue ?? '')}
501
494
  placeholder={propFieldSchema.placeholder ?? ''}
502
495
  oninput={(e) =>
@@ -596,7 +589,7 @@
596
589
  flex-direction: column;
597
590
  background-color: var(--fd-muted);
598
591
  border: 1px solid var(--fd-border);
599
- border-radius: var(--fd-radius-lg);
592
+ border-radius: var(--fd-control-radius);
600
593
  overflow: hidden;
601
594
  animation: itemFadeIn 0.25s ease-out forwards;
602
595
  opacity: 0;
@@ -697,62 +690,8 @@
697
690
  margin-left: auto;
698
691
  }
699
692
 
700
- .form-array__action-btn {
701
- display: flex;
702
- align-items: center;
703
- justify-content: center;
704
- width: 2rem;
705
- height: 2rem;
706
- padding: 0;
707
- border: 1px solid transparent;
708
- border-radius: 0.375rem;
709
- cursor: pointer;
710
- transition: all 0.15s;
711
- }
712
-
713
- .form-array__action-btn :global(svg) {
714
- width: 1rem;
715
- height: 1rem;
716
- }
717
-
718
- .form-array__action-btn:disabled {
719
- opacity: 0.35;
720
- cursor: not-allowed;
721
- }
722
-
723
- /* Move Up/Down buttons - Blue semantic color */
724
- .form-array__action-btn--move {
725
- background-color: var(--fd-primary-muted);
726
- border-color: var(--fd-primary);
727
- color: var(--fd-primary-hover);
728
- }
729
-
730
- .form-array__action-btn--move:hover:not(:disabled) {
731
- background-color: var(--fd-primary-muted);
732
- border-color: var(--fd-primary-hover);
733
- color: var(--fd-primary-hover);
734
- }
735
-
736
- .form-array__action-btn--move:active:not(:disabled) {
737
- background-color: var(--fd-primary);
738
- }
739
-
740
- /* Delete button - Red/Warning semantic color */
741
- .form-array__action-btn--delete {
742
- background-color: var(--fd-error-muted);
743
- border-color: var(--fd-error);
744
- color: var(--fd-error);
745
- }
746
-
747
- .form-array__action-btn--delete:hover:not(:disabled) {
748
- background-color: var(--fd-error-muted);
749
- border-color: var(--fd-error-hover);
750
- color: var(--fd-error-hover);
751
- }
752
-
753
- .form-array__action-btn--delete:active:not(:disabled) {
754
- background-color: var(--fd-error);
755
- }
693
+ /* Action buttons (move/delete) now render through IconButton.svelte
694
+ geometry + semantic tints live in base.css (.flowdrop-btn--icon*). */
756
695
 
757
696
  /* ============================================
758
697
  ITEM CONTENT
@@ -772,65 +711,6 @@
772
711
  INPUTS (Simple Types)
773
712
  ============================================ */
774
713
 
775
- .form-array__input {
776
- width: 100%;
777
- padding: 0.5rem 0.75rem;
778
- border: 1px solid var(--fd-border);
779
- border-radius: var(--fd-radius-md);
780
- font-size: var(--fd-text-sm);
781
- font-family: inherit;
782
- color: var(--fd-foreground);
783
- background-color: var(--fd-background);
784
- transition: all var(--fd-transition-normal);
785
- }
786
-
787
- .form-array__input::placeholder {
788
- color: var(--fd-muted-foreground);
789
- }
790
-
791
- .form-array__input:hover {
792
- border-color: var(--fd-border-strong);
793
- }
794
-
795
- .form-array__input:focus {
796
- border-color: var(--fd-primary);
797
- }
798
-
799
- .form-array__input--number {
800
- font-variant-numeric: tabular-nums;
801
- }
802
-
803
- .form-array__textarea {
804
- resize: vertical;
805
- min-height: 4rem;
806
- line-height: 1.5;
807
- }
808
-
809
- .form-array__select {
810
- width: 100%;
811
- padding: 0.5rem 2rem 0.5rem 0.75rem;
812
- border: 1px solid var(--fd-border);
813
- border-radius: var(--fd-radius-md);
814
- font-size: var(--fd-text-sm);
815
- font-family: inherit;
816
- color: var(--fd-foreground);
817
- background-color: var(--fd-background);
818
- cursor: pointer;
819
- appearance: none;
820
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%239ca3af'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
821
- background-repeat: no-repeat;
822
- background-position: right 0.5rem center;
823
- background-size: 1rem;
824
- }
825
-
826
- .form-array__select:hover {
827
- border-color: var(--fd-border-strong);
828
- }
829
-
830
- .form-array__select:focus {
831
- border-color: var(--fd-primary);
832
- }
833
-
834
714
  /* ============================================
835
715
  TOGGLE (Boolean in Array)
836
716
  ============================================ */
@@ -947,7 +827,7 @@
947
827
  padding: 2rem 1rem;
948
828
  background-color: var(--fd-muted);
949
829
  border: 2px dashed var(--fd-border-strong);
950
- border-radius: var(--fd-radius-lg);
830
+ border-radius: var(--fd-control-radius);
951
831
  }
952
832
 
953
833
  .form-array__empty :global(svg) {
@@ -975,7 +855,7 @@
975
855
  gap: 0.5rem;
976
856
  padding: 0.625rem 1rem;
977
857
  border: 1px solid var(--fd-success);
978
- border-radius: var(--fd-radius-lg);
858
+ border-radius: var(--fd-control-radius);
979
859
  background-color: var(--fd-success-muted);
980
860
  color: var(--fd-success-hover);
981
861
  font-size: 0.8125rem;
@@ -86,7 +86,7 @@
86
86
  padding: 0.75rem;
87
87
  background-color: var(--fd-muted);
88
88
  border: 1px solid var(--fd-border);
89
- border-radius: var(--fd-radius-lg);
89
+ border-radius: var(--fd-control-radius);
90
90
  }
91
91
 
92
92
  .form-checkbox-item {
@@ -43,6 +43,7 @@
43
43
  import FormAutocomplete from './FormAutocomplete.svelte';
44
44
  import type { FieldSchema } from './types.js';
45
45
  import { getSchemaOptions } from './types.js';
46
+ import { resolveBaseFieldType } from './resolveFieldType.js';
46
47
  import type { WorkflowNode, WorkflowEdge, AuthProvider } from '../../types/index.js';
47
48
  import { getResolvedTheme } from '../../stores/settingsStore.svelte.js';
48
49
  import { getInstance } from '../../stores/getInstance.svelte.js';
@@ -139,50 +140,10 @@
139
140
  return 'template-editor';
140
141
  }
141
142
 
142
- // Enum with multiple selection -> checkbox group
143
- if (schema.enum && schema.multiple) {
144
- return 'checkbox-group';
145
- }
146
-
147
- // Enum with single selection -> select
148
- if (schema.enum) {
149
- return 'select-enum';
150
- }
151
-
152
- // oneOf with labeled options (standard JSON Schema) -> select
153
- // Must be checked before basic type checks since oneOf schemas often have type: 'string'
154
- if (schema.oneOf && schema.oneOf.length > 0) {
155
- return 'select-options';
156
- }
157
-
158
- // Multiline string -> textarea
159
- if (schema.type === 'string' && schema.format === 'multiline') {
160
- return 'textarea';
161
- }
162
-
163
- // Range slider for number/integer with format: "range"
164
- if ((schema.type === 'number' || schema.type === 'integer') && schema.format === 'range') {
165
- return 'range';
166
- }
167
-
168
- // String -> text field
169
- if (schema.type === 'string') {
170
- return 'text';
171
- }
172
-
173
- // Number or integer -> number field
174
- if (schema.type === 'number' || schema.type === 'integer') {
175
- return 'number';
176
- }
177
-
178
- // Boolean -> toggle
179
- if (schema.type === 'boolean') {
180
- return 'toggle';
181
- }
182
-
183
- // Future: Array type support
184
- if (schema.type === 'array') {
185
- return 'array';
143
+ // Shared basic field resolution (enum/oneOf/primitive types).
144
+ const base = resolveBaseFieldType(schema);
145
+ if (base) {
146
+ return base;
186
147
  }
187
148
 
188
149
  // Object type without specific format -> CodeMirror JSON editor
@@ -48,6 +48,7 @@
48
48
  const fd = getInstance();
49
49
  import type { FieldSchema } from './types.js';
50
50
  import { getSchemaOptions } from './types.js';
51
+ import { resolveBaseFieldType } from './resolveFieldType.js';
51
52
  import type { WorkflowNode, WorkflowEdge } from '../../types/index.js';
52
53
  import type { AuthProvider } from '../../types/auth.js';
53
54
 
@@ -144,50 +145,10 @@
144
145
  return 'code-editor-fallback';
145
146
  }
146
147
 
147
- // Enum with multiple selection -> checkbox group
148
- if (schema.enum && schema.multiple) {
149
- return 'checkbox-group';
150
- }
151
-
152
- // Enum with single selection -> select
153
- if (schema.enum) {
154
- return 'select-enum';
155
- }
156
-
157
- // oneOf with labeled options (standard JSON Schema) -> select
158
- // Must be checked before basic type checks since oneOf schemas often have type: 'string'
159
- if (schema.oneOf && schema.oneOf.length > 0) {
160
- return 'select-options';
161
- }
162
-
163
- // Multiline string -> textarea
164
- if (schema.type === 'string' && schema.format === 'multiline') {
165
- return 'textarea';
166
- }
167
-
168
- // Range slider for number/integer with format: "range"
169
- if ((schema.type === 'number' || schema.type === 'integer') && schema.format === 'range') {
170
- return 'range';
171
- }
172
-
173
- // String -> text field
174
- if (schema.type === 'string') {
175
- return 'text';
176
- }
177
-
178
- // Number or integer -> number field
179
- if (schema.type === 'number' || schema.type === 'integer') {
180
- return 'number';
181
- }
182
-
183
- // Boolean -> toggle
184
- if (schema.type === 'boolean') {
185
- return 'toggle';
186
- }
187
-
188
- // Array type
189
- if (schema.type === 'array') {
190
- return 'array';
148
+ // Shared basic field resolution (enum/oneOf/primitive types).
149
+ const base = resolveBaseFieldType(schema);
150
+ if (base) {
151
+ return base;
191
152
  }
192
153
 
193
154
  // Fallback to text
@@ -121,7 +121,7 @@
121
121
 
122
122
  .form-fieldset--static {
123
123
  border: 1px solid var(--fd-border-muted);
124
- border-radius: var(--fd-radius-lg);
124
+ border-radius: var(--fd-control-radius);
125
125
  padding: var(--fd-space-xl);
126
126
  margin: 0;
127
127
  }
@@ -9,6 +9,8 @@
9
9
  -->
10
10
 
11
11
  <script lang="ts">
12
+ import Input from '../Input.svelte';
13
+
12
14
  interface Props {
13
15
  /** Field identifier */
14
16
  id: string;
@@ -62,10 +64,10 @@
62
64
  }
63
65
  </script>
64
66
 
65
- <input
67
+ <Input
66
68
  {id}
67
69
  type="number"
68
- class="form-number-field"
70
+ class="flowdrop-input--numeric"
69
71
  value={value ?? ''}
70
72
  {placeholder}
71
73
  {min}
@@ -76,33 +78,3 @@
76
78
  aria-required={required}
77
79
  oninput={handleInput}
78
80
  />
79
-
80
- <style>
81
- .form-number-field {
82
- width: 100%;
83
- padding: 0.625rem 0.875rem;
84
- border: 1px solid var(--fd-border);
85
- border-radius: var(--fd-radius-lg);
86
- font-size: var(--fd-text-sm);
87
- font-family: inherit;
88
- font-variant-numeric: tabular-nums;
89
- color: var(--fd-foreground);
90
- background-color: var(--fd-muted);
91
- transition: all var(--fd-transition-normal);
92
- box-shadow: var(--fd-shadow-sm);
93
- }
94
-
95
- .form-number-field::placeholder {
96
- color: var(--fd-muted-foreground);
97
- }
98
-
99
- .form-number-field:hover {
100
- border-color: var(--fd-border-strong);
101
- background-color: var(--fd-background);
102
- }
103
-
104
- .form-number-field:focus {
105
- border-color: var(--fd-primary);
106
- background-color: var(--fd-background);
107
- }
108
- </style>
@@ -94,7 +94,7 @@
94
94
  aria-valuemax={max}
95
95
  aria-valuenow={numericValue}
96
96
  oninput={handleInput}
97
- style="--progress: {progressPercentage}%"
97
+ style="--progress: {progressPercentage}"
98
98
  />
99
99
  </div>
100
100
  <div class="form-range-values">
@@ -119,6 +119,16 @@
119
119
  }
120
120
 
121
121
  .form-range-field {
122
+ /* Thumb width; the fill edge is offset by half of this so it tracks the
123
+ thumb centre instead of the raw 0–100% of the track. */
124
+ --fd-range-thumb-size: 18px;
125
+ /* `--progress` is a unitless 0–100 set inline. The browser keeps the thumb
126
+ inside the track, so its centre travels from thumb/2 to (100% − thumb/2);
127
+ mirror that here so the colour fill lines up at every value, on load too. */
128
+ --fd-range-fill: calc(
129
+ var(--fd-range-thumb-size) / 2 + (100% - var(--fd-range-thumb-size)) * var(--progress, 0) /
130
+ 100
131
+ );
122
132
  width: 100%;
123
133
  height: 6px;
124
134
  border-radius: 3px;
@@ -127,8 +137,8 @@
127
137
  background: linear-gradient(
128
138
  to right,
129
139
  var(--fd-primary) 0%,
130
- var(--fd-primary) var(--progress, 0%),
131
- var(--fd-border) var(--progress, 0%),
140
+ var(--fd-primary) var(--fd-range-fill),
141
+ var(--fd-border) var(--fd-range-fill),
132
142
  var(--fd-border) 100%
133
143
  );
134
144
  cursor: pointer;
@@ -145,8 +155,8 @@
145
155
  .form-range-field::-webkit-slider-thumb {
146
156
  -webkit-appearance: none;
147
157
  appearance: none;
148
- width: 18px;
149
- height: 18px;
158
+ width: var(--fd-range-thumb-size);
159
+ height: var(--fd-range-thumb-size);
150
160
  border-radius: 50%;
151
161
  background: linear-gradient(135deg, var(--fd-background) 0%, var(--fd-muted) 100%);
152
162
  border: 2px solid var(--fd-primary);
@@ -180,8 +190,8 @@
180
190
 
181
191
  /* Firefox - Thumb */
182
192
  .form-range-field::-moz-range-thumb {
183
- width: 18px;
184
- height: 18px;
193
+ width: var(--fd-range-thumb-size);
194
+ height: var(--fd-range-thumb-size);
185
195
  border-radius: 50%;
186
196
  background: linear-gradient(135deg, var(--fd-background) 0%, var(--fd-muted) 100%);
187
197
  border: 2px solid var(--fd-primary);