@selvajs/ui 6.6.0-beta.15 → 6.6.0-beta.17

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 (213) hide show
  1. package/README.md +0 -3
  2. package/dist/components/compute/AppLayout.svelte +5 -3
  3. package/dist/components/compute/CollapsedPanelStrip.svelte +1 -1
  4. package/dist/components/compute/ParameterPresetManager.svelte +1 -1
  5. package/dist/components/compute/SolveMessages.svelte +2 -2
  6. package/dist/components/layout/FooterItemRenderer.svelte +1 -1
  7. package/dist/components/layout/PageFooter.svelte +2 -2
  8. package/dist/components/layout/PageHeader.svelte +1 -1
  9. package/dist/components/layout/SideNav.svelte +3 -3
  10. package/dist/components/layout/SubNav.svelte +2 -2
  11. package/dist/components/preview/ChartOutput.svelte +3 -3
  12. package/dist/components/preview/Group.svelte +7 -7
  13. package/dist/components/preview/ImageOutput.svelte +6 -6
  14. package/dist/components/preview/InputControl.svelte +1 -0
  15. package/dist/components/preview/OutputDisplay.svelte +8 -6
  16. package/dist/components/preview/TabBar.svelte +6 -4
  17. package/dist/components/preview/TabBar.svelte.d.ts +2 -0
  18. package/dist/components/preview/TabContent.svelte +1 -1
  19. package/dist/components/preview/TabLayout.svelte +3 -4
  20. package/dist/components/preview/inputs/ChecklistInput.svelte +3 -3
  21. package/dist/components/preview/inputs/ColorInput.svelte +356 -14
  22. package/dist/components/preview/inputs/ColorInput.svelte.d.ts +1 -0
  23. package/dist/components/preview/inputs/FileInput.svelte +3 -3
  24. package/dist/components/preview/inputs/SegmentedInput.svelte +2 -1
  25. package/dist/components/preview/inputs/TileInput.svelte +1 -1
  26. package/dist/components/primitives/CalculateButton.svelte +2 -2
  27. package/dist/components/primitives/Callout.svelte +8 -6
  28. package/dist/components/primitives/Callout.svelte.d.ts +4 -4
  29. package/dist/components/primitives/DataTable.svelte +1 -1
  30. package/dist/components/primitives/ImageUploadField.svelte +1 -1
  31. package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  32. package/dist/components/primitives/badge/badge.svelte +2 -2
  33. package/dist/components/primitives/badge/badge.svelte.d.ts +6 -6
  34. package/dist/components/primitives/button/button.svelte +6 -7
  35. package/dist/components/primitives/button/button.svelte.d.ts +18 -18
  36. package/dist/components/primitives/card/card.svelte +1 -1
  37. package/dist/components/primitives/card/card.svelte.d.ts +1 -1
  38. package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
  39. package/dist/components/primitives/command/command-input.svelte +1 -1
  40. package/dist/components/primitives/command/command-item.svelte +1 -1
  41. package/dist/components/primitives/command/command-link-item.svelte +1 -1
  42. package/dist/components/primitives/command/command.svelte +1 -1
  43. package/dist/components/primitives/context-menu/context-menu-checkbox-item.svelte +1 -1
  44. package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
  45. package/dist/components/primitives/context-menu/context-menu-item.svelte +1 -1
  46. package/dist/components/primitives/context-menu/context-menu-radio-item.svelte +1 -1
  47. package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  48. package/dist/components/primitives/context-menu/context-menu-sub-trigger.svelte +1 -1
  49. package/dist/components/primitives/dialog/dialog-content.svelte +2 -2
  50. package/dist/components/primitives/drawer/Drawer.svelte +1 -1
  51. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +31 -11
  52. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +2 -0
  53. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +5 -1
  54. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +2 -0
  55. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +4 -2
  56. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +2 -1
  57. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
  58. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
  59. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
  60. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
  61. package/dist/components/primitives/index.d.ts +1 -1
  62. package/dist/components/primitives/index.js +1 -1
  63. package/dist/components/primitives/input/input.svelte +2 -2
  64. package/dist/components/primitives/input-group/input-group.svelte +1 -1
  65. package/dist/components/primitives/popover/popover-content.svelte +1 -1
  66. package/dist/components/primitives/resizable/resizable-handle.svelte +2 -2
  67. package/dist/components/primitives/search/search.svelte +1 -1
  68. package/dist/components/primitives/select/select-content.svelte +1 -1
  69. package/dist/components/primitives/select/select-item.svelte +1 -1
  70. package/dist/components/primitives/select/select-trigger.svelte +1 -1
  71. package/dist/components/primitives/slider/slider.svelte +1 -1
  72. package/dist/components/primitives/switch/switch.svelte +1 -1
  73. package/dist/components/primitives/tabs/tabs-list.svelte +1 -1
  74. package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +3 -3
  75. package/dist/components/primitives/tabs/tabs-trigger.svelte +1 -1
  76. package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +3 -3
  77. package/dist/components/primitives/textarea/textarea.svelte +1 -1
  78. package/dist/components/primitives/toggle/toggle.svelte +6 -3
  79. package/dist/components/primitives/toggle/toggle.svelte.d.ts +10 -7
  80. package/dist/components/primitives/toggle-group/toggle-group-item.svelte +1 -1
  81. package/dist/components/primitives/toggle-group/toggle-group.svelte +1 -1
  82. package/dist/components/primitives/tooltip/index.d.ts +6 -0
  83. package/dist/components/primitives/tooltip/index.js +8 -0
  84. package/dist/components/primitives/tooltip/tooltip-content.svelte +34 -0
  85. package/dist/components/primitives/tooltip/tooltip-content.svelte.d.ts +10 -0
  86. package/dist/components/primitives/tooltip/tooltip-portal.svelte +7 -0
  87. package/dist/components/primitives/tooltip/tooltip-portal.svelte.d.ts +4 -0
  88. package/dist/components/primitives/tooltip/tooltip-provider.svelte +7 -0
  89. package/dist/components/primitives/tooltip/tooltip-provider.svelte.d.ts +4 -0
  90. package/dist/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
  91. package/dist/components/primitives/tooltip/tooltip-trigger.svelte.d.ts +4 -0
  92. package/dist/components/primitives/tooltip/tooltip.svelte +7 -0
  93. package/dist/components/primitives/tooltip/tooltip.svelte.d.ts +4 -0
  94. package/dist/components/viewer/MeshMetadataPanel.svelte +1 -1
  95. package/dist/components/viewer/SceneManager.svelte +19 -19
  96. package/dist/components/viewer/Viewer.svelte +68 -157
  97. package/dist/components/viewer/ViewerDock.svelte +404 -0
  98. package/dist/components/viewer/ViewerDock.svelte.d.ts +42 -0
  99. package/dist/i18n/messages.d.ts +11 -0
  100. package/dist/i18n/messages.js +18 -0
  101. package/dist/index.d.ts +1 -0
  102. package/dist/index.js +1 -0
  103. package/dist/public.d.ts +1 -0
  104. package/dist/public.js +1 -0
  105. package/dist/schema/visibility-rules.d.ts +7 -1
  106. package/dist/schema/visibility-rules.js +17 -0
  107. package/dist/styles/base.css +81 -80
  108. package/dist/utils/color-space.d.ts +21 -0
  109. package/dist/utils/color-space.js +90 -0
  110. package/package.json +8 -9
  111. package/src/lib/components/compute/AppLayout.svelte +5 -3
  112. package/src/lib/components/compute/CollapsedPanelStrip.svelte +1 -1
  113. package/src/lib/components/compute/ParameterPresetManager.svelte +1 -1
  114. package/src/lib/components/compute/SolveMessages.svelte +2 -2
  115. package/src/lib/components/layout/FooterItemRenderer.svelte +1 -1
  116. package/src/lib/components/layout/PageFooter.svelte +2 -2
  117. package/src/lib/components/layout/PageHeader.svelte +1 -1
  118. package/src/lib/components/layout/SideNav.svelte +3 -3
  119. package/src/lib/components/layout/SubNav.svelte +2 -2
  120. package/src/lib/components/preview/ChartOutput.svelte +3 -3
  121. package/src/lib/components/preview/Group.svelte +7 -7
  122. package/src/lib/components/preview/ImageOutput.svelte +6 -6
  123. package/src/lib/components/preview/InputControl.svelte +1 -0
  124. package/src/lib/components/preview/OutputDisplay.svelte +8 -6
  125. package/src/lib/components/preview/TabBar.svelte +6 -4
  126. package/src/lib/components/preview/TabContent.svelte +1 -1
  127. package/src/lib/components/preview/TabLayout.svelte +3 -4
  128. package/src/lib/components/preview/inputs/ChecklistInput.svelte +3 -3
  129. package/src/lib/components/preview/inputs/ColorInput.svelte +356 -14
  130. package/src/lib/components/preview/inputs/FileInput.svelte +3 -3
  131. package/src/lib/components/preview/inputs/SegmentedInput.svelte +2 -1
  132. package/src/lib/components/preview/inputs/TileInput.svelte +1 -1
  133. package/src/lib/components/primitives/CalculateButton.svelte +2 -2
  134. package/src/lib/components/primitives/Callout.svelte +8 -6
  135. package/src/lib/components/primitives/DataTable.svelte +1 -1
  136. package/src/lib/components/primitives/ImageUploadField.svelte +1 -1
  137. package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  138. package/src/lib/components/primitives/badge/badge.svelte +2 -2
  139. package/src/lib/components/primitives/button/button.svelte +6 -7
  140. package/src/lib/components/primitives/card/card.svelte +1 -1
  141. package/src/lib/components/primitives/checkbox/checkbox.svelte +1 -1
  142. package/src/lib/components/primitives/command/command-input.svelte +1 -1
  143. package/src/lib/components/primitives/command/command-item.svelte +1 -1
  144. package/src/lib/components/primitives/command/command-link-item.svelte +1 -1
  145. package/src/lib/components/primitives/command/command.svelte +1 -1
  146. package/src/lib/components/primitives/context-menu/context-menu-checkbox-item.svelte +1 -1
  147. package/src/lib/components/primitives/context-menu/context-menu-content.svelte +1 -1
  148. package/src/lib/components/primitives/context-menu/context-menu-item.svelte +1 -1
  149. package/src/lib/components/primitives/context-menu/context-menu-radio-item.svelte +1 -1
  150. package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  151. package/src/lib/components/primitives/context-menu/context-menu-sub-trigger.svelte +1 -1
  152. package/src/lib/components/primitives/dialog/dialog-content.svelte +2 -2
  153. package/src/lib/components/primitives/drawer/Drawer.svelte +1 -1
  154. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +31 -11
  155. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +5 -1
  156. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +4 -2
  157. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
  158. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
  159. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
  160. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
  161. package/src/lib/components/primitives/index.ts +1 -1
  162. package/src/lib/components/primitives/input/input.svelte +2 -2
  163. package/src/lib/components/primitives/input-group/input-group.svelte +1 -1
  164. package/src/lib/components/primitives/popover/popover-content.svelte +1 -1
  165. package/src/lib/components/primitives/resizable/resizable-handle.svelte +2 -2
  166. package/src/lib/components/primitives/search/search.svelte +1 -1
  167. package/src/lib/components/primitives/select/select-content.svelte +1 -1
  168. package/src/lib/components/primitives/select/select-item.svelte +1 -1
  169. package/src/lib/components/primitives/select/select-trigger.svelte +1 -1
  170. package/src/lib/components/primitives/slider/slider.svelte +1 -1
  171. package/src/lib/components/primitives/switch/switch.svelte +1 -1
  172. package/src/lib/components/primitives/tabs/tabs-list.svelte +1 -1
  173. package/src/lib/components/primitives/tabs/tabs-trigger.svelte +1 -1
  174. package/src/lib/components/primitives/textarea/textarea.svelte +1 -1
  175. package/src/lib/components/primitives/toggle/toggle.svelte +6 -3
  176. package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +1 -1
  177. package/src/lib/components/primitives/toggle-group/toggle-group.svelte +1 -1
  178. package/src/lib/components/primitives/tooltip/index.ts +19 -0
  179. package/src/lib/components/primitives/tooltip/tooltip-content.svelte +34 -0
  180. package/src/lib/components/primitives/tooltip/tooltip-portal.svelte +7 -0
  181. package/src/lib/components/primitives/tooltip/tooltip-provider.svelte +7 -0
  182. package/src/lib/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
  183. package/src/lib/components/primitives/tooltip/tooltip.svelte +7 -0
  184. package/src/lib/components/viewer/MeshMetadataPanel.svelte +1 -1
  185. package/src/lib/components/viewer/SceneManager.svelte +19 -19
  186. package/src/lib/components/viewer/Viewer.svelte +68 -157
  187. package/src/lib/components/viewer/ViewerDock.svelte +404 -0
  188. package/src/lib/i18n/messages.ts +29 -0
  189. package/src/lib/index.ts +1 -0
  190. package/src/lib/public.ts +1 -0
  191. package/src/lib/schema/visibility-rules.ts +27 -1
  192. package/src/lib/styles/base.css +81 -80
  193. package/src/lib/utils/color-space.ts +107 -0
  194. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
  195. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte.d.ts +0 -3
  196. package/dist/components/primitives/theme-switcher/index.d.ts +0 -1
  197. package/dist/components/primitives/theme-switcher/index.js +0 -1
  198. package/dist/styles/themes/cyberpunk.css +0 -98
  199. package/dist/styles/themes/neutral.css +0 -72
  200. package/dist/styles/themes/ocean.css +0 -75
  201. package/dist/styles/themes/selva.css +0 -105
  202. package/dist/theme/themeStore.svelte.d.ts +0 -12
  203. package/dist/theme/themeStore.svelte.js +0 -40
  204. package/dist/theme/themes.d.ts +0 -5
  205. package/dist/theme/themes.js +0 -13
  206. package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
  207. package/src/lib/components/primitives/theme-switcher/index.ts +0 -1
  208. package/src/lib/styles/themes/cyberpunk.css +0 -98
  209. package/src/lib/styles/themes/neutral.css +0 -72
  210. package/src/lib/styles/themes/ocean.css +0 -75
  211. package/src/lib/styles/themes/selva.css +0 -105
  212. package/src/lib/theme/themeStore.svelte.ts +0 -50
  213. package/src/lib/theme/themes.ts +0 -18
