You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tui: model favorites toggle missing hint and dead in /models dialog (2.0.13) #50969
Model favourites can't be toggled in the V2 terminal: the /models dialog shows no favourite hint, and neither ctrl+f nor remapped keys star a model. Backend favourites still exist, so this looks like a V1→V2 compatibility regression.
Environment
opencode version: 2.0.13
OS: Linux 6.18.39-haos (x86_64, Home Assistant add-on container; client viewed from macOS Chrome)
Terminal: Chrome on macOS → Home Assistant OpenCode add-on terminal (Ingress ttyd/xterm). Cmd+F is browser Find.
Active plugins: TUI uses managed config with only file:///opt/opencode-v2-homeassistant/runtime-guard-plugin; no user plugins
Reproduction
Open the OpenCode terminal in Chrome on macOS.
Open the model list via <leader>m (or /models).
Press Esc, highlight a model row with up/down (filter input not focused).
Press ctrl+f — nothing happens.
Remap in cli.json: {"keybinds": {"model.dialog.favorite": ["ctrl+f", "alt+f"]}}, then ["ctrl+f", "f4"] — reload, repeat step 4 with Alt+F / F4 — still nothing.
Expected Behavior
Per V2 keybind docs, model.dialog.favorite defaults to ctrl+f and toggles favourite; V1 showed a "ctrl-f to save favourite" hint. Expect the hint and a working toggle (or remappable equivalent).
Actual Behavior
No favourite hint anywhere in the dialog. No key (Ctrl+F, Alt+F, F4) toggles favourite. Consistent since updating. TUI log shows clean start with no keybind rejection; model.json state still holds 3 existing favourites.
V1 key was model_favorite_toggle ctrl+f; V2 docs list model.dialog.favorite ctrl+f.
ctrl+f is overloaded in V2 (session.pin.toggle, permission.prompt.fullscreen, input.move.right), and Chrome reserves Cmd+F / Ctrl+F for Find, so browser delivery is part of it — but the missing hint and dead F4 remap point beyond key interception.
Temporary cli.json remap tested at /run/opencode-v2/tui/config/opencode/cli.json (ephemeral, resets on reboot); valid JSON, no effect.
I looked into this on v2 and can reproduce the cause: this is a regression from the redefinition of connected().
On dev, useConnected() reports whether a provider is configured (packages/tui/src/component/use-connected.tsx). On v2 it delegates to hasConnectedProvider, i.e. "some integration has connections" (packages/tui/src/util/connected-provider.ts: integrations.some((integration) => integration.connections.length > 0)).
The model dialog carried the old gates over verbatim, so with no integration connected:
packages/tui/src/component/dialog-model.tsx:160 hides the model.dialog.favorite action (hidden: !connected())
dialog-model.tsx:32 drops the favorites list, and :27/:31 gate the Favorites and Recents sections through showExtra
dialog-model.tsx:102 also feeds connected() into the option set
The missing hint and the dead toggle are the same cause: packages/tui/src/ui/dialog-select.tsx builds its keymap from shownActions() (:156 filters !item.hidden, :162 registers from it), so a hidden action is not just invisible, its keybinding is never registered and ctrl+f does nothing at all.
Your follow-up comment matches exactly: favorites and their hint returned as soon as an integration was connected again. Local favorites and recents are client-side preference state, so gating them on integration connectivity looks unintended; only the integration-facing entry (model.dialog.provider, title at :147) should depend on it.
I would like to work on this. Plan: stop gating the favorites list, the Favorites/Recents sections and the model.dialog.favorite action on integration connectivity, keep the gate for the integration entry, and add a dialog test covering the no-integration case.
Favorites and recents are client side preference state, so they are no longer gated on integration connectivity: the Favorites and Recent sections are listed again, the favorites list is read unconditionally, and the model.dialog.favorite action is always shown. Showing the action is what brings back both the hint and the binding, because DialogSelect registers its keymap entries from shownActions() and a hidden action is never bound. The integration entry keeps its connected() gate, since it is the only integration facing part of this dialog.
Two tests mount the real dialog on a fixture with no connected integration: the footer renders the ctrl+f hint and pressing it saves the highlighted model, and favorites already stored in model.json are listed under Favorites. Both fail before the change, where the captured frame shows only View all integrations ctrl+a.
Thanks for the detailed report. I can reproduce this on 2.0.13, and it still happens on the latest dev (fa1260d).
Steps: with a model provider available but no connected integration, open /models and highlight a model. The dialog shows no favourite hint, only "View all integrations ctrl+a". ctrl+f, and f4 after remapping model.dialog.favorite in cli.json, do nothing. The recording below shows the whole sequence, and the script below reproduces it.
This is not your terminal or browser: the favourite action is only shown when an integration has a saved connection. While it is hidden, no key is bound to it at all, which is why remapping made no difference. That also matches what you saw: your add-on probably provides models without a saved connection. A maintainer will look at it. Related: #51760 (a closed PR describing the same cause) and #49309 (same "connected" check, affecting provider names in /models).
Reproduction script (drive.ts)
// Repro for anomalyco/opencode#50969: with no connected integration, the /models// dialog shows no favorite hint and ctrl+f (or a remapped key) does not toggle favorites.import{Effect}from"effect"import{OpenCodeDriver}from"opencode-drive"exportdefaultOpenCodeDriver.use({
...(process.env.OC_DEV ? {opencode: {dev: process.env.OC_DEV}} : {}),// Also remap the action to f4 to show that remaps have no effect either.tuiConfig: {keybinds: {"model.dialog.favorite": ["ctrl+f","f4"]}},tui: {recording: true,keypressOverlay: true},},({ ui, opencode })=>Effect.gen(function*(){yield*ui.waitFor((s: any)=>true)yield*Effect.sleep(1500)yield*ui.submit("/models")yield*Effect.sleep(1500)yield*ui.screenshot("models-open")yield*ui.arrow("down")yield*ui.arrow("down")yield*Effect.sleep(300)yield*ui.screenshot("highlighted")yield*ui.press("f",{ctrl: true})yield*Effect.sleep(800)yield*ui.screenshot("after-ctrl-f")yield*ui.press("f4")yield*Effect.sleep(800)yield*ui.screenshot("after-f4")constframe=yield*ui.capture()yield*Effect.log("FRAME:\n"+(frameasany).lines.map((l: any)=>l.spans.map((s: any)=>s.text).join("")).join("\n"))}),)
Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Activity
cannockwolf commentedon Sep 23, 2026
favourites + hint returned after /connect as i got disconneted from openai and had to reconnect
argszero commentedon Sep 26, 2026
I looked into this on
v2and can reproduce the cause: this is a regression from the redefinition ofconnected().On
dev,useConnected()reports whether a provider is configured (packages/tui/src/component/use-connected.tsx). Onv2it delegates tohasConnectedProvider, i.e. "some integration has connections" (packages/tui/src/util/connected-provider.ts:integrations.some((integration) => integration.connections.length > 0)).The model dialog carried the old gates over verbatim, so with no integration connected:
packages/tui/src/component/dialog-model.tsx:160hides themodel.dialog.favoriteaction (hidden: !connected())dialog-model.tsx:32drops the favorites list, and:27/:31gate the Favorites and Recents sections throughshowExtradialog-model.tsx:102also feedsconnected()into the option setThe missing hint and the dead toggle are the same cause:
packages/tui/src/ui/dialog-select.tsxbuilds its keymap fromshownActions()(:156filters!item.hidden,:162registers from it), so a hidden action is not just invisible, its keybinding is never registered andctrl+fdoes nothing at all.Your follow-up comment matches exactly: favorites and their hint returned as soon as an integration was connected again. Local favorites and recents are client-side preference state, so gating them on integration connectivity looks unintended; only the integration-facing entry (
model.dialog.provider, title at:147) should depend on it.I would like to work on this. Plan: stop gating the favorites list, the Favorites/Recents sections and the
model.dialog.favoriteaction on integration connectivity, keep the gate for the integration entry, and add a dialog test covering the no-integration case.argszero commentedon Sep 28, 2026
Opened #51760 (
v2) with the fix.Favorites and recents are client side preference state, so they are no longer gated on integration connectivity: the Favorites and Recent sections are listed again, the favorites list is read unconditionally, and the
model.dialog.favoriteaction is always shown. Showing the action is what brings back both the hint and the binding, becauseDialogSelectregisters its keymap entries fromshownActions()and a hidden action is never bound. The integration entry keeps itsconnected()gate, since it is the only integration facing part of this dialog.Two tests mount the real dialog on a fixture with no connected integration: the footer renders the
ctrl+fhint and pressing it saves the highlighted model, and favorites already stored inmodel.jsonare listed under Favorites. Both fail before the change, where the captured frame shows onlyView all integrations ctrl+a.opencodejr commentedon Oct 28, 2026
Thanks for the detailed report. I can reproduce this on 2.0.13, and it still happens on the latest
dev(fa1260d).Steps: with a model provider available but no connected integration, open
/modelsand highlight a model. The dialog shows no favourite hint, only "View all integrations ctrl+a".ctrl+f, andf4after remappingmodel.dialog.favoriteincli.json, do nothing. The recording below shows the whole sequence, and the script below reproduces it.This is not your terminal or browser: the favourite action is only shown when an integration has a saved connection. While it is hidden, no key is bound to it at all, which is why remapping made no difference. That also matches what you saw: your add-on probably provides models without a saved connection. A maintainer will look at it. Related: #51760 (a closed PR describing the same cause) and #49309 (same "connected" check, affecting provider names in
/models).Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.