@flowdrop/flowdrop 2.0.0-beta.2 → 2.0.0-beta.4

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 (113) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/MIGRATION-2.0.md +13 -0
  3. package/README.md +5 -5
  4. package/dist/components/App.svelte +36 -191
  5. package/dist/components/App.svelte.d.ts +2 -7
  6. package/dist/components/Button.stories.svelte +65 -0
  7. package/dist/components/Button.stories.svelte.d.ts +19 -0
  8. package/dist/components/Button.svelte +62 -0
  9. package/dist/components/Button.svelte.d.ts +24 -0
  10. package/dist/components/CanvasIconButton.svelte +76 -0
  11. package/dist/components/CanvasIconButton.svelte.d.ts +18 -0
  12. package/dist/components/ConfigForm.svelte +4 -23
  13. package/dist/components/ConfigPanel.svelte +4 -3
  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/LogoWordmark.svelte +113 -0
  23. package/dist/components/LogoWordmark.svelte.d.ts +26 -0
  24. package/dist/components/Navbar.svelte +17 -63
  25. package/dist/components/Navbar.svelte.d.ts +3 -0
  26. package/dist/components/NodeSidebar.svelte +17 -122
  27. package/dist/components/NodeSwapPicker.svelte +10 -28
  28. package/dist/components/PortMappingRow.svelte +0 -2
  29. package/dist/components/SchemaForm.svelte +0 -12
  30. package/dist/components/Select.svelte +53 -0
  31. package/dist/components/Select.svelte.d.ts +15 -0
  32. package/dist/components/SettingsModal.svelte +0 -5
  33. package/dist/components/SettingsPanel.svelte +2 -6
  34. package/dist/components/Textarea.svelte +39 -0
  35. package/dist/components/Textarea.svelte.d.ts +12 -0
  36. package/dist/components/ThemeToggle.svelte +15 -94
  37. package/dist/components/UniversalNode.svelte +32 -1
  38. package/dist/components/WorkflowEditor.svelte +62 -51
  39. package/dist/components/WorkflowEditor.svelte.d.ts +18 -0
  40. package/dist/components/chat/AIChatPanel.svelte +1 -1
  41. package/dist/components/console/ConsoleAutocomplete.svelte +1 -1
  42. package/dist/components/console/ConsoleOutput.svelte +2 -2
  43. package/dist/components/form/FormArray.svelte +37 -173
  44. package/dist/components/form/FormAutocomplete.svelte +10 -6
  45. package/dist/components/form/FormCheckboxGroup.svelte +1 -5
  46. package/dist/components/form/FormCodeEditor.svelte +9 -7
  47. package/dist/components/form/FormField.svelte +5 -44
  48. package/dist/components/form/FormFieldLight.svelte +8 -47
  49. package/dist/components/form/FormFieldset.svelte +1 -1
  50. package/dist/components/form/FormMarkdownEditor.svelte +8 -5
  51. package/dist/components/form/FormNumberField.svelte +4 -36
  52. package/dist/components/form/FormRangeField.svelte +18 -27
  53. package/dist/components/form/FormSelect.svelte +13 -75
  54. package/dist/components/form/FormTemplateEditor.svelte +6 -4
  55. package/dist/components/form/FormTextField.svelte +3 -35
  56. package/dist/components/form/FormTextarea.svelte +4 -39
  57. package/dist/components/form/FormToggle.svelte +0 -4
  58. package/dist/components/form/resolveFieldType.d.ts +24 -0
  59. package/dist/components/form/resolveFieldType.js +55 -0
  60. package/dist/components/icons/CloseIcon.svelte +6 -0
  61. package/dist/components/icons/CloseIcon.svelte.d.ts +26 -0
  62. package/dist/components/icons/CommandLineIcon.svelte +15 -0
  63. package/dist/components/icons/CommandLineIcon.svelte.d.ts +26 -0
  64. package/dist/components/icons/MenuIcon.svelte +4 -0
  65. package/dist/components/icons/MenuIcon.svelte.d.ts +26 -0
  66. package/dist/components/icons/MenuOpenIcon.svelte +6 -0
  67. package/dist/components/icons/MenuOpenIcon.svelte.d.ts +26 -0
  68. package/dist/components/interrupt/ChoicePrompt.svelte +0 -10
  69. package/dist/components/interrupt/ConfirmationPrompt.svelte +0 -5
  70. package/dist/components/interrupt/InterruptBubble.svelte +0 -10
  71. package/dist/components/interrupt/ReviewPrompt.svelte +0 -20
  72. package/dist/components/interrupt/TextInputPrompt.svelte +0 -6
  73. package/dist/components/layouts/MainLayout.svelte +4 -5
  74. package/dist/components/nodes/AtomNode.svelte +46 -34
  75. package/dist/components/nodes/GatewayNode.svelte +91 -99
  76. package/dist/components/nodes/IdeaNode.svelte +62 -90
  77. package/dist/components/nodes/NodeConfigButton.svelte +86 -0
  78. package/dist/components/nodes/NodeConfigButton.svelte.d.ts +15 -0
  79. package/dist/components/nodes/NotesNode.svelte +70 -81
  80. package/dist/components/nodes/SimpleNode.svelte +28 -78
  81. package/dist/components/nodes/SquareNode.svelte +79 -109
  82. package/dist/components/nodes/TerminalNode.svelte +28 -86
  83. package/dist/components/nodes/ToolNode.svelte +82 -95
  84. package/dist/components/nodes/WorkflowNode.svelte +91 -100
  85. package/dist/components/playground/ChatInput.svelte +0 -1
  86. package/dist/components/playground/InputCollector.svelte +11 -48
  87. package/dist/components/playground/PlaygroundApp.svelte +1 -1
  88. package/dist/components/playground/PlaygroundStudio.svelte +0 -5
  89. package/dist/messages/index.d.ts +1 -1
  90. package/dist/messages/index.js +1 -1
  91. package/dist/openapi/v1/openapi.yaml +2 -2
  92. package/dist/playground/mount.d.ts +9 -5
  93. package/dist/playground/mount.js +9 -5
  94. package/dist/skins/drafter.d.ts +30 -0
  95. package/dist/skins/drafter.js +198 -0
  96. package/dist/skins/index.d.ts +2 -1
  97. package/dist/skins/index.js +4 -2
  98. package/dist/styles/base.css +285 -14
  99. package/dist/styles/tokens.css +60 -2
  100. package/dist/svelte-app.d.ts +6 -0
  101. package/dist/svelte-app.js +71 -109
  102. package/dist/themes/drafter.d.ts +2 -0
  103. package/dist/themes/drafter.js +15 -0
  104. package/dist/themes/index.d.ts +2 -1
  105. package/dist/themes/index.js +8 -2
  106. package/dist/types/events.d.ts +18 -0
  107. package/dist/types/events.js +2 -1
  108. package/dist/types/settings.d.ts +1 -1
  109. package/dist/types/settings.js +1 -1
  110. package/dist/types/skin.d.ts +1 -1
  111. package/dist/types/theme.d.ts +16 -2
  112. package/dist/utils/connections.js +14 -50
  113. package/package.json +1 -1
