-
Notifications
You must be signed in to change notification settings - Fork 1
Keybindings
This page covers how key handling actually works, not just which config keys exist — for the config syntax alone, see Config Reference.
keybinds.py's resolve_key() turns a string from your config into a curses key code, once, at startup — it's pure (no live screen needed), so you can check what a name resolves to without running tuicc at all:
>>> import sys; sys.path.insert(0, "src")
>>> from tuicc.keybinds import resolve_key
>>> resolve_key("Left")
258
>>> resolve_key("h")
104
>>> resolve_key("nonsense")
Traceback (most recent call last):
...
ValueError: Unknown key name: 'nonsense'. Expected one of [...], or a single character.Two input shapes are accepted: a named special (Left, Right, Up, Down, Tab, Shift+Tab, Enter, Escape, Space) or any single character, resolved via Python's ord(). Uppercase letters work as an implicit Shift for free — curses reports "A" and "a" as different codes on their own, so resolve_key("A") just works without any special-casing.
left, right, up, down, tab, switch_module, confirm, and quit are all read from [navigation.keys] in your config — see Config Reference for the full table.
The confirm dialog's y/n keys are still hardcoded (ord("y"), ord("n")) in main.py's pending_confirm handling, separate from the [navigation.keys] table entirely — this is the one remaining gap.
If you want vim-style navigation, you can remap every key covered above today; the confirm-dialog's y/n keys will need to stay as-is until that gap closes.
This is worth understanding if you're debugging unexpected navigation behavior, or writing a module that needs to interact with it.
Tab cycles through items within the currently active module — ordered (the full navigable item list) is filtered down to just the items whose id prefix matches ctx.active_module, then advances to the next one with wraparound.
switch_module (Shift+Tab by default) switches which module is "active" — cycles through the module names present in your current layout preset, and jumps selection to the first item in the newly-active module.
Arrow keys behave differently depending on what's currently selected:
- If the selected item is a
"window"(in the preview) and the direction is left/right, tuicc first tries_sibling_in_same_group()— the predictable left-to-right order within the preview, not spatial search. See Architecture: Spatial vs. linear navigation for why. - If that finds nothing (you're at the leftmost window) and the direction is left, it falls back to a deterministic rule: return to the sidebar entry for the workspace actually being viewed.
- Otherwise, it falls back to
nearest_in_direction()— genuine spatial search, used for moving between well-separated modules like sidebar ↔ preview.
Whichever of these actually moves the selection also updates ctx.active_module to match — so the highlighted module border (border_selected vs border in your theme) always reflects where the selection actually is, even after an arrow-key move that crosses module boundaries.
Confirm looks up a handler for the selected item's target_kind in ACTION_HANDLERS (built from actions.py's BASE_HANDLERS plus whatever modules register — see Writing a Module) and calls it. The handler decides whether tuicc exits immediately or shows a confirmation prompt first.
You don't need a full session to check that a config change resolves the way you expect:
>>> import sys; sys.path.insert(0, "src")
>>> from tuicc.config import load_config
>>> cfg = load_config()
>>> cfg.keybinds
{'left': 260, 'right': 261, 'up': 259, 'down': 258, 'tab': 9, 'confirm': 10, 'quit': 113}If a value here doesn't match what you put in config.toml, check for the usual culprit first: ~/.config/tuicc/config.toml might be out of date with a newer packaged default (delete it and let tuicc regenerate it) — see the note at the top of Config Reference.