Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Redesign model picker with favorites and search - #2153

Merged
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites
Apr 20, 2026
Merged

Redesign model picker with favorites and search#2153
juliusmarminge merged 29 commits into
pingdotgg:mainfrom
Chrono-byte:feature/model-picker-favorites

Conversation

@Chrono-byte

@Chrono-byteChrono-byte commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
  • Replace provider submenus with sidebar-based model selection
  • Add model search, favorites, and locked-provider handling
  • Update settings schema and tests for favorite model persistence

What Changed

Replace model picker with new model picker inspired by t3chat (a lot). Has search, favorites, etc.

For OpenCode models, the upstream provider from OpenCode is extracted out and used under the model name.

Why

Adding OpenCode made me very happy but also, the old model picker is cumbersome with so many models, difficult to navigate. Also generally just a nice to have improvement.

UI Changes

Before:

imageimage

After:

imageimageimage

Checklist

  • This PR is small and focused
    I feel bad not checking "small" but this is an XL pr so.

  • I explained what changed and why

  • I included before/after screenshots for any UI changes


Note

Medium Risk
Medium risk due to a large UI refactor that changes model selection flow, introduces new keybinding commands/contexts, and extends settings/contracts schemas (favorites + model metadata), which could affect persistence and shortcut resolution across desktop/web/server.

Overview
Redesigns the model picker into a popover-based experience with a provider sidebar, fuzzy search, and per-model favorites (persisted in new ClientSettings.favorites), including new components for the picker UI and search/ranking.

Adds model-picker keyboard support: new default keybindings (modelPicker.toggle and modelPicker.jump.1-9) plus a modelPickerOpen context, with global modifier-state tracking to control jump-hint visibility and shortcut routing.

Updates model metadata contracts to carry shortName/subProvider (notably for OpenCode flattening), adjusts /model to open the picker instead of inserting text, and refactors related tests and UI primitives (combobox/scroll-area) to support the new picker behavior.

Reviewed by Cursor Bugbot for commit ad3f024. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign model picker with sidebar, favorites, fuzzy search, and keyboard jump shortcuts

  • Replaces the nested submenu model picker with a Popover containing a provider sidebar rail (ModelPickerSidebar.tsx) and a searchable model list (ModelPickerContent.tsx) with fuzzy scoring via scoreModelPickerSearch.
  • Adds a favorites system stored in ClientSettings that pins favorite models to the top of the list and applies a ranking boost during search.
  • Adds keyboard jump shortcuts (modelPicker.jump.1–9) activated while the picker is open, and a toggle shortcut (modelPicker.toggle on Shift+Mod+M).
  • The /model slash command now opens the model picker instead of injecting a model search into the composer command menu, and the slash-model trigger kind is removed from detectComposerTrigger.
  • Thread jump hint visibility is refactored to use a new global shortcut modifier state store (shortcutModifierState.ts), so modelPickerOpen is factored into shortcut context across the sidebar and chat view.
  • OpenCode models now expose subProvider and use plain model name for name in flattenOpenCodeModels, and ServerProviderModel schema is extended with optional shortName/subProvider fields.
  • Behavioral Change: model picker open state is now shared globally via a Zustand store; ProviderModelPicker falls back to the active provider's first model when the current slug does not belong to that provider.

Macroscope summarized ad3f024.

- Replace provider submenus with sidebar-based model selection
- Add model search, favorites, and locked-provider handling
- Update settings schema and tests for favorite model persistence
CopilotAI review requested due to automatic review settings April 18, 2026 02:58
@coderabbitai

coderabbitaiBot commented Apr 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01905f0d-5859-4ccb-8aae-061684c4d720

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a major new feature (model picker redesign with favorites, search, and provider sidebar) with ~2800 lines of new code including new components, state management, and keybindings. Additionally, a HIGH severity review comment identifies that a debug tool (react-scan) is committed to production HTML and would load for all users, which must be removed before merging.

You can customize Macroscope's approvability policy. Learn more.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR redesigns the chat model picker UI to make large model catalogs easier to navigate by introducing a sidebar-based provider filter, inline search, and per-model favorites persisted in client settings.

Changes:

  • Add a favorites array to client settings and update persistence tests accordingly.
  • Replace the provider submenu model picker with a new ModelPickerContent (sidebar + searchable flat list) and supporting components/utilities.
  • Update UI styling (thin scrollbar) and adjust browser tests for the new picker behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.

