Skip to content

feat(ui): /model with no arg opens an interactive picker#387

Merged
esengine merged 1 commit intomainfrom
feat/371-model-picker
May 7, 2026
Merged

feat(ui): /model with no arg opens an interactive picker#387
esengine merged 1 commit intomainfrom
feat/371-model-picker

Conversation

@esengine
Copy link
Copy Markdown
Owner

@esengine esengine commented May 7, 2026

Closes #371.

Why

/model <id> required typing the model id by hand. New users in the issue thread report nearly closing the app on first use because there was no obvious way to switch — /models only listed available ids read-only, with no built-in way to act on what you saw.

What changes

Bare /model (no arg) now opens an interactive modal picker. /model <id> typed entry continues to work for users who know what they want.

  • src/cli/ui/ModelPicker.tsx — new component, mirrors SessionPicker shape:

    • ↑↓ navigate, confirm, [r] refresh catalog, esc cancel
    • Current model marked with a · current tag
    • Each row shows the existing modelBadgeFor() pill (flash / pro / r1) so the kind is at-a-glance
    • Seeds from the live DeepSeek catalog (useSessionInfo already fetches on mount); falls back to the four known ids when the catalog hasn't loaded yet so the picker isn't empty on first open
    • Current model is always included in the list, even if the API didn't return it (handles stale catalog / custom ids)
  • src/cli/ui/slash/types.ts — adds openModelPicker?: boolean to SlashResult, parallel to existing openSessionsPicker / openMcpHub flags.

  • src/cli/ui/slash/handlers/model.ts — bare /model returns { openModelPicker: true } instead of the prior info-only hint.

  • src/cli/ui/App.tsxpendingModelPicker state, slotted into the modalOpen guard and the modal-stack JSX. On select: loop.configure({ model: id }) + dispatches session.model.change (the event added in Model badge on assistant card still shows flash after switching to pro #372) so the next card pill reflects the new selection.

Tests

  • tests/ui-model-picker.test.tsx — 7 new render tests: catalog list, current marker, loading hint, fallback when null, empty-catalog hint, current id always included, keybind footer.
  • tests/slash.test.ts — updated the bare-/model case from "shows hint" to "returns openModelPicker: true" (existing behavior changed).

Test plan

  • npm run verify — 134 files / 2152 passed / 1 skipped
  • Manual: /model opens the picker, ↑↓ navigates, ⏎ selects, the next assistant card pill reflects the new model
  • Manual: /model deepseek-v4-pro typed entry still works (no picker shown)
  • Manual: open picker before catalog loads → fallback list visible; press [r] to refresh

`/model <id>` required typing the model id by hand and `/models` was
a separate read-only listing. New users had no obvious way to switch
without prior knowledge of the catalog.

Bare `/model` now opens a modal picker that mirrors SessionPicker's
shape: arrow-key list, ⏎ confirm, [r] refresh the API catalog, esc
cancel, current model marked. The list seeds from the live DeepSeek
catalog (`useSessionInfo` already fetches it on mount); when the
catalog hasn't loaded yet the picker falls back to the four known
DeepSeek ids so it's never empty on first open. The current model
is always included even when the API didn't return it.

Confirming a row routes through `loop.configure({ model })` AND the
`session.model.change` event added in #372 — so the badge on the
NEXT card pill reflects the new selection.

`/model <id>` typed entry continues to work for power users.
@esengine esengine merged commit daebf3d into main May 7, 2026
3 checks passed
@esengine esengine deleted the feat/371-model-picker branch May 7, 2026 14:54
@esengine esengine mentioned this pull request May 7, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C:/Program Files/Git/model switching requires typing an ID — needs an interactive picker

1 participant