@adea-ai/ui 0.105.0 → 0.106.1

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 (147) hide show
  1. package/README.md +44 -8
  2. package/dist/components/composites/action-button/action-button.d.ts.map +1 -1
  3. package/dist/components/composites/action-button/action-button.js +6 -8
  4. package/dist/components/composites/action-button/action-button.js.map +1 -1
  5. package/dist/components/composites/appearance-editor/accent-swatch-groups.d.ts +16 -0
  6. package/dist/components/composites/appearance-editor/accent-swatch-groups.d.ts.map +1 -0
  7. package/dist/components/composites/appearance-editor/accent-swatch-groups.js +151 -0
  8. package/dist/components/composites/appearance-editor/accent-swatch-groups.js.map +1 -0
  9. package/dist/components/composites/appearance-editor/appearance-choices.d.ts.map +1 -1
  10. package/dist/components/composites/appearance-editor/appearance-choices.js +24 -84
  11. package/dist/components/composites/appearance-editor/appearance-choices.js.map +1 -1
  12. package/dist/components/composites/appearance-editor/appearance-editor.d.ts +2 -2
  13. package/dist/components/composites/appearance-editor/appearance-editor.d.ts.map +1 -1
  14. package/dist/components/composites/appearance-editor/appearance-editor.js +25 -7
  15. package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -1
  16. package/dist/components/composites/appearance-editor/appearance-types.d.ts +9 -0
  17. package/dist/components/composites/appearance-editor/appearance-types.d.ts.map +1 -1
  18. package/dist/components/composites/appearance-editor/font-settings-group.d.ts +10 -0
  19. package/dist/components/composites/appearance-editor/font-settings-group.d.ts.map +1 -0
  20. package/dist/components/composites/appearance-editor/font-settings-group.js +310 -0
  21. package/dist/components/composites/appearance-editor/font-settings-group.js.map +1 -0
  22. package/dist/components/composites/appearance-editor/index.d.ts +2 -0
  23. package/dist/components/composites/appearance-editor/index.d.ts.map +1 -1
  24. package/dist/components/composites/appearance-editor/index.js +3 -1
  25. package/dist/components/composites/update-dialog/update-dialog.js +2 -2
  26. package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
  27. package/dist/components/conversation/message-row.d.ts.map +1 -1
  28. package/dist/components/conversation/message-row.js +3 -3
  29. package/dist/components/conversation/message-row.js.map +1 -1
  30. package/dist/components/conversation/paste-token-editor.js +1 -1
  31. package/dist/components/conversation/paste-token-editor.js.map +1 -1
  32. package/dist/components/layout/side-rail/side-rail.js +2 -2
  33. package/dist/components/layout/side-rail/side-rail.js.map +1 -1
  34. package/dist/components/layout/top-bar/top-bar.js +1 -1
  35. package/dist/components/layout/top-bar/top-bar.js.map +1 -1
  36. package/dist/components/theme/appearance-panel.d.ts +1 -1
  37. package/dist/components/theme/appearance-panel.d.ts.map +1 -1
  38. package/dist/components/theme/appearance-panel.js +45 -105
  39. package/dist/components/theme/appearance-panel.js.map +1 -1
  40. package/dist/components/theme/theme-provider.d.ts +5 -2
  41. package/dist/components/theme/theme-provider.d.ts.map +1 -1
  42. package/dist/components/theme/theme-provider.js +38 -19
  43. package/dist/components/theme/theme-provider.js.map +1 -1
  44. package/dist/components/ui/code-block/code-block.d.ts +3 -3
  45. package/dist/components/ui/code-block/code-block.js +6 -6
  46. package/dist/components/ui/code-block/code-block.js.map +1 -1
  47. package/dist/components/ui/command/command.js +1 -1
  48. package/dist/components/ui/command/command.js.map +1 -1
  49. package/dist/components/ui/context-menu/context-menu.js +1 -1
  50. package/dist/components/ui/context-menu/context-menu.js.map +1 -1
  51. package/dist/components/ui/diff-block/diff-block.js +5 -5
  52. package/dist/components/ui/diff-block/diff-block.js.map +1 -1
  53. package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts.map +1 -1
  54. package/dist/components/ui/dropdown-menu/dropdown-menu.js +101 -8
  55. package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
  56. package/dist/components/ui/kbd/kbd.js +1 -1
  57. package/dist/components/ui/kbd/kbd.js.map +1 -1
  58. package/dist/components/ui/menubar/menubar.js +1 -1
  59. package/dist/components/ui/menubar/menubar.js.map +1 -1
  60. package/dist/components/ui/typography/typography.d.ts +3 -5
  61. package/dist/components/ui/typography/typography.d.ts.map +1 -1
  62. package/dist/components/ui/typography/typography.js +6 -8
  63. package/dist/components/ui/typography/typography.js.map +1 -1
  64. package/dist/index.js +6 -3
  65. package/dist/lib/appearance-font-settings.d.ts +53 -0
  66. package/dist/lib/appearance-font-settings.d.ts.map +1 -0
  67. package/dist/lib/appearance-font-settings.js +160 -0
  68. package/dist/lib/appearance-font-settings.js.map +1 -0
  69. package/dist/lib/font-catalog.d.ts +72 -0
  70. package/dist/lib/font-catalog.d.ts.map +1 -0
  71. package/dist/lib/font-catalog.js +71 -0
  72. package/dist/lib/font-catalog.js.map +1 -0
  73. package/dist/lib/safe-script.d.ts +3 -0
  74. package/dist/lib/safe-script.d.ts.map +1 -0
  75. package/dist/lib/safe-script.js +9 -0
  76. package/dist/lib/safe-script.js.map +1 -0
  77. package/dist/lib/tokens.d.ts +2 -19
  78. package/dist/lib/tokens.d.ts.map +1 -1
  79. package/dist/lib/tokens.js +63 -39
  80. package/dist/lib/tokens.js.map +1 -1
  81. package/dist/r/appearance-editor.json +12 -1
  82. package/dist/r/lib.json +16 -1
  83. package/dist/r/registry.json +36 -3
  84. package/dist/r/src/components/composites/action-button/action-button.tsx +2 -1
  85. package/dist/r/src/components/composites/appearance-editor/accent-swatch-groups.tsx +147 -0
  86. package/dist/r/src/components/composites/appearance-editor/appearance-choices.tsx +17 -74
  87. package/dist/r/src/components/composites/appearance-editor/appearance-editor.tsx +19 -6
  88. package/dist/r/src/components/composites/appearance-editor/appearance-types.ts +9 -0
  89. package/dist/r/src/components/composites/appearance-editor/font-settings-group.tsx +350 -0
  90. package/dist/r/src/components/composites/appearance-editor/index.ts +19 -0
  91. package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +2 -2
  92. package/dist/r/src/components/conversation/message-row.tsx +7 -3
  93. package/dist/r/src/components/conversation/paste-token-editor.tsx +1 -1
  94. package/dist/r/src/components/layout/side-rail/side-rail.tsx +3 -3
  95. package/dist/r/src/components/layout/top-bar/top-bar.tsx +1 -1
  96. package/dist/r/src/components/theme/appearance-panel.tsx +34 -83
  97. package/dist/r/src/components/theme/theme-provider.tsx +50 -20
  98. package/dist/r/src/components/ui/code-block/code-block.tsx +6 -6
  99. package/dist/r/src/components/ui/command/command.tsx +1 -1
  100. package/dist/r/src/components/ui/context-menu/context-menu.tsx +1 -1
  101. package/dist/r/src/components/ui/diff-block/diff-block.tsx +7 -7
  102. package/dist/r/src/components/ui/dropdown-menu/dropdown-menu.tsx +145 -7
  103. package/dist/r/src/components/ui/kbd/kbd.tsx +1 -1
  104. package/dist/r/src/components/ui/menubar/menubar.tsx +1 -1
  105. package/dist/r/src/components/ui/typography/typography.tsx +6 -8
  106. package/dist/r/src/lib/appearance-font-settings.ts +233 -0
  107. package/dist/r/src/lib/font-catalog.ts +80 -0
  108. package/dist/r/src/lib/safe-script.ts +7 -0
  109. package/dist/r/src/lib/tokens.ts +64 -59
  110. package/dist/r/src/styles/appearance-font-settings.css +110 -0
  111. package/dist/r/src/styles/fonts.css +4 -5
  112. package/dist/r/src/styles/globals.css +4 -1
  113. package/dist/r/src/styles/theme.css +14 -0
  114. package/dist/r/theme.json +5 -0
  115. package/dist/r/theming.json +3 -1
  116. package/package.json +3 -2
  117. package/registry.json +36 -3
  118. package/src/components/composites/action-button/action-button.tsx +2 -1
  119. package/src/components/composites/appearance-editor/accent-swatch-groups.tsx +147 -0
  120. package/src/components/composites/appearance-editor/appearance-choices.tsx +17 -74
  121. package/src/components/composites/appearance-editor/appearance-editor.tsx +19 -6
  122. package/src/components/composites/appearance-editor/appearance-types.ts +9 -0
  123. package/src/components/composites/appearance-editor/font-settings-group.tsx +350 -0
  124. package/src/components/composites/appearance-editor/index.ts +19 -0
  125. package/src/components/composites/update-dialog/update-dialog.tsx +2 -2
  126. package/src/components/conversation/message-row.tsx +7 -3
  127. package/src/components/conversation/paste-token-editor.tsx +1 -1
  128. package/src/components/layout/side-rail/side-rail.tsx +3 -3
  129. package/src/components/layout/top-bar/top-bar.tsx +1 -1
  130. package/src/components/theme/appearance-panel.tsx +34 -83
  131. package/src/components/theme/theme-provider.tsx +50 -20
  132. package/src/components/ui/code-block/code-block.tsx +6 -6
  133. package/src/components/ui/command/command.tsx +1 -1
  134. package/src/components/ui/context-menu/context-menu.tsx +1 -1
  135. package/src/components/ui/diff-block/diff-block.tsx +7 -7
  136. package/src/components/ui/dropdown-menu/dropdown-menu.tsx +145 -7
  137. package/src/components/ui/kbd/kbd.tsx +1 -1
  138. package/src/components/ui/menubar/menubar.tsx +1 -1
  139. package/src/components/ui/typography/typography.tsx +6 -8
  140. package/src/lib/appearance-font-settings.ts +233 -0
  141. package/src/lib/font-catalog.ts +80 -0
  142. package/src/lib/safe-script.ts +7 -0
  143. package/src/lib/tokens.ts +64 -59
  144. package/src/styles/appearance-font-settings.css +110 -0
  145. package/src/styles/fonts.css +4 -5
  146. package/src/styles/globals.css +4 -1
  147. package/src/styles/theme.css +14 -0