Show a summary per file
FileDescription
packages/contracts/src/settings.tsAdds favorites to client settings and extends the settings patch schema.
apps/web/src/localApi.test.tsUpdates expected persisted client settings to include favorites.
apps/web/src/index.cssAdds thin scrollbar styling for the model picker list.
apps/web/src/components/chat/providerIconUtils.tsIntroduces shared provider icon mapping and provider/model labeling helpers.
apps/web/src/components/chat/ProviderModelPicker.tsxSwitches the picker popup to render the new ModelPickerContent.
apps/web/src/components/chat/ProviderModelPicker.browser.tsxUpdates UI tests for the new sidebar/search/favorites interactions.
apps/web/src/components/chat/ModelPickerSidebar.tsxNew sidebar component for provider/all/favorites filtering.
apps/web/src/components/chat/ModelPickerContent.tsxNew main picker UI with search, favorites persistence, and model list rendering.
apps/web/src/components/chat/ModelListRow.tsxNew row component for model selection + favorite toggle UI.
apps/web/src/components/CommandPalette.logic.tsRefactors item construction to use Object.assign while preserving conditional fields.
apps/desktop/src/clientPersistence.test.tsUpdates desktop client settings persistence test fixture to include favorites.
.codexFile present in PR contents (no diff shown).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadpackages/contracts/src/settings.ts
Replace the conditional skip with an explicit expect so the test
fails loudly if the Codex sidebar button is absent and proceeds to
click and assert model filtering afterward
Comment threadapps/web/src/components/chat/ProviderModelPicker.browser.tsx Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadpackages/contracts/src/settings.ts
Filter the All Models section to omit models already shown in Favorites,
adjust provider visibility logic, and add a test ensuring favorites
aren't duplicated across sections
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
Add a ClientSettingsPatch schema/type and export the
ServerSettingsPatch type. Update apps/web useSettings to import
patch types as type-only and use ClientSettingsPatch for the
clientPatch return value.
Remove persisted client settings key before and after the
ProviderModelPicker test to avoid state leakage. Use an aria-label
based lookup for the favorite star button, assert its initial label,
click it, and verify the aria-label toggles accordingly.
Comment threadapps/web/src/components/chat/ModelPickerSidebar.tsx Outdated
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Replace global document.body.textContent checks with helpers that query
the .model-picker-list element, and find favorited rows via filtered
querySelectorAll to improve test reliability
- boost favorite models in search ranking
- switch model picker to combobox/popover interactions
- improve scroll overflow handling and navigation tests
Comment threadapps/web/src/components/chat/ModelPickerContent.tsx
Comment threadapps/desktop/src/main.ts Outdated
- Switch model picker toggle to `mod+shift+m`
- Add favorites-focused model picker UI tweaks and provider icons
- Remove desktop shortcut bridge plumbing no longer needed
- Add the `new` badge to Cursor in the provider picker sidebar
Comment threadapps/web/src/components/chat/providerIconUtils.ts Outdated
- Remove provider-specific icon class helper
- Use shared sizing and viewport styling in the model picker
for (const command of MODEL_PICKER_JUMP_KEYBINDING_COMMANDS) {
const shortcut = findEffectiveShortcutForCommand(keybindings, command, options);
if (!shortcut) continue;
if (matchesShortcutModifiers(modifiers, shortcut, platform)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused exported model picker hint functions

Low Severity

shouldShowModelPickerJumpHints and shouldShowModelPickerJumpHintsForModifiers are new exported functions that are never called in any production code. They are only referenced in keybindings.test.ts. The model picker content component (ModelPickerContent) unconditionally renders jump labels when the picker is open, so these "should show hints" functions serve no purpose outside of tests. This is dead code that adds maintenance burden.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

},
},
};
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated refactoring of command palette thread items

Low Severity

The return statement in buildCommandPaletteThreadItems was refactored from spread syntax to Object.assign without any functional reason related to the model picker redesign. This increases the diff size, changes string quoting from "" to backtick templates, and introduces a different (arguably less readable) pattern while the original spread syntax was clear and idiomatic. This change makes the PR harder to review without any benefit.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit fc65ff8. Configure here.

