@urbicon-ui/blocks 6.43.0 → 6.43.2

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 (43) hide show
  1. package/README.md +1 -1
  2. package/dist/components/Calendar/calendar.variants.d.ts +63 -63
  3. package/dist/components/Chat/ChatMessage/ChatMessage.svelte +93 -67
  4. package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +7 -0
  5. package/dist/components/Chat/ChatMessage/chat-message.variants.js +45 -12
  6. package/dist/components/Chat/ChatMessage/index.d.ts +20 -5
  7. package/dist/components/Chat/CodeBlock/CodeBlock.svelte +59 -33
  8. package/dist/components/Chat/CodeBlock/code-block.variants.d.ts +7 -0
  9. package/dist/components/Chat/CodeBlock/code-block.variants.js +28 -13
  10. package/dist/components/Chat/CodeBlock/index.d.ts +39 -4
  11. package/dist/components/Chat/PromptInput/PromptInput.svelte +9 -16
  12. package/dist/components/Chat/PromptInput/prompt-input.variants.js +16 -15
  13. package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte +9 -4
  14. package/dist/components/Chat/ToolCallCard/index.d.ts +4 -3
  15. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.d.ts +0 -3
  16. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.js +4 -4
  17. package/dist/components/CopyButton/CopyButton.svelte +9 -19
  18. package/dist/components/CopyButton/copy-button.variants.d.ts +4 -4
  19. package/dist/i18n/index.d.ts +398 -2
  20. package/dist/internal/copy-state.svelte.d.ts +60 -0
  21. package/dist/internal/copy-state.svelte.js +76 -0
  22. package/dist/internal/field-chrome.js +1 -1
  23. package/dist/primitives/Alert/alert.variants.d.ts +8 -8
  24. package/dist/primitives/Combobox/combobox.variants.js +11 -4
  25. package/dist/primitives/JourneyTimeline/journey-timeline.variants.d.ts +22 -22
  26. package/dist/primitives/RadioGroup/radioGroup.variants.js +6 -1
  27. package/dist/primitives/Select/select.variants.js +1 -1
  28. package/dist/primitives/Spinner/spinner.variants.d.ts +16 -16
  29. package/dist/primitives/Stepper/stepper.variants.d.ts +11 -11
  30. package/dist/primitives/Stepper/stepper.variants.js +2 -1
  31. package/dist/primitives/Tab/tab.variants.js +4 -1
  32. package/dist/primitives/Textarea/textarea.variants.js +1 -1
  33. package/dist/primitives/Toast/toast.variants.d.ts +12 -12
  34. package/dist/primitives/Toggle/toggle.variants.js +9 -7
  35. package/dist/style/foundation.css +19 -9
  36. package/dist/style/semantic.css +49 -2
  37. package/dist/style/themes/forest.css +2 -2
  38. package/dist/style/themes/neutral.css +2 -2
  39. package/dist/style/themes/ocean.css +2 -2
  40. package/dist/style/themes/rose.css +2 -2
  41. package/dist/style/themes/sunset.css +2 -2
  42. package/dist/utils/figma-token-export.js +7 -3
  43. package/package.json +3 -3
@@ -52,7 +52,205 @@ import enTranslations from '../translations/en.js';
52
52
  * This keeps the `de` catalog out of English-only client bundles (the whole point
53
53
  * of the split) while making it eagerly present wherever it is actually rendered.
54
54
  */