package/README.md CHANGED
@@ -200,6 +200,7 @@ directories your application uses in its stylesheet, relative to that stylesheet
200
200
  @import 'tailwindcss';
201
201
  @import '@adea-ai/ui/theme.css';
202
202
  @import '@adea-ai/ui/base.css';
203
+ @import '@adea-ai/ui/appearance-font-settings.css'; /* optional: shared font roles and Appearance controls */
203
204
  @source '../node_modules/@adea-ai/ui/src/components/ui/button';
204
205
  ```
205
206
 
@@ -239,7 +240,11 @@ provider takes an initial selection and every axis is a plain value:
239
240
  darkThemeId: 'adea-dark',
240
241
  accent: 'theme', // or 'violet' | 'blue' | 'green' | 'amber' | 'cyan' | 'pink',
241
242
  // or a theme-carried accent such as 'ansi-blue'
242
- font: 'space-grotesk', // or 'system' | 'geist' | 'geist-mono' | 'jetbrains-mono'
243
+ fonts: {
244
+ ui: { family: 'system', size: 14 },
245
+ content: { family: 'system', size: 14 },
246
+ code: { family: 'system', size: 12 },
247
+ },
243
248
  }}
244
249
  >
245
250
  ```
@@ -249,7 +254,13 @@ provider takes an initial selection and every axis is a plain value:
249
254
  | Appearance | `light`, `dark`, `system` | a `dark` class plus `color-scheme` on `<html>` |
250
255
  | Theme | any of the 34 catalogue ids | the variant's tokens, written as custom properties on `<html>` |
251
256
  | Accent | `theme` (follow the variant), one of six presets, or an accent the theme pair carries (`ansi-blue`…) | a `data-accent` attribute on `<html>`, plus the resolved roles as custom properties |
252
- | Typeface | five options | a `data-font` attribute on `<html>` |
257
+ | Fonts | UI, content and code family ids from the shared catalogue; sizes from 10–32px | Stable `data-*-font` ids plus resolved `--font-*` family and pixel-size properties |
258
+
259
+ Older single-font `font` preferences remain readable and migrate their family to
260
+ the UI axis; new preferences store `fonts` with one family and size per axis.
261
+ Canonical size properties stay in pixels for native editor integrations; the
262
+ shared text tokens use rem-relative ratios so browser root-text enlargement still
263
+ scales UI, prose, keyboard keys and code.
253
264
 
254
265
  Read and change the current selection with `useTheme()`:
255
266
 