Comment threadapps/web/src/components/chat/ModelPickerContent.tsx Outdated
- Avoid redundant store updates when modifier values are unchanged
- Track bare modifier keydown and keyup events explicitly
- Load react-scan in the web app shell
Comment threadapps/web/index.html Outdated
- Keep the keyboard event test helper on one line
- No behavior change
Comment threadapps/web/index.html Outdated
<meta name="theme-color" content="#161616" />
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<script crossorigin="anonymous" src="//unpkg.com/react-scan/dist/auto.global.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug tool react-scan committed to production HTML

High Severity

The react-scan development debugging script is loaded from unpkg CDN in the production index.html. This tool renders visual overlays highlighting React re-renders and adds significant performance overhead. It will load for every user on every page load, degrading performance and showing debug UI in production.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 28445c5. Configure here.

- Preserve canonical model names and sub-provider labels separately
- Update picker search, trigger text, and favorites rendering
- Extend server contracts for `shortName` and `subProvider`
- Split model picker visibility out of shortcut modifier state
- Update sidebar and provider picker to use the new store

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

toggleModelPicker: () => {
setIsComposerModelPickerOpen((open) => !open);
},
isModelPickerOpen: () => isComposerModelPickerOpen,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in isModelPickerOpen imperative handle method

Medium Severity

The isModelPickerOpen method in the imperative handle captures isComposerModelPickerOpen by value. When toggleModelPicker() is called (e.g., via Mod+Shift+M), the state update is asynchronous — the handle isn't recreated until React commits the re-render. During this window, composerRef.current?.isModelPickerOpen() in ChatView.tsx's keydown handler returns the stale pre-toggle value. This means the shortcutContext.modelPickerOpen fed to resolveShortcutCommand can be wrong, causing e.g. thread.jump.1 to resolve instead of modelPicker.jump.1 immediately after toggling the picker open. Using a ref for the open state (or reading from the global Zustand store directly) would avoid this race.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d3119a9. Configure here.

