feat(desktop): inline model picker in the status bar - #37738
Conversation
Replace the status-bar model chip's modal with a Cursor-style dropdown: - providers grouped by name in a stable order (no recency reshuffle on select) - per-model hover-Edit submenu for reasoning effort + fast, gated by per-model capabilities now surfaced in the model.options payload - unified Fast toggle: flips the speed=fast param where supported, else swaps to the model's `-fast` variant (base and variant collapse into one row) - localStorage-backed "Edit Models" dialog to choose which models appear Adds reusable dropdown primitives (DropdownMenuSearch, shared row/label tokens, portaled + collision-aware submenus) and reads session state from nanostores rather than prop-drilling, so editing options doesn't rebuild and close the menu.
🔎 Lint report:
|
| Rule | Count |
|---|---|
invalid-assignment |
1 |
First entries
hermes_cli/inventory.py:186: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to `def get_model_capabilities(provider: str, model: str) -> ModelCapabilities | None`
✅ Fixed issues: none
Unchanged: 5019 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
There was a problem hiding this comment.
Pull request overview
This PR upgrades the desktop status-bar model picker from a full-screen modal to an inline, searchable dropdown with per-model option editing, and extends the backend model.options payload to include per-model capability flags so the UI can gate controls (fast/reasoning) appropriately.
Changes:
- Desktop: add inline status-bar model dropdown with search, stable provider grouping, per-model “Edit” submenu, and a persisted “visible models” customization dialog.
- Backend: extend
build_models_payload(..., capabilities=True)to attach per-provider{model: {fast, reasoning}}capability maps, and plumb it through both JSON-RPC and REST model-options endpoints. - Fast-mode semantics: clarify that Anthropic
speed=fastapplies to Opus 4.6 only; Opus 4.8 uses a separate…-fastmodel id.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tui_gateway/server.py | Requests capabilities=True for model.options JSON-RPC payload. |
| tests/cli/test_fast_command.py | Updates fast-mode tests/docs to reflect Opus 4.6-only speed=fast behavior. |
| hermes_cli/web_server.py | Requests capabilities=True for REST /api/model/options. |
| hermes_cli/models.py | Clarifies _is_anthropic_fast_model docstring to distinguish param vs …-fast model ids. |
| hermes_cli/inventory.py | Adds capabilities flag and _apply_capabilities() to include {fast, reasoning} per model. |
| apps/desktop/src/types/hermes.ts | Adds capabilities typing to model option providers. |
| apps/desktop/src/store/model-visibility.ts | Adds localStorage-backed model visibility state + base/…-fast family collapsing. |
| apps/desktop/src/lib/model-status-label.ts | Adds shared model display formatting + status bar label formatting. |
| apps/desktop/src/lib/model-status-label.test.ts | Adds vitest coverage for model name + status label formatting. |
| apps/desktop/src/components/ui/dropdown-menu.tsx | Adds dropdown search input + shared tokens + portaled/collision-aware submenus. |
| apps/desktop/src/components/model-visibility-dialog.tsx | Adds “Edit Models…” dialog for per-provider visible model selection. |
| apps/desktop/src/app/shell/statusbar-controls.tsx | Adds configurable menu alignment for status-bar dropdowns. |
| apps/desktop/src/app/shell/model-menu-panel.tsx | Implements the inline model dropdown panel (search, grouping, edit submenus). |
| apps/desktop/src/app/shell/model-edit-submenu.tsx | Implements per-model options submenu (Thinking/Fast/Effort) + fast control resolution. |
| apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx | Wires status-bar model chip to the new inline menu and live session state label. |
| apps/desktop/src/app/session/hooks/use-model-controls.ts | Makes selectModel async-returning to support “switch then edit” flows. |
| apps/desktop/src/app/model-visibility-overlay.tsx | Adds overlay wrapper for the model visibility dialog. |
| apps/desktop/src/app/desktop-controller.tsx | Wires the new model menu panel + model visibility overlay into the app shell. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- selectModel reports success; edits bail (and roll back) instead of landing on the previously active model when a switch fails - Fast toggle stays available to turn off a carried-over speed param even when the new model has no native fast mechanism - active row's "Fast" label derives from the same fastControl as the submenu toggle, so it's consistent and handles standalone `-fast` model ids
selectModel snapshots the prior model/provider and restores the store + query cache when the backend switch fails, so the UI never shows a model the backend didn't actually select.
The model row is a Radix sub-trigger (no onSelect), so switching was pointer-only. Wire Enter/Space alongside onClick so keyboard users can switch models too.
…model-menu feat(desktop): inline model picker in the status bar
feat(desktop): inline model picker in the status bar
…model-menu feat(desktop): inline model picker in the status bar
…model-menu feat(desktop): inline model picker in the status bar
…model-menu feat(desktop): inline model picker in the status bar
…model-menu feat(desktop): inline model picker in the status bar
…model-menu feat(desktop): inline model picker in the status bar
Summary
Replaces the status-bar model chip's full-screen modal with a Cursor-style inline dropdown, and makes its options real and persistent.
model.optionspayload — no toggle appears for an option the backend would reject.speed=fastrequest param where supported (Opus 4.6, OpenAI flagships), otherwise swaps to the model's-fastvariant. A base model and its-fastsibling collapse into a single row.Opus 4.8 · Fast Max.Architecture / reuse
components/ui/dropdown-menu:DropdownMenuSearch, shareddropdownMenuRow/dropdownMenuSectionLabeltokens, ahideChevronsub-trigger prop, and portaled + collision-aware submenus (no more clipping).build_models_payload(..., capabilities=True)attaches{model: {fast, reasoning}}per provider row (wired into themodel.optionsJSON-RPC + REST mirror)._is_anthropic_fast_modeldocstring clarified (logic unchanged: thespeedparam is Opus 4.6 only; Opus 4.8 fast is the separate-fastmodel).Test plan
model.options), open the model chip in the status bar-fastmodel; Fast toggle absent on models with no fast offeringscripts/run_tests.sh tests/cli/test_fast_command.pyand desktopnpm run type-check/lint/test:uipass