@@ -263,8 +274,9 @@ itself (`@adea-ai/themes`' `themeAccentPresets`): the palette's own blue,
263
274
  magenta, cyan and green, offered only when both themes clear the catalogue's
264
275
  accent floors. Their ids are role-shaped, so `ansi-blue` follows a theme switch
265
276
  to the new theme's blue; a slot the new pair cannot offer falls back to the
266
- theme's own primary. `AppearancePanel` and `AppearanceEditor` offer them after
267
- the six presets.
277
+ theme's own primary. The primary grid is the theme default plus five standard
278
+ presets; Violet remains available under Additional colors, and theme accents
279
+ have their own group.
268
280
 
269
281
  Two components ship for the settings surface: `AppearancePanel` is the whole
270
282
  appearance view, and `ThemeToggle` is the light/dark switch on its own.
@@ -294,16 +306,40 @@ Pass the same key to `ThemeProvider` as `storageKey`, and the two agree.
294
306
 
295
307
  ## Fonts
296
308
 
297
- Fonts are opt-in. `globals.css` does not import font assets; import
298
- `fonts.css` explicitly when those faces are wanted:
309
+ Font preference roles and font assets are separate opt-ins. Import
310
+ `appearance-font-settings.css` after the theme and base styles to enable the
311
+ shared UI/content/code projections and utilities. `globals.css` does not import
312
+ font assets; import `fonts.css` explicitly when those faces are wanted:
299
313
 
300
314
  ```css
301
315
  @import '@adea-ai/ui/theme.css';
302
316
  @import '@adea-ai/ui/base.css';
317
+ @import '@adea-ai/ui/appearance-font-settings.css';
303
318
  @import '@adea-ai/ui/fonts.css'; /* omit to bring your own */
304
319
  ```
305
320
 
306
- `--font-sans` and `--font-mono` are the two variables everything reads.
321
+ `appearance-font-settings.css` defaults `--font-ui`, `--font-content` and
322
+ `--font-code` to System and scales each text role. Import it after `theme.css` and
323
+ `base.css`, then apply settings with the shared runtime or prepaint helper; the
324
+ helper resolves catalogue ids to family tokens and writes canonical pixel sizes.
325
+ This stylesheet does not load any font files.
326
+ The `font-ui-size`, `font-content-size` and `font-code-size` entries in the token
327
+ gallery are runtime-projected properties: `applyAppearanceFontSettings` and
328
+ `fontSettingsBootstrapScript` write their pixel values from normalized font
329
+ settings. They are not fixed declarations in the theme stylesheet.
330
+ `--font-sans` and `--font-mono` remain compatibility aliases for UI and code.
331
+ The shared projection and prepaint helpers are exported from
332
+ `@adea-ai/ui/lib/appearance-font-settings`; hosts can normalize persisted ids,
333
+ apply the shared data attributes, resolve family ids through the published
334
+ `--font-family-*` tokens, and reuse the same bootstrap snippet without
335
+ duplicating catalogue rules.
336
+ For versioned host preferences, pass the expected version as the bootstrap
337
+ helper's second argument. Only an exact version match is applied before paint;
338
+ missing or unsupported records use the System defaults. Omitting the argument
339
+ retains the unversioned helper behavior.
340
+ Each UI, Content and Code chooser keeps the complete font catalogue, and Code
341
+ uses the exact selected catalogue family. Choose a `Mono` option when you want a
342
+ monospace face; `System` uses the platform's monospace counterpart for Code.
307
343
 
308
344
  ## Adding a component
309
345
 
@@ -321,7 +357,7 @@ Every component, every variant and the token galleries are in the workshop:
321
357
  bun run storybook
322
358
  ```
323
359
 
324
- The toolbar switches theme, accent, typeface and density live, which is the
360
+ The toolbar switches theme, accent, fonts and density live, which is the
325
361
  fastest way to see how a component behaves across the catalogue rather than in
326
362
  one theme.
327
363
 
@@ -1 +1 @@
1
- {"version":3,"file":"action-button.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/action-button/action-button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,GAAG,EACR,KAAK,cAAc,EACpB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAI1D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IACpF,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iGAAiG;IACjG,WAAW,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACjD,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA+CD,wBAAgB,YAAY,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,eAwG5F"}
1
+ {"version":3,"file":"action-button.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/action-button/action-button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,GAAG,EACR,KAAK,cAAc,EACpB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAI1D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IACpF,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iGAAiG;IACjG,WAAW,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACjD,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA+CD,wBAAgB,YAAY,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,eAyG5F"}
@@ -111,6 +111,9 @@ function ActionButton(props) {
111
111
  },
112
112
  get children() {
113
113
  return createComponent(Tooltip, {
114
+ get placement() {
115
+ return local.tooltipSide ?? "bottom";
116
+ },
114
117
  get open() {
115
118
  return tooltipOpen();
116
119
  },
@@ -126,14 +129,9 @@ function ActionButton(props) {
126
129
  get children() {
127
130
  return contents();
128
131
  }
129
- })), createComponent(TooltipContent, {
130
- get side() {
131
- return local.tooltipSide ?? "bottom";
132
- },
133
- get children() {
134
- return local.tooltip;
135
- }
136
- })];
132
+ })), createComponent(TooltipContent, { get children() {
133
+ return local.tooltip;
134
+ } })];
137
135
  }
138
136
  });
139
137
  }
@@ -1 +1 @@
1
- {"version":3,"file":"action-button.js","names":["Show","createSignal","splitProps","ComponentProps","JSX","ValidComponent","Button","ButtonProps","Spinner","Tooltip","TooltipContent","TooltipTrigger","ActionButtonProps","T","tooltip","tooltipSide","busy","busyLabel","invokeHandler","handler","event","Event","Array","isArray","callback","data","chainedHandler","before","guardedClick","disabled","onActivate","MouseEvent","preventDefault","stopImmediatePropagation","ButtonTargetProps","Omit","actionAs","ButtonTarget","props","local","rest","PolymorphicButton","targetProps","Element","_$createComponent","_$mergeProps","as","ActionButton","busyStatus","tooltipOpen","setTooltipOpen","tooltipSuppression","setTooltipSuppression","onClickCapture","buttonProps","PolymorphicButtonTarget","variant","size","touchTarget","class","filter","Boolean","join","tabIndex","undefined","role","href","onClick","detail","onPointerLeave","onBlur","Record","contents","when","children","label","_$memo","fallback","open","onOpenChange","side","_el$","_tmpl$","_$insert","_$createComponent","PolymorphicButton","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/action-button/action-button.tsx"],"sourcesContent":["import {\n Show,\n createSignal,\n splitProps,\n type ComponentProps,\n type JSX,\n type ValidComponent,\n} from 'solid-js'\nimport { Button, type ButtonProps } from '../../ui/button'\nimport { Spinner } from '../../ui/spinner'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'\n\n/**\n * ActionButton.\n *\n * A Button with optional explanatory tooltip and busy feedback. Use it for a\n * simple action that needs these states; actions with product-specific rules\n * should compose Button directly.\n *\n * The tooltip trigger renders the polymorphic Button itself. A disabled native\n * control cannot receive pointer or keyboard events. When a tooltip is set,\n * disabled actions therefore use focusable `aria-disabled` semantics and guard\n * activation; without a tooltip, native buttons retain native disabling.\n * Custom polymorphic components must forward aria, event, and href props for\n * these guarantees to apply.\n */\nexport type ActionButtonProps<T extends ValidComponent = 'button'> = ButtonProps<T> & {\n /** A short explanation, also shown on keyboard focus. The Button still needs its own name. */\n tooltip?: string\n /** Placement of the explanation; icon actions live in bars, so it defaults below the control. */\n tooltipSide?: 'top' | 'right' | 'bottom' | 'left'\n /** Disables the action and announces the busy label through a polite status region. */\n busy?: boolean\n /** The activity announced while busy. The Button's accessible name stays unchanged. */\n busyLabel?: string\n}\n\nfunction invokeHandler(handler: unknown, event: Event) {\n if (Array.isArray(handler)) {\n const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]\n callback(data, event)\n } else if (typeof handler === 'function') {\n const callback = handler as (event: Event) => void\n callback(event)\n }\n}\n\nfunction chainedHandler(handler: unknown, before: (event: Event) => void) {\n return (event: Event) => {\n before(event)\n invokeHandler(handler, event)\n }\n}\n\nfunction guardedClick(\n handler: unknown,\n disabled: () => boolean,\n onActivate: (event: MouseEvent) => void = () => {}\n) {\n return (event: MouseEvent) => {\n if (disabled()) {\n event.preventDefault()\n event.stopImmediatePropagation()\n return\n }\n\n onActivate(event)\n invokeHandler(handler, event)\n }\n}\n\ntype ButtonTargetProps<T extends ValidComponent = 'button'> = Omit<ButtonProps<T>, 'as'> & {\n actionAs?: T\n}\n\n/** Adapter keeps TooltipTrigger's `as` separate from Button's polymorphic `as`. */\nfunction ButtonTarget<T extends ValidComponent = 'button'>(props: ButtonTargetProps<T>) {\n const [local, rest] = splitProps(props as ButtonTargetProps, ['actionAs'])\n const PolymorphicButton = Button as (targetProps: ButtonProps<T>) => JSX.Element\n return <PolymorphicButton as={(local.actionAs ?? 'button') as T} {...(rest as ButtonProps<T>)} />\n}\n\nexport function ActionButton<T extends ValidComponent = 'button'>(props: ActionButtonProps<T>) {\n const [local, rest] = splitProps(props as ActionButtonProps, [\n 'as',\n 'variant',\n 'size',\n 'touchTarget',\n 'class',\n 'children',\n 'disabled',\n 'aria-busy',\n 'tooltip',\n 'tooltipSide',\n 'busy',\n 'busyLabel',\n ])\n const busy = () => local.busy ?? false\n const disabled = () => busy() || !!local.disabled\n const busyStatus = () => local.busyLabel ?? 'Working'\n const [tooltipOpen, setTooltipOpen] = createSignal(false)\n const [tooltipSuppression, setTooltipSuppression] = createSignal<'pointer' | 'focus' | null>(null)\n const onClickCapture = (rest as { onClickCapture?: unknown }).onClickCapture\n const PolymorphicButton = Button as (buttonProps: ButtonProps<T>) => JSX.Element\n const PolymorphicButtonTarget = ButtonTarget as (targetProps: ButtonTargetProps<T>) => JSX.Element\n\n const buttonProps = () => ({\n ...rest,\n variant: local.variant,\n size: local.size,\n touchTarget: local.touchTarget,\n class: [local.class, local.tooltip && disabled() && 'aria-disabled:pointer-events-auto']\n .filter(Boolean)\n .join(' '),\n disabled: disabled() && !local.tooltip,\n 'aria-disabled': disabled() ? true : rest['aria-disabled'],\n // Make tooltip-bearing native controls explicit tab stops in WebKit configurations that\n // otherwise skip implicit button stops. A caller's tabIndex remains authoritative.\n tabIndex:\n rest.tabIndex ??\n (local.as === 'a' && disabled() ? (local.tooltip ? 0 : -1) : local.tooltip ? 0 : undefined),\n role: local.as === 'a' && disabled() ? 'link' : rest.role,\n href: local.as === 'a' && disabled() ? undefined : (rest as { href?: string }).href,\n 'aria-busy': busy() || local['aria-busy'],\n onClick: guardedClick(rest.onClick, disabled, (event) => {\n if (!local.tooltip) return\n setTooltipSuppression(event.detail > 0 ? 'pointer' : 'focus')\n setTooltipOpen(false)\n }),\n onPointerLeave: chainedHandler(rest.onPointerLeave, () => {\n if (tooltipSuppression() === 'pointer') setTooltipSuppression(null)\n }),\n onBlur: chainedHandler(rest.onBlur, () => {\n if (tooltipSuppression() === 'focus') setTooltipSuppression(null)\n }),\n onClickCapture: onClickCapture ? guardedClick(onClickCapture, disabled) : undefined,\n 'on:click': guardedClick((rest as Record<string, unknown>)['on:click'], disabled),\n 'oncapture:click': guardedClick((rest as Record<string, unknown>)['oncapture:click'], disabled),\n })\n\n const contents = () => (\n <>\n <Show when={busy()}>\n <Spinner size=\"sm\" label={false} aria-hidden=\"true\" data-icon=\"inline-start\" />\n </Show>\n {local.children}\n </>\n )\n\n return (\n <>\n <Show\n when={local.tooltip}\n fallback={\n <PolymorphicButton\n as={(local.as ?? 'button') as T}\n {...(buttonProps() as unknown as ButtonProps<T>)}\n >\n {contents()}\n </PolymorphicButton>\n }\n >\n <Tooltip\n open={tooltipOpen()}\n onOpenChange={(open) => {\n if (open && tooltipSuppression()) return\n setTooltipOpen(open)\n }}\n >\n <TooltipTrigger\n as={PolymorphicButtonTarget}\n {...(buttonProps() as unknown as ComponentProps<typeof PolymorphicButtonTarget>)}\n actionAs={(local.as ?? 'button') as T}\n >\n {contents()}\n </TooltipTrigger>\n <TooltipContent side={local.tooltipSide ?? 'bottom'}>{local.tooltip}</TooltipContent>\n </Tooltip>\n </Show>\n <Show when={busy()}>\n <span role=\"status\" aria-live=\"polite\" class=\"visually-hidden\">\n {busyStatus()}\n </span>\n </Show>\n </>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"action-button.js","names":["Show","createSignal","splitProps","ComponentProps","JSX","ValidComponent","Button","ButtonProps","Spinner","Tooltip","TooltipContent","TooltipTrigger","ActionButtonProps","T","tooltip","tooltipSide","busy","busyLabel","invokeHandler","handler","event","Event","Array","isArray","callback","data","chainedHandler","before","guardedClick","disabled","onActivate","MouseEvent","preventDefault","stopImmediatePropagation","ButtonTargetProps","Omit","actionAs","ButtonTarget","props","local","rest","PolymorphicButton","targetProps","Element","_$createComponent","_$mergeProps","as","ActionButton","busyStatus","tooltipOpen","setTooltipOpen","tooltipSuppression","setTooltipSuppression","onClickCapture","buttonProps","PolymorphicButtonTarget","variant","size","touchTarget","class","filter","Boolean","join","tabIndex","undefined","role","href","onClick","detail","onPointerLeave","onBlur","Record","contents","when","children","label","_$memo","fallback","placement","open","onOpenChange","_el$","_tmpl$","_$insert","_$createComponent","PolymorphicButton","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/action-button/action-button.tsx"],"sourcesContent":["import {\n Show,\n createSignal,\n splitProps,\n type ComponentProps,\n type JSX,\n type ValidComponent,\n} from 'solid-js'\nimport { Button, type ButtonProps } from '../../ui/button'\nimport { Spinner } from '../../ui/spinner'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'\n\n/**\n * ActionButton.\n *\n * A Button with optional explanatory tooltip and busy feedback. Use it for a\n * simple action that needs these states; actions with product-specific rules\n * should compose Button directly.\n *\n * The tooltip trigger renders the polymorphic Button itself. A disabled native\n * control cannot receive pointer or keyboard events. When a tooltip is set,\n * disabled actions therefore use focusable `aria-disabled` semantics and guard\n * activation; without a tooltip, native buttons retain native disabling.\n * Custom polymorphic components must forward aria, event, and href props for\n * these guarantees to apply.\n */\nexport type ActionButtonProps<T extends ValidComponent = 'button'> = ButtonProps<T> & {\n /** A short explanation, also shown on keyboard focus. The Button still needs its own name. */\n tooltip?: string\n /** Placement of the explanation; icon actions live in bars, so it defaults below the control. */\n tooltipSide?: 'top' | 'right' | 'bottom' | 'left'\n /** Disables the action and announces the busy label through a polite status region. */\n busy?: boolean\n /** The activity announced while busy. The Button's accessible name stays unchanged. */\n busyLabel?: string\n}\n\nfunction invokeHandler(handler: unknown, event: Event) {\n if (Array.isArray(handler)) {\n const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]\n callback(data, event)\n } else if (typeof handler === 'function') {\n const callback = handler as (event: Event) => void\n callback(event)\n }\n}\n\nfunction chainedHandler(handler: unknown, before: (event: Event) => void) {\n return (event: Event) => {\n before(event)\n invokeHandler(handler, event)\n }\n}\n\nfunction guardedClick(\n handler: unknown,\n disabled: () => boolean,\n onActivate: (event: MouseEvent) => void = () => {}\n) {\n return (event: MouseEvent) => {\n if (disabled()) {\n event.preventDefault()\n event.stopImmediatePropagation()\n return\n }\n\n onActivate(event)\n invokeHandler(handler, event)\n }\n}\n\ntype ButtonTargetProps<T extends ValidComponent = 'button'> = Omit<ButtonProps<T>, 'as'> & {\n actionAs?: T\n}\n\n/** Adapter keeps TooltipTrigger's `as` separate from Button's polymorphic `as`. */\nfunction ButtonTarget<T extends ValidComponent = 'button'>(props: ButtonTargetProps<T>) {\n const [local, rest] = splitProps(props as ButtonTargetProps, ['actionAs'])\n const PolymorphicButton = Button as (targetProps: ButtonProps<T>) => JSX.Element\n return <PolymorphicButton as={(local.actionAs ?? 'button') as T} {...(rest as ButtonProps<T>)} />\n}\n\nexport function ActionButton<T extends ValidComponent = 'button'>(props: ActionButtonProps<T>) {\n const [local, rest] = splitProps(props as ActionButtonProps, [\n 'as',\n 'variant',\n 'size',\n 'touchTarget',\n 'class',\n 'children',\n 'disabled',\n 'aria-busy',\n 'tooltip',\n 'tooltipSide',\n 'busy',\n 'busyLabel',\n ])\n const busy = () => local.busy ?? false\n const disabled = () => busy() || !!local.disabled\n const busyStatus = () => local.busyLabel ?? 'Working'\n const [tooltipOpen, setTooltipOpen] = createSignal(false)\n const [tooltipSuppression, setTooltipSuppression] = createSignal<'pointer' | 'focus' | null>(null)\n const onClickCapture = (rest as { onClickCapture?: unknown }).onClickCapture\n const PolymorphicButton = Button as (buttonProps: ButtonProps<T>) => JSX.Element\n const PolymorphicButtonTarget = ButtonTarget as (targetProps: ButtonTargetProps<T>) => JSX.Element\n\n const buttonProps = () => ({\n ...rest,\n variant: local.variant,\n size: local.size,\n touchTarget: local.touchTarget,\n class: [local.class, local.tooltip && disabled() && 'aria-disabled:pointer-events-auto']\n .filter(Boolean)\n .join(' '),\n disabled: disabled() && !local.tooltip,\n 'aria-disabled': disabled() ? true : rest['aria-disabled'],\n // Make tooltip-bearing native controls explicit tab stops in WebKit configurations that\n // otherwise skip implicit button stops. A caller's tabIndex remains authoritative.\n tabIndex:\n rest.tabIndex ??\n (local.as === 'a' && disabled() ? (local.tooltip ? 0 : -1) : local.tooltip ? 0 : undefined),\n role: local.as === 'a' && disabled() ? 'link' : rest.role,\n href: local.as === 'a' && disabled() ? undefined : (rest as { href?: string }).href,\n 'aria-busy': busy() || local['aria-busy'],\n onClick: guardedClick(rest.onClick, disabled, (event) => {\n if (!local.tooltip) return\n setTooltipSuppression(event.detail > 0 ? 'pointer' : 'focus')\n setTooltipOpen(false)\n }),\n onPointerLeave: chainedHandler(rest.onPointerLeave, () => {\n if (tooltipSuppression() === 'pointer') setTooltipSuppression(null)\n }),\n onBlur: chainedHandler(rest.onBlur, () => {\n if (tooltipSuppression() === 'focus') setTooltipSuppression(null)\n }),\n onClickCapture: onClickCapture ? guardedClick(onClickCapture, disabled) : undefined,\n 'on:click': guardedClick((rest as Record<string, unknown>)['on:click'], disabled),\n 'oncapture:click': guardedClick((rest as Record<string, unknown>)['oncapture:click'], disabled),\n })\n\n const contents = () => (\n <>\n <Show when={busy()}>\n <Spinner size=\"sm\" label={false} aria-hidden=\"true\" data-icon=\"inline-start\" />\n </Show>\n {local.children}\n </>\n )\n\n return (\n <>\n <Show\n when={local.tooltip}\n fallback={\n <PolymorphicButton\n as={(local.as ?? 'button') as T}\n {...(buttonProps() as unknown as ButtonProps<T>)}\n >\n {contents()}\n </PolymorphicButton>\n }\n >\n <Tooltip\n placement={local.tooltipSide ?? 'bottom'}\n open={tooltipOpen()}\n onOpenChange={(open) => {\n if (open && tooltipSuppression()) return\n setTooltipOpen(open)\n }}\n >\n <TooltipTrigger\n as={PolymorphicButtonTarget}\n {...(buttonProps() as unknown as ComponentProps<typeof PolymorphicButtonTarget>)}\n actionAs={(local.as ?? 'button') as T}\n >\n {contents()}\n </TooltipTrigger>\n <TooltipContent>{local.tooltip}</TooltipContent>\n </Tooltip>\n </Show>\n <Show when={busy()}>\n <span role=\"status\" aria-live=\"polite\" class=\"visually-hidden\">\n {busyStatus()}\n </span>\n </Show>\n </>\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export declare const PRIMARY_ACCENT_IDS: readonly ["blue", "green", "amber", "cyan", "pink"];
2
+ type AccentOption = Readonly<{
3
+ id: string;
4
+ label: string;
5
+ light?: string;
6
+ dark?: string;
7
+ }>;
8
+ type AccentSwatchGroupsProps = {
9
+ accentOptions: readonly AccentOption[];
10
+ themeAccentOptions: readonly AccentOption[];
11
+ resolvedAppearance: 'light' | 'dark';
12
+ themeDefaultColor: string;
13
+ };
14
+ export declare function AccentSwatchGroups(props: AccentSwatchGroupsProps): import("solid-js").JSX.Element;
15
+ export {};
16
+ //# sourceMappingURL=accent-swatch-groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accent-swatch-groups.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/accent-swatch-groups.tsx"],"names":[],"mappings":"AA2BA,eAAO,MAAM,kBAAkB,qDAAsD,CAAA;AAGrF,KAAK,YAAY,GAAG,QAAQ,CAAC;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAC,CAAA;AAEF,KAAK,uBAAuB,GAAG;IAC7B,aAAa,EAAE,SAAS,YAAY,EAAE,CAAA;IACtC,kBAAkB,EAAE,SAAS,YAAY,EAAE,CAAA;IAC3C,kBAAkB,EAAE,OAAO,GAAG,MAAM,CAAA;IACpC,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,kCAoEhE"}
@@ -0,0 +1,151 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { createComponent, effect, insert, memo, setStyleProperty, template } from "solid-js/web";
3
+ import { For, Show } from "solid-js";
4
+ import { RadioGroup } from "@kobalte/core/radio-group";
5
+ //#region src/components/composites/appearance-editor/accent-swatch-groups.tsx
6
+ var _tmpl$ = /*#__PURE__*/ template(`<div class="grid grid-cols-3 gap-2"data-primary-accent-grid>`);
7
+ var _tmpl$2 = /*#__PURE__*/ template(`<div class="flex flex-col gap-1"><span class="text-xs font-medium text-muted-foreground">Additional colors</span><div class="flex flex-wrap gap-2">`);
8
+ var _tmpl$3 = /*#__PURE__*/ template(`<div class="flex flex-col gap-1"><span class="text-xs font-medium text-muted-foreground">Theme accents</span><div class="grid grid-cols-3 gap-2">`);
9
+ var _tmpl$4 = /*#__PURE__*/ template(`<span class="block size-full rounded-full"aria-hidden=true>`);
10
+ var _tmpl$5 = /*#__PURE__*/ template(`<span class=sr-only>`);
11
+ var PRIMARY_ACCENT_IDS = [
12
+ "blue",
13
+ "green",
14
+ "amber",
15
+ "cyan",
16
+ "pink"
17
+ ];
18
+ var primaryAccentIds = new Set(PRIMARY_ACCENT_IDS);
19
+ function AccentSwatchGroups(props) {
20
+ const primaryAccents = () => PRIMARY_ACCENT_IDS.flatMap((id) => {
21
+ const option = props.accentOptions.find((candidate) => candidate.id === id);
22
+ return option ? [option] : [];
23
+ });
24
+ const additionalAccents = () => props.accentOptions.filter((option) => option.id !== "theme" && !primaryAccentIds.has(option.id));
25
+ return [
26
+ (() => {
27
+ var _el$ = _tmpl$();
28
+ insert(_el$, createComponent(AccentSwatch, {
29
+ id: "theme",
30
+ label: "Theme default",
31
+ get color() {
32
+ return props.themeDefaultColor;
33
+ },
34
+ group: "primary"
35
+ }), null);
36
+ insert(_el$, createComponent(For, {
37
+ get each() {
38
+ return primaryAccents();
39
+ },
40
+ children: (option) => createComponent(AccentSwatch, {
41
+ get id() {
42
+ return option.id;
43
+ },
44
+ get label() {
45
+ return option.label;
46
+ },
47
+ get color() {
48
+ return (props.resolvedAppearance === "light" ? option.light : option.dark) ?? "";
49
+ },
50
+ group: "primary"
51
+ })
52
+ }), null);
53
+ return _el$;
54
+ })(),
55
+ createComponent(Show, {
56
+ get when() {
57
+ return additionalAccents().length > 0;
58
+ },
59
+ get children() {
60
+ var _el$2 = _tmpl$2(), _el$4 = _el$2.firstChild.nextSibling;
61
+ insert(_el$4, createComponent(For, {
62
+ get each() {
63
+ return additionalAccents();
64
+ },
65
+ children: (option) => createComponent(AccentSwatch, {
66
+ get id() {
67
+ return option.id;
68
+ },
69
+ get label() {
70
+ return option.label;
71
+ },
72
+ get color() {
73
+ return (props.resolvedAppearance === "light" ? option.light : option.dark) ?? "";
74
+ },
75
+ group: "additional"
76
+ })
77
+ }));
78
+ return _el$2;
79
+ }
80
+ }),
81
+ createComponent(Show, {
82
+ get when() {
83
+ return props.themeAccentOptions.length > 0;
84
+ },
85
+ get children() {
86
+ var _el$5 = _tmpl$3(), _el$7 = _el$5.firstChild.nextSibling;
87
+ insert(_el$7, createComponent(For, {
88
+ get each() {
89
+ return props.themeAccentOptions;
90
+ },
91
+ children: (option) => createComponent(AccentSwatch, {
92
+ get id() {
93
+ return option.id;
94
+ },
95
+ get label() {
96
+ return "Theme " + option.label.toLowerCase();
97
+ },
98
+ get color() {
99
+ return (props.resolvedAppearance === "light" ? option.light : option.dark) ?? "";
100
+ },
101
+ group: "theme",
102
+ dashed: true
103
+ })
104
+ }));
105
+ return _el$5;
106
+ }
107
+ })
108
+ ];
109
+ }
110
+ function AccentSwatch(props) {
111
+ return createComponent(RadioGroup.Item, {
112
+ get value() {
113
+ return props.id;
114
+ },
115
+ get ["data-accent-option"]() {
116
+ return props.id;
117
+ },
118
+ get ["data-primary-accent"]() {
119
+ return memo(() => props.group === "primary")() ? props.id : void 0;
120
+ },
121
+ get ["data-additional-accent"]() {
122
+ return memo(() => props.group === "additional")() ? props.id : void 0;
123
+ },
124
+ get ["data-theme-accent"]() {
125
+ return memo(() => props.group === "theme")() ? props.id : void 0;
126
+ },
127
+ "class": "rounded-full focus-within:ring-3 focus-within:ring-ring/50",
128
+ get children() {
129
+ return [createComponent(RadioGroup.ItemInput, {}), createComponent(RadioGroup.ItemLabel, {
130
+ get ["class"]() {
131
+ return cn("block size-7 cursor-pointer rounded-full border p-0.5 data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary", { "border-dashed data-[checked]:border-solid": props.dashed });
132
+ },
133
+ get children() {
134
+ return [(() => {
135
+ var _el$8 = _tmpl$4();
136
+ effect((_$p) => setStyleProperty(_el$8, "background-color", props.color));
137
+ return _el$8;
138
+ })(), (() => {
139
+ var _el$9 = _tmpl$5();
140
+ insert(_el$9, () => props.label);
141
+ return _el$9;
142
+ })()];
143
+ }
144
+ })];
145
+ }
146
+ });
147
+ }
148
+ //#endregion
149
+ export { AccentSwatchGroups, PRIMARY_ACCENT_IDS };
150
+
151
+ //# sourceMappingURL=accent-swatch-groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accent-swatch-groups.js","names":["RadioGroup","Radio","For","Show","cn","PRIMARY_ACCENT_IDS","const","primaryAccentIds","ReadonlySet","Set","AccentOption","Readonly","id","label","light","dark","AccentSwatchGroupsProps","accentOptions","themeAccentOptions","resolvedAppearance","themeDefaultColor","AccentSwatchGroups","props","primaryAccents","flatMap","option","find","candidate","additionalAccents","filter","has","_el$","_tmpl$","_$insert","_$createComponent","AccentSwatch","color","group","each","children","when","length","_el$2","_tmpl$2","_el$3","firstChild","_el$4","nextSibling","_el$5","_tmpl$3","_el$6","_el$7","toLowerCase","dashed","Item","value","data-accent-option","data-primary-accent","_$memo","undefined","data-additional-accent","data-theme-accent","ItemInput","ItemLabel","class","_el$8","_tmpl$4","_$effect","_$p","_$setStyleProperty","_el$9","_tmpl$5","_$createComponent","Radio","_$memo","_$setStyleProperty"],"sources":["../../../../src/components/composites/appearance-editor/accent-swatch-groups.tsx"],"sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2026 Wing\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\nimport { RadioGroup as Radio } from '@kobalte/core/radio-group'\nimport { For, Show } from 'solid-js'\nimport { cn } from '#lib/utils'\n\nexport const PRIMARY_ACCENT_IDS = ['blue', 'green', 'amber', 'cyan', 'pink'] as const\nconst primaryAccentIds: ReadonlySet<string> = new Set(PRIMARY_ACCENT_IDS)\n\ntype AccentOption = Readonly<{\n id: string\n label: string\n light?: string\n dark?: string\n}>\n\ntype AccentSwatchGroupsProps = {\n accentOptions: readonly AccentOption[]\n themeAccentOptions: readonly AccentOption[]\n resolvedAppearance: 'light' | 'dark'\n themeDefaultColor: string\n}\n\nexport function AccentSwatchGroups(props: AccentSwatchGroupsProps) {\n const primaryAccents = () =>\n PRIMARY_ACCENT_IDS.flatMap((id) => {\n const option = props.accentOptions.find((candidate) => candidate.id === id)\n return option ? [option] : []\n })\n const additionalAccents = () =>\n props.accentOptions.filter(\n (option) => option.id !== 'theme' && !primaryAccentIds.has(option.id)\n )\n\n return (\n <>\n <div class=\"grid grid-cols-3 gap-2\" data-primary-accent-grid>\n <AccentSwatch\n id=\"theme\"\n label=\"Theme default\"\n color={props.themeDefaultColor}\n group=\"primary\"\n />\n <For each={primaryAccents()}>\n {(option) => (\n <AccentSwatch\n id={option.id}\n label={option.label}\n color={(props.resolvedAppearance === 'light' ? option.light : option.dark) ?? ''}\n group=\"primary\"\n />\n )}\n </For>\n </div>\n <Show when={additionalAccents().length > 0}>\n <div class=\"flex flex-col gap-1\">\n <span class=\"text-xs font-medium text-muted-foreground\">Additional colors</span>\n <div class=\"flex flex-wrap gap-2\">\n <For each={additionalAccents()}>\n {(option) => (\n <AccentSwatch\n id={option.id}\n label={option.label}\n color={(props.resolvedAppearance === 'light' ? option.light : option.dark) ?? ''}\n group=\"additional\"\n />\n )}\n </For>\n </div>\n </div>\n </Show>\n <Show when={props.themeAccentOptions.length > 0}>\n <div class=\"flex flex-col gap-1\">\n <span class=\"text-xs font-medium text-muted-foreground\">Theme accents</span>\n <div class=\"grid grid-cols-3 gap-2\">\n <For each={props.themeAccentOptions}>\n {(option) => (\n <AccentSwatch\n id={option.id}\n label={'Theme ' + option.label.toLowerCase()}\n color={(props.resolvedAppearance === 'light' ? option.light : option.dark) ?? ''}\n group=\"theme\"\n dashed\n />\n )}\n </For>\n </div>\n </div>\n </Show>\n </>\n )\n}\n\nfunction AccentSwatch(props: {\n id: string\n label: string\n color: string\n group: 'primary' | 'additional' | 'theme'\n dashed?: boolean\n}) {\n return (\n <Radio.Item\n value={props.id}\n data-accent-option={props.id}\n data-primary-accent={props.group === 'primary' ? props.id : undefined}\n data-additional-accent={props.group === 'additional' ? props.id : undefined}\n data-theme-accent={props.group === 'theme' ? props.id : undefined}\n class=\"rounded-full focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel\n class={cn(\n 'block size-7 cursor-pointer rounded-full border p-0.5 data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary',\n { 'border-dashed data-[checked]:border-solid': props.dashed }\n )}\n >\n <span\n class=\"block size-full rounded-full\"\n aria-hidden=\"true\"\n style={{ 'background-color': props.color }}\n />\n <span class=\"sr-only\">{props.label}</span>\n </Radio.ItemLabel>\n </Radio.Item>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"appearance-choices.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/appearance-choices.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAc/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,qBAAqB,kCAyCvD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAOpE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,qBAAqB,kCAoGzD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,qBAAqB,kCA6BxD"}
1
+ {"version":3,"file":"appearance-choices.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/appearance-choices.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAa/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,qBAAqB,kCAyCvD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAOpE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,qBAAqB,kCA2CzD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,qBAAqB,kCA6BxD"}
@@ -1,15 +1,13 @@
1
1
  import { isThemeAccentId } from "../../../lib/theme-accents.js";
2
+ import { AccentSwatchGroups } from "./accent-swatch-groups.js";
2
3
  import { ThemeMiniature, ThemeMiniatureSplit } from "./theme-preview.js";
3
- import { createComponent, effect, insert, memo, setStyleProperty, template } from "solid-js/web";
4
+ import { createComponent, insert, memo, template } from "solid-js/web";
4
5
  import { For, Show } from "solid-js";
5
6
  import { RadioGroup } from "@kobalte/core/radio-group";
6
7
  //#region src/components/composites/appearance-editor/appearance-choices.tsx
7
8
  var _tmpl$ = /*#__PURE__*/ template(`<span class="block h-20 w-full">`);
8
9
  var _tmpl$2 = /*#__PURE__*/ template(`<span>`);
9
- var _tmpl$3 = /*#__PURE__*/ template(`<span class="block size-full rounded-full"aria-hidden=true>`);
10
- var _tmpl$4 = /*#__PURE__*/ template(`<span class=sr-only>Theme default`);
11
- var _tmpl$5 = /*#__PURE__*/ template(`<div class="grid grid-cols-4 gap-2">`);
12
- var _tmpl$6 = /*#__PURE__*/ template(`<span class=sr-only>`);
10
+ var _tmpl$3 = /*#__PURE__*/ template(`<div class="flex flex-col gap-1"><span class="text-xs font-medium text-muted-foreground">Custom`);
13
11
  var MODES = [
14
12
  {
15
13
  value: "system",
@@ -127,94 +125,36 @@ function AccentChoices(props) {
127
125
  },
128
126
  orientation: "horizontal",
129
127
  "aria-label": "Accent",
130
- "class": "flex items-center gap-3",
128
+ "class": "flex flex-col items-start gap-3",
131
129
  onChange: (accent) => props.onChange({ accent: accent === "custom" ? props.customAccentValue ?? "" : accent }),
132
130
  get children() {
133
- return [(() => {
134
- var _el$3 = _tmpl$5();
131
+ return [createComponent(AccentSwatchGroups, {
132
+ get accentOptions() {
133
+ return props.accentOptions;
134
+ },
135
+ get themeAccentOptions() {
136
+ return themeAccents();
137
+ },
138
+ get resolvedAppearance() {
139
+ return resolvedAppearance();
140
+ },
141
+ get themeDefaultColor() {
142
+ return memo(() => resolvedAppearance() === "light")() ? props.lightTheme.colors.accent : props.darkTheme.colors.accent;
143
+ }
144
+ }), (() => {
145
+ var _el$3 = _tmpl$3();
146
+ _el$3.firstChild;
135
147
  insert(_el$3, createComponent(RadioGroup.Item, {
136
- value: "theme",
137
- "class": "rounded-full focus-within:ring-3 focus-within:ring-ring/50",
148
+ value: "custom",
138
149
  get children() {
139
150
  return [createComponent(RadioGroup.ItemInput, {}), createComponent(RadioGroup.ItemLabel, {
140
- "class": "block size-7 cursor-pointer rounded-full border p-0.5 data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary",
141
- get children() {
142
- return [(() => {
143
- var _el$4 = _tmpl$3();
144
- effect((_$p) => setStyleProperty(_el$4, "background-color", resolvedAppearance() === "light" ? props.lightTheme.colors.accent : props.darkTheme.colors.accent));
145
- return _el$4;
146
- })(), _tmpl$4()];
147
- }
151
+ "class": "flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs whitespace-nowrap data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary",
152
+ children: "Custom"
148
153
  })];
149
154
  }
150
155
  }), null);
151
- insert(_el$3, createComponent(For, {
152
- get each() {
153
- return props.accentOptions;
154
- },
155
- children: (option) => createComponent(RadioGroup.Item, {
156
- get value() {
157
- return option.id;
158
- },
159
- "class": "rounded-full focus-within:ring-3 focus-within:ring-ring/50",
160
- get children() {
161
- return [createComponent(RadioGroup.ItemInput, {}), createComponent(RadioGroup.ItemLabel, {
162
- "class": "block size-7 cursor-pointer rounded-full border p-0.5 data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary",
163
- get children() {
164
- return [(() => {
165
- var _el$6 = _tmpl$3();
166
- effect((_$p) => setStyleProperty(_el$6, "background-color", resolvedAppearance() === "light" ? option.light : option.dark));
167
- return _el$6;
168
- })(), (() => {
169
- var _el$7 = _tmpl$6();
170
- insert(_el$7, () => option.label);
171
- return _el$7;
172
- })()];
173
- }
174
- })];
175
- }
176
- })
177
- }), null);
178
- insert(_el$3, createComponent(For, {
179
- get each() {
180
- return themeAccents();
181
- },
182
- children: (option) => createComponent(RadioGroup.Item, {
183
- get value() {
184
- return option.id;
185
- },
186
- get ["data-theme-accent"]() {
187
- return option.id;
188
- },
189
- "class": "rounded-full focus-within:ring-3 focus-within:ring-ring/50",
190
- get children() {
191
- return [createComponent(RadioGroup.ItemInput, {}), createComponent(RadioGroup.ItemLabel, {
192
- "class": "block size-7 cursor-pointer rounded-full border border-dashed p-0.5 data-[checked]:border-solid data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary",
193
- get children() {
194
- return [(() => {
195
- var _el$8 = _tmpl$3();
196
- effect((_$p) => setStyleProperty(_el$8, "background-color", resolvedAppearance() === "light" ? option.light : option.dark));
197
- return _el$8;
198
- })(), (() => {
199
- var _el$9 = _tmpl$6();
200
- insert(_el$9, () => `Theme ${option.label.toLowerCase()}`);
201
- return _el$9;
202
- })()];
203
- }
204
- })];
205
- }
206
- })
207
- }), null);
208
156
  return _el$3;
209
- })(), createComponent(RadioGroup.Item, {
210
- value: "custom",
211
- get children() {
212
- return [createComponent(RadioGroup.ItemInput, {}), createComponent(RadioGroup.ItemLabel, {
213
- "class": "flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs whitespace-nowrap data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary",
214
- children: "Custom"
215
- })];
216
- }
217
- })];
157
+ })()];
218
158
  }
219
159
  });
220
160
  }
@@ -1 +1 @@
1
- {"version":3,"file":"appearance-choices.js","names":["RadioGroup","Radio","For","Show","isThemeAccentId","AppearanceEditorProps","ThemeMiniature","ThemeMiniatureSplit","MODES","value","label","const","SURFACES","ModeChoices","props","_$createComponent","draft","mode","disabled","saving","orientation","onChange","option","find","candidate","children","each","Item","data-mode","ItemInput","ItemLabel","_el$","_tmpl$","_$insert","when","fallback","theme","_$memo","lightTheme","darkTheme","light","dark","_el$2","_tmpl$2","isCustomAccent","accent","accentOptions","some","id","AccentChoices","resolvedAppearance","themeAccents","themeAccentOptions","accentSelection","customAccentValue","_el$3","_tmpl$5","_el$4","_tmpl$3","_$effect","_$p","_$setStyleProperty","colors","_tmpl$4","_el$6","_el$7","_tmpl$6","data-theme-accent","_el$8","_el$9","toLowerCase","GlassChoices","surface","surfaceCapability","frosted","_$createComponent","Radio","_$memo","_$setStyleProperty"],"sources":["../../../../src/components/composites/appearance-editor/appearance-choices.tsx"],"sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2026 Wing\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n// Pinned mode-card, accent-swatch, and glass-chip composition. Kobalte replaces\n// the app port's manual roving focus; it does not replace the donor hierarchy.\nimport { RadioGroup as Radio } from '@kobalte/core/radio-group'\nimport { For, Show } from 'solid-js'\nimport { isThemeAccentId } from '#lib/theme-accents'\nimport type { AppearanceEditorProps } from './appearance-types'\nimport { ThemeMiniature, ThemeMiniatureSplit } from './theme-preview'\n\nconst MODES = [\n { value: 'system', label: 'System' },\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n] as const\nconst SURFACES = [\n { value: 'theme', label: 'Theme default' },\n { value: 'frosted', label: 'Frosted' },\n { value: 'opaque', label: 'Opaque' },\n] as const\n\nexport function ModeChoices(props: AppearanceEditorProps) {\n return (\n <Radio\n value={props.draft.mode}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Appearance mode\"\n onChange={(mode) => {\n const option = MODES.find((candidate) => candidate.value === mode)\n if (option) props.onChange({ mode: option.value })\n }}\n class=\"grid grid-cols-1 gap-3 px-4 py-4 sm:grid-cols-3\"\n >\n <For each={MODES}>\n {(option) => (\n <Radio.Item\n value={option.value}\n data-mode={option.value}\n class=\"min-w-0 rounded-lg focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"flex cursor-pointer flex-col gap-2 rounded-lg border p-2 text-center text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\">\n <span class=\"block h-20 w-full\">\n <Show\n when={option.value === 'system'}\n fallback={\n <ThemeMiniature\n theme={option.value === 'light' ? props.lightTheme : props.darkTheme}\n />\n }\n >\n <ThemeMiniatureSplit light={props.lightTheme} dark={props.darkTheme} />\n </Show>\n </span>\n <span>{option.label}</span>\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n </Radio>\n )\n}\n\n/**\n * Whether the draft accent is the host's custom colour: not the theme default,\n * not a preset, and not a theme accent id. A theme accent id counts as known even\n * when the current pair cannot offer it — it is a stored role, not a colour the\n * user typed, and showing it in the custom field would invite editing `ansi-blue`.\n */\nexport function isCustomAccent(props: AppearanceEditorProps): boolean {\n const accent = props.draft.accent\n return (\n accent !== 'theme' &&\n !props.accentOptions.some((option) => option.id === accent) &&\n !isThemeAccentId(accent)\n )\n}\n\nexport function AccentChoices(props: AppearanceEditorProps) {\n // The resolved appearance decides which side of a preset pair — and which\n // theme accent — the swatches paint, so the row previews each choice as the\n // user will see it.\n const resolvedAppearance = () =>\n props.draft.mode === 'system' ? props.resolvedAppearance : props.draft.mode\n // The accents the previewed theme pair carries, offered after the presets.\n const themeAccents = () => props.themeAccentOptions ?? []\n const accentSelection = () => (isCustomAccent(props) ? 'custom' : props.draft.accent)\n return (\n <Radio\n value={accentSelection()}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Accent\"\n // One chip (custom) beside a capped swatch grid: a chip-per-state column\n // beside a full-width swatch line overflowed the settings sidebar's\n // control column, so the pair stays a narrow chip plus a wrapped grid.\n class=\"flex items-center gap-3\"\n onChange={(accent) =>\n props.onChange({\n accent: accent === 'custom' ? (props.customAccentValue ?? '') : accent,\n })\n }\n >\n <div class=\"grid grid-cols-4 gap-2\">\n {/* The theme's own accent is the first swatch. Picking it is the same\n act the old dedicated \"Theme default\" chip performed, without a\n second control for the state the row starts in. */}\n <Radio.Item\n value=\"theme\"\n class=\"rounded-full focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"block size-7 cursor-pointer rounded-full border p-0.5 data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary\">\n <span\n class=\"block size-full rounded-full\"\n aria-hidden=\"true\"\n style={{\n 'background-color':\n resolvedAppearance() === 'light'\n ? props.lightTheme.colors.accent\n : props.darkTheme.colors.accent,\n }}\n />\n <span class=\"sr-only\">Theme default</span>\n </Radio.ItemLabel>\n </Radio.Item>\n <For each={props.accentOptions}>\n {(option) => (\n <Radio.Item\n value={option.id}\n class=\"rounded-full focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"block size-7 cursor-pointer rounded-full border p-0.5 data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary\">\n <span\n class=\"block size-full rounded-full\"\n aria-hidden=\"true\"\n style={{\n 'background-color':\n resolvedAppearance() === 'light' ? option.light : option.dark,\n }}\n />\n <span class=\"sr-only\">{option.label}</span>\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n <For each={themeAccents()}>\n {(option) => (\n <Radio.Item\n value={option.id}\n data-theme-accent={option.id}\n class=\"rounded-full focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"block size-7 cursor-pointer rounded-full border border-dashed p-0.5 data-[checked]:border-solid data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary\">\n <span\n class=\"block size-full rounded-full\"\n aria-hidden=\"true\"\n style={{\n 'background-color':\n resolvedAppearance() === 'light' ? option.light : option.dark,\n }}\n />\n <span class=\"sr-only\">{`Theme ${option.label.toLowerCase()}`}</span>\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n </div>\n <Radio.Item value=\"custom\">\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs whitespace-nowrap data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary\">\n Custom\n </Radio.ItemLabel>\n </Radio.Item>\n </Radio>\n )\n}\n\nexport function GlassChoices(props: AppearanceEditorProps) {\n return (\n <Radio\n value={props.draft.surface}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Glass\"\n class=\"flex flex-wrap gap-1\"\n onChange={(surface) => {\n const option = SURFACES.find((candidate) => candidate.value === surface)\n if (option) props.onChange({ surface: option.value })\n }}\n >\n <For each={SURFACES}>\n {(option) => (\n <Radio.Item\n value={option.value}\n disabled={option.value === 'frosted' && !props.surfaceCapability.frosted}\n class=\"rounded-md focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\">\n {option.label}\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n </Radio>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"appearance-choices.js","names":["RadioGroup","Radio","For","Show","isThemeAccentId","AccentSwatchGroups","AppearanceEditorProps","ThemeMiniature","ThemeMiniatureSplit","MODES","value","label","const","SURFACES","ModeChoices","props","_$createComponent","draft","mode","disabled","saving","orientation","onChange","option","find","candidate","children","each","Item","data-mode","ItemInput","ItemLabel","_el$","_tmpl$","_$insert","when","fallback","theme","_$memo","lightTheme","darkTheme","light","dark","_el$2","_tmpl$2","isCustomAccent","accent","accentOptions","some","id","AccentChoices","resolvedAppearance","themeAccents","themeAccentOptions","accentSelection","customAccentValue","themeDefaultColor","colors","_el$3","_tmpl$3","_el$4","firstChild","GlassChoices","surface","surfaceCapability","frosted","_$createComponent","Radio","_$memo"],"sources":["../../../../src/components/composites/appearance-editor/appearance-choices.tsx"],"sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2026 Wing\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n// Pinned mode-card, accent-swatch, and glass-chip composition. Kobalte replaces\n// the app port's manual roving focus; it does not replace the donor hierarchy.\nimport { RadioGroup as Radio } from '@kobalte/core/radio-group'\nimport { For, Show } from 'solid-js'\nimport { isThemeAccentId } from '#lib/theme-accents'\nimport { AccentSwatchGroups } from './accent-swatch-groups'\nimport type { AppearanceEditorProps } from './appearance-types'\nimport { ThemeMiniature, ThemeMiniatureSplit } from './theme-preview'\n\nconst MODES = [\n { value: 'system', label: 'System' },\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n] as const\nconst SURFACES = [\n { value: 'theme', label: 'Theme default' },\n { value: 'frosted', label: 'Frosted' },\n { value: 'opaque', label: 'Opaque' },\n] as const\nexport function ModeChoices(props: AppearanceEditorProps) {\n return (\n <Radio\n value={props.draft.mode}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Appearance mode\"\n onChange={(mode) => {\n const option = MODES.find((candidate) => candidate.value === mode)\n if (option) props.onChange({ mode: option.value })\n }}\n class=\"grid grid-cols-1 gap-3 px-4 py-4 sm:grid-cols-3\"\n >\n <For each={MODES}>\n {(option) => (\n <Radio.Item\n value={option.value}\n data-mode={option.value}\n class=\"min-w-0 rounded-lg focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"flex cursor-pointer flex-col gap-2 rounded-lg border p-2 text-center text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\">\n <span class=\"block h-20 w-full\">\n <Show\n when={option.value === 'system'}\n fallback={\n <ThemeMiniature\n theme={option.value === 'light' ? props.lightTheme : props.darkTheme}\n />\n }\n >\n <ThemeMiniatureSplit light={props.lightTheme} dark={props.darkTheme} />\n </Show>\n </span>\n <span>{option.label}</span>\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n </Radio>\n )\n}\n\n/**\n * Whether the draft accent is the host's custom colour: not the theme default,\n * not a preset, and not a theme accent id. A theme accent id counts as known even\n * when the current pair cannot offer it — it is a stored role, not a colour the\n * user typed, and showing it in the custom field would invite editing `ansi-blue`.\n */\nexport function isCustomAccent(props: AppearanceEditorProps): boolean {\n const accent = props.draft.accent\n return (\n accent !== 'theme' &&\n !props.accentOptions.some((option) => option.id === accent) &&\n !isThemeAccentId(accent)\n )\n}\n\nexport function AccentChoices(props: AppearanceEditorProps) {\n // The resolved appearance decides which side of a preset pair — and which\n // theme accent — the swatches paint, so the row previews each choice as the\n // user will see it.\n const resolvedAppearance = () =>\n props.draft.mode === 'system' ? props.resolvedAppearance : props.draft.mode\n // The accents the previewed theme pair carries, offered after the presets.\n const themeAccents = () => props.themeAccentOptions ?? []\n const accentSelection = () => (isCustomAccent(props) ? 'custom' : props.draft.accent)\n return (\n <Radio\n value={accentSelection()}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Accent\"\n class=\"flex flex-col items-start gap-3\"\n onChange={(accent) =>\n props.onChange({\n accent: accent === 'custom' ? (props.customAccentValue ?? '') : accent,\n })\n }\n >\n <AccentSwatchGroups\n accentOptions={props.accentOptions}\n themeAccentOptions={themeAccents()}\n resolvedAppearance={resolvedAppearance()}\n themeDefaultColor={\n resolvedAppearance() === 'light'\n ? props.lightTheme.colors.accent\n : props.darkTheme.colors.accent\n }\n />\n <div class=\"flex flex-col gap-1\">\n <span class=\"text-xs font-medium text-muted-foreground\">Custom</span>\n <Radio.Item value=\"custom\">\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs whitespace-nowrap data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary\">\n Custom\n </Radio.ItemLabel>\n </Radio.Item>\n </div>\n </Radio>\n )\n}\n\nexport function GlassChoices(props: AppearanceEditorProps) {\n return (\n <Radio\n value={props.draft.surface}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Glass\"\n class=\"flex flex-wrap gap-1\"\n onChange={(surface) => {\n const option = SURFACES.find((candidate) => candidate.value === surface)\n if (option) props.onChange({ surface: option.value })\n }}\n >\n <For each={SURFACES}>\n {(option) => (\n <Radio.Item\n value={option.value}\n disabled={option.value === 'frosted' && !props.surfaceCapability.frosted}\n class=\"rounded-md focus-within:ring-3 focus-within:ring-ring/50\"\n >\n <Radio.ItemInput />\n <Radio.ItemLabel class=\"flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50\">\n {option.label}\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n </Radio>\n )\n}\n"],"mappings":"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"}
@@ -4,8 +4,8 @@ import type { AppearanceEditorProps, AppearancePopoverProps } from './appearance
4
4
  * No ThemeProvider is required, and no catalogue, renderer engine or storage is
5
5
  * imported. Hosts normalize unknown IDs and custom colors, report recovery,
6
6
  * snapshot on open, preview on changes, commit on Save, and restore on dismissal.
7
- * Both theme rows remain mounted when mode changes. Typeface/density axes are
8
- * deliberately absent from Adea #425's accepted surface.
7
+ * Both theme rows remain mounted when mode changes. Density remains outside the
8
+ * accepted appearance surface; typeface settings are shared and host-persisted.
9
9
  */
10
10
  export declare function AppearanceEditor(props: AppearanceEditorProps): import("solid-js").JSX.Element;
11
11
  /** Modal anchoring/dismissal and nested-menu behavior come from Kobalte.
@@ -1 +1 @@
1
- {"version":3,"file":"appearance-editor.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/appearance-editor.tsx"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAIvF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,kCAqK5D;AAED;;6DAE6D;AAC7D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,kCAyB9D"}
1
+ {"version":3,"file":"appearance-editor.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/appearance-editor.tsx"],"names":[],"mappings":"AAiDA,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAIvF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,kCA2K5D;AAED;;6DAE6D;AAC7D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,kCA8B9D"}