Why
v0.8.60/v0.8.61 is adding important composer behavior, especially queued steering and Ctrl+S for sending a queued message. That is useful, but the current keyboard story is becoming hard to discover and easy to mis-trigger:
Ctrl+S can now send a queued message, but the UI does not clearly teach that affordance.
- On macOS,
Ctrl+Enter can be intercepted by terminal/app/OS behavior and may bring up a separate command/surface instead of reliably steering/sending in CodeWhale.
- Users cannot easily inspect or customize what keybindings CodeWhale thinks are active for their terminal/platform.
- Composer mode, queued-steer state, normal send, interrupt/steer, and submit-next-queued-message need one coherent keyboard model instead of scattered shortcuts.
This should build on #3203 and #2054, but it is broader than the immediate Ctrl+S fix.
Related issues
Product direction
Treat keyboard behavior as a first-class configurable surface:
- CodeWhale should have an explicit keybinding registry, not one-off checks spread through TUI code.
- Default shortcuts should be platform-aware, especially for macOS terminal conflicts.
- The active composer state should expose the relevant send/queue/steer shortcut inline or via an inspectable help surface.
- Users should be able to remap or disable conflicting bindings in config.
Suggested implementation seams
Start by inventorying current key handling before changing defaults:
- Search TUI input handling for
KeyCode, KeyEvent, KeyModifiers, Enter, Char('s'), Ctrl, Cmd, Super, Meta, queued steer, composer submit, and interrupt/steer behavior.
- Identify whether shortcuts are handled in a central TUI app event loop or scattered across composer/sidebar/modal handlers.
- Build a typed
KeyBindingAction enum for high-level actions such as:
composer.submit
composer.submit_queued_steer
composer.queue_for_next_turn
composer.steer_running_turn
turn.cancel_or_interrupt
jobs.cancel_selected
help.show_keybindings
mode.cycle_reasoning
- Add a resolver that maps raw terminal events into
KeyBindingAction, including platform/terminal notes.
- Route existing handlers through the resolver instead of duplicating modifier logic.
Config shape to consider
Use the normal CodeWhale config path, not a side channel. Suggested shape:
[tui.keybindings]
"composer.submit" = ["enter"]
"composer.submit_queued_steer" = ["ctrl+s"]
"composer.steer_running_turn" = ["ctrl+enter", "cmd+enter"]
"turn.cancel_or_interrupt" = ["ctrl+c", "esc"]
"help.show_keybindings" = ["?"]
[tui.keybindings.platform.macos]
"composer.steer_running_turn" = ["cmd+enter"]
The exact names can change, but the high-level concept should be stable: actions are configurable; defaults are platform-aware; conflicts are visible.
UX requirements
- When a queued message exists, the composer should visibly indicate the active action, e.g.
Ctrl+S send queued steer or an equivalent compact hint.
- If
Ctrl+Enter is unavailable/intercepted on macOS, the default should not rely on it as the only path.
/help, /status, or a dedicated keybinding view should show active bindings and their resolved source: default, platform default, user config.
- If two actions share the same binding, CodeWhale should warn or deterministically resolve with an inspectable explanation.
- The UI should distinguish normal submit, queued submit, steering the running turn, and cancelling/interrupting.
- Avoid hidden shortcuts that only appear after users already know them.
Tests / acceptance criteria
- Unit tests cover raw key event ->
KeyBindingAction resolution for macOS, Linux, and Windows-style terminals.
- Tests prove
Ctrl+S sends queued steering only when a queued message exists or the current state expects it; it must not silently discard normal composer text.
- Tests prove
Ctrl+Enter conflict handling has a fallback path on macOS.
- Tests cover config override, config disable/unbind, duplicate binding detection, and platform-specific defaults.
- Snapshot or UI tests cover the composer hint when queued steering is available.
- Release smoke should include a keyboard-config sanity check so new shortcuts do not ship undocumented.
Why
v0.8.60/v0.8.61 is adding important composer behavior, especially queued steering and
Ctrl+Sfor sending a queued message. That is useful, but the current keyboard story is becoming hard to discover and easy to mis-trigger:Ctrl+Scan now send a queued message, but the UI does not clearly teach that affordance.Ctrl+Entercan be intercepted by terminal/app/OS behavior and may bring up a separate command/surface instead of reliably steering/sending in CodeWhale.This should build on #3203 and #2054, but it is broader than the immediate Ctrl+S fix.
Related issues
Product direction
Treat keyboard behavior as a first-class configurable surface:
Suggested implementation seams
Start by inventorying current key handling before changing defaults:
KeyCode,KeyEvent,KeyModifiers,Enter,Char('s'),Ctrl,Cmd,Super,Meta, queued steer, composer submit, and interrupt/steer behavior.KeyBindingActionenum for high-level actions such as:composer.submitcomposer.submit_queued_steercomposer.queue_for_next_turncomposer.steer_running_turnturn.cancel_or_interruptjobs.cancel_selectedhelp.show_keybindingsmode.cycle_reasoningKeyBindingAction, including platform/terminal notes.Config shape to consider
Use the normal CodeWhale config path, not a side channel. Suggested shape:
The exact names can change, but the high-level concept should be stable: actions are configurable; defaults are platform-aware; conflicts are visible.
UX requirements
Ctrl+S send queued steeror an equivalent compact hint.Ctrl+Enteris unavailable/intercepted on macOS, the default should not rely on it as the only path./help,/status, or a dedicated keybinding view should show active bindings and their resolved source: default, platform default, user config.Tests / acceptance criteria
KeyBindingActionresolution for macOS, Linux, and Windows-style terminals.Ctrl+Ssends queued steering only when a queued message exists or the current state expects it; it must not silently discard normal composer text.Ctrl+Enterconflict handling has a fallback path on macOS.