Skip to content

[BUG] Keybinds execute in background when dialogs/pickers are open #6016

@anntnzrb

Description

@anntnzrb

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/), where packages/app/src/context/command.tsx has 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:

  1. Handler in init() (lines 46-55): Processes all registered commands with keybinds (including agent.cycle). This handler checks suspended() but does not check if a dialog is open (dialog.stack.length > 0).

  2. Handler in CommandProvider (lines 100-108): Only handles opening the command palette. This handler correctly checks both suspended() and dialog.stack.length > 0.

The inconsistency between these two handlers causes the bug.

Technical Analysis

Options Considered:

  1. Add dialog.stack.length > 0 check (chosen solution)

    • Add the same guard used in CommandProvider to the init() handler
    • Minimal change (1 line), follows existing pattern in the same file
    • No architectural changes needed
  2. Use command.keybinds(false/true) in dialogs (similar to autocomplete)

    • Make dialogs suspend keybinds when opening and resume when closing
    • Would require DialogProvider to access CommandProvider, but DialogProvider is initialized before CommandProvider in the component tree, making this impractical without restructuring
  3. Reactive effect on dialog.stack.length

    • Use createEffect to observe dialog state and update suspendCount
    • More complex than option 1, achieves the same result indirectly

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.tsx handles the same scenario (line 254)
  • Minimal change with no risk of introducing regressions
  • No architectural changes required

Steps to reproduce

  1. Open the TUI (opencode)
  2. Open any dialog/picker (e.g., Ctrl+P for Command Palette or <leader>m for Model Selector)
  3. While the dialog is open, press Tab (agent cycle keybind)
  4. Close the dialog
  5. 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

Metadata

Metadata

Assignees

Labels

opentuiThis relates to changes in v1.0, now that opencode uses opentui

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions