You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Extract two reusable Ark dropdown primitives (ui/Select, ui/Combobox) as the dropdown-uniformization foundation
M0 of the dropdown-uniformization effort: one clean, macOS-y dropdown architecture every in-scope picker can converge onto, so the app stops carrying several unrelated dropdown idioms.
- **`lib/ui/Select.svelte`**: presentational, items-driven single-select on Ark `Select`. The house dropdown (native-`<select>` replacement). Items carry `value` + `label` + optional `description` + optional `group` (Ark `ItemGroup`/`ItemGroupLabel` bucketing for the viewer's encoding picker). Forwards `onChange`, `onHighlightChange`, `disabled`, `placeholder`, `ariaLabel`, and a content-level `contentClass` hook. Keeps the stable `.select-trigger` / `.select-item` / `.select-content` / `.option-description` class contract that `SettingSelect`'s focus-restore and the a11y-contrast checker key on. Standardized Lucide `chevron-down` with a real hit-area replaces the tiny `▼` glyph.
- **`lib/ui/Combobox.svelte`**: presentational text-field-with-suggestions on Ark `Combobox` for the AI model picker. Text is driven by a controlled `inputValue` (separate from `value`), with `selectionBehavior="preserve"` + `allowCustomValue`, so it never blanks on a cold-start / mid-fetch empty list or a typed custom model name (the load-bearing finding #4 trap). Open-on-focus wired via controlled `open`; own in-field spinner overlay for `loading` (Ark has none); graceful empty-state row.
- **`SettingSelect.svelte`** now renders `ui/Select`: keeps its registry reads, the `allowCustom` "Custom…" inline-number flow (incl. the load-bearing `setTimeout(0)` focus-restore), immediate-apply-on-highlight, and the `custom-highlighted` content class. The `__custom__` sentinel stays in `SettingSelect`; `ui/Select` never sees it. Behavior is unchanged for the three consumers.
- Cataloged both in Debug > Components (new `SelectSection` + `ComboboxSection`, wired into both the catalog page and the debug-window sidebar), with the Combobox demoing the empty / loading / free-text states.
- Tests: tier-3 axe a11y for both primitives, plus a functional Combobox test asserting the field keeps its `inputValue` with an empty list and shows non-member custom values (finding #4 guard).
- Docs: both primitives in `lib/ui/DETAILS.md` + must-know lines in `lib/ui/CLAUDE.md` (stable `.select-*` contract, the Combobox value-model trap); `settings/components/CLAUDE.md` notes `SettingSelect` wraps `ui/Select`.
No new IPC, no native `<select>` migrated yet (M1–M3 own those). `pnpm check` green.
|`SettingsSidebar.svelte`| Left-column nav: search input, top-level + nested section list, "section has matches" highlight. Declares `TOP_LEVEL_ORDER` (keep in sync with the E2E test in `settings.spec.ts`) |
15
-
|`SettingsContent.svelte`| Right-pane router: maps `selectedSection` to one of the 18 `sections/*.svelte` components, or renders `SectionSummary` for the four top-level sections that have subsections (`Appearance`, `Behavior`, `File systems`, `Developer`) |
16
-
|`SettingsSection.svelte`| Shared section wrapper: h2 title + slot. One per `sections/*.svelte` so the title styling lives in one place |
17
-
|`SectionSummary.svelte`| Card grid shown when a top-level section with subsections is selected; each card deep-links into a subsection |
18
-
|`SettingRow.svelte`| Label + description + control + reset-pip + restart-required badge wrapper. Carries `split` (see below) and `searchQuery` (for `<mark>`-style label highlighting via `highlightMatches`) |
|`SettingCheckbox.svelte`| Less prominent boolean control (Ark UI `Checkbox`). Use for secondary booleans that hang off a switch or live inside a denser layout |
21
-
|`SettingSelect.svelte`| Dropdown for enum settings (Ark UI `Select`). Supports `allowCustom` from the registry, which renders an inline text input when the user picks `Custom…`|
22
-
|`SettingToggleGroup.svelte`| Segmented control for short enum lists. Thin wrapper around `lib/ui/ToggleGroup` with `semantics="toggles"`. `labelOverrides` lets a button label track another reactive setting (for example, `kB` ↔ `KB` on binary/SI switch) |
23
-
|`SettingRadioGroup.svelte`| Vertical radio list for longer enum lists or when each option needs a `customContent` snippet (description, sublabel, preview) |
24
-
|`SettingSlider.svelte`| Slider + paired number input (Ark UI `Slider` + `NumberInput`). Reads `min` / `max` / `step` / `sliderStops` from the registry constraints |
25
-
|`SettingNumberInput.svelte`| Standalone number input for settings without a slider (small ranges, exact values). Clamps to registry `min`/`max` on change |
26
-
|`SettingPasswordInput.svelte`| Masked text input with reveal toggle. Two modes (see § "Password-input modes" below) |
27
-
|`SettingColorSwatchPicker.svelte`| Circle trigger + 4×4 swatch popover for picking a tint color. Used by the per-volume-type pane tints under `Appearance > Colors and formats`|
28
-
|`swatch-keyboard.ts`| Pure arrow-key/Home/End/PageUp/PageDown index resolver for the swatch grid. Extracted from the picker so the traversal arithmetic is unit-testable without a DOM |
|`SettingsSidebar.svelte`| Left-column nav: search input, top-level + nested section list, "section has matches" highlight. Declares `TOP_LEVEL_ORDER` (keep in sync with the E2E test in `settings.spec.ts`) |
15
+
|`SettingsContent.svelte`| Right-pane router: maps `selectedSection` to one of the 18 `sections/*.svelte` components, or renders `SectionSummary` for the four top-level sections that have subsections (`Appearance`, `Behavior`, `File systems`, `Developer`) |
16
+
|`SettingsSection.svelte`| Shared section wrapper: h2 title + slot. One per `sections/*.svelte` so the title styling lives in one place |
17
+
|`SectionSummary.svelte`| Card grid shown when a top-level section with subsections is selected; each card deep-links into a subsection |
18
+
|`SettingRow.svelte`| Label + description + control + reset-pip + restart-required badge wrapper. Carries `split` (see below) and `searchQuery` (for `<mark>`-style label highlighting via `highlightMatches`) |
|`SettingCheckbox.svelte`| Less prominent boolean control (Ark UI `Checkbox`). Use for secondary booleans that hang off a switch or live inside a denser layout |
21
+
|`SettingSelect.svelte`| Dropdown for enum settings: a thin registry wrapper around `lib/ui/Select`. Builds the items array (incl. its `__custom__` sentinel row) and owns the `allowCustom` inline-number flow; `ui/Select` never sees `__custom__`. Supports `allowCustom` from the registry, which renders an inline text input when the user picks `Custom…`|
22
+
|`SettingToggleGroup.svelte`| Segmented control for short enum lists. Thin wrapper around `lib/ui/ToggleGroup` with `semantics="toggles"`. `labelOverrides` lets a button label track another reactive setting (for example, `kB` ↔ `KB` on binary/SI switch) |
23
+
|`SettingRadioGroup.svelte`| Vertical radio list for longer enum lists or when each option needs a `customContent` snippet (description, sublabel, preview) |
24
+
|`SettingSlider.svelte`| Slider + paired number input (Ark UI `Slider` + `NumberInput`). Reads `min` / `max` / `step` / `sliderStops` from the registry constraints |
25
+
|`SettingNumberInput.svelte`| Standalone number input for settings without a slider (small ranges, exact values). Clamps to registry `min`/`max` on change |
26
+
|`SettingPasswordInput.svelte`| Masked text input with reveal toggle. Two modes (see § "Password-input modes" below) |
27
+
|`SettingColorSwatchPicker.svelte`| Circle trigger + 4×4 swatch popover for picking a tint color. Used by the per-volume-type pane tints under `Appearance > Colors and formats`|
28
+
|`swatch-keyboard.ts`| Pure arrow-key/Home/End/PageUp/PageDown index resolver for the swatch grid. Extracted from the picker so the traversal arithmetic is unit-testable without a DOM |
29
29
30
30
Each `.svelte` ships with `*.a11y.test.ts` (axe-core tier-3 audit). `SettingColorSwatchPicker` and `swatch-keyboard`
0 commit comments