This document describes how keyboard input is mapped to commands in TaskWarrior GPUI.
src/keymap/command.rs:Commandenum lists every action the UI can handle.src/keymap/context.rs:ContextIddefines the active key contexts (Global, Table, TableHeaders, SidebarProjects, SidebarTags, Modal, FilterBar, TextInput).src/keymap/chord.rs:Key,Mods,KeyChordnormalize keys.KeyChord::from_gpuibuilds chords fromgpui::KeyDownEvent,KeyChord::parseparses strings likeCtrl+F, andDisplayformats chords as strings.src/keymap/keymap.rs:KeymapLayerstoresContextId -> (KeyChord -> Command)bindings.KeymapStackresolves by checking the top-most layer first and falling back toGlobalif nothing matches.src/keymap/defaults.rs:build_default_keymapdefines all default bindings and is the only layer pushed today.src/keymap/active_context.rs:FocusTargetmaps UI focus toContextId.src/keymap/dispatcher.rs:CommandDispatchertrait abstracts command handling.
- The root UI in
src/view/app_layout.rsregisterson_key_downon the app container. App::handle_key_downinsrc/app.rsreceives the event and:- Converts it to
KeyChordviaKeyChord::from_gpui. - Computes the active
ContextIdviaApp::active_context:- If the task detail modal is open, the context is
Modal. - If focus is on the table and the filter bar is active, the context becomes
TextInputorFilterBarbased onTaskTable::get_active_filter_context. - Otherwise it uses
FocusTarget::to_context.
- If the task detail modal is open, the context is
- Resolves the command with
KeymapStack::resolve, which checks the latest layer first and falls back toGlobal.
- Converts it to
- If the modal is open,
App::handle_key_downonly allowsCloseModal,SaveModal,Sync, and modal scroll commands; all other commands are ignored. - Some commands are handled inline in
App::handle_key_down(focus transitions around the table, filter bar, and search input). - Everything else is routed through
CommandDispatcher:Appimplements it insrc/dispatcher.rsand forwards commands toTaskTableorSidebarbased on focus.TaskTableandSidebarimplementCommandDispatcherto apply selection, filter, and navigation changes.
The same keymap can yield different commands depending on the active context. The context is computed from focus and UI state:
FocusTarget(insrc/keymap/active_context.rs) represents which major area owns focus (table, headers, sidebars).App::active_context(insrc/app.rs) converts that focus into aContextIdand overrides it when:- The modal is open (
ContextId::Modal). - The filter bar is active (
ContextId::TextInputorContextId::FilterBarbased onTaskTable::get_active_filter_context).
- The modal is open (
KeymapStack::resolveuses thatContextIdto find a command, so the same key (likej) can mean "select next row" in the table context or "scroll down" in the modal context.
App::handle_key_downis the gatekeeper: it resolves the command, enforces modal-only commands when the modal is open, and handles focus-related commands itself.- Commands that mutate app-level state (focus switching, opening the modal) are handled directly in
App::handle_key_downor helper methods onApp(open_task_detail,open_selected_task). - All other commands go through
App'sCommandDispatcherimplementation insrc/dispatcher.rs, which decides whether to route the command toTaskTable,Sidebar, or perform app-level actions (sync, close modal, scroll modal, filter state changes).
- UI emits
KeyDownEventon the root container. App::handle_key_downconverts it toKeyChord, resolvesContextId, and maps to aCommand.Appeither:- Handles the command directly (focus changes, modal open/close), or
- Dispatches it to
TaskTable/Sidebar, which update their local state and callcx.notify().
- Add a new
Commandvariant insrc/keymap/command.rs. - Bind a key in
src/keymap/defaults.rs(or create a newKeymapLayerand push it onKeymapStackinApp::run). - Handle the command in
App::handle_key_down(for focus/app-level behavior) or insrc/dispatcher.rsand the appropriate component dispatcher (TaskTableorSidebar). - Update
docs/keyboard-shortcuts.mdto reflect the new binding.
- The search input (
src/components/input/mod.rs) handles text editing directly and updates filters on change; it early-returns onCtrl+H/Ctrl+Lso focus-navigation shortcuts still reach the app-level keymap. src/keymap/mod.rsstill contains legacyKeyBinding/TableAction/GlobalActiontypes for compatibility; they are not used in the current flow.