@@ -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;
@@ -1044,7 +1044,7 @@
1044
1044
  justify-content: center;
1045
1045
  gap: var(--fd-space-xs);
1046
1046
  padding: 0.625rem var(--fd-space-xl);
1047
- border-radius: var(--fd-radius-lg);
1047
+ border-radius: var(--fd-control-radius);
1048
1048
  font-size: var(--fd-text-sm);
1049
1049
  font-weight: 600;
1050
1050
  font-family: inherit;
@@ -1073,11 +1073,6 @@
1073
1073
  color: var(--fd-foreground);
1074
1074
  }
1075
1075
 
1076
- .config-form__button--secondary:focus-visible {
1077
- outline: none;
1078
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
1079
- }
1080
-
1081
1076
  .config-form__button--primary {
1082
1077
  background: linear-gradient(135deg, var(--fd-primary) 0%, var(--fd-primary-hover) 100%);
1083
1078
  color: var(--fd-primary-foreground);
@@ -1098,13 +1093,6 @@
1098
1093
  transform: translateY(0);
1099
1094
  }
1100
1095
 
1101
- .config-form__button--primary:focus-visible {
1102
- outline: none;
1103
- box-shadow:
1104
- 0 0 0 3px rgba(59, 130, 246, 0.4),
1105
- 0 4px 12px rgba(59, 130, 246, 0.35);
1106
- }
1107
-
1108
1096
  /* ============================================
1109
1097
  UI EXTENSIONS SECTION
1110
1098
  ============================================ */
