Skip to content

Add global favorite model for new chats#23

Merged
gabrielMalonso merged 27 commits intomainfrom
upstream-sync
Mar 14, 2026
Merged

Add global favorite model for new chats#23
gabrielMalonso merged 27 commits intomainfrom
upstream-sync

Conversation

@gabrielMalonso
Copy link
Copy Markdown
Owner

Summary

  • Adds a star icon (⭐) next to each model in the provider/model picker
  • Clicking the star sets that model as the global default for all new chats
  • The favorite determines both the provider and the model — e.g., favoriting "Claude Opus 4.6" makes new chats open with Claude Code provider + Opus 4.6
  • Persisted in localStorage via AppSettings, with toast feedback on toggle

Files changed

  • apps/web/src/appSettings.tsFavoriteModel schema + getFavoriteModel() / toggleFavoriteModel() helpers
  • apps/web/src/appSettings.test.ts — 10 new unit tests
  • apps/web/src/components/chat/ProviderModelPicker.tsx — Star icon UI with stopPropagation handling
  • apps/web/src/components/ChatView.tsx — Global favorite influences provider + model resolution for new threads

Test plan

  • Typecheck passes (bun run typecheck)
  • All 16 tests pass (bun run test)
  • Lint clean (bun run lint)
  • Manual: favorite a model → open new chat → correct provider + model shown
  • Manual: unfavorite → new chat uses system default
  • Manual: switch favorite across providers → works correctly

🤖 Generated with Claude Code

juliusmarminge and others added 25 commits March 14, 2026 05:28
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…d Linux) (pingdotgg#841)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…tgg#1006)

Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
- ensure `chat.new` creates a fresh draft after a promoted draft thread
- enforce terminal cap per split group (4) while allowing additional terminal groups
- refine sidebar row selected/active styling via shared class-name logic and tests
- move chat-wide key handling into `_chat` route-level shortcut handler
- extract reusable `useHandleNewThread` hook and `isTerminalFocused` helper
- update browser WS fixture to support `terminalOpen` RPC shape
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… `useLocalStorage` helper (pingdotgg#662)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
# Conflicts:
#	apps/web/src/components/ChatView.tsx
#	apps/web/src/composer-logic.test.ts
- persist a single favorite provider/model in app settings
- use favorite model as default for new local draft chats
- add star toggle UI in model picker with toast feedback
- cover favorite read/toggle behavior with app settings tests
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@github-actions github-actions bot added the size:XXL 1,000+ changed lines (additions + deletions). label Mar 14, 2026
): FavoriteModel | null {
const fav = settings.favoriteModel;
if (!fav || !fav.provider || !fav.model) return null;
const provider = fav.provider as ProviderKind;
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Bug: Unsafe cast of fav.provider as ProviderKind can crash the app

getFavoriteModel casts fav.provider as ProviderKind (line 177) without validating the string is actually one of "codex" | "claudeCode" | "cursor". Since FavoriteModelSchema only validates provider as Schema.String, any arbitrary string stored in localStorage (via corruption, browser extensions, or manual editing) will pass schema validation.

When an invalid provider string reaches normalizeModelSlug, MODEL_SLUG_ALIASES_BY_PROVIDER[provider] returns undefined, and the subsequent aliases[trimmed] property access throws a TypeError, crashing the entire ChatView component tree during render.

Suggested fix: Either add a guard before the cast:

const VALID_PROVIDERS = new Set(["codex", "claudeCode", "cursor"]);
if (!VALID_PROVIDERS.has(fav.provider)) return null;

Or change FavoriteModelSchema to use Schema.Literals(["codex", "claudeCode", "cursor"]) instead of Schema.String for the provider field.

gabrielMalonso and others added 2 commits March 14, 2026 09:30
Use Schema.Literals instead of Schema.String for the provider field
in FavoriteModelSchema, ensuring invalid provider values from
localStorage are rejected at the schema level. Add runtime guard in
getFavoriteModel as defense-in-depth. Remove unsafe `as ProviderKind`
casts in appSettings.ts and ChatView.tsx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gabrielMalonso gabrielMalonso merged commit abc718b into main Mar 14, 2026
@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. and removed size:XXL 1,000+ changed lines (additions + deletions). labels Mar 14, 2026
@gabrielMalonso gabrielMalonso deleted the upstream-sync branch March 14, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.