@@ -1,23 +1,365 @@
1
1
  <script lang="ts">
2
+ import { untrack } from 'svelte';
3
+ import { Check, Pipette } from '@lucide/svelte';
4
+ import * as Popover from '../../primitives/popover';
5
+ import { Input } from '../../primitives/input';
6
+ import { getLocaleContext } from '../../../i18n/localeContext.svelte';
7
+ import { formatMessage } from '../../../i18n/messages';
8
+ import { debounce } from '../../../utils/debounce';
9
+ import {
10
+ contrastRatio,
11
+ hexToHsv,
12
+ hsvToHex,
13
+ normalizeHex,
14
+ type Hsv
15
+ } from '../../../utils/color-space';
16
+
2
17
  interface Props {
3
18
  inputId: string;
4
19
  value?: string;
5
20
  onChange: (value: string) => void;
21
+ disabled?: boolean;
22
+ }
23
+
24
+ let { inputId, value = '#000000', onChange, disabled = false }: Props = $props();
25
+
26
+ const PRESETS = [
27
+ '#000000',
28
+ '#ffffff',
29
+ '#9ca3af',
30
+ '#ef4444',
31
+ '#f97316',
32
+ '#eab308',
33
+ '#22c55e',
34
+ '#14b8a6',
35
+ '#3b82f6',
36
+ '#8b5cf6',
37
+ '#ec4899',
38
+ '#78350f'
39
+ ];
40
+
41
+ interface EyeDropperApi {
42
+ open(): Promise<{ sRGBHex: string }>;
43
+ }
44
+ const eyeDropperCtor = (globalThis as { EyeDropper?: new () => EyeDropperApi }).EyeDropper;
45
+
46
+ const locale = getLocaleContext();
47
+ const t = $derived(locale.messages);
48
+
49
+ const parse = (hex: string): Hsv => hexToHsv(normalizeHex(hex) ?? '#000000')!;
50
+
51
+ let hsv = $state<Hsv>(untrack(() => parse(value)));
52
+ let draft = $state('');
53
+ let hexError = $state(false);
54
+ let areaEl = $state<HTMLElement | null>(null);
55
+
56
+ const currentHex = $derived(hsvToHex(hsv));
57
+ const hueColor = $derived(hsvToHex({ h: hsv.h, s: 1, v: 1 }));
58
+
59
+ // An external value only replaces the picker's state when it names a different color.
60
+ // Rebuilding from the hex alone would reset the hue whenever s or v hits 0.
61
+ $effect(() => {
62
+ const next = normalizeHex(value) ?? '#000000';
63
+ if (next !== untrack(() => currentHex)) hsv = parse(next);
64
+ });
65
+
66
+ $effect(() => {
67
+ draft = currentHex;
68
+ hexError = false;
69
+ });
70
+
71
+ // Dragging only updates local state; the host sees one change per gesture, since each
72
+ // change can trigger a solve.
73
+ function commit() {
74
+ commitDebounced.cancel();
75
+ if (currentHex !== normalizeHex(value)) onChange(currentHex);
76
+ }
77
+ const commitDebounced = debounce(commit, 150);
78
+
79
+ const clamp01 = (n: number) => Math.min(1, Math.max(0, n));
80
+
81
+ function setFromPointer(e: PointerEvent) {
82
+ if (!areaEl) return;
83
+ const rect = areaEl.getBoundingClientRect();
84
+ hsv = {
85
+ h: hsv.h,
86
+ s: clamp01((e.clientX - rect.left) / rect.width),
87
+ v: clamp01(1 - (e.clientY - rect.top) / rect.height)
88
+ };
89
+ }
90
+
91
+ function onAreaPointerDown(e: PointerEvent) {
92
+ if (disabled || e.button !== 0) return;
93
+ e.preventDefault();
94
+ areaEl?.focus();
95
+ areaEl?.setPointerCapture(e.pointerId);
96
+ setFromPointer(e);
97
+ }
98
+
99
+ function onAreaPointerMove(e: PointerEvent) {
100
+ if (areaEl?.hasPointerCapture(e.pointerId)) setFromPointer(e);
101
+ }
102
+
103
+ function onAreaPointerUp(e: PointerEvent) {
104
+ if (!areaEl?.hasPointerCapture(e.pointerId)) return;
105
+ areaEl.releasePointerCapture(e.pointerId);
106
+ commit();
107
+ }
108
+
109
+ function onAreaKeydown(e: KeyboardEvent) {
110
+ const step = e.shiftKey ? 0.1 : 0.01;
111
+ let { s, v } = hsv;
112
+ switch (e.key) {
113
+ case 'ArrowLeft':
114
+ s -= step;
115
+ break;
116
+ case 'ArrowRight':
117
+ s += step;
118
+ break;
119
+ case 'ArrowUp':
120
+ v += step;
121
+ break;
122
+ case 'ArrowDown':
123
+ v -= step;
124
+ break;
125
+ case 'PageUp':
126
+ v += 0.1;
127
+ break;
128
+ case 'PageDown':
129
+ v -= 0.1;
130
+ break;
131
+ default:
132
+ return;
133
+ }
134
+ e.preventDefault();
135
+ hsv = { h: hsv.h, s: clamp01(s), v: clamp01(v) };
136
+ commitDebounced();
6
137
  }
