Skip to content

Keybindings

Mirror of spec §5.2. [keybindings] entries in <config-dir>/config.toml are validated by KeymapStore and installed at runtime through BindingResolver. Only action IDs with registered handlers receive live Textual bindings.

The cross-platform defaults avoid F-keys and terminal-specific command modifiers. They are letter-driven, with the command palette (: or Ctrl+K) as the universal escape hatch.

Wiring status: rows below tagged (deferred) are declared in KeymapStore.DEFAULT_BINDINGS but the matching action_* handler has not yet been added to AwsTuiApp. They remain valid action IDs, but BindingResolver leaves them unbound until the matching handler ships.

1. Default bindings

1.1. Navigation

Action Default Notes
Cursor up / down ↑ ↓ or k j vi-style alternatives are first-class
Descend into directory / bucket Enter
Ascend one level Backspace or left
Switch pane focus Tab / Shift+Tab

1.2. Selection

Action Default Notes
Enter multi-select mode pane.enter_multiselect action — (deferred) Spec'd on v; handler not wired in v0.8.x
Toggle row selection pane.toggle_select action — (deferred) Spec'd on Space (in multi-select); not wired
Extend selection one row Shift+↑ / Shift+↓ Marks the row the cursor is leaving + moves cursor
Modifier+click on row Shift+Click, Cmd+Click, Ctrl+Click Toggles mark on the clicked row; on macOS terminals reserve Shift+Click, so Cmd+Click is the reliable path there
Select all pane.select_all action — (deferred) Spec'd on a (in multi-select); not wired
Clear selection no shipped command — (deferred) Persistent marks can be toggled individually; a keyboard clear-all action is not registered.

1.3. File operations

Action Default Notes
Copy across panes c Streams through CrossFsCopy, shows confirm modal
Move across panes pane.move action — (deferred) The move handler is not yet wired in AwsTuiApp; the always-visible navigation rail does not claim m, so the key remains available when move wiring lands.
Delete (with confirm) d Confirm modal; destructive ops always ask
New folder pane.new action — (deferred) No handler wired in v0.8.x
Rename in place pane.move action — (deferred) Bundled into the move handler; not wired
Refresh pane r
Copy cursor entry path p Copies the full path of the entry under the cursor. Also listed in the command palette as Copy cursor entry path.
Copy pane path P (shift+p) Copies the pane's current directory path; the pane's top border is also a click target. Also listed in the command palette as Copy pane path.

1.4. Overlays

Action Default Notes
Quick Look Space Streams the first 64 KB of the selected file
Fuzzy find pane.fuzzy_find action — (deferred) Spec'd on Ctrl+P; not wired
Filter pane pane.filter action — (deferred) Spec'd on /; not wired
Command palette : / Ctrl+K Opens the fuzzy app-command palette
Theme picker (modal) t
Cycle to next theme (no modal) Shift+T (T)
Help overlay ?

1.5. Pane chrome

Action Default Notes
Open Settings , (comma) Opens the in-app Settings nav page directly. Equivalent to arrow-keying down to the Settings row in the rail and pressing Enter.
Switch source Shift+S (S) On S3, cycles the focused pane through local and resolver-ordered configured sources. On single-context AWS services such as EMR, Glue, and Athena, rebuilds the current service under the next supported AWS profile.

Nav-menu visibility: the left rail is always visible at a single fixed width and shows text labels (Settings is docked at the bottom with its settings icon). The former m-key collapse/expand toggle was dropped because there is no longer a collapsed mode to toggle into; BindingResolver does not emit m because the deferred pane.move action has no registered handler (§1.3).

1.6. Connection and Authentication

Action Default Notes
Authenticate (when auth toast active) auth.authenticate action — (deferred) Spec'd on a; handler not wired in v0.8.x
Connection switcher no shipped command — (deferred) Dynamic connection switch <name> palette entries are not registered.

The command palette opens today with : or Ctrl+K; only the dynamic connection-switch entries in the row above remain deferred.

1.7. App

Action Default Notes
Cancel / dismiss modal Esc Modal-owned; works on every modal that ships in v0.8.x
Quit q or Ctrl+C

1.8. EMR Serverless

These actions are wired by EmrServerlessPage. The EMR page is mounted in place of the S3 dual-pane when the EMR navigation row is selected. Bindings resolve at App level and short-circuit through _emr_active_pane() before the dual-pane guard fires. Non-printable navigation keys use App priority where appropriate; printable action keys remain non-priority so a focused editable widget can consume text first.

