Originally created by: sosidudku1
Closes [#62]. Based on the branch of the base-URL prefill fix; merge that one first.
After first-time setup the /v1/models list is unreachable: the Cloud pane renders a single model row for an openai-compatible provider and Enter re-selects it, /model only jumps to the LLM tab, and the only in-TUI way to switch models is re-running the wizard from the API key screen.
modelPicker modal in the LLM panel. Enter on the model row of an openai-compatible provider reopens the server's model list: 12-row window with wrap-around cursor, the current model marked, count in the footer. Enter switches the model mid-session through the existing providers_select_chat_model path (which already persists and applies the change), Esc cancels. An unreachable or unauthorized endpoint degrades to a closable error box instead of trapping the user./model now opens the picker for the active text provider by emitting providers_chat_model_picker_requested. The request no-ops for curated kinds (their models are already first-class rows), so the previous tab-jump behavior is unchanged there.ProvidersOrchestrator, following the existing bus-subscription pattern, and reuses fetchOpenAiCompatModels with its 1h per-URL-and-key cache from [#41]. Stale responses arriving after the picker was closed or reopened for another provider are dropped in the reducer.Out of scope, possible follow-ups: fuzzy search inside the picker, recent-models ordering, a global hotkey.
10 new tests: reducer transitions (open/loaded with cursor on the current model/stale-response guards/failed) and modal keys (wrap-around navigation, select-and-close routing, Esc, loading swallows keys, error closes on Enter). The pre-named /model slash test now asserts the picker request it was named after. src/tui plus src/llm/provider: 730 passing, the 6 remaining failures are the pre-existing set documented in [#50], verified identical on clean main. tsc --noEmit clean.
Originally posted by: sosidudku1
All three addressed, thanks for the review.
Namespace/ownership. The picker state moved to
providersPanel(chatModelPicker) and the actions are nowproviders_chat_model_picker_*, soProvidersOrchestratoronly emits its own namespace. The reducer cases moved toproviders-reducer.tsalongsidewizard/removeConfirm, and the tests moved with them tosrc/tui/providers/providers-chat-model-picker.test.ts.llmTabBusy. Added the open picker. While there I also addedllmPanel.externalUrlDraft, which has been missing from that guard since [#50], so Tab/Ctrl+B leaked past the external URL editor too.Stale-response guard. Now keyed on a generation counter bumped on every open (
chatModelPickerGenerationon the orchestrator, mirrored into the picker state) rather than onproviderId, so the close-and-reopen-same-provider race you described is rejected properly rather than incidentally. There is a test for exactly that sequence.tscclean;src/tuiplussrc/llm/providershow the same pre-existing failures as main (the twoLlmHealthPollerones are timing-flaky and pass in isolation).Related
Tickets:
#50Ticket changed by: Ooooze