@@ -1112,7 +1100,7 @@
1112
1100
  .config-form__extensions {
1113
1101
  background-color: var(--fd-muted);
1114
1102
  border: 1px solid var(--fd-border);
1115
- border-radius: var(--fd-radius-lg);
1103
+ border-radius: var(--fd-control-radius);
1116
1104
  overflow: hidden;
1117
1105
  margin-top: var(--fd-space-xs);
1118
1106
  }
@@ -1283,7 +1271,7 @@
1283
1271
  .config-form__debug {
1284
1272
  background-color: var(--fd-warning-muted);
1285
1273
  border: 1px solid var(--fd-warning);
1286
- border-radius: var(--fd-radius-lg);
1274
+ border-radius: var(--fd-control-radius);
1287
1275
  overflow: hidden;
1288
1276
  }
1289
1277
 
@@ -1437,7 +1425,7 @@
1437
1425
  .config-form__error {
1438
1426
  background-color: var(--fd-error-muted);
1439
1427
  border: 1px solid var(--fd-error);
1440
- border-radius: var(--fd-radius-lg);
1428
+ border-radius: var(--fd-control-radius);
1441
1429
  overflow: hidden;
1442
1430
  }
1443
1431
 
@@ -1559,11 +1547,4 @@
1559
1547
  .config-form__button--external:active {
1560
1548
  transform: translateY(0);
1561
1549
  }
1562
-
1563
- .config-form__button--external:focus-visible {
1564
- outline: none;
1565
- box-shadow:
1566
- 0 0 0 3px rgba(99, 102, 241, 0.4),
1567
- 0 4px 12px rgba(99, 102, 241, 0.35);
1568
- }
1569
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
 
