User config¶
Roost reads a single user-level config file at ~/.config/roost/config.conf
(XDG-style on macOS by deliberate divergence from Apple HIG — matching
Ghostty / nvim / fish). Both UIs (Swift Mac app, Linux gtk4-rs binary)
parse the same file with the same semantics, so a config tuned on one
platform is portable to the other.
The file is plain text, one key = value per line, #-prefixed comments
allowed, whitespace forgiving. Unknown keys are silently dropped — this
keeps user files forward-compatible with future Roost versions and with
keys that Ghostty consumes but Roost doesn't.
A missing file is fine: every setting has a compiled-in default.
To override the path for testing, set the ROOST_CONFIG environment
variable to an absolute file path. The E2E harness uses this to seed
the launcher with deterministic commands.
Settings¶
| Key | Type | Default | Effect |
|---|---|---|---|
theme |
string | bundled roost-dark |
Theme name (see themes.md). |
font-family |
string | platform default | Monospaced font family. Quoted values supported ("JetBrains Mono"). |
font-size |
number | 13 (Linux), 14 (Mac) |
Point size for the terminal font. Must be > 0. |
tab-min-width |
number | 80 (Mac) |
Minimum tab pill width in points. 0 disables the floor. Mac-only. |
tab-max-width |
number | 220 (Mac) |
Maximum tab pill width in points. 0 disables the cap (pills grow to fit). Mac-only. |
keybind |
<trigger> = <action> |
(see Keybindings) | Append a custom keybinding. Repeatable; later entries override earlier ones. |
command |
label="…" run="…" [hold=…] |
none | Launcher entry (⌘⇧T / Alt+Shift+T) that runs a fixed command in a new tab. Repeatable. See Extending Roost. |
provider |
label="…" run="…" [timeout=…] [limit=…] |
none | Dynamic, script-backed menu in the custom palette (⌘⇧E / Alt+Shift+E). The script generates rows on demand and acts on the choice. Repeatable; executables in providers/ (beside this file) are also discovered. See Extending Roost. |
copy-on-select |
off | true | clipboard |
true |
What a mouse-drag selection writes to the clipboard on release. See the dedicated section below. |
clipboard-write |
allow | deny |
allow |
Whether a program running in the terminal can write the host clipboard via OSC 52. See the dedicated section below. |
link-modifier |
ctrl | alt | super |
Cmd (Mac) / Alt (Linux) | Which held modifier reveals + opens a URL on hover/click. GTK app only today. See the dedicated section below. |
copy-on-select¶
Controls what happens to a text selection the moment the user releases the mouse after a drag. Vocabulary matches Ghostty 1:1 — three values with platform-specific behaviors:
| Value | What it writes on drag-release | What pastes from there |
|---|---|---|
off |
nothing | n/a — user must press the explicit copy shortcut (⌘C on Mac, Ctrl+Shift+C on Linux) |
true (default) |
the selection clipboard only | middle-click anywhere in Roost; ⌘V / Ctrl+Shift+V are not affected |
clipboard |
both the selection clipboard and the system clipboard | middle-click (selection); ⌘V / Ctrl+Shift+V (system) |
Per-platform details¶
Linux. The selection clipboard is the X11 / Wayland PRIMARY
selection — the conventional one-finger-middle-click target. The
system clipboard is CLIPBOARD (the Ctrl+C / Ctrl+V target most
non-terminal apps use). true matches the long-standing X11 convention
where dragging in a terminal writes PRIMARY but leaves CLIPBOARD
untouched.
macOS. Mac has no native equivalent of PRIMARY, so Roost
synthesizes one as a custom-named NSPasteboard
(ai.stridelabs.Roost.selection). Other Mac apps cannot read it — it
exists purely so Roost can offer middle-click paste without clobbering
the system pasteboard that ⌘V reads from. Practical consequence:
with the default copy-on-select = true, dragging a selection in
Roost and then pressing ⌘V in another app will paste whatever was last
⌘C'd, not the dragged text. This surprises Mac users who expect drag
to update the system clipboard. To get the "drag and paste anywhere"
behavior, set copy-on-select = clipboard.
Middle-click paste¶
Middle-click anywhere in a Roost terminal pastes from the selection
clipboard, regardless of which copy-on-select value is set. The
gesture works on both platforms even when copy-on-select = off; in
that case the selection clipboard is just empty (whatever the user
last explicitly wrote via the copy shortcut isn't in there), so the
paste is a no-op.
Paste contents are routed through the same bracketed-paste-aware
encoder as ⌘V / Ctrl+Shift+V, so an nvim or fish that enables
DECSET 2004 receives ESC[200~ … ESC[201~ wrappers around the
selection.
Trimming¶
The text written to either clipboard has trailing whitespace stripped
per row (matches Ghostty's clipboard-trim-trailing-spaces default
behavior). Leading/trailing entirely-blank rows are also dropped so
multi-row selections don't carry stray newlines.
Limitation (v1)¶
If a selection extends beyond the visible viewport (the user drags inside the visible area, then scrolls so the selection is partly or fully off-screen), copy returns only the still-visible portion. The visible highlight rectangle scrolls with the content, so the visual contract is correct — but the copied text is what's currently shown, not the entirety of the original selection. A future PR will add scroll-walk-restore to copy off-screen rows; the bug-fix #146 explicitly leaves this as a known limitation.
clipboard-write¶
Controls whether a program running inside the terminal can write the
host clipboard by emitting the OSC 52 escape sequence (\e]52;c;<base64>\a).
This is the path opencode-over-SSH, nvim with g:clipboard = osc52,
tmux set -s set-clipboard on, kitten ssh, yazi, and other TUIs use
to get text back to your local clipboard.
| Value | Behavior |
|---|---|
allow (default) |
OSC 52 writes the host clipboard. Matches Ghostty's default. |
deny |
OSC 52 sequences are parsed and silently dropped — logged at info, no clipboard side-effect. |
Phase 2 will add ask with a per-tab consent banner ("opencode wants
to write 42 bytes to your clipboard — Allow once / Always / Deny");
phase 1 is intentionally allow/deny only to keep the surface small.
Read direction (OSC 52 ?)¶
OSC 52 also supports a read direction (the program asks the terminal
to send the clipboard contents back). Roost always drops read
requests in phase 1 — there's no consent UI for them yet and reading
the clipboard from a remote process is the more sensitive direction
(shoulder-surfing a password manager value). This will become its own
clipboard-read = allow | ask | deny setting in phase 2.
Targets¶
OSC 52 carries a Ps selector indicating which clipboard to write:
c(default) → system clipboard (NSPasteboard.generalon Mac,CLIPBOARDon Linux — what ⌘V / Ctrl+V pastes from).pors→ selection clipboard (namedNSPasteboardon Mac, X11 / WaylandPRIMARYon Linux — what middle-click pastes from).- Any other selector falls through to system (matches Ghostty's permissive handling of emitters that pad the selector with letters).
link-modifier¶
Holding this modifier while hovering a URL reveals it (underline + hand
cursor); holding it while clicking opens it in your default browser.
URLs come from both OSC 8 hyperlinks (what tools like Claude Code emit)
and plain https://… text matched on screen.
| Value | Modifier |
|---|---|
ctrl |
Control |
alt |
Alt / Option |
super (aliases: cmd, command, meta) |
Super / Command |
Defaults are platform-native, mirroring the keybinding scheme:
- macOS: Cmd — matches the Swift app and native Mac apps (⌘-click).
- Linux: Alt — the GTK app's single "primary" modifier, leaving Ctrl free for the shell/readline.
Linux users who prefer the traditional Ctrl+click (the ghostty / common-terminal convention) just set:
Scope: this setting is honored by the GTK app only. The Swift Mac app's modifier is currently fixed to Cmd, so the key is silently ignored there (harmless — unknown keys are always dropped).
Heads up (Linux): some window managers/compositors grab
Alt+drag to move windows, which can swallowAlt+click. If link-clicking feels flaky on your desktop, switch tolink-modifier = ctrl.
Prefer Ctrl across the board? (Linux)¶
The GTK app defaults to an Alt-centric scheme (Alt+T new tab,
Alt+W close, etc.) so Ctrl stays free for the shell. If you'd rather
use the more familiar Ctrl shortcuts, you can remap both the link
modifier and any keybinding — keybind lines are repeatable and
last-wins:
# Ctrl+click to open links
link-modifier = ctrl
# Restore Ctrl-based shortcuts (examples — see the keybindings doc)
keybind = ctrl+t = new_tab
keybind = ctrl+w = close_tab
See Keybindings for the full action list and trigger syntax.
Example¶
# ~/.config/roost/config.conf
theme = Catppuccin Mocha
font-family = "JetBrains Mono"
font-size = 14
# Drag-to-select writes to PRIMARY (Linux) / Roost's named selection
# pasteboard (Mac). Middle-click pastes. Cmd-V / Ctrl+Shift+V are
# untouched.
copy-on-select = true
# To make drag-to-select also write the system clipboard, so Cmd-V
# in another Mac app gets the dragged text:
#
# copy-on-select = clipboard
# Default: programs in the terminal can write your clipboard via
# OSC 52 (the opencode-over-SSH path, nvim's g:clipboard = osc52,
# tmux set-clipboard, etc.). Set `deny` to opt out.
clipboard-write = allow
# Open links with Ctrl+click instead of the platform default
# (Cmd on Mac, Alt on Linux). GTK app only.
link-modifier = ctrl
# Add a custom trigger (here, restoring the pre-Alt Ctrl+T for new_tab
# on Linux — the default is now Alt+T). See docs/getting-started/keybindings.md.
keybind = ctrl+t = new_tab
# A fixed launcher command (⌘⇧T / Alt+Shift+T):
command = label="Claude" run="claude --resume"
# A dynamic, script-backed menu (⌘⇧E / Alt+Shift+E). The script prints
# its rows on `list` and acts on the choice on `activate`:
provider = label="Open shed" run="~/.config/roost/providers/shed.sh"
See Extending Roost for the full command = /
provider = contract (with bash / Python / TypeScript examples),
paths.md for where the file lives on each platform, and
themes.md for the theme value enumeration.