---
name: Keyboard Shortcuts
slug: keyboard-shortcuts
category: Automation
description: Keyboard Shortcuts documents Sidecar’s keybinding registry, shortcut contexts, and complete assignment table. Use it when adding or changing shortcuts, checking conflicts, or comparing bindings with vim and other TUI tools.
github: "https://github.com/marcus/sidecar/tree/main/.claude/skills/keyboard-shortcuts"
language: Go
stars: 1049
forks: 79
install: "npx degit https://github.com/marcus/sidecar/tree/main/.claude/skills/keyboard-shortcuts ~/.claude/skills/keyboard-shortcuts"
installs_to: ~/.claude/skills/keyboard-shortcuts
source_path: .claude/skills/keyboard-shortcuts/SKILL.md
collection_size: 16
category_size: 1523
collection_url: "https://dirskills.com/collections/marcus/sidecar"
added: 2026-08-21T05:13:52.687Z
last_synced: 2026-08-21T05:13:52.687Z
canonical_url: "https://dirskills.com/skills/keyboard-shortcuts"
---

# Keyboard Shortcuts

Keyboard Shortcuts documents Sidecar’s keybinding registry, shortcut contexts, and complete assignment table. Use it when adding or changing shortcuts, checking conflicts, or comparing bindings with vim and other TUI tools.

**Install:**

```bash
npx degit https://github.com/marcus/sidecar/tree/main/.claude/skills/keyboard-shortcuts ~/.claude/skills/keyboard-shortcuts
```

## README

# Keyboard Shortcuts

Complete shortcut listings and context reference for all sidecar plugins. For implementation patterns, see `docs/guides/deprecated/ui-feature-guide.md`. For a detailed assessment of inconsistencies, vim alignment, mnemonic quality, and improvement proposals, see `references/assessment.md` in this skill directory.

## Architecture

- **Centralized binding registry**: `internal/keymap/bindings.go` is the single source of truth for key bindings.
- **Context-based dispatch**: Each plugin defines contexts; bindings are scoped to contexts.
- **Command palette** (`?`): Auto-discovers bindings for discoverability.
- **User overrides**: Supported via `~/.config/sidecar/config.json`.
- **Key sequences**: Compound commands like `g g` are supported with 500ms timeout.

### Adding a New Shortcut

1. Add the binding in `internal/keymap/bindings.go` under the appropriate context.
2. Add command handling in the plugin's `Update()` method (usually in a `handlers.go` file).
3. Add the command to the plugin's `Commands()` method for footer hint and command palette.
4. Keep command names short (1 word preferred) to prevent footer wrapping.

### TD Monitor Shortcuts

TD shortcuts are dynamically exported from TD itself via `ExportBindings()` and `ExportCommands()` in `pkg/monitor/keymap/`. TD is the single source of truth. To add TD shortcuts:

1. Add binding to TD's `pkg/monitor/keymap/bindings.go`
2. Add command constant to TD's `pkg/monitor/keymap/registry.go`
3. Add metadata to TD's `pkg/monitor/keymap/export.go`
4. Handle in TD's `pkg/monitor/model.go`

## Global Shortcuts