7
138
 
8
- let { inputId, value = '#000000', onChange }: Props = $props();
139
+ function onHueInput(e: Event) {
140
+ hsv = { ...hsv, h: Number((e.currentTarget as HTMLInputElement).value) };
141
+ }
142
+
143
+ function applyHex(hex: string) {
144
+ hsv = parse(hex);
145
+ commit();
146
+ }
147
+
148
+ function commitDraft() {
149
+ const hex = normalizeHex(draft);
150
+ if (!hex) {
151
+ hexError = true;
152
+ return;
153
+ }
154
+ hexError = false;
155
+ applyHex(hex);
156
+ draft = hex;
157
+ }
158
+
159
+ function onHexKeydown(e: KeyboardEvent) {
160
+ if (e.key === 'Enter') {
161
+ e.preventDefault();
162
+ commitDraft();
163
+ }
164
+ }
165
+
166
+ function onHexBlur() {
167
+ if (normalizeHex(draft)) commitDraft();
168
+ else {
169
+ draft = currentHex;
170
+ hexError = false;
171
+ }
172
+ }
173
+
174
+ async function pickFromScreen() {
175
+ if (!eyeDropperCtor) return;
176
+ try {
177
+ const { sRGBHex } = await new eyeDropperCtor().open();
178
+ applyHex(sRGBHex);
179
+ } catch {
180
+ // The user dismissed the eyedropper.
181
+ }
182
+ }
183
+
184
+ const checkOn = (hex: string) =>
185
+ (contrastRatio(hex, '#000000') ?? 0) > (contrastRatio(hex, '#ffffff') ?? 0)
186
+ ? 'text-black'
187
+ : 'text-white';
9
188
  </script>