Action Default Notes
Open application picker a Opens the applications dropdown above the LEFT pane. The picker overlays the current layout without resizing the runs or detail panes. It is also reachable with Tab and opens with Enter or Space.
Select AWS source Tab, then Enter / Space The bordered source selector chooses an exact configured profile and region; Shift+S remains the resolver-order shortcut.
Cycle next application Shift+A Cycles to the next EMR application without opening the picker. Shift+S remains available to switch the EMR page to the next configured AWS profile.
State filter chips 1 2 3 4 5 Multi-select toggles, one chip per state in this key order: SUCCESS / RUNNING / PENDING / FAILED / CANCELLED. Source of truth: _KEY_TO_STATE in ui/widgets/emr_serverless/job_runs_pane.py. The transient pre-terminal states SUBMITTED / SCHEDULED / QUEUED / CANCELLING are NOT chip-filterable — they always render (they're members of the initial all-on default filter set and have no toggle key).
Cursor up / down ↑ ↓ (also k / j) Moves the LEFT-pane row cursor; master-detail follows the cursor (the RIGHT pane re-loads on every cursor move, not only on Enter).
Select run (explicit) Enter Re-emits RunSelected for the cursor row.
Refresh r Forces an immediate poll on the active pane (apps if LEFT focused on the picker, runs if LEFT focused on the runs list, detail if RIGHT focused).
Clone selected job run c Opens the Spark clone form from the focused run. Edit arguments, Spark parameters and advanced settings as JSON, choose Review, inspect source identity and changes, then Submit. Enter in a single-line field opens review; Tab reaches the fixed footer. Unsupported drivers are refused. Escape closes the form; it does not cancel an AWS job already submitted.
Cycle pane focus Tab / Shift+Tab 6-slot cycle: nav rail → source selector → application selector → runs pane → detail pane → logs pane → nav rail.
Backspace Backspace No-op on EMR (symmetric to Descend having an EMR branch).
Load logs (on-demand) Enter Loads logs from S3 into the RIGHT-logs pane (first press in the logs slot after Tab-focusing). File chips preserve exact retry attempt and Spark executor or Hive/Tez worker identity, including rotated stdout/stderr objects.
Reload logs r Re-fetches logs from S3 even on cache hit.
Open log filter modal f Edit regex patterns, toggle "Show all" or "Match case"; Apply re-fetches.
Reset log filter Shift+F Clears the logs filter and returns to the default log view.
Scroll log lines up / down ↑ ↓ (also k / j) Navigate the loaded log line view (when RIGHT-logs pane is focused).

Right-pane refresh note: r refreshes the currently focused right-side surface: detail focus reloads the selected job-run detail; logs focus re-fetches logs from S3.

1.9. AWS Glue

Glue is a single-context AWS service. It keeps one active connection and region for the whole page; S3-compatible connections are excluded. The bordered AWS source selector stands on its own. Jobs and Crawlers add an adjacent bordered state selector without an enclosing context frame. The untitled segmented view frame is one Tab stop; its active view keeps accent text after focus moves into content and gains a soft fill while the frame is focused. Focus a selector and press Enter or Space to open it; use the arrow keys and Enter to commit a value, or Esc to close it without changing the value. Every selector dropdown overlays the current layout; opening or closing it leaves the context row, view tabs, and adjacent content panes at the same size.

Action Default Notes
Catalog / Jobs / Crawlers 1 / 2 / 3 Selects the corresponding Glue view.
Choose job-run state Shift+F (F) Runs glue.choose_run_state, focuses and opens the bordered Run state selector in Jobs.
Choose crawler state Shift+G (G) Runs glue.choose_crawler_state, focuses and opens the bordered Crawler state selector in Crawlers.
Cursor up / down ↑ ↓ (also k / j) Moves the focused resource list or scrolls detail.
Cycle focus Tab / Shift+Tab Walks the complete deterministic Glue ring described below; reverse traversal is the exact inverse.
Refresh active view r Reloads only the selected Catalog, Jobs, or Crawlers view.
Load more rows in the focused list l, or : / Ctrl+K, then Load more Glue rows Runs glue.load_more. Fetches the next page for the focused Glue list (databases, tables, partitions, jobs, runs, crawlers, or the focused Iceberg metadata tab); clicking a list footer that reads more available does the same. Disabled when the list has no further page or hit its 1,000-item safety limit. On the Iceberg Peek tab the same control instead reruns a genuinely new local DuckDB query at the next row-limit step (100 → 1,000 → 10,000); its ceiling is real, not a local-window widen, and its footer never reads more available.
Switch AWS source Shift+S Runs app.swap_source and rebuilds Glue under the next resolver-ordered supported AWS profile and region. The bordered Source selector can instead choose an exact source.
Copy selected table reference y Runs glue.copy_table_ref. The canonical, fully quoted identifier and its source identity are retained in the authoritative typed in-app clipboard; the OS clipboard write then goes through the single app-level writer, whose toast names the channel that actually accepted the text and never reports an unacknowledged OSC 52 write as a copy.
Open selected table location in S3 : / Ctrl+K, then Open table location in S3 glue.open_s3_location is palette-only and absent from KeymapStore.DEFAULT_BINDINGS. It preserves the exact Glue connection name and region; malformed or missing locations do not navigate.
Query selected table in Athena Shift+Q (Q) or : / Ctrl+K, then Query table in Athena Runs glue.query_in_athena for a visible selected Glue table. It opens Athena, resolves the exact catalog/database request context, and prefills the quoted SELECT * FROM "database"."table" LIMIT 5; it does not execute the query.
Query selected Iceberg snapshot in Athena Shift+V (V), : / Ctrl+K, then Query Iceberg snapshot in Athena, or the Iceberg time-travel button Runs glue.time_travel_in_athena only for a visible selected snapshot on the Snapshots tab. It opens Athena with the same exact source and bounded SQL plus FOR VERSION AS OF <snapshot-id> before LIMIT 5; it does not execute the query.

Glue's forward focus order is:

  • Catalog: Source, view tabs, databases, tables, table detail, then every visible and enabled Iceberg tab/control, and the navigation rail. The seventh Iceberg tab, Peek, previews table rows through a local DuckDB engine; it needs the optional duckdb extra and an AWS profile connection, so it is simply absent without an AWS profile connection, but stays present and selectable — never disabled — when the extra is missing: choosing it is how the install prompt is reached, the same Iceberg-only visibility gating as the six metadata tabs it follows.
  • Jobs: Source, Run state, view tabs, jobs, runs, job detail, and the navigation rail.
  • Crawlers: Source, Crawler state, view tabs, crawlers, crawler detail, and the navigation rail.

Disabled Iceberg load-more/retry/time-travel controls are omitted. Shift+Tab walks the same active ring in reverse.

1.10. Amazon Athena

Athena is a single-context AWS service; its controls do not appear for S3-compatible connections. Source, Workgroup, Catalog, and Database are four individually framed selectors in one unframed row. Focus one and press Enter or Space to open it. Each option list overlays the current layout; opening or closing a picker does not resize the selector row, view tabs, or query surface.

The untitled segmented view frame uses the same persistent active accent text, focused soft fill, and single keyboard focus stop as Glue.

Action Default Notes
Query / History / Results / Saved 1 / 2 / 3 / 4 Selects the matching Athena view.
Choose workgroup / catalog / database Shift+W / Shift+C / Shift+D Runs athena.choose_workgroup, athena.choose_catalog, or athena.choose_database and opens the corresponding selector.
Execute editor SQL Ctrl+Enter Runs athena.execute only after the local read-only SQL validation succeeds.
Stop query Esc Runs athena.cancel; Stop interrupts query submission or stops the active app-owned execution.
Load more result rows l Runs athena.load_more when the selected result has another page.
Switch AWS source Shift+S Runs app.swap_source and rebuilds Athena under the next resolver-ordered supported AWS profile and region. The bordered Source selector can instead choose an exact source.
Insert copied table reference i Runs athena.insert_table_ref, selecting Query when needed and inserting at the editor cursor or replacing its active selection. It refuses a copied connection/region that differs from Athena's active source and leaves both editor and typed clipboard unchanged; it never switches profiles because that could discard unrelated editor state.
Open result artifact in S3 : / Ctrl+K, then Open Athena result in S3 athena.open_result_location is palette-only and absent from KeymapStore.DEFAULT_BINDINGS; it validates the successful execution's exact connection, region, and S3 URI before navigating.
Open query table in Glue : / Ctrl+K, then Open query table in Glue athena.open_in_glue is palette-only and works only when the current read-only SQL resolves to one visible table.

Athena's forward focus order is Source, Workgroup, its enabled load-more button, Catalog, its enabled load-more button, Database, its enabled load-more button, view tabs, the active view's controls, and the navigation rail. The Query view lays out Query controls above the Query editor, then Execution detail. The active-view controls are:

  • Query: editor, enabled Execute, enabled Cancel, query detail.
  • History: history list, enabled Load more, enabled Results, history detail.
  • Results: results table, enabled Load more.
  • Saved: named queries, enabled named-query Load more, prepared statements, enabled prepared-statement Load more, saved-query detail, enabled Open in editor.

Unavailable buttons are omitted, and Shift+Tab is the exact reverse order. Bare printable bindings, including i, W, C, and D, are deliberately non-priority: a focused Athena editor receives them as text. Use the command palette or move focus outside the editor to invoke those actions.

2. Customizing

A binding can be a single keystroke or a list of fallback keystrokes:

[keybindings]
"pane.copy" = "c"
"pane.delete" = "d"
"app.command_palette" = ["ctrl+k", ":"]
"app.help" = "?"
"app.themes" = "t"
"app.cycle_theme" = "T"
"app.swap_source" = "S"
"emr.next_application" = "A"
"glue.catalog" = "1"
"glue.jobs" = "2"
"glue.crawlers" = "3"
"glue.choose_run_state" = "F"
"glue.choose_crawler_state" = "G"
"glue.copy_table_ref" = "y"
"glue.query_in_athena" = "Q"
"glue.time_travel_in_athena" = "V"
"athena.query" = "1"
"athena.history" = "2"
"athena.results" = "3"
"athena.saved" = "4"
"athena.choose_workgroup" = "W"
"athena.choose_catalog" = "C"
"athena.choose_database" = "D"
"athena.insert_table_ref" = "i"
"athena.execute" = "ctrl+enter"
"athena.cancel" = "escape"
"athena.load_more" = "l"

The default map is declared in infra/keymap_store.py. At composition time, aws-tui validates the overlay and BindingResolver installs keys only for registered actions. Unknown action IDs are logged and the app continues with the default keymap so a typo does not crash startup. For example, use "pane.copy" = "ctrl+y" to move pane copy without claiming bare y, which is reserved by glue.copy_table_ref.

The bindings that are wired today include q, Ctrl+C, Tab / Shift+Tab, ↑/↓ (and j/k), Enter, Backspace, left, →, r, ?, :, t, T, , (comma → Settings), c, d, S (Shift+S), A (Shift+A), Glue 1 / 2 / 3, Athena 1 / 2 / 3 / 4, F, G, y, Q, W, C, D, i, V, Ctrl+Enter, Esc, l, p, P (Shift+P), Space, f, Shift+↑, and Shift+↓.

3. Action IDs

The wired? column marks whether AwsTuiApp currently registers a matching ActionRegistry handler. (deferred) rows are valid action IDs you can overlay ahead of time; BindingResolver leaves them unbound until a handler ships.

Action ID Default key Wired? What it does
app.quit q / ctrl+c yes Graceful shutdown
app.open_settings , yes Open the Settings navigation page
app.command_palette : / ctrl+k yes Open the command palette
app.help ? yes Help overlay
app.themes t yes Open theme picker modal
app.cycle_theme T (shift+t) yes Cycle to next theme without opening the modal
app.swap_source S (shift+s) yes Switch the focused S3 pane source, or rebuild the current single-context AWS service under the next profile
pane.move_up / pane.move_down up / down (also k / j) yes Move cursor
pane.descend enter yes Descend into folder / bucket
pane.ascend backspace yes Parent path
pane.mark_up shift+up yes Extend the marked selection upward
pane.mark_down shift+down yes Extend the marked selection downward
pane.switch_focus tab yes Cycle the active page's focus ring
pane.switch_focus_back shift+tab yes Cycle the active page's focus ring in reverse
pane.quick_look space (normal mode) yes Stream first 64 KB
pane.filter / (deferred) Local pane filter
pane.fuzzy_find ctrl+p (deferred) Fuzzy find paths / buckets
pane.enter_multiselect v (deferred) Enter multi-select mode
pane.toggle_select space (multi-select) (deferred) Add / remove from selection
pane.select_all a (deferred) Select all in pane
pane.copy c yes Copy marked entries to other pane
pane.move m (deferred) Move marked entries (or rename one); the always-visible navigation rail leaves m available when the wiring lands.
pane.delete d yes Delete marked entries (confirms)
pane.new n (deferred) New folder / bucket
pane.refresh r yes Re-run provider.list()
pane.copy_entry_path p yes Copy the cursor entry's full path to the clipboard; keyed and palette-visible (Copy cursor entry path, file manager only)
pane.copy_path P (shift+p) yes Copy the pane's current path to the clipboard; keyed and palette-visible (Copy pane path, file manager only)
auth.authenticate a (when auth toast active) (deferred) Reserved for a future auth helper; currently run aws sso login --profile <name> yourself
emr.next_application A (shift+a) yes Cycle to the next EMR application
emr.clone c (when EMR page mounted) yes Open the EMR clone-job-run modal pre-filled from the focused run.
emr.logs.filter f (when EMR logs pane focused) yes Open the EMR logs filter modal
glue.catalog 1 yes Select the Glue Catalog view
glue.jobs 2 yes Select the Glue Jobs view
glue.crawlers 3 yes Select the Glue Crawlers view
glue.choose_run_state F (shift+f) yes Focus and open the Jobs run-state selector
glue.choose_crawler_state G (shift+g) yes Focus and open the Crawlers state selector
glue.copy_table_ref y yes Copy the selected table's canonical identifier and source identity
glue.open_s3_location none (command palette) yes Open the selected Glue table's S3 location under the exact source connection and region
glue.query_in_athena Q (shift+q) yes Open the selected Glue table in Athena and prefill exact quoted SELECT * ... LIMIT 5 SQL without executing it
glue.time_travel_in_athena V (shift+v) yes Open the selected visible Iceberg snapshot in Athena and prefill exact bounded FOR VERSION AS OF SQL without executing it
glue.load_more l yes Fetch the next page for the focused Glue list
athena.query 1 yes Select the Athena Query view
athena.history 2 yes Select the Athena History view
athena.results 3 yes Select the Athena Results view
athena.saved 4 yes Select the Athena Saved view
athena.choose_workgroup W (shift+w) yes Focus and open the Workgroup selector
athena.choose_catalog C (shift+c) yes Focus and open the Catalog selector
athena.choose_database D (shift+d) yes Focus and open the Database selector
athena.insert_table_ref i yes Insert the same-source typed table clipboard value into the query editor
athena.execute ctrl+enter yes Submit validated, read-only editor SQL
athena.cancel escape yes Stop interrupts query submission or stops an active app-owned Athena execution
athena.load_more l yes Fetch the next available result page
athena.open_result_location none (command palette) yes Open a validated successful Athena result artifact in S3 under its exact source identity
athena.open_in_glue none (command palette) yes Open the one unambiguous visible query table in Glue
pane.modal_left left yes Route left-arrow modal or pane navigation
pane.modal_right right yes Route right-arrow modal or pane navigation

Rows with a default key are registered by KeymapStore.DEFAULT_BINDINGS and may be overlaid in [keybindings]. glue.open_s3_location, athena.open_result_location, and athena.open_in_glue are palette-only, registered in ActionRegistry, and absent from KeymapStore.DEFAULT_BINDINGS; assigning them a key is not currently supported. Any unknown overlay id is logged and causes the app to fall back to the default keymap.

Modal and selector cancellation remains locally owned by each overlay and uses Escape; it is intentionally not a configurable App action.

All live App-level bindings are installed through BindingResolver, including Shift+↑ / Shift+↓ for extend-selection. Their pane.mark_up / pane.mark_down entries may be rebound through [keybindings].

Commands pane — the bottom legend has exactly one compact content row at wide and narrow supported widths. Service commands precede global commands, and each content-sized chip exposes a tooltip with the full shortcut, effect, execution or mutation behavior, and any unmet prerequisite. When all hints do not fit, lower-priority entries are removed deterministically instead of wrapping; [:] more and [q] quit remain visible, and [:] more opens the service-scoped command palette. Clicking an enabled visible chip dispatches the same registered action as its shortcut and command-palette entry; disabled chips remain inert. Chips do not enter the Tab sequence. Hidden actions remain bound and palette-visible.

4. Modal Forwarding for Enter Escape and Arrow Keys

Textual dispatches App-level priority=True bindings before modal screen bindings. Without that, pressing Enter inside the theme picker or confirm modal would fire the dual-pane descend action and never reach the modal's confirm handler.

AwsTuiApp works around this via _forward_to_modal(*action_names): when a modal is on top of the screen stack, action_descend / action_ascend / action_move_up / action_move_down first look for the corresponding handler on the active screen and forward there. The result: Enter confirms in any modal, Esc (or Backspace) cancels, and ↑/↓ navigate the picker even though the app reserves them for the dual-pane cursor.

5. Layer separation

Views route through action IDs and BindingResolver; service-neutral VM messages handle cross-service requests. Keep new keyed actions in KeymapStore and ActionRegistry together. Palette-only commands such as glue.open_s3_location belong in ActionRegistry and the curated palette, but do not need a default key. The same rule applies to athena.open_result_location. The Glue table and snapshot Athena handoffs are keyed, palette-visible actions; keyboard, palette, and button entry points all dispatch their registered action IDs before the VM publishes the typed request.