@@ -0,0 +1,44 @@
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import EditorStatusBar from './EditorStatusBar.svelte';
4
+ import { fn } from 'storybook/test';
5
+
6
+ const { Story } = defineMeta({
7
+ title: 'Editor/EditorStatusBar',
8
+ component: EditorStatusBar,
9
+ tags: ['autodocs'],
10
+ parameters: {
11
+ // The banner is full-width editor chrome — show it edge to edge.
12
+ layout: 'fullscreen'
13
+ },
14
+ args: {
15
+ onRetry: fn(),
16
+ onSetApiUrl: fn(),
17
+ onTestApi: fn(),
18
+ onDismiss: fn()
19
+ }
20
+ });
21
+ </script>
22
+
23
+ <Story
24
+ name="Default"
25
+ args={{
26
+ error: 'API Error: Failed to fetch. No node types available.'
27
+ }}
28
+ />
29
+
30
+ <!-- The real message when the endpoint serves HTML (e.g. a 404 page) instead of JSON. -->
31
+ <Story
32
+ name="Long Message"
33
+ args={{
34
+ error:
35
+ 'API Error: Unexpected token \'<\', "<!doctype "... is not valid JSON. No node types available.'
36
+ }}
37
+ />
38
+
39
+ <Story
40
+ name="Endpoint Unreachable"
41
+ args={{
42
+ error: 'API Error: NetworkError when attempting to fetch resource. No node types available.'
43
+ }}
44
+ />
@@ -0,0 +1,27 @@
1
+ export default EditorStatusBar;
2
+ type EditorStatusBar = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const EditorStatusBar: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ import EditorStatusBar from './EditorStatusBar.svelte';
15
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
16
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
17
+ $$bindings?: Bindings;
18
+ } & Exports;
19
+ (internal: unknown, props: {
20
+ $$events?: Events;
21
+ $$slots?: Slots;
22
+ }): Exports & {
23
+ $set?: any;
24
+ $on?: any;
25
+ };
26
+ z_$$bindings?: Bindings;
27
+ }
@@ -0,0 +1,99 @@
1
+ <!--
2
+ EditorStatusBar — the dismissible error banner shown above the editor canvas
3
+ when node types fail to load (endpoint missing or unreachable).
4
+
5
+ Extracted from App.svelte so it can be storied/tested in isolation. The action
6
+ buttons route through the shared Button primitive; all colours come from design
7
+ tokens, so the banner stays in sync with the rest of the editor chrome.
8
+ -->
9
+ <script lang="ts">
10
+ import Button from './Button.svelte';
11
+ import CloseIcon from './icons/CloseIcon.svelte';
12
+
13
+ interface Props {
14
+ /** The error message to display (rendered after an "Error:" prefix). */
15
+ error: string;
16
+ /** Retry loading node types. */
17
+ onRetry: () => void;
18
+ /** Prompt for / set a new backend API URL. */
19
+ onSetApiUrl: () => void;
20
+ /** Run a connectivity test against the configured endpoint. */
21
+ onTestApi: () => void;
22
+ /** Dismiss the banner. */
23
+ onDismiss: () => void;
24
+ }
25
+
26
+ let { error, onRetry, onSetApiUrl, onTestApi, onDismiss }: Props = $props();
27
+ </script>
28
+
29
+ <!-- aria-live announces the API error dynamically without requiring focus -->
30
+ <div class="flowdrop-status flowdrop-status--error" aria-live="polite" aria-atomic="true">
31
+ <div class="flowdrop-status__content">
32
+ <div class="flowdrop-status__message">
33
+ <div class="flowdrop-status__indicator"></div>
34
+ <span class="flowdrop-status__text">Error: {error}</span>
35
+ </div>
36
+ <div class="flowdrop-status__actions">
37
+ <Button variant="primary" size="sm" onclick={onRetry}>Retry</Button>
38
+ <Button variant="outline" size="sm" onclick={onSetApiUrl}>Set API URL</Button>
39
+ <Button variant="outline" size="sm" onclick={onTestApi}>Test API</Button>
40
+ <Button variant="ghost" size="sm" ariaLabel="Dismiss error" onclick={onDismiss}>
41
+ <span class="flowdrop-status__dismiss-icon"><CloseIcon /></span>
42
+ </Button>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <style>
48
+ .flowdrop-status {
49
+ padding: var(--fd-space-xl);
50
+ }
51
+
52
+ .flowdrop-status--error {
53
+ background-color: var(--fd-error-muted);
54
+ border-bottom: 1px solid var(--fd-error);
55
+ }
56
+
57
+ .flowdrop-status__content {
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: space-between;
61
+ gap: var(--fd-space-md);
62
+ }
63
+
64
+ .flowdrop-status__message {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: var(--fd-space-md);
68
+ min-width: 0;
69
+ }
70
+
71
+ .flowdrop-status__indicator {
72
+ flex-shrink: 0;
73
+ width: 0.5rem;
74
+ height: 0.5rem;
75
+ border-radius: 50%;
76
+ background-color: var(--fd-error);
77
+ }
78
+
79
+ .flowdrop-status__text {
80
+ font-size: var(--fd-text-sm);
81
+ line-height: 1.25rem;
82
+ font-weight: 500;
83
+ }
84
+
85
+ .flowdrop-status__actions {
86
+ display: flex;
87
+ gap: var(--fd-space-xs);
88
+ flex-shrink: 0;
89
+ }
90
+
91
+ .flowdrop-status__dismiss-icon {
92
+ display: inline-flex;
93
+ }
94
+
95
+ .flowdrop-status__dismiss-icon :global(svg) {
96
+ width: 0.875rem;
97
+ height: 0.875rem;
98
+ }
99
+ </style>
@@ -0,0 +1,15 @@
1
+ interface Props {
2
+ /** The error message to display (rendered after an "Error:" prefix). */
3
+ error: string;
4
+ /** Retry loading node types. */
5
+ onRetry: () => void;
6
+ /** Prompt for / set a new backend API URL. */
7
+ onSetApiUrl: () => void;
8
+ /** Run a connectivity test against the configured endpoint. */
9
+ onTestApi: () => void;
10
+ /** Dismiss the banner. */
11
+ onDismiss: () => void;
12
+ }
13
+ declare const EditorStatusBar: import("svelte").Component<Props, {}, "">;
14
+ type EditorStatusBar = ReturnType<typeof EditorStatusBar>;
15
+ export default EditorStatusBar;
@@ -0,0 +1,80 @@
1
+ <!--
2
+ IconButton — typed wrapper over the `.flowdrop-btn--icon` system (base.css).
3
+
4
+ The icon-only sibling of Button.svelte: a square button that holds a single
5
+ glyph (Icon, inline SVG, or character). All geometry, variants, sizes and the
6
+ centralized focus ring live in base.css; this component is the ergonomic,
7
+ type-safe entry point so callers pick `variant`/`size` instead of hand-writing
8
+ class strings and re-declaring widths/tints in scoped styles.
9
+
10
+ Internal for now (not exported from any public entry) so the API isn't frozen
11
+ before GA. Existing hand-rolled square buttons migrate onto it incrementally.
12
+ Canvas-overlay buttons keep their bespoke components (CanvasIconButton,
13
+ NodeConfigButton) — those add absolute positioning, shadows and backdrop blur
14
+ this primitive deliberately stays out of.
15
+ -->
16
+
17
+ <script lang="ts">
18
+ import type { Snippet } from 'svelte';
19
+
20
+ interface Props {
21
+ /**
22
+ * Visual style. `ghost` (default) is transparent until hover; `default` is a
23
+ * flat surface with a resting border; `primary`/`danger`/`success` are quiet
24
+ * semantic tints (muted fill + coloured border) that go solid on press.
25
+ */
26
+ variant?: 'ghost' | 'default' | 'primary' | 'danger' | 'success';
27
+ /** Square size — `md` (32px) is the base; `sm` (28px) / `lg` (36px) add a modifier */
28
+ size?: 'sm' | 'md' | 'lg';
29
+ /** Native button type */
30
+ type?: 'button' | 'submit' | 'reset';
31
+ /** Tooltip text */
32
+ title?: string;
33
+ /** Accessible label — required, since the button is icon-only */
34
+ ariaLabel: string;
35
+ /** Toggle/pressed state — applies the active tint and sets `aria-pressed` */
36
+ active?: boolean;
37
+ /** Disabled state */
38
+ disabled?: boolean;
39
+ /** Extra classes appended to the root button */
40
+ class?: string;
41
+ /** Click handler */
42
+ onclick?: (event: MouseEvent) => void;
43
+ /** The glyph (icon, svg, or character) */
44
+ children: Snippet;
45
+ }
46
+
47
+ let {
48
+ variant = 'ghost',
49
+ size = 'md',
50
+ type = 'button',
51
+ title,
52
+ ariaLabel,
53
+ active = false,
54
+ disabled = false,
55
+ class: className = '',
56
+ onclick,
57
+ children
58
+ }: Props = $props();
59
+
60
+ // `ghost` reuses the shared `.flowdrop-btn--ghost`; the other variants have
61
+ // icon-specific tint classes so they don't collide with the solid text buttons.
62
+ const variantClass = $derived(
63
+ variant === 'ghost' ? 'flowdrop-btn--ghost' : `flowdrop-btn--icon-${variant}`
64
+ );
65
+ // 'md' is the unmodified base size; only 'sm'/'lg' need a size modifier.
66
+ const sizeClass = $derived(size === 'md' ? '' : `flowdrop-btn--${size}`);
67
+ </script>
68
+
69
+ <button
70
+ class="flowdrop-btn flowdrop-btn--icon {variantClass} {sizeClass} {className}"
71
+ class:is-active={active}
72
+ {type}
73
+ {title}
74
+ {disabled}
75
+ aria-label={ariaLabel}
76
+ aria-pressed={active ? true : undefined}
77
+ {onclick}
78
+ >
79
+ {@render children()}
80
+ </button>
@@ -0,0 +1,30 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /**
4
+ * Visual style. `ghost` (default) is transparent until hover; `default` is a
5
+ * flat surface with a resting border; `primary`/`danger`/`success` are quiet
6
+ * semantic tints (muted fill + coloured border) that go solid on press.
7
+ */
8
+ variant?: 'ghost' | 'default' | 'primary' | 'danger' | 'success';
9
+ /** Square size — `md` (32px) is the base; `sm` (28px) / `lg` (36px) add a modifier */
10
+ size?: 'sm' | 'md' | 'lg';
11
+ /** Native button type */
12
+ type?: 'button' | 'submit' | 'reset';
13
+ /** Tooltip text */
14
+ title?: string;
15
+ /** Accessible label — required, since the button is icon-only */
16
+ ariaLabel: string;
17
+ /** Toggle/pressed state — applies the active tint and sets `aria-pressed` */
18
+ active?: boolean;
19
+ /** Disabled state */
20
+ disabled?: boolean;
21
+ /** Extra classes appended to the root button */
22
+ class?: string;
23
+ /** Click handler */
24
+ onclick?: (event: MouseEvent) => void;
25
+ /** The glyph (icon, svg, or character) */
26
+ children: Snippet;
27
+ }
28
+ declare const IconButton: import("svelte").Component<Props, {}, "">;
29
+ type IconButton = ReturnType<typeof IconButton>;
30
+ export default IconButton;
@@ -0,0 +1,74 @@
1
+ <!--
2
+ Input — typed wrapper over the shared `.flowdrop-input` system (base.css).
3
+
4
+ All control styling (surface, border, radius, sizes, focus ring, disabled =
5
+ the only muted state) lives in base.css. This component is the ergonomic,
6
+ type-safe entry point so callers pick `size`/`invalid`/`leading`/`trailing`
7
+ instead of hand-writing class strings — the single place text-like fields
8
+ should route through. Mirrors Button.svelte.
9
+
10
+ Native attributes (type, value, placeholder, disabled, id, aria-*, oninput…)
11
+ are forwarded verbatim to the underlying <input>.
12
+
13
+ Internal for now (not exported from any public entry) so the API isn't frozen
14
+ before GA. Existing hand-rolled inputs migrate onto it incrementally.
15
+ -->
16
+
17
+ <script lang="ts">
18
+ import type { Snippet } from 'svelte';
19
+ import type { HTMLInputAttributes } from 'svelte/elements';
20
+
21
+ // Omit native numeric `size` so our design-token size (matching Button) wins.
22
+ interface Props extends Omit<HTMLInputAttributes, 'size'> {
23
+ /** Size — `md` is the base `.flowdrop-input`; `sm`/`lg` add a modifier */
24
+ size?: 'sm' | 'md' | 'lg';
25
+ /** Renders the error-border state */
26
+ invalid?: boolean;
27
+ /** Extra classes appended to the input */
28
+ class?: string;
29
+ /** Optional leading affordance (e.g. a search icon) */
30
+ leading?: Snippet;
31
+ /** Optional trailing affordance */
32
+ trailing?: Snippet;
33
+ }
34
+
35
+ let {
36
+ size = 'md',
37
+ invalid = false,
38
+ class: className = '',
39
+ leading,
40
+ trailing,
41
+ ...rest
42
+ }: Props = $props();
43
+
44
+ const inputClass = $derived(
45
+ [
46
+ 'flowdrop-input',
47
+ size === 'md' ? '' : `flowdrop-input--${size}`,
48
+ invalid ? 'flowdrop-input--invalid' : '',
49
+ leading ? 'flowdrop-input--has-leading' : '',
50
+ trailing ? 'flowdrop-input--has-trailing' : '',
51
+ className
52
+ ]
53
+ .filter(Boolean)
54
+ .join(' ')
55
+ );
56
+ </script>
57
+
58
+ {#if leading || trailing}
59
+ <div class="flowdrop-input-wrap">
60
+ {#if leading}
61
+ <span class="flowdrop-input-wrap__icon flowdrop-input-wrap__icon--leading">
62
+ {@render leading()}
63
+ </span>
64
+ {/if}
65
+ <input class={inputClass} {...rest} />
66
+ {#if trailing}
67
+ <span class="flowdrop-input-wrap__icon flowdrop-input-wrap__icon--trailing">
68
+ {@render trailing()}
69
+ </span>
70
+ {/if}
71
+ </div>
72
+ {:else}
73
+ <input class={inputClass} {...rest} />
74
+ {/if}
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ interface Props extends Omit<HTMLInputAttributes, 'size'> {
4
+ /** Size — `md` is the base `.flowdrop-input`; `sm`/`lg` add a modifier */
5
+ size?: 'sm' | 'md' | 'lg';
6
+ /** Renders the error-border state */
7
+ invalid?: boolean;
8
+ /** Extra classes appended to the input */
9
+ class?: string;
10
+ /** Optional leading affordance (e.g. a search icon) */
11
+ leading?: Snippet;
12
+ /** Optional trailing affordance */
13
+ trailing?: Snippet;
14
+ }
15
+ declare const Input: import("svelte").Component<Props, {}, "">;
16
+ type Input = ReturnType<typeof Input>;
17
+ export default Input;