10
189
 
11
- <div class="gap-2 flex items-center">
12
- <input
13
- id={inputId}
14
- type="color"
15
- value={value || '#000000'}
16
- class="h-9 w-12 rounded p-1 cursor-pointer border border-input bg-background dark:bg-input/30"
17
- onchange={(e) => {
18
- const newValue = (e.currentTarget as HTMLInputElement).value;
19
- onChange(newValue);
20
- }}
21
- />
22
- <span class="font-mono text-sm text-muted-foreground">{value || '#000000'}</span>
23
- </div>
190
+ <Popover.Root>
191
+ <Popover.Trigger>
192
+ {#snippet child({ props })}
193
+ <button
194
+ {...props}
195
+ type="button"
196
+ id={inputId}
197
+ {disabled}
198
+ aria-label={formatMessage(t.colorPick, { color: currentHex })}
199
+ class="h-9 gap-2.5 px-2.5 shadow-xs max-w-48 flex w-full cursor-pointer items-center rounded-md border border-input bg-background text-left outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
200
+ >
201
+ <span
202
+ class="size-5 shrink-0 rounded-md bg-(--swatch) ring-1 ring-border ring-inset"
203
+ style:--swatch={currentHex}
204
+ ></span>
205
+ <span class="font-mono text-sm text-muted-foreground">{currentHex}</span>
206
+ </button>
207
+ {/snippet}
208
+ </Popover.Trigger>
209
+
210
+ <Popover.Content align="start">
211
+ <div
212
+ bind:this={areaEl}
213
+ role="slider"
214
+ tabindex={disabled ? -1 : 0}
215
+ aria-label={t.colorArea}
216
+ aria-valuemin={0}
217
+ aria-valuemax={100}
218
+ aria-valuenow={Math.round(hsv.v * 100)}
219
+ aria-valuetext={formatMessage(t.colorAreaValue, {
220
+ s: Math.round(hsv.s * 100),
221
+ v: Math.round(hsv.v * 100)
222
+ })}
223
+ aria-disabled={disabled}
224
+ class="color-area h-36 relative w-full cursor-crosshair touch-none rounded-md outline-none focus-visible:ring-3 focus-visible:ring-ring"
225
+ style:--hue={hueColor}
226
+ onpointerdown={onAreaPointerDown}
227
+ onpointermove={onAreaPointerMove}
228
+ onpointerup={onAreaPointerUp}
229
+ onpointercancel={onAreaPointerUp}
230
+ onkeydown={onAreaKeydown}
231
+ >
232
+ <span
233
+ class="color-thumb size-4 border-white pointer-events-none absolute -translate-x-1/2 -translate-y-1/2 rounded-full border-2 bg-(--swatch)"
234
+ style:--x="{hsv.s * 100}%"
235
+ style:--y="{(1 - hsv.v) * 100}%"
236
+ style:--swatch={currentHex}
237
+ ></span>
238
+ </div>
239
+
240
+ <input
241
+ type="range"
242
+ class="color-hue"
243
+ min="0"
244
+ max="360"
245
+ step="1"
246
+ aria-label={t.colorHue}
247
+ value={Math.round(hsv.h)}
248
+ {disabled}
249
+ oninput={onHueInput}
250
+ onchange={commit}
251
+ />
252
+
253
+ <div class="gap-2 flex items-start">
254
+ <div class="min-w-0 flex-1">
255
+ <Input
256
+ type="text"
257
+ aria-label={t.colorHex}
258
+ aria-invalid={hexError}
259
+ aria-describedby={hexError ? `${inputId}-hex-error` : undefined}
260
+ autocomplete="off"
261
+ spellcheck={false}
262
+ maxlength={7}
263
+ {disabled}
264
+ bind:value={draft}
265
+ onkeydown={onHexKeydown}
266
+ onblur={onHexBlur}
267
+ />
268
+ </div>
269
+ {#if eyeDropperCtor}
270
+ <button
271
+ type="button"
272
+ {disabled}
273
+ aria-label={t.colorEyedropper}
274
+ title={t.colorEyedropper}
275
+ class="size-9 flex shrink-0 cursor-pointer items-center justify-center rounded-md border border-input bg-background outline-none hover:bg-hover focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
276
+ onclick={pickFromScreen}
277
+ >
278
+ <Pipette class="size-4" aria-hidden="true" />
279
+ </button>
280
+ {/if}
281
+ </div>
282
+ {#if hexError}
283
+ <p id="{inputId}-hex-error" role="alert" class="text-xs text-destructive">
284
+ {t.colorHexInvalid}
285
+ </p>
286
+ {/if}
287
+
288
+ <div role="group" aria-label={t.colorPresets} class="gap-1.5 grid grid-cols-6">
289
+ {#each PRESETS as preset (preset)}
290
+ {@const selected = preset === currentHex}
291
+ <button
292
+ type="button"
293
+ {disabled}
294
+ aria-label={preset}
295
+ aria-pressed={selected}
296
+ class="h-6 flex cursor-pointer items-center justify-center rounded-md bg-(--swatch) ring-1 ring-border outline-none ring-inset focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed"
297
+ style:--swatch={preset}
298
+ onclick={() => applyHex(preset)}
299
+ >
300
+ {#if selected}
301
+ <Check class="size-3.5 {checkOn(preset)}" aria-hidden="true" />
302
+ {/if}
303
+ </button>
304
+ {/each}
305
+ </div>
306
+ </Popover.Content>
307
+ </Popover.Root>
308
+
309
+ <style>
310
+ .color-area {
311
+ background-color: var(--hue);
312
+ background-image:
313
+ linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent);
314
+ }
315
+ .color-thumb {
316
+ left: var(--x);
317
+ top: var(--y);
318
+ box-shadow: 0 0 0 1px rgb(0 0 0 / 0.4);
319
+ }
320
+ .color-hue {
321
+ appearance: none;
322
+ -webkit-appearance: none;
323
+ height: 0.75rem;
324
+ width: 100%;
325
+ margin: 0;
326
+ border-radius: 9999px;
327
+ cursor: pointer;
328
+ outline: none;
329
+ background: linear-gradient(
330
+ to right,
331
+ #f00 0%,
332
+ #ff0 16.67%,
333
+ #0f0 33.33%,
334
+ #0ff 50%,
335
+ #00f 66.67%,
336
+ #f0f 83.33%,
337
+ #f00 100%
338
+ );
339
+ }
340
+ .color-hue::-webkit-slider-thumb {
341
+ -webkit-appearance: none;
342
+ height: 1rem;
343
+ width: 1rem;
344
+ border-radius: 9999px;
345
+ border: 2px solid #fff;
346
+ background: transparent;
347
+ box-shadow: 0 0 0 1px rgb(0 0 0 / 0.4);
348
+ }
349
+ .color-hue::-moz-range-thumb {
350
+ height: 1rem;
351
+ width: 1rem;
352
+ box-sizing: border-box;
353
+ border-radius: 9999px;
354
+ border: 2px solid #fff;
355
+ background: transparent;
356
+ box-shadow: 0 0 0 1px rgb(0 0 0 / 0.4);
357
+ }
358
+ .color-hue:focus-visible {
359
+ box-shadow: 0 0 0 3px var(--ring);
360
+ }
361
+ .color-hue:disabled {
362
+ cursor: not-allowed;
363
+ opacity: 0.5;
364
+ }
365
+ </style>
@@ -2,6 +2,7 @@ interface Props {
2
2
  inputId: string;
3
3
  value?: string;
4
4
  onChange: (value: string) => void;
5
+ disabled?: boolean;
5
6
  }
6
7
  declare const ColorInput: import("svelte").Component<Props, {}, "">;
7
8
  type ColorInput = ReturnType<typeof ColorInput>;
@@ -282,10 +282,10 @@
282
282
  type="button"
283
283
  onclick={() => (activeMode = mode)}
284
284
  aria-pressed={activeMode === mode}
285
- class={`px-3 py-1 font-medium flex-1 rounded-sm transition-colors ${
285
+ class={`px-3 py-1 font-medium flex-1 rounded-sm border transition-colors ${
286
286
  activeMode === mode
287
- ? 'shadow-xs bg-background text-foreground'
288
- : 'text-muted-foreground hover:text-foreground'
287
+ ? 'shadow-xs border-border-strong bg-raised text-foreground'
288
+ : 'border-transparent text-muted-foreground hover:text-foreground'
289
289
  }`}
290
290
  >
291
291
  {mode === 'upload' ? t.fileModeUpload : t.fileModeUrl}
@@ -23,7 +23,8 @@
23
23
 
24
24
  <ToggleGroup.Root
25
25
  type="single"
26
- variant="accent"
26
+ variant="segmented"
27
+ spacing={1}
27
28
  size={hasIcons ? 'stacked' : 'default'}
28
29
  class="w-full"
29
30
  aria-labelledby={labelId}
@@ -53,7 +53,7 @@
53
53
  onkeydown={(e) => onKeydown(e, index)}
54
54
  class="gap-1.5 p-1.5 min-w-0 flex flex-col rounded-lg border text-left transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 {selected
55
55
  ? 'border-primary bg-primary/5 ring-1 ring-primary'
56
- : 'border-input hover:bg-accent/40'}"
56
+ : 'border-input hover:bg-hover'}"
57
57
  >
58
58
  <span
59
59
  class="flex aspect-square w-full items-center justify-center overflow-hidden rounded-md bg-muted text-muted-foreground"
@@ -38,7 +38,7 @@
38
38
  });
39
39
  </script>
40
40
 
41
- <div class="bottom-0 mt-3 pb-3 pt-2 backdrop-blur-sm px-2 sticky border-t border-border/50">
41
+ <div class="bottom-0 mt-3 pb-3 pt-2 backdrop-blur-sm px-2 sticky border-t border-border">
42
42
  <span class="sr-only" role="status" aria-live="polite">{announcement}</span>
43
43
  <div class="gap-2 flex">
44
44
  <Button
@@ -55,7 +55,7 @@
55
55
  <Loader class="h-4 w-4 animate-spin" />
56
56
  {t.solvingEllipsis}
57
57
  <kbd
58
- class="px-1 font-sans rounded border border-current/30 text-2xs opacity-70"
58
+ class="px-1 font-sans rounded-sm border border-current/30 text-2xs opacity-70"
59
59
  title={t.solveMessagesEscHint}
60
60
  >
61
61
  Esc
@@ -8,7 +8,7 @@
8
8
  variants: {
9
9
  tone: {
10
10
  info: 'border-info/40 bg-info/10',
11
- tip: 'border-border bg-muted/40',
11
+ tip: 'border-border bg-muted',
12
12
  success: 'border-success/40 bg-success/10',
13
13
  warning: 'border-warning/40 bg-warning/10',
14
14
  danger: 'border-destructive/40 bg-destructive/10'
@@ -38,7 +38,7 @@
38
38
  title?: string;
39
39
  /** Replaces the tone's default icon. `null` drops the icon entirely. */
40
40
  icon?: Component | null;
41
- children: Snippet;
41
+ children?: Snippet;
42
42
  }
43
43
 
44
44
  let { tone = 'info', title, icon, class: className, children, ...restProps }: Props = $props();
@@ -55,12 +55,14 @@
55
55
  {#if Icon}
56
56
  <Icon class={cn('mt-0.5 h-4 w-4 shrink-0', accent)} />
57
57
  {/if}
58
- <div class="min-w-0 space-y-1">
58
+ <div class="min-w-0 space-y-1 wrap-anywhere">
59
59
  {#if title}
60
60
  <p class={cn('leading-tight font-medium', accent)}>{title}</p>
61
61
  {/if}
62
- <div class="leading-relaxed text-muted-foreground [&_a]:underline [&_strong]:text-foreground">
63
- {@render children()}
64
- </div>
62
+ {#if children}
63
+ <div class="leading-relaxed text-muted-foreground [&_a]:underline [&_strong]:text-foreground">
64
+ {@render children()}
65
+ </div>
66
+ {/if}
65
67
  </div>
66
68
  </div>
@@ -3,7 +3,7 @@ import type { Component } from 'svelte';
3
3
  export declare const calloutVariants: import("tailwind-variants").TVReturnType<{
4
4
  tone: {
5
5
  info: "border-info/40 bg-info/10";
6
- tip: "border-border bg-muted/40";
6
+ tip: "border-border bg-muted";
7
7
  success: "border-success/40 bg-success/10";
8
8
  warning: "border-warning/40 bg-warning/10";
9
9
  danger: "border-destructive/40 bg-destructive/10";
@@ -11,7 +11,7 @@ export declare const calloutVariants: import("tailwind-variants").TVReturnType<{
11
11
  }, undefined, "flex items-start gap-2.5 rounded-lg border px-3.5 py-3 text-sm", {
12
12
  tone: {
13
13
  info: "border-info/40 bg-info/10";
14
- tip: "border-border bg-muted/40";
14
+ tip: "border-border bg-muted";
15
15
  success: "border-success/40 bg-success/10";
16
16
  warning: "border-warning/40 bg-warning/10";
17
17
  danger: "border-destructive/40 bg-destructive/10";
@@ -19,7 +19,7 @@ export declare const calloutVariants: import("tailwind-variants").TVReturnType<{
19
19
  }, undefined, import("tailwind-variants").TVReturnTypeLike<{
20
20
  tone: {
21
21
  info: "border-info/40 bg-info/10";
22
- tip: "border-border bg-muted/40";
22
+ tip: "border-border bg-muted";
23
23
  success: "border-success/40 bg-success/10";
24
24
  warning: "border-warning/40 bg-warning/10";
25
25
  danger: "border-destructive/40 bg-destructive/10";
@@ -33,7 +33,7 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
33
33
  title?: string;
34
34
  /** Replaces the tone's default icon. `null` drops the icon entirely. */
35
35
  icon?: Component | null;
36
- children: Snippet;
36
+ children?: Snippet;
37
37
  }
38
38
  declare const Callout: Component<Props, {}, "">;
39
39
  type Callout = ReturnType<typeof Callout>;
@@ -41,7 +41,7 @@
41
41
  style:--table-cols-sm={mobileTemplate}
42
42
  >
43
43
  <div
44
- class="data-table-row gap-4 px-4 py-2 text-xs font-medium tracking-wide sm:grid-cols-(--table-cols) grid grid-cols-(--table-cols-sm) bg-muted/40 text-muted-foreground uppercase {hiddenCols}"
44
+ class="data-table-row gap-4 px-4 py-2 text-xs font-medium tracking-wide sm:grid-cols-(--table-cols) grid grid-cols-(--table-cols-sm) bg-muted text-muted-foreground uppercase {hiddenCols}"
45
45
  >
46
46
  {#each columns as col (col.label)}
47
47
  <span class={col.align === 'right' ? 'text-right' : ''}>{col.label}</span>
@@ -60,7 +60,7 @@
60
60
  accept="image/*"
61
61
  {disabled}
62
62
  onchange={onFileSelected}
63
- class="h-10 px-3 py-2 text-sm file:text-sm file:font-medium flex flex-1 rounded-md border border-input bg-background file:border-0 file:bg-transparent focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:opacity-50"
63
+ class="h-10 px-3 py-2 text-sm file:text-sm file:font-medium flex flex-1 rounded-md border border-input bg-field file:border-0 file:bg-transparent focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:opacity-50"
64
64
  />
65
65
  <Button
66
66
  size="sm"
@@ -19,7 +19,7 @@
19
19
  bind:ref
20
20
  data-slot="alert-dialog-content"
21
21
  class={cn(
22
- 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 gap-4 p-6 shadow-lg sm:max-w-lg fixed start-1/2 top-1/2 z-50 grid max-h-dialog w-screen-inset -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-lg border bg-background duration-200',
22
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 gap-4 p-6 sm:max-w-lg fixed start-1/2 top-1/2 z-50 grid max-h-dialog w-screen-inset -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border bg-raised shadow-popover duration-200',
23
23
  className
24
24
  )}
25
25
  {...restProps}
@@ -7,10 +7,10 @@
7
7
  variant: {
8
8
  default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent',
9
9
  secondary:
10
- 'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent',
10
+ 'bg-secondary text-secondary-foreground [a&]:hover:bg-selected border-transparent',
11
11
  destructive:
12
12
  'bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground',
13
- outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground'
13
+ outline: 'text-foreground [a&]:hover:bg-hover [a&]:hover:text-foreground'
14
14
  }
15
15
  },
16
16
  defaultVariants: {
@@ -2,23 +2,23 @@ import { type VariantProps } from 'tailwind-variants';
2
2
  export declare const badgeVariants: import("tailwind-variants").TVReturnType<{
3
3
  variant: {
4
4
  default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent";
5
- secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent";
5
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-selected border-transparent";
6
6
  destructive: "bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground";
7
- outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground";
7
+ outline: "text-foreground [a&]:hover:bg-hover [a&]:hover:text-foreground";
8
8
  };
9
9
  }, undefined, "focus-visible:border-ring focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border px-2 py-0.5 text-xs font-medium whitespace-nowrap transition focus-visible:ring-3 [&>svg]:pointer-events-none [&>svg]:size-3", {
10
10
  variant: {
11
11
  default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent";
12
- secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent";
12
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-selected border-transparent";
13
13
  destructive: "bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground";
14
- outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground";
14
+ outline: "text-foreground [a&]:hover:bg-hover [a&]:hover:text-foreground";
15
15
  };
16
16
  }, undefined, import("tailwind-variants").TVReturnTypeLike<{
17
17
  variant: {
18
18
  default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent";
19
- secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent";
19
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-selected border-transparent";
20
20
  destructive: "bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground";
21
- outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground";
21
+ outline: "text-foreground [a&]:hover:bg-hover [a&]:hover:text-foreground";
22
22
  };
23
23
  }, undefined>>;
24
24
  export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
@@ -8,17 +8,16 @@
8
8
  variants: {
9
9
  variant: {
10
10
  default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
11
- destructive:
12
- 'bg-destructive shadow-xs hover:bg-destructive/90 dark:bg-destructive/60 text-destructive-foreground',
11
+ destructive: 'bg-destructive shadow-xs hover:bg-destructive/90 text-destructive-foreground',
13
12
  outline:
14
- 'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border',
13
+ 'border border-border-strong bg-transparent shadow-xs hover:bg-hover hover:text-foreground',
15
14
  // Outline with an opaque dark fill: the translucent one washes out over the light 3D canvas.
16
15
  overlay:
17
- 'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input border',
18
- secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
19
- ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
16
+ 'border border-border-strong bg-raised shadow-raised hover:bg-muted hover:text-foreground',
17
+ secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-selected',
18
+ ghost: 'hover:bg-hover hover:text-foreground',
20
19
  dashed:
21
- 'border border-dashed border-input bg-background text-muted-foreground hover:bg-muted hover:text-foreground',
20
+ 'border border-dashed border-border-strong bg-transparent text-muted-foreground hover:border-input hover:bg-hover hover:text-foreground',
22
21
  link: 'text-primary underline-offset-4 hover:underline'
23
22
  },
24
23
  size: {