- Use the active provider's first model when the current slug belongs to another provider
- Add a regression test for stale cross-provider model labels
@juliusmarminge
juliusmarminge merged commit 66c326b into pingdotgg:mainApr 20, 2026
12 checks passed
@Chrono-byte
Chrono-byte deleted the feature/model-picker-favorites branch April 20, 2026 06:03
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 20, 2026
Upstream additions:
- fix(web): restore manual sort drag and keep per-group expand state (pingdotgg#2221)
- fix: Change right panel sheet to be below title bar / action bar (pingdotgg#2224)
- Refactor OpenCode lifecycle and structured output handling (pingdotgg#2218)
- effect-codex-app-server (pingdotgg#1942)
- Redesign model picker with favorites and search (pingdotgg#2153)
- fix(server): prevent probeClaudeCapabilities from wasting API requests (pingdotgg#2192)
- fix(server): handle OpenCode text response format in commit message gen (pingdotgg#2202)
- Devcontainer / IDE updates (pingdotgg#2208)
- Expand leading ~ in Codex home paths before exporting CODEX_HOME (pingdotgg#2210)
- fix(release): use v<semver> tag format for nightly releases (pingdotgg#2186)
Fork adaptations:
- Took upstream's redesigned model picker with favorites and search
- Removed deleted codexAppServerManager (replaced by effect-codex-app-server)
- Stubbed fetchCodexUsage (manager-based readout no longer available)
- Extended PROVIDER_ICON_BY_PROVIDER for all 8 fork providers
- Extended modelOptionsByProvider test fixtures for all 8 providers
- Inline ClaudeSlashCommand type (not yet re-exported from SDK)
- Updated SettingsPanels imports for new picker module structure
- Preserved fork's CI customizations (ubuntu-24.04 not Blacksmith)
Marve10s added a commit to Marve10s/t3code that referenced this pull request Apr 20, 2026
Resolve conflict in apps/web/src/components/Sidebar.tsx by keeping
clampSidebarThreadPreviewCount (still used by this PR's stepper input)
and dropping threadJumpLabelMapsEqual, which upstream removed along with
its callers in the model picker redesign (pingdotgg#2153).
fav-devs pushed a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
aaditagrawal added a commit to aaditagrawal/t3code that referenced this pull request Apr 22, 2026
Skipped 6 tests from upstream's new model picker (pingdotgg#2153) that assert
specific model names or provider orderings (codex/claudeAgent/cursor/
opencode only) that don't match our fork's extended 8-provider setup.
orlaya added a commit to orlaya/t3code that referenced this pull request Apr 25, 2026
Brings upstream PRs pingdotgg#1942 (effect-codex-app-server), pingdotgg#2023 (toast close
buttons), pingdotgg#2153 (model picker), pingdotgg#2221 (sort-order restore), pingdotgg#2246 (option
arrays), pingdotgg#2311 (dynamic_tool_call), pingdotgg#2314 (task sidebar auto-open), and the
new ProviderOptionDescriptor / ProviderOptionSelection contract.
Conflict resolutions:
- packages/contracts/src/settings.ts: kept both our customSlashCommands
feature and upstream's favorites + ClientSettingsPatch additions. Both
imports needed; bodies auto-merged cleanly.
- apps/web/src/uiStateStore.ts: combined upstream's logical-key/sort-restore
refactor with our pinnedThreadKeys, accordion, and
threadListExpandedByProject. Kept default-collapsed semantic on top of
upstream's richer fallback chain. persistState now exported per upstream.
- apps/web/src/components/settings/SettingsPanels.tsx: kept both our
Work-log-history Select row and upstream's Task-sidebar Switch row as
siblings. Restore-defaults footer (moved from header to footer for
accidental-click safety) preserved alongside upstream's stackedThreadToast
refactor and capabilities-via-optionDescriptors changes.
- apps/web/src/index.css: kept our search-highlight CSS Custom Highlight API
rules + upstream's .model-picker-list scrollbar styling.
- apps/web/src/components/Sidebar.tsx: rip-and-replace. Upstream refactored
thread-jump-hint visibility into a hook-based architecture
(useShortcutModifierState + shouldShowThreadJumpHintsForModifiers +
updateThreadJumpHintsVisibility). Our manual keyup/blur listeners and the
shouldIgnoreThreadJumpHintUpdate / clearThreadJumpHints / setLabel-by-key
/ shouldShowThreadJumpHints machinery were redundant under the new system
and removed.
- apps/web/src/components/chat/ChatComposer.tsx: dropped the inline
type-/-to-search-models feature. Ripped the external model-picker
open-state machinery (isComposerModelPickerOpen, openModelPicker /
toggleModelPicker / isModelPickerOpen handle methods) and the
modelPicker.toggle keybind path; our local ProviderModelPicker
self-manages its open state, so the external wiring was dead. Removed the
built-in /model, /plan, /default slash commands (custom + provider slash
commands kept). Dropped now-unused keybindings/terminalOpen and
handleInteractionModeChange props from the interface and call site.
- apps/web/src/components/ChatView.tsx: kept ours wholesale (isTerminalOnly
guards on header + main content, traffic-light inset transitions for
electron, animated-width plan sidebar wrapper, RightPanelSheet fallback
for narrow viewports via canFitPlanSidebarInline, search/working-indicator
/activityIndicators features, workspaceRoot pass-through). Upstream's
shouldUsePlanSidebarSheet was a parallel solve to our
canFitPlanSidebarInline; ours is tuned for narrow screens and stays.
Kept our search.toggle and terminalSearch.toggle keyboard handlers.
Stopgap:
- apps/server/src/provider/Layers/CodexAdapter.ts: hardcoded
agentKind: "primary" on runtime events to satisfy new contract; full
sub-agent classification (closure-based factory + active-task-id Set)
deferred per __notes/post-merge-restorations.md.
Cleanup performed in the same pass:
- Removed stale oxlint-disable directive in CommandPalette.logic.ts (rule
no longer triggered after upstream's Object.assign refactor landed).
- Refactored apps/web/src/modelSelection.ts map-spread to Object.assign to
match the convention.
- Hoisted createLocalStorageStub() out of the describe block in
uiStateStore.test.ts.
Status: fmt + typecheck + lint all clean (0 errors, 0 warnings). Test suite
not yet run.
MuneerAhmed03 pushed a commit to MuneerAhmed03/t3code that referenced this pull request Apr 26, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Morphexe pushed a commit to Morphexe/t3code that referenced this pull request May 4, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
youpele52 referenced this pull request in youpele52/bigbud Jun 17, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
NeilTheFisher pushed a commit to NeilTheFisher/t3code that referenced this pull request Aug 18, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Chrono-byte@juliusmarminge