| Key | Command | Description |
|-----|---------|-------------|
| `j` / `down` | cursor-down | Move cursor down |
| `k` / `up` | cursor-up | Move cursor up |
| `G` | cursor-bottom | Jump to bottom |
| `g g` | cursor-top | Jump to top |
| `ctrl+d` | page-down | Page down |
| `ctrl+u` | page-up | Page up |
| `enter` | select | Select item |
| `esc` | back | Go back / close |
| `` ` `` | next-plugin | Next header entry |
| `~` | prev-plugin | Previous header entry |
| `]` | next-plugin | Next header entry |
| `[` | prev-plugin | Previous header entry |
| `1`-`7` | focus-plugin-N | Focus the Nth project tab (positional; stops at 7) |
| `8` | focus-sessions | Sessions (global) |
| `9` | focus-activity | Activity (global) |
| `0` | focus-tasks | Tasks (global; no-op when the Tasks host is disabled) |
| `?` | toggle-palette | Command palette |
| `!` | toggle-diagnostics | Diagnostics overlay |
| `@` | switch-project | Project switcher |
| `W` | switch-worktree | Worktree switcher |
| `#` | switch-theme | Theme switcher |
| `,` | open-configuration | Open Configuration (contexts that bind `,` win) |
| `i` | open-issue | Open issue |
| `r` | refresh | Refresh |
| `q` | quit | Quit (root contexts only) |
| `ctrl+c` | quit | Force quit |

### The header row is one ring

Sidecar's header is a single row of entries: the global ones in the left cluster
(Sessions, Activity, and Tasks when its feature is on) followed by the project's
plugin tabs on the right.

`[` / `]` (and their `~` / `` ` `` aliases) wrap through **all** of it, in that
order, and the ring is identical from either scope — the project tabs are
painted only in project scope, but they stay in the ring from the global space
so the cycle is never a trap and `]` then `[` is always the identity. Tasks is
absent from the ring whenever its feature is off.

The number row addresses the same row, but by two different rules:

- `1`-`7` are **positional** project tabs. They stop at 7. An eighth plugin tab
  is reached with `[` / `]` or from the command palette.
- `8` / `9` / `0` are **named** global entries — Sessions, Activity, Tasks — and
  mean the same thing in every scope. A key whose entry is disabled (`0` with
  the Tasks host off) does nothing at all, silently; it never falls through to a
  plugin tab.

All ten digits and the four cycling keys are in `keymap.GlobalKeys`, so no
plugin may claim them, and all of them yield to a focused text input.

## Configuration (`config` / `config-edit` / `config-confirm` contexts)

Opened with `,` or by clicking the header gear, always on Sidecar Setup. Like the Overview,
Configuration covers the plugin pane and owns keyboard focus: unhandled keys are swallowed
rather than leaking to the hidden plugin. `?` still opens the command palette.

| Key | Command | Context | Action |
|-----|---------|---------|--------|
| `j` / `k` / `up` / `down` | cursor-down / cursor-up | config | Move through sidebar destinations |
| `enter` | select | config | Open the selected destination |
| `/` | search | config | Focus Search (enters `config-edit`) |
| `tab` | focus-search | config | Move focus between sidebar and Search |
| `esc` | close-configuration | config | Return from a child route, else close and restore the prior surface |
| `down` | first-result | config-edit | Move from Search to the first visible result |
| `up` | focus-search | config-edit | Return to Search from the first result |
| `esc` | clear-search | config-edit | Clear the query and restore the full sidebar |
| `enter` / `y` | confirm | config-confirm | Confirm a consequential change |
| `esc` / `n` | cancel | config-confirm | Cancel it |

## Agent Overview (`overview` context)

Opened with `K` or by clicking the Sidecar logo. The Overview covers the plugin pane and
owns keyboard focus: a plugin left in interactive/text-input mode underneath it (embedded
shell, inline editor) does not receive keys while it is open, and unhandled keys are
swallowed rather than leaking to the hidden plugin.

| Key | Action |
|-----|--------|
| `h` / `l` / `left` / `right` | Move between lanes |
| `j` / `k` / `up` / `down` | Move within a lane |
| `enter` | Open the selected workspace (switches project) |
| `r` | Refresh the board |
| `esc` / `K` | Close the Overview |
| `q` | Quit Sidecar (confirmation modal) |

Global shortcuts stay live while it is open: `` ` ``/`~`, `[`/`]`, `1-9`, `@`, `#`, `W`, `?`, `!`,
`^`, `i`, `ctrl+c`, `q`. Plugin-switching keys (`` ` ``, `~`, `1-9`) close the Overview first.
`esc` on the Agents board or Workspaces list leaves the global space. `q` opens Sidecar's quit modal.

## Global Workspaces

Contexts: `global-workspaces` (list, root), `global-workspaces-filter`, `global-workspaces-rename`, `global-workspaces-create`, `global-workspaces-delete`, `global-workspaces-terminal` (typing), `global-workspaces-doc`, `global-workspaces-doc-search`, `global-workspaces-doc-find`, `global-workspaces-issue`, `global-workspaces-diff`.

There is no watched-preview focus: hiding the sidebar is layout only. `l` / `→` do not move focus to the preview. Clicking a file or td id focuses a content leaf with its own context; footer, help, and the palette follow `WorkspaceFocusContext()`.

| Key | Action |
|-----|--------|
| `j` / `k` / arrows / `g` / `G` | Move selection; preview follows; not typing |
| `enter` / `E` | Start typing in the selected live pane. A dead row stays put |
| click in pane | Start typing. Clicking Diff/Task action chips opens a leaf and does not type |
| click a file tab | Select that file in the document preview. `{` / `}` also cycle when the document is focused |
| click an issue tab | Select that issue in the issue preview. `{` / `}` also cycle when the issue is focused |
| click a list row | Select it; preview follows; not typing |
| click another row while typing | Switch session and stay typing |
| double-click a row | Open that identity in its owning project |
| wheel on terminal | Scroll only; do not activate |
| `ctrl+\` / `esc esc` | Stop typing and land on the list |
| `i` | Find TD task (`open-issue`). Not interactive |
| `n` | Open Create Workspace (Worktree) |
| `ctrl+n` | Open Create Workspace with Shell selected (modal) |
| `D` | Delete the selected shell (shown only for shell rows) |
| `m` | Open the owning project's established merge strategy workflow for a safe worktree |
| `/` | Filter |
| `v` / `s` | Open View: sort the list. `v` matches the project sidebar; `s` is the original alias |
| `\` | Toggle sidebar |
| `esc` | Leave the global space (or clear the filter first) |
| `q` | Quit Sidecar (confirmation modal) |
| `K` | Toggle the global space |

`ctrl+]` attach stays project-only and is off unless `tmux_full_attach` is enabled. While typing, `i` and `q` go to the pane.

### Focused document (`global-workspaces-doc`)

Same keys as the project document pane: `q`/`esc` close, `x` close tab,
`{`/`}` cycle tabs, `m` toggle render, `Y` yank path — and the same three
searches, all rooted at the pane's own directory: `/` in-file search,
`ctrl+p` file finder, `f` project search. The finder and project search are
`internal/panesearch`; the in-file bar is `internal/docview`. Contexts while
one is up: `global-workspaces-doc-search` and `global-workspaces-doc-find`.

### Focused issue (`global-workspaces-issue`)

An unmodified click on a `td-…` link opens it beside the terminal. A
second click appends a tab; an already-open ID is focused. Click a
drawn tab to select it. The header is only the tab strip: ID plus
headline, truncated at the end so the ID stays visible. Footer hints
are Tab× Tab← Tab→. There is no close chip.

`enter` on a parent or subtask uses the same open-or-focus path (no
duplicate, no silent replace). Tabs stay in memory for the selected
row and are not written to disk.

| Key | Command | Description |
|-----|---------|-------------|
| `enter` | open-item | Open or focus the selected parent or subtask as a tab |
| `O` | open-in-td | Open the selected issue in td (same jump as the preview modal's `o`) |
| `x` | close-tab | Close the active tab. Last tab closes the pane and forgets the set |
| `{` / `}` | prev-tab / next-tab | Previous / next issue tab |
| `y` | yank-issue | Copy issue as markdown |
| `Y` | yank-issue-key | Copy issue ID |
| `q` / `esc` | close | Close the pane and forget this row's in-memory tabs |

### Project issue pane (`workspace-issue`)

Same open/append/click/cycle/close journey and the same `{` / `}` / `x`
/ yank / enter keys. `tab` / `shift+tab` cycle panes; `\` toggles the
sidebar. `q` / `esc` hide the pane and retain tabs for that surface;
last `x` forgets. Switching shells or relaunching restores tabs, the
active tab, and each tab's scroll.

| Key | Command | Description |
|-----|---------|-------------|
| `enter` | open-item | Open or focus the selected parent or subtask as a tab |
| `O` | open-in-td | Open the selected issue in td (same jump as the preview modal's `o`) |
| `x` | close-tab | Close the active tab. Last tab forgets the pane |
| `{` / `}` | prev-tab / next-tab | Previous / next issue tab |
| `y` | yank-issue | Copy issue as markdown |
| `Y` | yank-issue-key | Copy issue ID |
| `tab` / `shift+tab` | next-pane / prev-pane | Move focus between sidebar, terminal, document, and issue |
| `\` | toggle-sidebar | Toggle sidebar visibility |
| `q` / `esc` | close | Hide the pane. Tabs stay remembered for this surface |

## Sidebar Controls (All Two-Pane Plugins)

| Key | Action |
|-----|--------|
| `Tab` / `Shift+Tab` | Switch focus between panes |
| `\` | Toggle sidebar visibility |
| `h` / `left` | Focus left pane |
| `l` / `right` | Focus right pane |
| `+` | Grow sidebar width |
| `-` | Shrink sidebar width |

## Git Status Plugin

### Contexts

| Context | View |
|---------|------|
| `git-status` | File list (root) |
| `git-status-commits` | Recent commits sidebar (root) |
| `git-status-diff` | Inline diff pane (root) |
| `git-commit-preview` | Commit detail in right pane |
| `git-diff` | Full-screen diff |
| `git-commit` | Commit editor |
| `git-push-menu` | Push strategy selection |
| `git-pull-menu` | Pull strategy selection |
| `git-pull-conflict` | Conflict resolution |
| `git-history` | Commit history |
| `git-commit-detail` | Single commit view |

### File List Shortcuts

| Key | Command | Description |
|-----|---------|-------------|
| `s` | stage-file | Stage selected file |
| `u` | unstage-file | Unstage selected file |
| `S` | stage-all | Stage all modified |
| `U` | unstage-all | Unstage all |
| `c` | commit | Open commit editor |
| `A` | amend | Amend last commit |
| `d` / `enter` | show-diff | View file changes |
| `D` | discard-changes | Discard unstaged changes |
| `h` | show-history | Open commit history |
| `P` | push | Open push menu |
| `L` | pull | Open pull menu |
| `f` | fetch | Fetch from remote |
| `b` | branch | Branch operations |
| `z` | stash | Stash changes |
| `Z` | stash-pop | Pop stash |
| `ctrl+z` | stash-apply | Stash apply |
| `o` | open-in-github | Open in GitHub |
| `O` | open-in-file-browser | Open in file browser |
| `y` | yank-file | Copy file info |
| `Y` | yank-path | Copy file path |

### Inline Diff Pane (`git-status-diff`)

The right-hand pane of the Git tab, focused with `enter` / `l` from the file
list.

| Key | Command | Description |
|-----|---------|-------------|
| `j` / `k` | scroll-down / scroll-up | Scroll the diff |
| `ctrl+d` / `ctrl+u` | page-down / page-up | Scroll half a page |
| `g` / `G` | — | Jump to start (also resets the horizontal axis) / end |
| `h` / `l` | — | Scroll horizontally; `h` at column 0 returns to the sidebar |
| `\|` | reset-hscroll | Snap the horizontal scroll back to column 0 |
| `enter` | full-diff | Open the full-screen diff |
| `s` / `u` | stage-file / unstage-file | Stage / unstage the file |
| `v` | toggle-diff-view | Cycle unified → split → full-file |
| `w` | toggle-wrap | Toggle line wrap |
| `\` | toggle-sidebar | Toggle the sidebar |
| `+` / `-` | resize-pane-grow / resize-pane-shrink | Resize the split |

`|` is vim's goto-column key. It reads as the odd choice next to vim's `0`, and
`0` is what this pane used to bind — but the whole number row belongs to the
header (see "The header row is one ring"), so `0` never reaches the plugin. It
was a live handler that had quietly stopped being reachable; `|` is the
replacement, and unlike `0` it is registered, so it appears in the footer and
in `?`.

### Full-Screen Diff (`git-diff`)

| Key | Command | Description |
|-----|---------|-------------|
| `,` / `.` | prev-file / next-file | Previous / next changed file |
| `s` / `u` | stage-file / unstage-file | Stage / unstage the file on screen |
| `v` | toggle-diff-view | Cycle the diff view mode |
| `w` | toggle-wrap | Toggle line wrap |
| `y` | yank-diff | Copy the diff |
| `c` | commit | Open the commit editor |
| `q` / `esc` | close-diff | Leave the diff |

This view has no tabs, so `{` / `}` are deliberately unbound here rather than
made to mean "next file" — that would be the one place in Sidecar where a brace
did something other than cycle tabs, and a silent wrong action is worse than a
no-op. File stepping is `,` / `.`, the same as in the Workspaces Diff pane.

### Commit List Shortcuts

| Key | Command | Description |
|-----|---------|-------------|
| `enter` / `d` | view-commit | Open commit details |
| `h` | show-history | Open history view |
| `y` | yank-commit | Copy commit as markdown |
| `Y` | yank-id | Copy commit hash |
| `/` | search-history | Search commit messages |
| `f` | filter-author | Filter by author |
| `p` | filter-path | Filter by path |
| `F` | clear-filter | Clear filters |
| `n` | next-match | Next search match |
| `N` | prev-match | Previous match |
| `o` | open-in-github | Open commit in GitHub |
| `v` | toggle-graph | Toggle commit graph |

### Pull Menu

| Key | Command |
|-----|---------|
| `p` | pull-merge |
| `r` | pull-rebase |
| `f` | pull-ff-only |
| `a` | pull-autostash |

## File Browser Plugin

### Contexts

| Context | View |
|---------|------|
| `file-browser-tree` | Tree view (root) |
| `file-browser-preview` | Preview pane |
| `file-browser-search` | Filename search |
| `file-browser-content-search` | Content search |
| `file-browser-quick-open` | Fuzzy file finder |
| `file-browser-project-search` | Ripgrep search modal |
| `file-browser-file-op` | File operation input |
| `file-browser-inline-edit` | Inline vim editor (all keys forwarded, global shortcuts bypassed) |

### Tree Shortcuts

| Key | Command | Description |
|-----|---------|-------------|
| `/` | search | Filter files by name |
| `ctrl+p` | quick-open | Find — a file by name (same key, same name as a workspace file pane) |
| `f` | project-search | Search — the project's contents (ripgrep) |
| `/` (preview) | search-content | InFile — this file's contents |
| `a` | create-file | Create new file |
| `A` | create-dir | Create new directory |
| `d` | delete | Delete (with confirmation) |
| `t` | new-tab | Open in new tab |
| `{` | prev-tab | Previous tab |
| `}` | next-tab | Next tab |
| `x` | close-tab | Close active tab |
| `y` | yank | Copy to clipboard |
| `p` | paste | Paste from clipboard |
| `s` | sort | Cycle sort mode |
| `m` | move | Move file/directory |
| `R` | rename | Rename |
| `ctrl+r` | reveal | Reveal in file manager |

## Conversations Plugin

### Contexts

| Context | View |
|---------|------|
| `conversations` | Session list single-pane (root) |
| `conversations-sidebar` | Session list two-pane (root) |
| `conversations-main` | Messages pane |
| `conversations-search` | Search mode |
| `conversations-filter` | Adapter filter |
| `conversation-detail` | Turn list |
| `message-detail` | Single turn content |
| `analytics` | Usage stats |

## Workspaces Plugin

### Contexts

| Context | View |
|---------|------|
| `workspace-list` | Workspace list (root) |
| `workspace-preview` | Preview pane |
| `workspace-doc` | File tabs beside the terminal (hide with `q`) |
| `workspace-doc-search` | A pane's file finder / project search (owns the keyboard) |
| `workspace-doc-find` | A pane's in-file search bar (owns the keyboard) |
| `workspace-doc-edit` | A pane's inline editor (owns every key, ctrl+c included) |
| `workspace-issue` | Issue tabs beside the terminal (hide with `q`; last `x` forgets) |
| `workspace-diff` | Diff tabs beside the terminal (hide with `q`; last `x` forgets) |
| `workspace-create` | Create Workspace form |
| `workspace-task-link` | Task selection modal |
| `workspace-merge` | Merge workflow modal |
| `workspace-interactive` | Embedded terminal |

### List Shortcuts

| Key | Command | Description |
|-----|---------|-------------|
| `n` | new-workspace | Open Create Workspace (Worktree) |
| `ctrl+n` | new-shell | Create a new shell immediately (shadows the global `ctrl+n` cursor-down in this context) |
| `v` | open-view | Open View: sort the list (Manual, Activity, Recent, Name) |
| `V` | toggle-view | Toggle list/kanban |
| `D` | delete-workspace | Delete workspace / delete shell (confirm) |
| `d` | show-diff | Open working-tree Diff leaf |
| `p` | push | Push branch |
| `m` | merge-workflow | Start merge workflow |
| `T` | link-task | Link/unlink task |
| `s` | start-agent | Start agent |
| `enter` / `E` | interactive | Enter interactive mode |
| `i` | open-issue | Find TD task (global; not interactive) |
| `t` | attach | Full tmux attach (`tmux_full_attach`, default off) |
| `S` | stop-agent | Stop agent |
| `F` | find-file | Open a file pane on the fuzzy file finder |
| `P` | fetch-pr | Fetch a remote PR as a workspace |


### Preview Shortcuts

`g` / `G` jump to the top / bottom of the preview's scrollback. `0` is
deliberately **not** bound here: it is the header's global Tasks shortcut, and a
context-local binding would make the same key mean two different things one tab
apart. (It previously carried a `reset-scroll` command that had no handler
anywhere in the tree.)

### Interactive Mode

| Key | Command |
|-----|---------|
| `ctrl+\` | exit |
| `ctrl+]` | attach (`tmux_full_attach`, default off) |
| `ctrl+t` / `alt+t` | toggle / switch terminal panel (`workspace_terminal_panel`, default off) |
| `alt+c` | copy |
| `super+c` | copy (Cmd+C, when the emulator passes it through) |
| `alt+v` | paste |

### Document Pane

An unmodified click on a resolvable file path in workspace or shell terminal
