-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Description
Description
Global keybinds (like Tab for agent cycling) continue to execute when a dialog or picker is open in the TUI. This causes unintended side effects in the background while the user is interacting with a modal.
Example: When the Model Selector (<leader>m) or Command Palette (Ctrl+P) is open, pressing Tab will cycle through agents in the background instead of being ignored or handled by the dialog.
Note: This bug likely also exists in the Desktop/Web app (
packages/app/), wherepackages/app/src/context/command.tsxhas a similar pattern - the keybind handler does not check if a dialog is active before executing commands.
Root Cause
In packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx, there are two useKeyboard handlers:
-
Handler in
init()(lines 46-55): Processes all registered commands with keybinds (includingagent.cycle). This handler checkssuspended()but does not check if a dialog is open (dialog.stack.length > 0). -
Handler in
CommandProvider(lines 100-108): Only handles opening the command palette. This handler correctly checks bothsuspended()anddialog.stack.length > 0.
The inconsistency between these two handlers causes the bug.
Technical Analysis
Options Considered:
-
Add
dialog.stack.length > 0check (chosen solution)- Add the same guard used in
CommandProviderto theinit()handler - Minimal change (1 line), follows existing pattern in the same file
- No architectural changes needed
- Add the same guard used in
-
Use
command.keybinds(false/true)in dialogs (similar to autocomplete)- Make dialogs suspend keybinds when opening and resume when closing
- Would require
DialogProviderto accessCommandProvider, butDialogProvideris initialized beforeCommandProviderin the component tree, making this impractical without restructuring
-
Reactive effect on
dialog.stack.length- Use
createEffectto observe dialog state and updatesuspendCount - More complex than option 1, achieves the same result indirectly
- Use
Decision: Option 1 was chosen because:
- It follows the existing pattern already used in the same file (line 102)
- It's consistent with how
session/index.tsxhandles the same scenario (line 254) - Minimal change with no risk of introducing regressions
- No architectural changes required
Steps to reproduce
- Open the TUI (
opencode) - Open any dialog/picker (e.g.,
Ctrl+Pfor Command Palette or<leader>mfor Model Selector) - While the dialog is open, press
Tab(agent cycle keybind) - Close the dialog
- Observe that the agent has changed in the background
Expected: Keybinds should not execute while a dialog is open
Actual: Keybinds execute in the background, causing unintended state changes
OpenCode version
v1.0.191
Operating System
macOS (applies to all platforms)
Terminal
Any terminal