55
- export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<Translations>;
55
+ export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<{
56
+ readonly accessibility: {
57
+ readonly avatar: "Avatar";
58
+ readonly avatarGroup: "User avatars";
59
+ readonly breadcrumb: "Breadcrumb";
60
+ readonly breadcrumbExpand: "Show all breadcrumb items";
61
+ readonly clearInput: "Clear input";
62
+ readonly clearSearch: "Clear search";
63
+ readonly clearSelection: "Clear selection";
64
+ readonly closeDialog: "Close dialog";
65
+ readonly closeDrawer: "Close drawer";
66
+ readonly copied: "Copied";
67
+ readonly copy: "Copy";
68
+ readonly copyFailed: "Copy failed";
69
+ readonly dismiss: "Dismiss";
70
+ readonly fileUpload: "File upload";
71
+ readonly loading: "Loading";
72
+ readonly maximum: "Maximum";
73
+ readonly minimum: "Minimum";
74
+ readonly pagination: "Page navigation";
75
+ readonly pinInputCell: "Character {{index}} of {{total}}";
76
+ readonly progress: "Progress";
77
+ readonly qrCode: "QR code";
78
+ readonly slider: "Slider";
79
+ readonly timeHours: "Hours";
80
+ readonly timeMinutes: "Minutes";
81
+ readonly timeSeconds: "Seconds";
82
+ readonly timeMeridiem: "AM or PM";
83
+ readonly toggle: "Toggle";
84
+ readonly toggleOptions: "Toggle options";
85
+ readonly removableBadge: "Removable badge";
86
+ readonly removeBadge: "Remove badge";
87
+ readonly removeFile: "Remove {{name}}";
88
+ readonly removeTag: "Remove {{label}}";
89
+ };
90
+ readonly button: {
91
+ readonly close: "Close";
92
+ readonly apply: "Apply";
93
+ readonly cancel: "Cancel";
94
+ readonly confirm: "Confirm";
95
+ };
96
+ readonly menu: {
97
+ readonly placeholder: "Select an option...";
98
+ };
99
+ readonly common: {
100
+ readonly loading: "Loading...";
101
+ };
102
+ readonly languages: {
103
+ readonly de: "Deutsch";
104
+ readonly en: "English";
105
+ readonly es: "Español";
106
+ readonly fr: "Français";
107
+ readonly it: "Italiano";
108
+ readonly nl: "Nederlands";
109
+ };
110
+ readonly localeSwitcher: {
111
+ readonly ariaLabel: "Language selection";
112
+ readonly placeholder: "Select language...";
113
+ };
114
+ readonly pagination: {
115
+ readonly next: "Next";
116
+ readonly first: "First";
117
+ readonly last: "Last";
118
+ readonly page: "Page";
119
+ readonly previous: "Previous";
120
+ readonly pageInfo: "{{label}} {{current}} of {{total}}";
121
+ readonly rangeInfo: "{{start}}–{{end}} of {{total}}";
122
+ };
123
+ readonly calendar: {
124
+ readonly recurring: "Recurring event";
125
+ readonly previousMonth: "Previous month";
126
+ readonly nextMonth: "Next month";
127
+ readonly previousWeek: "Previous week";
128
+ readonly nextWeek: "Next week";
129
+ readonly previousDay: "Previous day";
130
+ readonly nextDay: "Next day";
131
+ readonly previousYear: "Previous year";
132
+ readonly nextYear: "Next year";
133
+ readonly today: "Today";
134
+ readonly calendarGrid: "Calendar";
135
+ readonly weekdays: "Weekdays";
136
+ readonly weekNumber: "Week number";
137
+ readonly events: "Events";
138
+ readonly noEvents: "No events";
139
+ readonly showMore: "{{count}} more";
140
+ readonly legend: "Legend";
141
+ readonly yearView: "Year overview";
142
+ readonly weekView: "Week view";
143
+ readonly viewMonth: "Month";
144
+ readonly viewYear: "Year";
145
+ readonly viewWeek: "Week";
146
+ readonly viewDay: "Day";
147
+ readonly multiDayLabel: "Day {{current}} of {{total}}";
148
+ readonly agendaView: "Agenda view";
149
+ readonly viewAgenda: "Agenda";
150
+ readonly viewSwitcher: "View mode";
151
+ };
152
+ readonly planner: {
153
+ readonly previousWeek: "Previous week";
154
+ readonly nextWeek: "Next week";
155
+ readonly previousMonth: "Previous month";
156
+ readonly nextMonth: "Next month";
157
+ readonly previousRange: "Previous range";
158
+ readonly nextRange: "Next range";
159
+ readonly today: "Today";
160
+ readonly grid: "Planner";
161
+ readonly itemCount: "{{count}} items";
162
+ };
163
+ readonly commandPalette: {
164
+ readonly noResults: "No results found.";
165
+ readonly search: "Search...";
166
+ readonly hints: {
167
+ readonly navigate: "Navigate";
168
+ readonly select: "Select";
169
+ readonly close: "Close";
170
+ };
171
+ };
172
+ readonly compositionBar: {
173
+ readonly total: "Total";
174
+ readonly summary: "Composition: {{total}} ({{count}} shares) — {{parts}}";
175
+ readonly share: "Share";
176
+ readonly value: "Value";
177
+ readonly percent: "Percent";
178
+ readonly remaining: "remaining";
179
+ };
180
+ readonly guide: {
181
+ readonly next: "Next";
182
+ readonly previous: "Back";
183
+ readonly skip: "Skip tour";
184
+ readonly done: "Done";
185
+ readonly step: "Step {{current}} of {{total}}";
186
+ readonly tour: "Guided tour";
187
+ readonly close: "Close";
188
+ readonly openHelp: "Help";
189
+ readonly backToList: "All topics";
190
+ readonly info: "More information";
191
+ readonly infoAbout: "More information about {{label}}";
192
+ readonly dismiss: "Dismiss hint";
193
+ readonly startTour: "Start the guided tour";
194
+ readonly actionRequired: "Complete the highlighted action to continue";
195
+ readonly filterPlaceholder: "Filter topics…";
196
+ readonly noResults: "No matching topics";
197
+ };
198
+ readonly journeyTimeline: {
199
+ readonly label: "Journey";
200
+ readonly status: {
201
+ readonly complete: "Completed";
202
+ readonly active: "In progress";
203
+ readonly pending: "Pending";
204
+ readonly attention: "Needs attention";
205
+ readonly blocked: "Blocked";
206
+ readonly skipped: "Skipped";
207
+ };
208
+ };
209
+ readonly datepicker: {
210
+ readonly placeholder: "Select a date...";
211
+ readonly rangePlaceholder: "Select a date range...";
212
+ readonly openCalendar: "Open calendar";
213
+ readonly invalidDate: "Invalid date";
214
+ readonly outOfRange: "Date is outside the allowed range";
215
+ readonly invalidRange: "Invalid date range";
216
+ };
217
+ readonly fileUpload: {
218
+ readonly exists: "File already added";
219
+ readonly invalidType: "File type {{type}} is not allowed";
220
+ readonly tooLarge: "File exceeds {{size}} limit";
221
+ readonly tooMany: "Maximum {{count}} files allowed";
222
+ readonly tooSmall: "File must be at least {{size}}";
223
+ };
224
+ readonly sankey: {
225
+ readonly summary: "Sankey diagram: {{nodes}} nodes, {{links}} links — {{flows}}";
226
+ readonly source: "Source";
227
+ readonly target: "Target";
228
+ readonly value: "Value";
229
+ };
230
+ readonly slider: {
231
+ readonly rangeStatus: {
232
+ readonly insideRecommended: "In recommended range";
233
+ readonly insideValid: "In valid range";
234
+ readonly insideValidOnly: "Outside recommended range, but valid";
235
+ readonly outsideValid: "Outside valid range";
236
+ };
237
+ };
238
+ readonly themeSwitcher: {
239
+ readonly lightMode: "Light mode";
240
+ readonly darkMode: "Dark mode";
241
+ readonly systemTheme: "System theme";
242
+ };
243
+ readonly chart: {
244
+ readonly category: "Category";
245
+ readonly series: "Series {{index}}";
246
+ readonly segment: "Segment";
247
+ readonly value: "Value";
248
+ readonly share: "Share";
249
+ };
250
+ readonly stepper: {
251
+ readonly optional: "Optional";
252
+ };
253
+ }>;
56
254
  /**
57
255
  * Context-scoped translation hook for the blocks package. Call during component
58
256
  * initialisation, then use the returned `t` (conventionally aliased `bt`):
@@ -67,7 +265,205 @@ export declare const blocksI18n: import("@urbicon-ui/i18n").PackageI18n<Translat
67
265
  * locale (`de`) that isn't loaded yet, resolution falls back to the base locale
68
266
  * (`en`); see the module docblock and `registerBlocksLocale` for the SSR path.
69
267
  */
