Keybindings¶
Roost uses platform-native modifiers: Cmd on macOS. On Linux, Alt is the app modifier (the role Cmd plays on macOS), leaving Ctrl to the shell — the only Ctrl defaults are Ctrl-1…Ctrl-9 (switch tab) and Ctrl-Shift-C / Ctrl-Shift-V (copy/paste). The same actions are available on both platforms — only the modifier differs.
Every binding is overridable in config.conf (see Custom keybindings) — e.g. keybind = ctrl+t = new_tab restores the pre-Alt Alt-T chord.
macOS¶
Tab management (active project)¶
| Shortcut | Action |
|---|---|
Cmd-T |
New tab (opens in the active tab's live cwd — see cwd tracking); on a project living on a host session, the new tab opens on that same host |
Cmd-W |
Close the active tab |
Cmd-R |
Rename the active tab |
Cmd-Shift-] |
Cycle to the next tab (stops at the last tab; no wrap-around) |
Cmd-Shift-[ |
Cycle to the previous tab (stops at the first tab; no wrap-around) |
Ctrl-1 … Ctrl-9 |
Switch to tab at position 1 .. 9 |
Project management¶
| Shortcut | Action |
|---|---|
Cmd-N |
Create a new project (untitled, untitled 2, …) — on a host session, creates on the currently selected project's host |
Cmd-Shift-N |
"New Project on…" — pick which host (or LOCAL) to create on; offered once at least one host is saved, and the picker lists the connected ones |
Cmd-Shift-R |
Rename the active project |
Cmd-Shift-W |
Close the active project (confirms when the project has 2+ tabs) |
Cmd-B |
Toggle the projects sidebar |
Cmd-Shift-A |
Toggle agent rows under each project in the sidebar |
Cmd-Shift-U |
Jump to the next tab with a pending notification (active project first, then others) |
Cmd-1 … Cmd-9 |
Switch to the project at sidebar position 1 .. 9 |
Commands¶
| Shortcut | Action |
|---|---|
Cmd-Shift-P |
Open the command palette (fuzzy-find + run any command) |
Cmd-Shift-T |
Open the command launcher (run a configured command = entry in a new tab) |
Cmd-Shift-E |
Open the custom palette (script-backed provider = menus — see Extending Roost) |
Cmd-Shift-O |
Open the agent palette (jump to a running agent, listed by urgency, each row naming the agent alongside status, elapsed time, and git metrics) |
Clipboard¶
| Shortcut | Action |
|---|---|
Cmd-C |
Copy the current terminal selection |
Cmd-V |
Paste the system clipboard into the active terminal |
Ctrl-Shift-C |
Same as Cmd-C (terminal-convention alternate) |
Ctrl-Shift-V |
Same as Cmd-V (terminal-convention alternate) |
Bare Ctrl-C is left as SIGINT — it's not overloaded for copy.
Font sizing¶
| Shortcut | Action |
|---|---|
Cmd-+ / Cmd-= |
Increase font size for the active tab |
Cmd-- |
Decrease font size for the active tab |
Cmd-0 |
Reset font size to the font_size from config |
Font size adjustments are per-tab and held in memory only. They do not persist across restarts, and new tabs always start at font_size from config.conf. The size is clamped to 6 .. 72 points; out-of-range steps saturate.
Linux¶
Tab management (active project)¶
| Shortcut | Action |
|---|---|
Alt-T |
New tab (opens in the active tab's live cwd — see cwd tracking); on a project living on a host session, the new tab opens on that same host |
Alt-W |
Close the active tab |
Alt-R |
Rename the active tab |
Alt-Shift-] |
Cycle to the next tab (stops at the last tab; no wrap-around) |
Alt-Shift-[ |
Cycle to the previous tab (stops at the first tab; no wrap-around) |
Ctrl-1 … Ctrl-9 |
Switch to tab at position 1 .. 9 |
Project management¶
| Shortcut | Action |
|---|---|
Alt-N |
Create a new project (untitled, untitled 2, …) — on a host session, creates on the currently selected project's host |
Alt-Shift-N |
"New Project on…" — pick which host (or LOCAL) to create on; offered once at least one host is saved, and the picker lists the connected ones |
Alt-Shift-R |
Rename the active project |
Alt-Shift-W |
Close the active project (confirms when the project has 2+ tabs) |
Alt-B |
Toggle the projects sidebar |
Alt-Shift-A |
Toggle agent rows under each project in the sidebar |
Alt-Shift-U |
Jump to the next tab with a pending notification (active project first, then others) |
Alt-1 … Alt-9 |
Switch to the project at sidebar position 1 .. 9 |
Commands¶
| Shortcut | Action |
|---|---|
Alt-Shift-P |
Open the command palette (fuzzy-find + run any command) |
Alt-Shift-T |
Open the command launcher (run a configured command = entry in a new tab) |
Alt-Shift-E |
Open the custom palette (script-backed provider = menus — see Extending Roost) |
Alt-Shift-O |
Open the agent palette (jump to a running agent, listed by urgency, each row naming the agent alongside status, elapsed time, and git metrics) |
Clipboard¶
| Shortcut | Action |
|---|---|
Alt-C |
Copy the current terminal selection |
Alt-V |
Paste the system clipboard into the active terminal |
Ctrl-Shift-C |
Same as Alt-C (terminal-convention alternate) |
Ctrl-Shift-V |
Same as Alt-V (terminal-convention alternate) |
| Middle-click | Paste the X11/Wayland PRIMARY selection into the focused terminal |
Bare Ctrl-C is left as SIGINT — it's not overloaded for copy. Copying (or simply finishing a drag-selection) also writes to the X11/Wayland PRIMARY clipboard, so middle-click paste in other apps — and into Roost — works.
Font sizing¶
| Shortcut | Action |
|---|---|
Alt-+ / Alt-= |
Increase font size for the active tab |
Alt-- |
Decrease font size for the active tab |
Alt-0 |
Reset font size to the font_size from config |
Font size adjustments are per-tab and held in memory only. They do not persist across restarts, and new tabs always start at font_size from config.conf. The size is clamped to 6 .. 72 points; out-of-range steps saturate.
Terminal keys¶
Anything not bound as an app shortcut flows to the focused terminal through libghostty-vt's key encoder, which produces the right escape sequence for the foreground app's current modes (legacy xterm, application-cursor, Kitty keyboard protocol, etc.):
| Key | Effect |
|---|---|
| Printable characters | Sent to the shell as typed |
| Enter, Backspace, Tab | Sent to the shell |
| Shift-Tab | Sent as \x1b[Z (back-tab) — used by Claude Code to cycle modes |
| Shift-Enter | Disambiguated from Enter (xterm modifyOtherKeys form, or Kitty CSI-u when the app opts in) — used by Claude Code for newlines in its prompt. Query-first apps (crossterm's supports_keyboard_enhancement) now opt in successfully: roost answers the Kitty keyboard query ESC[?u (#247), so the app pushes Kitty flags instead of timing out and receiving a bare \r |
| Arrow keys, Home, End | Standard CSI in normal mode; SS3 (\x1bOA/B/C/D) in application-cursor mode (e.g. inside vim's : prompt) |
| Page Up / Page Down | Sent as standard CSI sequences |
| Esc | Sent to the shell |
Ctrl-letter |
Sent as the corresponding control byte (Ctrl-C → SIGINT, etc.) |
macOS Option-letter |
Composes Unicode (Option+e e → é); does not behave as Alt |
Ctrl-1 … Ctrl-9 are reserved for tab switching and do not reach the shell. On macOS, all Cmd-* and Super-* combinations are reserved for app shortcuts and never reach the shell.
How the shortcut controller is wired¶
App shortcuts are resolved before the keystroke is offered to the terminal, on both UIs. That's why Cmd-T (or Alt-T on Linux) works while the terminal is focused: the shortcut table is consulted first, the action dispatches, and the keystroke never reaches the shell. Anything not in the table falls through to the terminal as usual.
On macOS this is the AppKit main menu plus the window's own key handling. On Linux the iced UI handles every key press in one place: the terminal widget deliberately never captures keyboard events, so they arrive at the app-level handler (App::keyboard in crates/roost-iced/src/app.rs), which walks a fixed order — an open modal or inline rename first, then the command palette, then the keybinding table, and only then the terminal. There is no toolkit focus-traversal to fight: Tab and Shift-Tab are ordinary keys that fall straight through to the encoder.
Mouse¶
| Action | Effect |
|---|---|
| Click | Focus the terminal |
| Click + drag | Select cells; a translucent accent overlay highlights the selection ribbon |
| Link-modifier + hover / click (Cmd on macOS, Alt on Linux — configurable) | Reveal a URL (underline + hand cursor) and open it in your browser. Covers OSC 8 hyperlinks (e.g. Claude Code) and plain https://… text. See Opening links below. |
| Wheel / two-finger scroll | Scroll the terminal scrollback (smooth-scroll on macOS trackpads) |
| Wheel on alt-screen apps (vim, less, jed) | Translated to ArrowUp / ArrowDown keystrokes so trackpad navigation works |
Click in a mouse-tracking app (vim with :set mouse=a, htop, tmux) |
Forwarded to the app as encoded mouse-event escape sequences |
| Wheel in a mouse-tracking app | Forwarded as button-4 / button-5 press+release pairs |
| Shift-click / Shift-drag / Shift-wheel | Bypasses mouse-tracking (xterm convention) so you can always select / scroll locally even when the app is grabbing the mouse |
Selection clears automatically on any PTY output, on resize, and on a new click. (Most terminals clear-on-any-output rather than tracking which rows changed; matches user expectation and avoids the bookkeeping.)
Opening links¶
Hold the link modifier and hover a URL to highlight it (underline + hand cursor); click while holding it to open the URL in your default browser. This works for both OSC 8 hyperlinks (what tools like Claude Code emit) and plain https://… text matched on screen.
The modifier is platform-native by default — Cmd on macOS, Alt on Linux — and configurable via link-modifier in config.conf. Linux users who prefer the conventional Ctrl+click set link-modifier = ctrl. (Some Linux WMs grab Alt+drag to move windows, so Ctrl can be the more reliable choice.) The link-modifier setting is honored by the iced UI; the Swift Mac app is fixed to Cmd. See config.md for details and the broader "prefer Ctrl on Linux" recipe.
Pressing any input-producing key when the viewport is scrolled back snaps the viewport to the bottom before delivering the keystroke — same behavior as every other terminal multiplexer.
Sidebar mouse¶
The sidebar still supports mouse-driven rename: double-click a project row to rename it inline, or right-click for a Rename / Close menu.
If you close the last tab in a project, Roost closes that project too. The "Are you sure?" confirmation dialog only appears for explicit close-project actions (the sidebar X button or the right-click menu); Cmd-W / Alt-W on the final tab closes the project silently.
Tab titles set via Cmd-R / Alt-R are persisted and locked: subsequent OSC 1/2 escapes from the shell (\e]2;new-title\a, common in shell prompts) are silently ignored on a renamed tab. The same lock applies to titles set via roostctl set-title --tab <id> --title "...". v1 has no in-app way to clear the lock; renaming again with Cmd-R / Alt-R updates the displayed label but the lock stays on. To revert to shell-driven titles, delete and recreate the tab.
Custom keybindings¶
Roost reads ~/.config/roost/config.conf (more precisely $XDG_CONFIG_HOME/roost/config.conf) on both platforms. The keybinding syntax mirrors Ghostty:
Each keybind line either binds a trigger to an action, or unbinds one. Multiple keybind lines accumulate; later lines override earlier ones for the same trigger (last-wins per trigger).
Modifiers¶
Combine with +. Aliases are accepted on both sides:
| Canonical | Aliases |
|---|---|
shift |
|
ctrl |
control |
alt |
opt, option |
super |
cmd, command |
The key segment is the last token that isn't a modifier; it's matched case-insensitively by name (t, 1, tab, return, bracketleft, plus, …) against the same name the UI derives from the pressed key. On a non-Latin keyboard layout the physical key's Latin equivalent is used, so super+t still fires where a QWERTY T sits.
Examples¶
# Add Cmd-J as a second trigger for new_tab. Cmd-T (the default) still works.
keybind = super+j = new_tab
# Disable the default rename-project shortcut.
keybind = super+shift+r = unbind
# Reassign Cmd-T to close the active tab. Cmd-W still also closes (default).
keybind = super+t = close_tab
Use only leading-line # comments. A # after a keybind value is treated as part of the action string, not as an inline comment.
Available actions¶
| Action | Default (macOS / Linux) |
|---|---|
new_tab |
super+t / alt+t |
close_tab |
super+w / alt+w |
rename_tab |
super+r / alt+r |
cycle_tab_prev |
super+shift+bracketleft / alt+shift+bracketleft |
cycle_tab_next |
super+shift+bracketright / alt+shift+bracketright |
paste |
super+v, ctrl+shift+v / alt+v, ctrl+shift+v |
copy |
super+c, ctrl+shift+c / alt+c, ctrl+shift+c |
new_project |
super+n / alt+n |
new_project_on_host |
super+shift+n / alt+shift+n |
rename_project |
super+shift+r / alt+shift+r |
close_project |
super+shift+w / alt+shift+w |
toggle_sidebar |
super+b / alt+b |
toggle_sidebar_agents |
super+shift+a / alt+shift+a |
jump_to_unread |
super+shift+u / alt+shift+u |
command_palette |
super+shift+p / alt+shift+p |
command_launcher |
super+shift+t / alt+shift+t |
custom_palette |
super+shift+e / alt+shift+e |
agent_palette |
super+shift+o / alt+shift+o |
switch_project_1..9 |
super+1..9 / alt+1..9 |
switch_tab_1..9 |
ctrl+1..9 / ctrl+1..9 |
font_increase |
super+plus, super+equal / alt+plus, alt+equal |
font_decrease |
super+minus / alt+minus |
font_reset |
super+0 / alt+0 |
Defaults with multiple triggers (cycle_tab_*, paste, copy) keep both triggers; an unbind line removes only the listed one.
Triggers using Ghostty prefixes (global:, all:, unconsumed:, performable:) and unknown action names are logged and skipped — they're out of scope for v1.
The config file is read once at startup; restart Roost to pick up edits.