🎛️ feat: Redesign Settings with Registry-Driven Dialog, Search, and Mobile Drill-In#13722
Conversation
… search, results a11y - SpeechControls.tsx: read sttExternal/ttsExternal from useGetCustomConfigSpeechQuery instead of hardcoding false, so external engine options appear on qualifying deployments - Sidebar: Escape clears search input when non-empty, stops propagation to avoid closing dialog - Content: persistent aria-live="polite" wrapper covers both populated results and empty state - context: useMemo on returned ctx object so Content's useMemo deps are referentially stable - locales/README.md: update stale path from deleted General.tsx to Selectors.tsx
…, add About - Re-categorize settings into logical groups (username display -> Chat/Messages, keep-screen-awake -> Accessibility, fork/prompts surfaced into Chat sections) - Dissolve thin Personalization tab; move Memory into Data & Privacy - Remove the Advanced collapsible; all settings always visible, destructive actions grouped in an always-visible Danger zone - Wire the new About tab into the registry-driven dialog - Standardize spacing with bordered, evenly-divided section cards - Use semantic text-text-* / border tokens so dark mode renders correctly - Sync LangSelector language-loading indicator from dev
Add an Archived chats item to the account dropdown next to My Files, opening the archived chats table in a modal. Removes it from the settings dialog where it no longer fit the data/privacy grouping.
- Flatten the build-info into a single divided key/value list (drop the redundant inner card now that it sits inside a section card) - Replace the hand-rolled copy button with the shared animated CopyButton - Shorten the copied label so it fits the button without clipping
Leaf control labels rendered without a text color and fell back to the browser default (black), making them invisible on the dark panel. Set text-text-primary on the section and search-results row containers so labels inherit a visible color, matching the old container behavior.
The plain multiplication-sign close button had no text color and was invisible on the dark panel. Replace it with the lucide X icon using text-text-secondary/hover:text-text-primary so it shows in both themes.
… only The account-settings popover drew a 2px ring around the active menu item. Remove that override so items show only the standard hover background, consistent with every other menu.
The settings search used type=search, whose native WebKit clear control rendered as a blue X. Switch to a text input and add a real lucide X clear button styled text-text-secondary, shown only when there's a query.
… off Add a disabled prop to the shared Dropdown component, then gate the speech engine/voice/language dropdowns and the automatic-playback switch on their parent toggle (speechToText / textToSpeech), matching the controls that already disabled correctly.
On small screens the horizontal scrolling tab row is replaced with a full-width vertical list (with chevrons); tapping a tab drills into its content with a Back header. Searching shows results full-width. Desktop keeps the side-by-side sidebar + content layout unchanged.
🚨 Unused i18next Keys DetectedThe following translation keys are defined in
|
There was a problem hiding this comment.
Pull request overview
This PR redesigns the client Settings UI into a registry-driven dialog with unified search and an improved mobile “drill-in” navigation, while keeping existing preference storage intact.
Changes:
- Replaced the legacy multi-tab settings containers with a single
SettingsDialogdriven by a centralized settings registry (tabs/sections/visibility). - Added full-settings search that filters by localized labels and keyword synonyms, rendering the real controls in results.
- Improved control behavior and UI polish (e.g.,
Dropdowngainsdisabled, speech dropdowns/switches disable when their parent toggle is off; About copy uses sharedCopyButton; archived chats moved to account menu).
Reviewed changes
Copilot reviewed 46 out of 46 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/client/src/components/Dropdown.tsx | Adds disabled support and disabled styling for shared dropdown control. |
| client/src/style.css | Removes a popover-specific active-item ring style. |
| client/src/locales/README.md | Updates locale docs to point to new language selector file location. |
| client/src/locales/en/translation.json | Tweaks About copy text and adds many new settings/search/section labels. |
| client/src/components/Share/ShareView.tsx | Updates imports for moved theme/language selectors. |
| client/src/components/Nav/SettingsTabs/Speech/TTS/VoiceDropdown.tsx | Disables voice dropdown when TTS is off. |
| client/src/components/Nav/SettingsTabs/Speech/TTS/EngineTTSDropdown.tsx | Disables TTS engine dropdown when TTS is off. |
| client/src/components/Nav/SettingsTabs/Speech/TTS/AutomaticPlaybackSwitch.tsx | Disables autoplay switch when TTS is off. |
| client/src/components/Nav/SettingsTabs/Speech/STT/LanguageSTTDropdown.tsx | Disables STT language dropdown when STT is off. |
| client/src/components/Nav/SettingsTabs/Speech/STT/EngineSTTDropdown.tsx | Disables STT engine dropdown when STT is off. |
| client/src/components/Nav/SettingsTabs/Speech/Speech.tsx | Removes legacy Speech tab container implementation. |
| client/src/components/Nav/SettingsTabs/Personalization.tsx | Removes legacy Personalization tab (memory folded elsewhere). |
| client/src/components/Nav/SettingsTabs/index.ts | Updates exports to match new Settings organization. |
| client/src/components/Nav/SettingsTabs/General/ThemeSelector.spec.tsx | Updates test import path for Theme selector. |
| client/src/components/Nav/SettingsTabs/General/Selectors.tsx | Splits selectors out of the old General container; keeps Theme/Lang controls. |
| client/src/components/Nav/SettingsTabs/General/LangSelector.spec.tsx | Updates test import path for Language selector. |
| client/src/components/Nav/SettingsTabs/General/ArchivedChatsModal.tsx | New archived chats modal for account menu entry. |
| client/src/components/Nav/SettingsTabs/General/ArchivedChats.tsx | Removes archived chats control from Settings (moved to menu). |
| client/src/components/Nav/SettingsTabs/Data/Data.tsx | Removes legacy Data tab container implementation. |
| client/src/components/Nav/SettingsTabs/Commands/Commands.tsx | Removes legacy Commands tab container implementation. |
| client/src/components/Nav/SettingsTabs/Chat/Chat.tsx | Removes legacy Chat tab container implementation. |
| client/src/components/Nav/SettingsTabs/Balance/Balance.tsx | Removes legacy Balance tab container implementation. |
| client/src/components/Nav/SettingsTabs/Account/Account.tsx | Removes legacy Account tab container implementation. |
| client/src/components/Nav/SettingsTabs/Account/Account.spec.tsx | Removes tests for legacy Account container. |
| client/src/components/Nav/SettingsTabs/About/About.tsx | Updates About layout and uses shared CopyButton UX. |
| client/src/components/Nav/Settings/types.ts | Introduces Settings tab/section metadata and context types. |
| client/src/components/Nav/Settings/SpeechControls.tsx | Adds wrappers to wire custom speech config into engine dropdowns. |
| client/src/components/Nav/Settings/Sidebar.tsx | New sidebar with search box and tab list (mobile chevrons supported). |
| client/src/components/Nav/Settings/Section.tsx | New section wrapper with optional “danger” styling. |
| client/src/components/Nav/Settings/search.ts | Implements diacritic-insensitive search matching + filtering. |
| client/src/components/Nav/Settings/registry.tsx | Central registry of settings entries (tab/section/label/visibility/component). |
| client/src/components/Nav/Settings/MemoryToggle.tsx | Rehomes memory preference toggle from removed Personalization tab. |
| client/src/components/Nav/Settings/index.ts | Exposes the new SettingsDialog entry point. |
| client/src/components/Nav/Settings/Dialog.tsx | New settings dialog shell with mobile drill-in and search mode. |
| client/src/components/Nav/Settings/controls.tsx | New registry-friendly wrappers for toggles and theme/lang settings. |
| client/src/components/Nav/Settings/context.tsx | Computes visibility context (permissions, config flags, provider state). |
| client/src/components/Nav/Settings/Content.tsx | Renders sections or search results using the registry. |
| client/src/components/Nav/Settings/BillingControls.tsx | Extracts balance controls into registry-friendly components. |
| client/src/components/Nav/Settings/tests/Sidebar.spec.tsx | Adds unit tests for sidebar rendering and search input behavior. |
| client/src/components/Nav/Settings/tests/search.spec.ts | Adds unit tests for normalization and query matching. |
| client/src/components/Nav/Settings/tests/registry.spec.ts | Adds unit tests validating registry integrity and translation keys. |
| client/src/components/Nav/Settings/tests/Dialog.spec.tsx | Adds component tests for dialog default render and search mode switching. |
| client/src/components/Nav/Settings.tsx | Switches top-level Settings entry to render the new SettingsDialog. |
| client/src/components/Nav/Settings.spec.tsx | Removes tests for legacy Settings dialog implementation. |
| client/src/components/Nav/AccountSettings.tsx | Adds “Archived chats” entry to account menu and renders new modal. |
| client/src/components/Audio/Voices.tsx | Adds disabled prop to voice dropdowns and forwards to shared Dropdown. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ew notes - Drop the i18n keys left unused after the refactor (old Commands/Balance/ Personalization tab labels, the Speech simple/advanced labels, and the former About section headings) - Sort imports in the rebased files the lint-staged hook never touched - Guard the language fallback against an empty navigator.languages - Import the RefObject type instead of leaning on the React namespace
Add an opt-in searchable mode to the shared Dropdown (Ariakit Select + Combobox) and use it for the language selector, which has 40+ options. The trigger styling is unchanged so it stays consistent with the other settings rows; only the popover gains a filter input. Accessibility: the filtered listbox is labeled, the empty state is moved out of the listbox and announced via an aria-live status region, and the decorative selected-state checkmark is hidden from assistive tech.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 70758ef10f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Fall back to the General tab when the active tab becomes hidden (e.g. About when buildInfo is disabled) instead of rendering an empty panel. - Normalize a deprecated/invalid engineTTS (e.g. 'edge') back to browser during speech init so read-aloud controls keep rendering. - Hide the cloud browser voices toggle unless Browser TTS is active.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 77966dd3bb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…llision
The agent builder spec asserted the creation toast with a non-exact
getByText, which also matched Radix Toast's transient role="status"
announce region ("Notification Successfully created ..."), causing a
strict-mode violation. Mirror the mcp spec by using { exact: true }.
Wrap the non-search settings body in Tabs.Content so the selected panel gets role=tabpanel with Radix's id/aria-labelledby wiring, resolving the aria-controls target on each tab trigger. Search results stay a labeled live region (the tab list is hidden during mobile search, so a tabpanel aria-labelledby would dangle).
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
…le Endpoints (#13864) Adds a "Provider API keys" entry under Settings → Data controls → API keys that lists every endpoint requiring a user-provided credential and lets users set or rotate its key via SetKeyDialog. This is always reachable, so keys can be managed even when `interface.modelSelect` is hidden by `modelSpecs`. The endpoint list is filtered the same way the mention popover and model selector menu are: - No modelSpecs → every user-provided endpoint. - modelSpecs configured → limited to spec endpoints ∪ `modelSpecs.addedEndpoints`. - agents reachable (with access) → expanded to the agents `allowedProviders` (all providers when unrestricted). Reworks #13303 onto the registry-driven Settings dialog (#13722); the prior standalone tab and the `APIKeys` directory are superseded (the latter also collided with the agent `ApiKeys` feature from #13819).
Summary
The settings dialog had grown into eight tabs of uneven weight, with a few settings in odd places (for example "Display username in messages" living under Account), no way to find a setting without knowing its tab, and a layout that felt cramped. This reworks it.
Everything now renders from a single registry of settings, so the tab view and the new search share one source of truth.
Layout. Six tabs (General, Chat, Speech, Data & Privacy, Account, About) with named sections. A few settings moved to where they actually belong (username display to Chat, keep-screen-awake to General > Accessibility). The one-toggle Personalization tab is gone and Memory folded into Data & Privacy.
No advanced disclosure. All settings are visible. Destructive actions (clear chats, delete cache, revoke keys, delete account) sit in an always-visible Danger zone with its own styling.
Search. A search box filters every setting by label plus keyword synonyms, and results render the real controls so you can change a setting straight from there.
Mobile. The horizontal tab strip is replaced with a drill-in: a full-width list of tabs, tap one to open its content with a back button. Desktop keeps the sidebar and content side by side.
Also in here: the new About panel is wired in (its copy button now uses the shared CopyButton), Archived chats moved out of settings into the account menu next to My Files, dark mode color tokens were fixed where labels and the close/clear buttons were invisible on the dark panel, the speech engine/voice/language dropdowns and the autoplay switch now disable along with their parent toggle (this needed a
disabledprop on the shared Dropdown), and row spacing was standardized.Leaf controls are reused as-is and no store atom keys changed, so existing preferences carry over. The eight old settings container components are removed.
Change Type
Testing
Added unit and component tests for the registry, the search filter, the sidebar, the dialog shell, and the disclosure behavior. Also verified by hand against a running dev build:
Checked the above in both light and dark mode, at desktop and mobile widths.
Test Configuration:
Checklist