70
- export declare const useBlocksI18n: any;
268
+ export declare const useBlocksI18n: () => import("@urbicon-ui/i18n").TypedTranslationFunction<{
269
+ readonly accessibility: {
270
+ readonly avatar: "Avatar";
271
+ readonly avatarGroup: "User avatars";
272
+ readonly breadcrumb: "Breadcrumb";
273
+ readonly breadcrumbExpand: "Show all breadcrumb items";
274
+ readonly clearInput: "Clear input";
275
+ readonly clearSearch: "Clear search";
276
+ readonly clearSelection: "Clear selection";
277
+ readonly closeDialog: "Close dialog";
278
+ readonly closeDrawer: "Close drawer";
279
+ readonly copied: "Copied";
280
+ readonly copy: "Copy";
281
+ readonly copyFailed: "Copy failed";
282
+ readonly dismiss: "Dismiss";
283
+ readonly fileUpload: "File upload";
284
+ readonly loading: "Loading";
285
+ readonly maximum: "Maximum";
286
+ readonly minimum: "Minimum";
287
+ readonly pagination: "Page navigation";
288
+ readonly pinInputCell: "Character {{index}} of {{total}}";
289
+ readonly progress: "Progress";
290
+ readonly qrCode: "QR code";
291
+ readonly slider: "Slider";
292
+ readonly timeHours: "Hours";
293
+ readonly timeMinutes: "Minutes";
294
+ readonly timeSeconds: "Seconds";
295
+ readonly timeMeridiem: "AM or PM";
296
+ readonly toggle: "Toggle";
297
+ readonly toggleOptions: "Toggle options";
298
+ readonly removableBadge: "Removable badge";
299
+ readonly removeBadge: "Remove badge";
300
+ readonly removeFile: "Remove {{name}}";
301
+ readonly removeTag: "Remove {{label}}";
302
+ };
303
+ readonly button: {
304
+ readonly close: "Close";
305
+ readonly apply: "Apply";
306
+ readonly cancel: "Cancel";
307
+ readonly confirm: "Confirm";
308
+ };
309
+ readonly menu: {
310
+ readonly placeholder: "Select an option...";
311
+ };
312
+ readonly common: {
313
+ readonly loading: "Loading...";
314
+ };
315
+ readonly languages: {
316
+ readonly de: "Deutsch";
317
+ readonly en: "English";
318
+ readonly es: "Español";
319
+ readonly fr: "Français";
320
+ readonly it: "Italiano";
321
+ readonly nl: "Nederlands";
322
+ };
323
+ readonly localeSwitcher: {
324
+ readonly ariaLabel: "Language selection";
325
+ readonly placeholder: "Select language...";
326
+ };
327
+ readonly pagination: {
328
+ readonly next: "Next";
329
+ readonly first: "First";
330
+ readonly last: "Last";
331
+ readonly page: "Page";
332
+ readonly previous: "Previous";
333
+ readonly pageInfo: "{{label}} {{current}} of {{total}}";
334
+ readonly rangeInfo: "{{start}}–{{end}} of {{total}}";
335
+ };
336
+ readonly calendar: {
337
+ readonly recurring: "Recurring event";
338
+ readonly previousMonth: "Previous month";
339
+ readonly nextMonth: "Next month";
340
+ readonly previousWeek: "Previous week";
341
+ readonly nextWeek: "Next week";
342
+ readonly previousDay: "Previous day";
343
+ readonly nextDay: "Next day";
344
+ readonly previousYear: "Previous year";
345
+ readonly nextYear: "Next year";
346
+ readonly today: "Today";
347
+ readonly calendarGrid: "Calendar";
348
+ readonly weekdays: "Weekdays";
349
+ readonly weekNumber: "Week number";
350
+ readonly events: "Events";
351
+ readonly noEvents: "No events";
352
+ readonly showMore: "{{count}} more";
353
+ readonly legend: "Legend";
354
+ readonly yearView: "Year overview";
355
+ readonly weekView: "Week view";
356
+ readonly viewMonth: "Month";
357
+ readonly viewYear: "Year";
358
+ readonly viewWeek: "Week";
359
+ readonly viewDay: "Day";
360
+ readonly multiDayLabel: "Day {{current}} of {{total}}";
361
+ readonly agendaView: "Agenda view";
362
+ readonly viewAgenda: "Agenda";
363
+ readonly viewSwitcher: "View mode";
364
+ };
365
+ readonly planner: {
366
+ readonly previousWeek: "Previous week";
367
+ readonly nextWeek: "Next week";
368
+ readonly previousMonth: "Previous month";
369
+ readonly nextMonth: "Next month";
370
+ readonly previousRange: "Previous range";
371
+ readonly nextRange: "Next range";
372
+ readonly today: "Today";
373
+ readonly grid: "Planner";
374
+ readonly itemCount: "{{count}} items";
375
+ };
376
+ readonly commandPalette: {
377
+ readonly noResults: "No results found.";
378
+ readonly search: "Search...";
379
+ readonly hints: {
380
+ readonly navigate: "Navigate";
381
+ readonly select: "Select";
382
+ readonly close: "Close";
383
+ };
384
+ };
385
+ readonly compositionBar: {
386
+ readonly total: "Total";
387
+ readonly summary: "Composition: {{total}} ({{count}} shares) — {{parts}}";
388
+ readonly share: "Share";
389
+ readonly value: "Value";
390
+ readonly percent: "Percent";
391
+ readonly remaining: "remaining";
392
+ };
393
+ readonly guide: {
394
+ readonly next: "Next";
395
+ readonly previous: "Back";
396
+ readonly skip: "Skip tour";
397
+ readonly done: "Done";
398
+ readonly step: "Step {{current}} of {{total}}";
399
+ readonly tour: "Guided tour";
400
+ readonly close: "Close";
401
+ readonly openHelp: "Help";
402
+ readonly backToList: "All topics";
403
+ readonly info: "More information";
404
+ readonly infoAbout: "More information about {{label}}";
405
+ readonly dismiss: "Dismiss hint";
406
+ readonly startTour: "Start the guided tour";
407
+ readonly actionRequired: "Complete the highlighted action to continue";
408
+ readonly filterPlaceholder: "Filter topics…";
409
+ readonly noResults: "No matching topics";
410
+ };
411
+ readonly journeyTimeline: {
412
+ readonly label: "Journey";
413
+ readonly status: {
414
+ readonly complete: "Completed";
415
+ readonly active: "In progress";
416
+ readonly pending: "Pending";
417
+ readonly attention: "Needs attention";
418
+ readonly blocked: "Blocked";
419
+ readonly skipped: "Skipped";
420
+ };
421
+ };
422
+ readonly datepicker: {
423
+ readonly placeholder: "Select a date...";
424
+ readonly rangePlaceholder: "Select a date range...";
425
+ readonly openCalendar: "Open calendar";
426
+ readonly invalidDate: "Invalid date";
427
+ readonly outOfRange: "Date is outside the allowed range";
428
+ readonly invalidRange: "Invalid date range";
429
+ };
430
+ readonly fileUpload: {
431
+ readonly exists: "File already added";
432
+ readonly invalidType: "File type {{type}} is not allowed";
433
+ readonly tooLarge: "File exceeds {{size}} limit";
434
+ readonly tooMany: "Maximum {{count}} files allowed";
435
+ readonly tooSmall: "File must be at least {{size}}";
436
+ };
437
+ readonly sankey: {
438
+ readonly summary: "Sankey diagram: {{nodes}} nodes, {{links}} links — {{flows}}";
439
+ readonly source: "Source";
440
+ readonly target: "Target";
441
+ readonly value: "Value";
442
+ };
443
+ readonly slider: {
444
+ readonly rangeStatus: {
445
+ readonly insideRecommended: "In recommended range";
446
+ readonly insideValid: "In valid range";
447
+ readonly insideValidOnly: "Outside recommended range, but valid";
448
+ readonly outsideValid: "Outside valid range";
449
+ };
450
+ };
451
+ readonly themeSwitcher: {
452
+ readonly lightMode: "Light mode";
453
+ readonly darkMode: "Dark mode";
454
+ readonly systemTheme: "System theme";
455
+ };
456
+ readonly chart: {
457
+ readonly category: "Category";
458
+ readonly series: "Series {{index}}";
459
+ readonly segment: "Segment";
460
+ readonly value: "Value";
461
+ readonly share: "Share";
462
+ };
463
+ readonly stepper: {
464
+ readonly optional: "Optional";
465
+ };
466
+ }>;
71
467
  /**
72
468
  * Eagerly register the blocks bundle for a lazy locale (currently `de`) so it is
73
469
  * present for SSR / the first render, instead of loading client-side on mount.
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Shared clipboard-copy interaction state.
3
+ *
4
+ * Three components ship a copy affordance — `CopyButton`, `CodeBlock` and
5
+ * `ChatMessage` — and each had hand-rolled the same sequence: write to the
6
+ * clipboard, flip a flag, start a revert timer, clear that timer on unmount,
7
+ * expose a live region so the outcome is announced. Three copies of one
8
+ * interaction drift, and they had: only `CopyButton` surfaced a FAILED copy at
9
+ * all. The other two logged to the console and left the button looking idle, so
10
+ * a user whose clipboard permission was denied got no feedback whatsoever.
11
+ *
12
+ * This module is that interaction, once. It owns the state machine and the timer;
13
+ * the call site owns the chrome (icon, label, live region markup) and the
14
+ * callbacks, because those legitimately differ — a labelled Button, a header
15
+ * icon, a hover action bar.
16
+ *
17
+ * Call {@link createCopyState} during component initialisation: it registers an
18
+ * `$effect` teardown so a pending revert timer can never fire into an unmounted
19
+ * component, which is exactly the cleanup each call site used to repeat.
20
+ *
21
+ * Not a component and imports nothing from other component dirs, so it is exempt
22
+ * from the cross-component import guard (imports-lint treats `internal/**` as the
23
+ * extraction target) — which is the point: `CodeBlock` gets the shared behaviour
24
+ * WITHOUT importing the public `CopyButton`, so no public-to-public edge is
25
+ * created and no consumer bundle grows a `Button` it never asked for.
26
+ */
27
+ /** Resting → confirmed → failed. `error` reverts on the same timer as `copied`. */
28
+ export type CopyPhase = 'idle' | 'copied' | 'error';
29
+ export interface CopyStateOptions {
30
+ /**
31
+ * Milliseconds before reverting to `idle`; `0` keeps the outcome pinned.
32
+ * A getter, not a number, for the same reason the outcome is returned instead
33
+ * of delivered by callback: the options object is built once at init, so a
34
+ * plain `timeout` would freeze a reactive prop at its first value (the Svelte
35
+ * compiler flags it as "only captures the initial value").
36
+ * @default () => 2000
37
+ */
38
+ timeout?: () => number;
39
+ }
40
+ /**
41
+ * Outcome of one copy attempt, returned rather than delivered through
42
+ * callbacks. Callbacks would have to be captured when the state is created —
43
+ * i.e. once, at component init — which silently pins whatever handler the
44
+ * consumer passed on the first render (the Svelte compiler flags exactly this as
45
+ * "only captures the initial value"). Returning the result lets each call site
46
+ * read its own live props at the moment of the click.
47
+ */
48
+ export type CopyResult = {
49
+ ok: true;
50
+ } | {
51
+ ok: false;
52
+ error: unknown;
53
+ };
54
+ export interface CopyState {
55
+ /** Current phase. Reactive — read it in `$derived` / markup. */
56
+ readonly phase: CopyPhase;
57
+ /** Write `value` to the clipboard and advance the phase. Never throws. */
58
+ copy: (value: string) => Promise<CopyResult>;
59
+ }
60
+ export declare function createCopyState(options?: CopyStateOptions): CopyState;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Shared clipboard-copy interaction state.
3
+ *
4
+ * Three components ship a copy affordance — `CopyButton`, `CodeBlock` and
5
+ * `ChatMessage` — and each had hand-rolled the same sequence: write to the
6
+ * clipboard, flip a flag, start a revert timer, clear that timer on unmount,
7
+ * expose a live region so the outcome is announced. Three copies of one
8
+ * interaction drift, and they had: only `CopyButton` surfaced a FAILED copy at
9
+ * all. The other two logged to the console and left the button looking idle, so
10
+ * a user whose clipboard permission was denied got no feedback whatsoever.
11
+ *
12
+ * This module is that interaction, once. It owns the state machine and the timer;
13
+ * the call site owns the chrome (icon, label, live region markup) and the
14
+ * callbacks, because those legitimately differ — a labelled Button, a header
15
+ * icon, a hover action bar.
16
+ *
17
+ * Call {@link createCopyState} during component initialisation: it registers an
18
+ * `$effect` teardown so a pending revert timer can never fire into an unmounted
19
+ * component, which is exactly the cleanup each call site used to repeat.
20
+ *
21
+ * Not a component and imports nothing from other component dirs, so it is exempt
22
+ * from the cross-component import guard (imports-lint treats `internal/**` as the
23
+ * extraction target) — which is the point: `CodeBlock` gets the shared behaviour
24
+ * WITHOUT importing the public `CopyButton`, so no public-to-public edge is
25
+ * created and no consumer bundle grows a `Button` it never asked for.
26
+ */
27
+ export function createCopyState(options = {}) {
28
+ const readTimeout = options.timeout ?? (() => 2000);
29
+ let phase = $state('idle');
30
+ let timer;
31
+ function scheduleRevert() {
32
+ if (timer)
33
+ clearTimeout(timer);
34
+ timer = undefined;
35
+ const ms = readTimeout();
36
+ if (ms <= 0)
37
+ return;
38
+ timer = setTimeout(() => {
39
+ phase = 'idle';
40
+ timer = undefined;
41
+ }, ms);
42
+ }
43
+ // Registered here rather than left to the call site: every previous copy of
44
+ // this logic had to remember the same teardown, and a missed one leaks a timer
45
+ // that fires into a torn-down component.
46
+ $effect(() => () => {
47
+ if (timer)
48
+ clearTimeout(timer);
49
+ });
50
+ return {
51
+ get phase() {
52
+ return phase;
53
+ },
54
+ async copy(value) {
55
+ let result;
56
+ try {
57
+ // Optional-chained on purpose: a non-secure context has no
58
+ // `navigator.clipboard` at all, and reading `.writeText` off undefined
59
+ // would throw a TypeError instead of reaching the error phase.
60
+ if (!navigator.clipboard?.writeText)
61
+ throw new Error('Clipboard API unavailable');
62
+ await navigator.clipboard.writeText(value);
63
+ phase = 'copied';
64
+ result = { ok: true };
65
+ }
66
+ catch (error) {
67
+ phase = 'error';
68
+ result = { ok: false, error };
69
+ }
70
+ // The phase is settled and the revert armed BEFORE the caller runs its
71
+ // callback, so a throwing consumer handler cannot leave the button stuck.
72
+ scheduleRevert();
73
+ return result;
74
+ }
75
+ };
76
+ }
@@ -57,7 +57,7 @@ export const fieldFocusRing = (f) => `${f}:border-primary ${f}:ring-2 ${f}:ring-
57
57
  */
58
58
  export const fieldSurfaceVariants = (f) => ({
59
59
  outlined: 'border-border-subtle',
60
- filled: `bg-surface-interactive border-transparent hover:bg-surface-hover ${f}:bg-surface-base`,
60
+ filled: `bg-surface-interactive border-transparent hover:bg-surface-interactive-hover ${f}:bg-surface-base`,
61
61
  ghost: `bg-transparent hover:bg-surface-subtle ${f}:bg-surface-base ${f}:border-border-subtle`
62
62
  });
63
63
  /**
@@ -1,53 +1,53 @@
1
1
  import { type SlotNames, type VariantProps } from '../../utils/variants.js';
2
2
  export declare const alertVariants: ((props?: {
3
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
3
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
4
4
  variant?: "inline" | "filled" | "soft" | undefined;
5
5
  size?: "sm" | "md" | "lg" | undefined;
6
6
  } | undefined) => {
7
7
  base: (props?: ({
8
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
8
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
9
9
  variant?: "inline" | "filled" | "soft" | undefined;
10
10
  size?: "sm" | "md" | "lg" | undefined;
11
11
  } & {
12
12
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
13
13
  }) | undefined) => string;
14
14
  icon: (props?: ({
15
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
15
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
16
16
  variant?: "inline" | "filled" | "soft" | undefined;
17
17
  size?: "sm" | "md" | "lg" | undefined;
18
18
  } & {
19
19
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
20
20
  }) | undefined) => string;
21
21
  content: (props?: ({
22
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
22
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
23
23
  variant?: "inline" | "filled" | "soft" | undefined;
24
24
  size?: "sm" | "md" | "lg" | undefined;
25
25
  } & {
26
26
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
27
27
  }) | undefined) => string;
28
28
  title: (props?: ({
29
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
29
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
30
30
  variant?: "inline" | "filled" | "soft" | undefined;
31
31
  size?: "sm" | "md" | "lg" | undefined;
32
32
  } & {
33
33
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
34
34
  }) | undefined) => string;
35
35
  description: (props?: ({
36
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
36
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
37
37
  variant?: "inline" | "filled" | "soft" | undefined;
38
38
  size?: "sm" | "md" | "lg" | undefined;
39
39
  } & {
40
40
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
41
41
  }) | undefined) => string;
42
42
  actions: (props?: ({
43
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
43
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
44
44
  variant?: "inline" | "filled" | "soft" | undefined;
45
45
  size?: "sm" | "md" | "lg" | undefined;
46
46
  } & {
47
47
  class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
48
48
  }) | undefined) => string;
49
49
  dismissButton: (props?: ({
50
- intent?: "primary" | "success" | "warning" | "danger" | "neutral" | "info" | undefined;
50
+ intent?: "info" | "primary" | "success" | "warning" | "danger" | "neutral" | undefined;
51
51
  variant?: "inline" | "filled" | "soft" | undefined;
52
52
  size?: "sm" | "md" | "lg" | undefined;
53
53
  } & {