@adea-ai/ui 0.146.9 → 0.146.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"appearance-choices.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/appearance-choices.tsx"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAe/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,qBAAqB,kCAmDvD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAOpE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,qBAAqB,kCA6IzD;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":"AAgCA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAe/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,qBAAqB,kCAmDvD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAOpE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,qBAAqB,kCA8IzD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,qBAAqB,kCA6BxD"}
@@ -294,6 +294,7 @@ function AccentChoices(props) {
294
294
  type: "button",
295
295
  variant: "outline",
296
296
  size: "sm",
297
+ tabIndex: 0,
297
298
  get disabled() {
298
299
  return props.saving;
299
300
  },
@@ -1 +1 @@
1
- {"version":3,"file":"appearance-choices.js","names":["RadioGroup","Radio","Monitor","Moon","Sun","For","Show","isThemeAccentId","Button","Input","AccentSwatchGroups","AppearanceEditorProps","ThemeMiniature","ThemeMiniatureSplit","MODES","value","label","const","SURFACES","MODE_ICONS","system","light","dark","ModeChoices","props","_$createComponent","draft","mode","disabled","saving","orientation","onChange","option","find","candidate","children","each","Icon","Item","data-mode","ItemInput","ItemLabel","_el$","_tmpl$","_$insert","when","fallback","theme","_$memo","lightTheme","darkTheme","showModeIcons","_el$3","_tmpl$3","_el$2","_tmpl$2","isCustomAccent","accent","accentOptions","some","id","AccentChoices","resolvedAppearance","accentSelection","showThemeDefault","showThemeDefaultAccent","entryStyle","accentEntryStyle","themeDefaultColor","colors","labelEntries","presets","filter","colorPicker","HTMLInputElement","openCustomColor","showPicker","click","customAccentValue","_el$6","_tmpl$7","showCustomAccentCaption","_tmpl$6","_el$5","_tmpl$5","data-accent-option","_el$4","_tmpl$4","layout","type","variant","size","aria-pressed","onClick","ref","element","tabIndex","test","event","currentTarget","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 { Monitor, Moon, Sun } from 'lucide-solid'\nimport { For, Show } from 'solid-js'\nimport { isThemeAccentId } from '#lib/theme-accents'\nimport { Button } from '../../ui/button/button'\nimport { Input } from '../../ui/input/input'\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\nconst MODE_ICONS = { system: Monitor, light: Sun, dark: Moon } 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 const Icon = MODE_ICONS[option.value]\n return (\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 {/* The glyph names the axis beside the miniature, so the cards read\n like ThemeModeToggle without losing the previews. */}\n <Show when={props.showModeIcons} fallback={<span>{option.label}</span>}>\n <span class=\"flex items-center justify-center gap-1.5\">\n <Icon aria-hidden=\"true\" class=\"size-3.5\" />\n {option.label}\n </span>\n </Show>\n </Radio.ItemLabel>\n </Radio.Item>\n )\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 const accentSelection = () => (isCustomAccent(props) ? 'custom' : props.draft.accent)\n const showThemeDefault = () => props.showThemeDefaultAccent ?? true\n const entryStyle = () => props.accentEntryStyle ?? 'swatches'\n const themeDefaultColor = () =>\n resolvedAppearance() === 'light'\n ? props.lightTheme.colors.accent\n : props.darkTheme.colors.accent\n // The named-chip style feeds the same resolved preset list to both entries:\n // \"Theme default\" leads only when the host has not folded it into a preset.\n const labelEntries = () => {\n const presets = props.accentOptions.filter((option) => option.id !== 'theme')\n return showThemeDefault() ? [{ id: 'theme', label: 'Theme default' }, ...presets] : presets\n }\n // The `row` style's Custom entry is a button that opens the native colour\n // selector, so the draft only changes once a colour is chosen there. The\n // input stays in the tree for the selector to anchor to and for the value a\n // saved hex restores.\n let colorPicker: HTMLInputElement | undefined\n const openCustomColor = () => {\n if (!colorPicker || props.saving) return\n if (typeof colorPicker.showPicker === 'function') colorPicker.showPicker()\n else colorPicker.click()\n }\n return (\n <Show\n when={entryStyle() === 'row'}\n fallback={\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 <Show\n when={entryStyle() === 'labels'}\n fallback={\n <>\n <AccentSwatchGroups\n accentOptions={props.accentOptions}\n resolvedAppearance={resolvedAppearance()}\n showThemeDefault={showThemeDefault()}\n themeDefaultColor={themeDefaultColor()}\n />\n <div class=\"flex flex-col gap-1\">\n <Show when={props.showCustomAccentCaption ?? true}>\n <span class=\"text-xs font-medium text-muted-foreground\">Custom</span>\n </Show>\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 </>\n }\n >\n {/* The named-chip style: one grid, every choice a labelled control,\n including the custom colour — nothing to caption from above. */}\n <div class=\"grid grid-cols-2 gap-2 sm:grid-cols-3\" data-accent-grid>\n <For each={labelEntries()}>\n {(option) => (\n <Radio.Item value={option.id} class=\"min-w-0\" data-accent-option={option.id}>\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 {option.label}\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n <Radio.Item value=\"custom\" class=\"min-w-0\">\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 </Show>\n </Radio>\n }\n >\n <div class=\"flex w-full min-w-0 items-center gap-2\" data-accent-choices>\n <Radio\n value={accentSelection()}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Accent\"\n class=\"min-w-0 flex-1\"\n onChange={(accent) => props.onChange({ accent })}\n >\n <AccentSwatchGroups\n accentOptions={props.accentOptions}\n resolvedAppearance={resolvedAppearance()}\n showThemeDefault={showThemeDefault()}\n themeDefaultColor={themeDefaultColor()}\n layout=\"row\"\n />\n </Radio>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={props.saving}\n aria-pressed={isCustomAccent(props)}\n onClick={openCustomColor}\n >\n Custom\n </Button>\n <Input\n ref={(element) => {\n colorPicker = element\n }}\n type=\"color\"\n // `w-px` beats the Input's own `w-full`: an absolutely positioned\n // full-width input sized the sheet's scroll width.\n class=\"sr-only w-px\"\n tabIndex={-1}\n aria-label=\"Custom accent color\"\n value={\n /^#[0-9a-f]{6}$/i.test(props.draft.accent)\n ? props.draft.accent\n : (props.customAccentValue ?? '#000000')\n }\n disabled={props.saving}\n onChange={(event) => props.onChange({ accent: event.currentTarget.value })}\n />\n </div>\n </Show>\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 whitespace-nowrap 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","Monitor","Moon","Sun","For","Show","isThemeAccentId","Button","Input","AccentSwatchGroups","AppearanceEditorProps","ThemeMiniature","ThemeMiniatureSplit","MODES","value","label","const","SURFACES","MODE_ICONS","system","light","dark","ModeChoices","props","_$createComponent","draft","mode","disabled","saving","orientation","onChange","option","find","candidate","children","each","Icon","Item","data-mode","ItemInput","ItemLabel","_el$","_tmpl$","_$insert","when","fallback","theme","_$memo","lightTheme","darkTheme","showModeIcons","_el$3","_tmpl$3","_el$2","_tmpl$2","isCustomAccent","accent","accentOptions","some","id","AccentChoices","resolvedAppearance","accentSelection","showThemeDefault","showThemeDefaultAccent","entryStyle","accentEntryStyle","themeDefaultColor","colors","labelEntries","presets","filter","colorPicker","HTMLInputElement","openCustomColor","showPicker","click","customAccentValue","_el$6","_tmpl$7","showCustomAccentCaption","_tmpl$6","_el$5","_tmpl$5","data-accent-option","_el$4","_tmpl$4","layout","type","variant","size","tabIndex","aria-pressed","onClick","ref","element","test","event","currentTarget","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 { Monitor, Moon, Sun } from 'lucide-solid'\nimport { For, Show } from 'solid-js'\nimport { isThemeAccentId } from '#lib/theme-accents'\nimport { Button } from '../../ui/button/button'\nimport { Input } from '../../ui/input/input'\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\nconst MODE_ICONS = { system: Monitor, light: Sun, dark: Moon } 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 const Icon = MODE_ICONS[option.value]\n return (\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 {/* The glyph names the axis beside the miniature, so the cards read\n like ThemeModeToggle without losing the previews. */}\n <Show when={props.showModeIcons} fallback={<span>{option.label}</span>}>\n <span class=\"flex items-center justify-center gap-1.5\">\n <Icon aria-hidden=\"true\" class=\"size-3.5\" />\n {option.label}\n </span>\n </Show>\n </Radio.ItemLabel>\n </Radio.Item>\n )\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 const accentSelection = () => (isCustomAccent(props) ? 'custom' : props.draft.accent)\n const showThemeDefault = () => props.showThemeDefaultAccent ?? true\n const entryStyle = () => props.accentEntryStyle ?? 'swatches'\n const themeDefaultColor = () =>\n resolvedAppearance() === 'light'\n ? props.lightTheme.colors.accent\n : props.darkTheme.colors.accent\n // The named-chip style feeds the same resolved preset list to both entries:\n // \"Theme default\" leads only when the host has not folded it into a preset.\n const labelEntries = () => {\n const presets = props.accentOptions.filter((option) => option.id !== 'theme')\n return showThemeDefault() ? [{ id: 'theme', label: 'Theme default' }, ...presets] : presets\n }\n // The `row` style's Custom entry is a button that opens the native colour\n // selector, so the draft only changes once a colour is chosen there. The\n // input stays in the tree for the selector to anchor to and for the value a\n // saved hex restores.\n let colorPicker: HTMLInputElement | undefined\n const openCustomColor = () => {\n if (!colorPicker || props.saving) return\n if (typeof colorPicker.showPicker === 'function') colorPicker.showPicker()\n else colorPicker.click()\n }\n return (\n <Show\n when={entryStyle() === 'row'}\n fallback={\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 <Show\n when={entryStyle() === 'labels'}\n fallback={\n <>\n <AccentSwatchGroups\n accentOptions={props.accentOptions}\n resolvedAppearance={resolvedAppearance()}\n showThemeDefault={showThemeDefault()}\n themeDefaultColor={themeDefaultColor()}\n />\n <div class=\"flex flex-col gap-1\">\n <Show when={props.showCustomAccentCaption ?? true}>\n <span class=\"text-xs font-medium text-muted-foreground\">Custom</span>\n </Show>\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 </>\n }\n >\n {/* The named-chip style: one grid, every choice a labelled control,\n including the custom colour — nothing to caption from above. */}\n <div class=\"grid grid-cols-2 gap-2 sm:grid-cols-3\" data-accent-grid>\n <For each={labelEntries()}>\n {(option) => (\n <Radio.Item value={option.id} class=\"min-w-0\" data-accent-option={option.id}>\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 {option.label}\n </Radio.ItemLabel>\n </Radio.Item>\n )}\n </For>\n <Radio.Item value=\"custom\" class=\"min-w-0\">\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 </Show>\n </Radio>\n }\n >\n <div class=\"flex w-full min-w-0 items-center gap-2\" data-accent-choices>\n <Radio\n value={accentSelection()}\n disabled={props.saving}\n orientation=\"horizontal\"\n aria-label=\"Accent\"\n class=\"min-w-0 flex-1\"\n onChange={(accent) => props.onChange({ accent })}\n >\n <AccentSwatchGroups\n accentOptions={props.accentOptions}\n resolvedAppearance={resolvedAppearance()}\n showThemeDefault={showThemeDefault()}\n themeDefaultColor={themeDefaultColor()}\n layout=\"row\"\n />\n </Radio>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n tabIndex={0}\n disabled={props.saving}\n aria-pressed={isCustomAccent(props)}\n onClick={openCustomColor}\n >\n Custom\n </Button>\n <Input\n ref={(element) => {\n colorPicker = element\n }}\n type=\"color\"\n // `w-px` beats the Input's own `w-full`: an absolutely positioned\n // full-width input sized the sheet's scroll width.\n class=\"sr-only w-px\"\n tabIndex={-1}\n aria-label=\"Custom accent color\"\n value={\n /^#[0-9a-f]{6}$/i.test(props.draft.accent)\n ? props.draft.accent\n : (props.customAccentValue ?? '#000000')\n }\n disabled={props.saving}\n onChange={(event) => props.onChange({ accent: event.currentTarget.value })}\n />\n </div>\n </Show>\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 whitespace-nowrap 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 +1 @@
1
- {"version":3,"file":"font-settings-group.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/font-settings-group.tsx"],"names":[],"mappings":"AAaA,OAAO,EAOL,KAAK,4BAA4B,EAClC,MAAM,+BAA+B,CAAA;AAEtC,MAAM,MAAM,gCAAgC,GAAG;IAC7C,QAAQ,EAAE,4BAA4B,CAAA;IACtC,eAAe,CAAC,EAAE,WAAW,CAAA;IAC7B,QAAQ,EAAE,CAAC,QAAQ,EAAE,4BAA4B,KAAK,IAAI,CAAA;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAyND,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,gCAAgC,kCAoHlF"}
1
+ {"version":3,"file":"font-settings-group.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/appearance-editor/font-settings-group.tsx"],"names":[],"mappings":"AAaA,OAAO,EAOL,KAAK,4BAA4B,EAClC,MAAM,+BAA+B,CAAA;AAEtC,MAAM,MAAM,gCAAgC,GAAG;IAC7C,QAAQ,EAAE,4BAA4B,CAAA;IACtC,eAAe,CAAC,EAAE,WAAW,CAAA;IAC7B,QAAQ,EAAE,CAAC,QAAQ,EAAE,4BAA4B,KAAK,IAAI,CAAA;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AA2ND,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,gCAAgC,kCAoHlF"}
@@ -58,8 +58,8 @@ function FontFamilyMenu(props) {
58
58
  if (adjacent) {
59
59
  event.preventDefault();
60
60
  focusTargetAfterTab = adjacent;
61
- setMenuOpen(false);
62
61
  adjacent.focus({ preventScroll: true });
62
+ setMenuOpen(false);
63
63
  } else {
64
64
  focusTargetAfterTab = void 0;
65
65
  setMenuOpen(false);
@@ -1 +1 @@
1
- {"version":3,"file":"font-settings-group.js","names":["BookOpen","ChevronDown","Code2","PanelsTopLeft","For","createMemo","createSignal","createUniqueId","fontOptions","Button","DropdownMenu","DropdownMenuContent","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuTrigger","InputControl","APPEARANCE_EDITOR_FONT_SIZE_MAX","APPEARANCE_EDITOR_FONT_SIZE_MIN","normalizeAppearanceEditorFontSettings","normalizeAppearanceEditorFontSize","AppearanceEditorFontAxis","AppearanceEditorFontFamilyId","AppearanceEditorFontSettings","AppearanceFontSettingsGroupProps","settings","menuPortalMount","HTMLElement","onChange","disabled","FONT_ROWS","axis","label","description","icon","const","catalogFontOptions","filter","option","id","MENU_FOCUS_TARGET_SELECTOR","isMenuTabStop","element","tabIndex","matches","hasAttribute","getAttribute","closest","getClientRects","length","FontFamilyMenu","props","family","onSelect","menuOpen","setMenuOpen","restoreTriggerFocus","setRestoreTriggerFocus","triggerElement","setTriggerElement","HTMLButtonElement","focusTargetAfterTab","moveFocusOnTab","event","KeyboardEvent","key","isComposing","trigger","owner","parentDialog","focusScope","ownerDocument","body","focusStops","Array","from","querySelectorAll","orderedFocusStops","toSorted","a","b","triggerIndex","indexOf","direction","shiftKey","adjacent","at","stopPropagation","preventDefault","focus","preventScroll","undefined","selectedLabel","find","_$createComponent","modal","open","onOpenChange","children","ref","as","variant","size","aria-label","_el$","_tmpl$","_$insert","hideArrow","portalMount","onFocusOutside","defaultPrevented","detail","originalEvent","target","contains","onInteractOutside","type","Element","focusTarget","onKeyDown","onEscapeKeyDown","onCloseAutoFocus","queueMicrotask","isConnected","value","closeOnSelect","candidate","each","AppearanceFontSettingsGroup","sizeDrafts","setSizeDrafts","Partial","Record","sizeHintPrefix","updateAxis","patch","current","commitSize","input","next","_el$2","_tmpl$2","row","Icon","inputValue","String","sizeHintId","sizeIsInvalid","draft","trim","parsed","Number","isFinite","isInteger","_el$3","_tmpl$3","_el$4","firstChild","_el$5","nextSibling","_el$6","_el$7","_el$8","_el$9","_el$0","_el$1","_el$10","_el$14","_el$11","_el$15","_el$13","min","max","step","aria-invalid","onInput","currentTarget","onBlur","_$setAttribute","_$effect","_$createComponent","_$setAttribute"],"sources":["../../../../src/components/composites/appearance-editor/font-settings-group.tsx"],"sourcesContent":["import { BookOpen, ChevronDown, Code2, PanelsTopLeft } from 'lucide-solid'\nimport { For, createMemo, createSignal, createUniqueId } from 'solid-js'\nimport { fontOptions } from '#lib/font-catalog'\nimport { Button } from '../../ui/button/button'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../../ui/dropdown-menu/dropdown-menu'\nimport { InputControl } from '../../ui/input/input-control'\nimport {\n APPEARANCE_EDITOR_FONT_SIZE_MAX,\n APPEARANCE_EDITOR_FONT_SIZE_MIN,\n normalizeAppearanceEditorFontSettings,\n normalizeAppearanceEditorFontSize,\n type AppearanceEditorFontAxis,\n type AppearanceEditorFontFamilyId,\n type AppearanceEditorFontSettings,\n} from '#lib/appearance-font-settings'\n\nexport type AppearanceFontSettingsGroupProps = {\n settings: AppearanceEditorFontSettings\n menuPortalMount?: HTMLElement\n onChange: (settings: AppearanceEditorFontSettings) => void\n disabled?: boolean\n}\n\nconst FONT_ROWS = [\n {\n axis: 'ui',\n label: 'UI',\n description: 'Controls and interface labels.',\n icon: PanelsTopLeft,\n },\n {\n axis: 'content',\n label: 'Content',\n description: 'Reading and long-form text.',\n icon: BookOpen,\n },\n {\n axis: 'code',\n label: 'Code',\n description: 'Code, terminals, and file paths.',\n icon: Code2,\n },\n] as const satisfies readonly {\n axis: AppearanceEditorFontAxis\n label: string\n description: string\n icon: typeof PanelsTopLeft\n}[]\n\nconst catalogFontOptions = fontOptions.filter((option) => option.id !== 'system')\nconst MENU_FOCUS_TARGET_SELECTOR =\n 'a[href], area[href], button, input, select, textarea, iframe, [tabindex], [contenteditable], details > summary:first-of-type'\n\nfunction isMenuTabStop(element: HTMLElement) {\n return (\n (element.tabIndex >= 0 ||\n (element.matches('details > summary:first-of-type') && !element.hasAttribute('tabindex'))) &&\n !element.matches(':disabled') &&\n element.getAttribute('aria-disabled') !== 'true' &&\n element.closest('[hidden], [inert], [aria-hidden=\"true\"]') === null &&\n element.getClientRects().length > 0\n )\n}\n\nfunction FontFamilyMenu(props: {\n label: string\n family: AppearanceEditorFontFamilyId\n menuPortalMount?: HTMLElement\n disabled?: boolean\n onSelect: (family: AppearanceEditorFontFamilyId) => void\n}) {\n const [menuOpen, setMenuOpen] = createSignal(false)\n const [restoreTriggerFocus, setRestoreTriggerFocus] = createSignal(false)\n const [triggerElement, setTriggerElement] = createSignal<HTMLButtonElement>()\n let focusTargetAfterTab: HTMLElement | undefined\n\n const moveFocusOnTab = (event: KeyboardEvent) => {\n if (event.key !== 'Tab' || event.isComposing) return\n\n const trigger = triggerElement()\n const owner = props.menuPortalMount\n const parentDialog = owner?.closest<HTMLElement>('[role=\"dialog\"]')\n const focusScope = parentDialog ?? owner?.ownerDocument.body\n if (!trigger || !parentDialog || !focusScope) return\n\n const focusStops = Array.from(focusScope.querySelectorAll<HTMLElement>('*')).filter(\n (element) =>\n isMenuTabStop(element) &&\n element.closest('[role=\"menu\"], [hidden], [inert], [aria-hidden=\"true\"]') === null\n )\n const orderedFocusStops = [\n ...focusStops\n .filter((element) => element.tabIndex > 0)\n .toSorted((a, b) => a.tabIndex - b.tabIndex),\n ...focusStops.filter((element) => element.tabIndex <= 0),\n ]\n const triggerIndex = orderedFocusStops.indexOf(trigger)\n if (triggerIndex < 0) return\n const direction = event.shiftKey ? -1 : 1\n const adjacent =\n orderedFocusStops[triggerIndex + direction] ??\n (direction > 0 ? orderedFocusStops[0] : orderedFocusStops.at(-1))\n\n event.stopPropagation()\n setRestoreTriggerFocus(false)\n if (adjacent) {\n event.preventDefault()\n focusTargetAfterTab = adjacent\n setMenuOpen(false)\n adjacent.focus({ preventScroll: true })\n } else {\n focusTargetAfterTab = undefined\n setMenuOpen(false)\n }\n }\n const selectedLabel = () =>\n fontOptions.find((option) => option.id === props.family)?.label ??\n fontOptions.find((option) => option.id === 'system')?.label ??\n 'System'\n\n return (\n <DropdownMenu\n modal={false}\n open={menuOpen()}\n onOpenChange={(open) => {\n setMenuOpen(open)\n if (open) setRestoreTriggerFocus(false)\n }}\n >\n <DropdownMenuTrigger\n ref={setTriggerElement}\n as={Button}\n variant=\"outline\"\n size=\"sm\"\n aria-label={`${props.label} font family`}\n disabled={props.disabled}\n class=\"w-32 max-w-full justify-between\"\n >\n <span class=\"truncate\">{selectedLabel()}</span>\n <ChevronDown aria-hidden=\"true\" class=\"ms-2 size-4 shrink-0 text-muted-foreground\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent\n hideArrow\n portalMount={props.menuPortalMount}\n class=\"min-w-48\"\n onFocusOutside={(event) => {\n // WebKit may briefly focus the owning Popover root while the menu's\n // focus scope starts. Keep the menu open for that transient target;\n // genuine focus moves to another control still dismiss it.\n if (\n !event.defaultPrevented &&\n props.menuPortalMount &&\n event.detail.originalEvent.target === props.menuPortalMount\n ) {\n setRestoreTriggerFocus(true)\n event.preventDefault()\n } else if (\n event.detail.originalEvent.target instanceof HTMLElement &&\n props.menuPortalMount?.contains(event.detail.originalEvent.target)\n ) {\n setRestoreTriggerFocus(false)\n }\n }}\n onInteractOutside={(event) => {\n if (event.detail.originalEvent.type !== 'pointerdown') return\n const owner = props.menuPortalMount\n const target = event.detail.originalEvent.target\n if (!(target instanceof Element) || !owner?.contains(target)) return\n\n const focusTarget = target.closest<HTMLElement>(MENU_FOCUS_TARGET_SELECTOR)\n setRestoreTriggerFocus(!focusTarget || !isMenuTabStop(focusTarget))\n }}\n onKeyDown={moveFocusOnTab}\n onEscapeKeyDown={() => setRestoreTriggerFocus(true)}\n onCloseAutoFocus={(event) => {\n if (menuOpen()) {\n event.preventDefault()\n return\n }\n if (focusTargetAfterTab) {\n const focusTarget = focusTargetAfterTab\n focusTargetAfterTab = undefined\n event.preventDefault()\n queueMicrotask(() => {\n if (!menuOpen() && focusTarget.isConnected) {\n focusTarget.focus({ preventScroll: true })\n }\n })\n return\n }\n if (!restoreTriggerFocus()) return\n\n event.preventDefault()\n setRestoreTriggerFocus(false)\n const trigger = triggerElement()\n if (\n trigger?.isConnected &&\n !trigger.disabled &&\n trigger.closest('[hidden], [inert], [aria-hidden=\"true\"]') === null &&\n trigger.getClientRects().length > 0\n ) {\n trigger.focus({ preventScroll: true })\n }\n }}\n >\n <DropdownMenuRadioGroup\n value={props.family}\n aria-label={`${props.label} system font`}\n onChange={(family) => {\n if (family === 'system') props.onSelect('system')\n }}\n >\n <DropdownMenuRadioItem value=\"system\" closeOnSelect>\n System\n </DropdownMenuRadioItem>\n </DropdownMenuRadioGroup>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={props.family}\n aria-label={`${props.label} available fonts`}\n onChange={(family) => {\n const option = fontOptions.find((candidate) => candidate.id === family)\n if (option && option.id !== 'system') props.onSelect(option.id)\n }}\n >\n <For each={catalogFontOptions}>\n {(option) => (\n <DropdownMenuRadioItem value={option.id} closeOnSelect>\n {option.label}\n </DropdownMenuRadioItem>\n )}\n </For>\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/** Controlled font controls shared by AppearanceEditor and host-owned settings. */\nexport function AppearanceFontSettingsGroup(props: AppearanceFontSettingsGroupProps) {\n const [sizeDrafts, setSizeDrafts] = createSignal<\n Partial<Record<AppearanceEditorFontAxis, string>>\n >({})\n const sizeHintPrefix = createUniqueId()\n const settings = createMemo(() => normalizeAppearanceEditorFontSettings(props.settings).settings)\n\n const updateAxis = (\n axis: AppearanceEditorFontAxis,\n patch: { family?: AppearanceEditorFontFamilyId; size?: number }\n ) => {\n const current = settings()\n props.onChange({\n ...current,\n [axis]: {\n ...current[axis],\n ...patch,\n },\n })\n }\n\n const commitSize = (axis: AppearanceEditorFontAxis) => {\n const input = sizeDrafts()[axis]\n if (input === undefined) return\n const size = normalizeAppearanceEditorFontSize(input, settings()[axis].size)\n setSizeDrafts((current) => {\n const next = { ...current }\n delete next[axis]\n return next\n })\n if (size !== settings()[axis].size) updateAxis(axis, { size })\n }\n\n return (\n <div class=\"global-appearance-font-settings\">\n <For each={FONT_ROWS}>\n {(row) => {\n const Icon = row.icon\n const value = () => settings()[row.axis]\n const inputValue = () => sizeDrafts()[row.axis] ?? String(value().size)\n const sizeHintId = `${sizeHintPrefix}-${row.axis}-font-size-help`\n const sizeIsInvalid = () => {\n const draft = sizeDrafts()[row.axis]\n if (draft === undefined || draft.trim() === '') return false\n const parsed = Number(draft)\n return (\n !Number.isFinite(parsed) ||\n !Number.isInteger(parsed) ||\n parsed < APPEARANCE_EDITOR_FONT_SIZE_MIN ||\n parsed > APPEARANCE_EDITOR_FONT_SIZE_MAX\n )\n }\n return (\n <section class=\"flex items-center gap-3 px-4 py-4\" data-font-settings-row={row.axis}>\n <span\n class=\"flex size-9 shrink-0 items-center justify-center rounded-lg border bg-foreground/5 text-muted-foreground [&_svg]:size-4\"\n aria-hidden=\"true\"\n >\n <Icon />\n </span>\n {/* One line, always: the copy truncates rather than wrapping the\n controls to a second line, so the family menu, the size field\n and the title/subtitle stay aligned. The compact widths keep\n that true at the appearance sheet's own width. */}\n <div class=\"min-w-0 flex-1\">\n <h3 class=\"text-sm font-medium\">{row.label}</h3>\n <div class=\"mt-0.5 truncate text-xs text-muted-foreground\">{row.description}</div>\n </div>\n <div class=\"flex shrink-0 items-center gap-2\">\n <FontFamilyMenu\n label={row.label}\n family={value().family}\n menuPortalMount={props.menuPortalMount}\n disabled={props.disabled}\n onSelect={(family) => updateAxis(row.axis, { family })}\n />\n <div class=\"flex items-center gap-1\">\n <InputControl\n type=\"number\"\n min={APPEARANCE_EDITOR_FONT_SIZE_MIN}\n max={APPEARANCE_EDITOR_FONT_SIZE_MAX}\n step={1}\n value={inputValue()}\n disabled={props.disabled}\n aria-label={`${row.label} font size in pixels`}\n aria-describedby={sizeHintId}\n aria-invalid={sizeIsInvalid()}\n class=\"w-12\"\n onInput={(event) =>\n setSizeDrafts((current) => ({\n ...current,\n [row.axis]: event.currentTarget.value,\n }))\n }\n onBlur={() => commitSize(row.axis)}\n onKeyDown={(event) => {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitSize(row.axis)\n }}\n />\n <span aria-hidden=\"true\" class=\"text-xs text-muted-foreground\">\n px\n </span>\n </div>\n <span id={sizeHintId} class=\"sr-only\">\n Enter a whole number from {APPEARANCE_EDITOR_FONT_SIZE_MIN} to{' '}\n {APPEARANCE_EDITOR_FONT_SIZE_MAX} pixels.\n </span>\n </div>\n </section>\n )\n }}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"font-settings-group.js","names":["BookOpen","ChevronDown","Code2","PanelsTopLeft","For","createMemo","createSignal","createUniqueId","fontOptions","Button","DropdownMenu","DropdownMenuContent","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuTrigger","InputControl","APPEARANCE_EDITOR_FONT_SIZE_MAX","APPEARANCE_EDITOR_FONT_SIZE_MIN","normalizeAppearanceEditorFontSettings","normalizeAppearanceEditorFontSize","AppearanceEditorFontAxis","AppearanceEditorFontFamilyId","AppearanceEditorFontSettings","AppearanceFontSettingsGroupProps","settings","menuPortalMount","HTMLElement","onChange","disabled","FONT_ROWS","axis","label","description","icon","const","catalogFontOptions","filter","option","id","MENU_FOCUS_TARGET_SELECTOR","isMenuTabStop","element","tabIndex","matches","hasAttribute","getAttribute","closest","getClientRects","length","FontFamilyMenu","props","family","onSelect","menuOpen","setMenuOpen","restoreTriggerFocus","setRestoreTriggerFocus","triggerElement","setTriggerElement","HTMLButtonElement","focusTargetAfterTab","moveFocusOnTab","event","KeyboardEvent","key","isComposing","trigger","owner","parentDialog","focusScope","ownerDocument","body","focusStops","Array","from","querySelectorAll","orderedFocusStops","toSorted","a","b","triggerIndex","indexOf","direction","shiftKey","adjacent","at","stopPropagation","preventDefault","focus","preventScroll","undefined","selectedLabel","find","_$createComponent","modal","open","onOpenChange","children","ref","as","variant","size","aria-label","_el$","_tmpl$","_$insert","hideArrow","portalMount","onFocusOutside","defaultPrevented","detail","originalEvent","target","contains","onInteractOutside","type","Element","focusTarget","onKeyDown","onEscapeKeyDown","onCloseAutoFocus","queueMicrotask","isConnected","value","closeOnSelect","candidate","each","AppearanceFontSettingsGroup","sizeDrafts","setSizeDrafts","Partial","Record","sizeHintPrefix","updateAxis","patch","current","commitSize","input","next","_el$2","_tmpl$2","row","Icon","inputValue","String","sizeHintId","sizeIsInvalid","draft","trim","parsed","Number","isFinite","isInteger","_el$3","_tmpl$3","_el$4","firstChild","_el$5","nextSibling","_el$6","_el$7","_el$8","_el$9","_el$0","_el$1","_el$10","_el$14","_el$11","_el$15","_el$13","min","max","step","aria-invalid","onInput","currentTarget","onBlur","_$setAttribute","_$effect","_$createComponent","_$setAttribute"],"sources":["../../../../src/components/composites/appearance-editor/font-settings-group.tsx"],"sourcesContent":["import { BookOpen, ChevronDown, Code2, PanelsTopLeft } from 'lucide-solid'\nimport { For, createMemo, createSignal, createUniqueId } from 'solid-js'\nimport { fontOptions } from '#lib/font-catalog'\nimport { Button } from '../../ui/button/button'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../../ui/dropdown-menu/dropdown-menu'\nimport { InputControl } from '../../ui/input/input-control'\nimport {\n APPEARANCE_EDITOR_FONT_SIZE_MAX,\n APPEARANCE_EDITOR_FONT_SIZE_MIN,\n normalizeAppearanceEditorFontSettings,\n normalizeAppearanceEditorFontSize,\n type AppearanceEditorFontAxis,\n type AppearanceEditorFontFamilyId,\n type AppearanceEditorFontSettings,\n} from '#lib/appearance-font-settings'\n\nexport type AppearanceFontSettingsGroupProps = {\n settings: AppearanceEditorFontSettings\n menuPortalMount?: HTMLElement\n onChange: (settings: AppearanceEditorFontSettings) => void\n disabled?: boolean\n}\n\nconst FONT_ROWS = [\n {\n axis: 'ui',\n label: 'UI',\n description: 'Controls and interface labels.',\n icon: PanelsTopLeft,\n },\n {\n axis: 'content',\n label: 'Content',\n description: 'Reading and long-form text.',\n icon: BookOpen,\n },\n {\n axis: 'code',\n label: 'Code',\n description: 'Code, terminals, and file paths.',\n icon: Code2,\n },\n] as const satisfies readonly {\n axis: AppearanceEditorFontAxis\n label: string\n description: string\n icon: typeof PanelsTopLeft\n}[]\n\nconst catalogFontOptions = fontOptions.filter((option) => option.id !== 'system')\nconst MENU_FOCUS_TARGET_SELECTOR =\n 'a[href], area[href], button, input, select, textarea, iframe, [tabindex], [contenteditable], details > summary:first-of-type'\n\nfunction isMenuTabStop(element: HTMLElement) {\n return (\n (element.tabIndex >= 0 ||\n (element.matches('details > summary:first-of-type') && !element.hasAttribute('tabindex'))) &&\n !element.matches(':disabled') &&\n element.getAttribute('aria-disabled') !== 'true' &&\n element.closest('[hidden], [inert], [aria-hidden=\"true\"]') === null &&\n element.getClientRects().length > 0\n )\n}\n\nfunction FontFamilyMenu(props: {\n label: string\n family: AppearanceEditorFontFamilyId\n menuPortalMount?: HTMLElement\n disabled?: boolean\n onSelect: (family: AppearanceEditorFontFamilyId) => void\n}) {\n const [menuOpen, setMenuOpen] = createSignal(false)\n const [restoreTriggerFocus, setRestoreTriggerFocus] = createSignal(false)\n const [triggerElement, setTriggerElement] = createSignal<HTMLButtonElement>()\n let focusTargetAfterTab: HTMLElement | undefined\n\n const moveFocusOnTab = (event: KeyboardEvent) => {\n if (event.key !== 'Tab' || event.isComposing) return\n\n const trigger = triggerElement()\n const owner = props.menuPortalMount\n const parentDialog = owner?.closest<HTMLElement>('[role=\"dialog\"]')\n const focusScope = parentDialog ?? owner?.ownerDocument.body\n if (!trigger || !parentDialog || !focusScope) return\n\n const focusStops = Array.from(focusScope.querySelectorAll<HTMLElement>('*')).filter(\n (element) =>\n isMenuTabStop(element) &&\n element.closest('[role=\"menu\"], [hidden], [inert], [aria-hidden=\"true\"]') === null\n )\n const orderedFocusStops = [\n ...focusStops\n .filter((element) => element.tabIndex > 0)\n .toSorted((a, b) => a.tabIndex - b.tabIndex),\n ...focusStops.filter((element) => element.tabIndex <= 0),\n ]\n const triggerIndex = orderedFocusStops.indexOf(trigger)\n if (triggerIndex < 0) return\n const direction = event.shiftKey ? -1 : 1\n const adjacent =\n orderedFocusStops[triggerIndex + direction] ??\n (direction > 0 ? orderedFocusStops[0] : orderedFocusStops.at(-1))\n\n event.stopPropagation()\n setRestoreTriggerFocus(false)\n if (adjacent) {\n event.preventDefault()\n focusTargetAfterTab = adjacent\n // Let the nonmodal layer observe the outside focus before close-autofocus runs;\n // Kobalte otherwise restores focus to this trigger.\n adjacent.focus({ preventScroll: true })\n setMenuOpen(false)\n } else {\n focusTargetAfterTab = undefined\n setMenuOpen(false)\n }\n }\n const selectedLabel = () =>\n fontOptions.find((option) => option.id === props.family)?.label ??\n fontOptions.find((option) => option.id === 'system')?.label ??\n 'System'\n\n return (\n <DropdownMenu\n modal={false}\n open={menuOpen()}\n onOpenChange={(open) => {\n setMenuOpen(open)\n if (open) setRestoreTriggerFocus(false)\n }}\n >\n <DropdownMenuTrigger\n ref={setTriggerElement}\n as={Button}\n variant=\"outline\"\n size=\"sm\"\n aria-label={`${props.label} font family`}\n disabled={props.disabled}\n class=\"w-32 max-w-full justify-between\"\n >\n <span class=\"truncate\">{selectedLabel()}</span>\n <ChevronDown aria-hidden=\"true\" class=\"ms-2 size-4 shrink-0 text-muted-foreground\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent\n hideArrow\n portalMount={props.menuPortalMount}\n class=\"min-w-48\"\n onFocusOutside={(event) => {\n // WebKit may briefly focus the owning Popover root while the menu's\n // focus scope starts. Keep the menu open for that transient target;\n // genuine focus moves to another control still dismiss it.\n if (\n !event.defaultPrevented &&\n props.menuPortalMount &&\n event.detail.originalEvent.target === props.menuPortalMount\n ) {\n setRestoreTriggerFocus(true)\n event.preventDefault()\n } else if (\n event.detail.originalEvent.target instanceof HTMLElement &&\n props.menuPortalMount?.contains(event.detail.originalEvent.target)\n ) {\n setRestoreTriggerFocus(false)\n }\n }}\n onInteractOutside={(event) => {\n if (event.detail.originalEvent.type !== 'pointerdown') return\n const owner = props.menuPortalMount\n const target = event.detail.originalEvent.target\n if (!(target instanceof Element) || !owner?.contains(target)) return\n\n const focusTarget = target.closest<HTMLElement>(MENU_FOCUS_TARGET_SELECTOR)\n setRestoreTriggerFocus(!focusTarget || !isMenuTabStop(focusTarget))\n }}\n onKeyDown={moveFocusOnTab}\n onEscapeKeyDown={() => setRestoreTriggerFocus(true)}\n onCloseAutoFocus={(event) => {\n if (menuOpen()) {\n event.preventDefault()\n return\n }\n if (focusTargetAfterTab) {\n const focusTarget = focusTargetAfterTab\n focusTargetAfterTab = undefined\n event.preventDefault()\n queueMicrotask(() => {\n if (!menuOpen() && focusTarget.isConnected) {\n focusTarget.focus({ preventScroll: true })\n }\n })\n return\n }\n if (!restoreTriggerFocus()) return\n\n event.preventDefault()\n setRestoreTriggerFocus(false)\n const trigger = triggerElement()\n if (\n trigger?.isConnected &&\n !trigger.disabled &&\n trigger.closest('[hidden], [inert], [aria-hidden=\"true\"]') === null &&\n trigger.getClientRects().length > 0\n ) {\n trigger.focus({ preventScroll: true })\n }\n }}\n >\n <DropdownMenuRadioGroup\n value={props.family}\n aria-label={`${props.label} system font`}\n onChange={(family) => {\n if (family === 'system') props.onSelect('system')\n }}\n >\n <DropdownMenuRadioItem value=\"system\" closeOnSelect>\n System\n </DropdownMenuRadioItem>\n </DropdownMenuRadioGroup>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={props.family}\n aria-label={`${props.label} available fonts`}\n onChange={(family) => {\n const option = fontOptions.find((candidate) => candidate.id === family)\n if (option && option.id !== 'system') props.onSelect(option.id)\n }}\n >\n <For each={catalogFontOptions}>\n {(option) => (\n <DropdownMenuRadioItem value={option.id} closeOnSelect>\n {option.label}\n </DropdownMenuRadioItem>\n )}\n </For>\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/** Controlled font controls shared by AppearanceEditor and host-owned settings. */\nexport function AppearanceFontSettingsGroup(props: AppearanceFontSettingsGroupProps) {\n const [sizeDrafts, setSizeDrafts] = createSignal<\n Partial<Record<AppearanceEditorFontAxis, string>>\n >({})\n const sizeHintPrefix = createUniqueId()\n const settings = createMemo(() => normalizeAppearanceEditorFontSettings(props.settings).settings)\n\n const updateAxis = (\n axis: AppearanceEditorFontAxis,\n patch: { family?: AppearanceEditorFontFamilyId; size?: number }\n ) => {\n const current = settings()\n props.onChange({\n ...current,\n [axis]: {\n ...current[axis],\n ...patch,\n },\n })\n }\n\n const commitSize = (axis: AppearanceEditorFontAxis) => {\n const input = sizeDrafts()[axis]\n if (input === undefined) return\n const size = normalizeAppearanceEditorFontSize(input, settings()[axis].size)\n setSizeDrafts((current) => {\n const next = { ...current }\n delete next[axis]\n return next\n })\n if (size !== settings()[axis].size) updateAxis(axis, { size })\n }\n\n return (\n <div class=\"global-appearance-font-settings\">\n <For each={FONT_ROWS}>\n {(row) => {\n const Icon = row.icon\n const value = () => settings()[row.axis]\n const inputValue = () => sizeDrafts()[row.axis] ?? String(value().size)\n const sizeHintId = `${sizeHintPrefix}-${row.axis}-font-size-help`\n const sizeIsInvalid = () => {\n const draft = sizeDrafts()[row.axis]\n if (draft === undefined || draft.trim() === '') return false\n const parsed = Number(draft)\n return (\n !Number.isFinite(parsed) ||\n !Number.isInteger(parsed) ||\n parsed < APPEARANCE_EDITOR_FONT_SIZE_MIN ||\n parsed > APPEARANCE_EDITOR_FONT_SIZE_MAX\n )\n }\n return (\n <section class=\"flex items-center gap-3 px-4 py-4\" data-font-settings-row={row.axis}>\n <span\n class=\"flex size-9 shrink-0 items-center justify-center rounded-lg border bg-foreground/5 text-muted-foreground [&_svg]:size-4\"\n aria-hidden=\"true\"\n >\n <Icon />\n </span>\n {/* One line, always: the copy truncates rather than wrapping the\n controls to a second line, so the family menu, the size field\n and the title/subtitle stay aligned. The compact widths keep\n that true at the appearance sheet's own width. */}\n <div class=\"min-w-0 flex-1\">\n <h3 class=\"text-sm font-medium\">{row.label}</h3>\n <div class=\"mt-0.5 truncate text-xs text-muted-foreground\">{row.description}</div>\n </div>\n <div class=\"flex shrink-0 items-center gap-2\">\n <FontFamilyMenu\n label={row.label}\n family={value().family}\n menuPortalMount={props.menuPortalMount}\n disabled={props.disabled}\n onSelect={(family) => updateAxis(row.axis, { family })}\n />\n <div class=\"flex items-center gap-1\">\n <InputControl\n type=\"number\"\n min={APPEARANCE_EDITOR_FONT_SIZE_MIN}\n max={APPEARANCE_EDITOR_FONT_SIZE_MAX}\n step={1}\n value={inputValue()}\n disabled={props.disabled}\n aria-label={`${row.label} font size in pixels`}\n aria-describedby={sizeHintId}\n aria-invalid={sizeIsInvalid()}\n class=\"w-12\"\n onInput={(event) =>\n setSizeDrafts((current) => ({\n ...current,\n [row.axis]: event.currentTarget.value,\n }))\n }\n onBlur={() => commitSize(row.axis)}\n onKeyDown={(event) => {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitSize(row.axis)\n }}\n />\n <span aria-hidden=\"true\" class=\"text-xs text-muted-foreground\">\n px\n </span>\n </div>\n <span id={sizeHintId} class=\"sr-only\">\n Enter a whole number from {APPEARANCE_EDITOR_FONT_SIZE_MIN} to{' '}\n {APPEARANCE_EDITOR_FONT_SIZE_MAX} pixels.\n </span>\n </div>\n </section>\n )\n }}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -227,6 +227,7 @@ export function AccentChoices(props: AppearanceEditorProps) {
227
227
  type="button"
228
228
  variant="outline"
229
229
  size="sm"
230
+ tabIndex={0}
230
231
  disabled={props.saving}
231
232
  aria-pressed={isCustomAccent(props)}
232
233
  onClick={openCustomColor}
@@ -113,8 +113,10 @@ function FontFamilyMenu(props: {
113
113
  if (adjacent) {
114
114
  event.preventDefault()
115
115
  focusTargetAfterTab = adjacent
116
- setMenuOpen(false)
116
+ // Let the nonmodal layer observe the outside focus before close-autofocus runs;
117
+ // Kobalte otherwise restores focus to this trigger.
117
118
  adjacent.focus({ preventScroll: true })
119
+ setMenuOpen(false)
118
120
  } else {
119
121
  focusTargetAfterTab = undefined
120
122
  setMenuOpen(false)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.146.9",
3
+ "version": "0.146.10",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
@@ -227,6 +227,7 @@ export function AccentChoices(props: AppearanceEditorProps) {
227
227
  type="button"
228
228
  variant="outline"
229
229
  size="sm"
230
+ tabIndex={0}
230
231
  disabled={props.saving}
231
232
  aria-pressed={isCustomAccent(props)}
232
233
  onClick={openCustomColor}
@@ -113,8 +113,10 @@ function FontFamilyMenu(props: {
113
113
  if (adjacent) {
114
114
  event.preventDefault()
115
115
  focusTargetAfterTab = adjacent
116
- setMenuOpen(false)
116
+ // Let the nonmodal layer observe the outside focus before close-autofocus runs;
117
+ // Kobalte otherwise restores focus to this trigger.
117
118
  adjacent.focus({ preventScroll: true })
119
+ setMenuOpen(false)
118
120
  } else {
119
121
  focusTargetAfterTab = undefined
120
122
  setMenuOpen(false)