Skip to content

Configuration

mike-ward edited this page Aug 12, 2026 · 3 revisions

Configuration

go-term reads an optional INI-style config file at startup. The workspace layer reads it, so it applies to every embedder that uses term/workspace, including falcon. A bare term.Term embedder sets its options in code through term.Cfg. See Embedding.

Everything in the file is optional. A missing file, an unknown section, or an unknown key is ignored. A malformed line is logged and skipped, and the rest of the file still applies. A broken config never prevents the terminal from starting.

Location

The file is found in this order:

  1. The path the embedder passes as workspace.Cfg.ConfigPath, if set
  2. $XDG_CONFIG_HOME/go-term/config
  3. ~/.config/go-term/config
  4. os.UserConfigDir()/go-term/config

Falcon uses the default location. Cmd+, opens the file in your editor and writes a commented stub first when the file does not exist.

Format

# Comments start with '#'. Blank lines are ignored.
[section]
key = value

Whitespace around = is trimmed. Keys and values are capped at 128 bytes.

Reloading

Cmd+Shift+, (Ctrl+Alt+, on Windows) re-reads the file and applies it to every open pane without a restart. Font, theme, scrollback, bell, scrollbar, and every keybinding change live. [env] applies to panes created after the reload.

A setting deleted from the file reverts to the embedder's built-in default on the next reload. The file always applies to a pristine base, never on top of previously loaded values.

Reload leaves per-pane state alone, with one exception. The font zoom resets, and only when the font actually changed. The zoom is an absolute point size. Without the reset, it outranks the new configured size until Cmd+0.

[font]

Key Type Default Meaning
family string embedder's default Font family, as the font's own name table spells it
size number embedder's default Point size, clamped to 4–72
[font]
family = JetBrainsMono NFM
size   = 12

The family must be the name the font reports, not its marketing name. JetBrains Mono Nerd Font Mono reports as JetBrainsMono NFM.

[general]

Key Type Default Meaning
theme string embedder's first theme Color theme, by display name, case-insensitive
scrollback integer 5000 Scrollback rows. 0 restores the default, negative disables
bell enum auto auto, audible, visual, both, none
scrollbar number 4 Thumb width in px. Negative hides the scrollbar
minimum-contrast number 1 (off) WCAG ratio text is forced to reach against its cell background
middle-click-paste boolean on for Linux Paste with the middle mouse button
notify-after duration 0 (off) Notify when a command this long finishes while you look away
[general]
theme              = Dracula
scrollback         = 5000
bell               = auto
scrollbar          = 4
minimum-contrast   = 1
middle-click-paste = true

Booleans accept true/false, yes/no, on/off, and 1/0.

Theme

theme matches by name against the themes the embedder registered. An unknown name is logged and the default is kept. Nothing loads a theme from disk.

Falcon registers the built-in Default plus the whole bundled corpus: 602 themes, 473 dark and 129 light. Press Cmd+Shift+T to browse them with a live preview and a filter. That is the intended way to pick one.

Theme names that shipped before the corpus still resolve to their closest corpus equivalent, so existing config files keep working.

A light theme also switches falcon's window chrome to light, and tells any child app subscribed to mode 2031 that the color scheme changed. A neovim or delta that follows the terminal re-themes itself along with it. COLORFGBG is set at spawn from the startup theme, which is how vim, less, and some prompts decide the same question. A child started under a dark theme still reports dark after a switch, because the variable cannot change in a running process.

Minimum contrast

minimum-contrast fixes the one thing a theme cannot fix. A 24-bit color sequence is not themeable. eza, starship, and most ls color schemes emit colors chosen for a dark background, and on a light theme they arrive exactly as sent.

Set a ratio, and any foreground below it is pushed toward white or black, whichever direction has room against that cell's background. The color blends rather than being replaced, so a red that fails by a little stays recognizably red.

Value Effect
1 Off, the default. A color against itself is 1:1
3 Fixes the worst dark-tuned colors, leaves most palettes alone
4.5 The WCAG floor for body text
7 WCAG AAA. Expect most colors visibly adjusted

The clamp is render-only. The grid keeps the color the app sent, so copy, search, and session recordings are unaffected. It costs about 11 ns per cell when on.

Bell

auto plays the system alert sound and falls back to a visual flash where the platform has none. audible never flashes. visual never beeps. both does both. none ignores BEL entirely.

Scrollback

Scrollback is clamped to at most 100000 rows.

[env]

Environment variables handed to every child process. The full child environment is the parent's, with the host terminal identity scrubbed and the pane's own TERM, COLORTERM, and COLORFGBG set. Entries here apply last, so they win over all of it, including TERM_PROGRAM.

Key Type Default Meaning
any NAME string inherited Added to every child's environment. An empty value unsets it
[env]
TERM_PROGRAM = Ghostty
PATH         = /opt/homebrew/bin:/usr/bin:/bin

TERM_PROGRAM is what TUI file managers key their image protocol off. yazi and superfile use the Kitty Graphics Protocol under a name they recognize and fall back to sixel otherwise. go-term implements KGP, so naming a known emulator here gets you its image quality. Falcon advertises its own name by default.

Variables apply to panes created after the reload. A running child's environment is fixed at spawn.

[keybindings]

Each entry rebinds one action. Keys are namespaced:

  • workspace.<command> — window-level commands: tabs, panes, overlays
  • term.<action> — terminal-level actions: copy, find, scrollback, zoom

A key with no prefix means workspace., the form that predates the term. namespace. New configs use the explicit form.

[keybindings]
workspace.splitVertical = Cmd+D
workspace.newTab        = Cmd+T
term.copy               = Cmd+Shift+C
term.find               = Cmd+F
term.scroll-page-up     = none

The value none unbinds the action. That hands the key back to the child process, which is the point. For example, freeing Ctrl+R lets the shell's reverse search work instead of toggling regex search.

Chord syntax

Modifiers first, then the key, joined with +:

Cmd (alias Super), Ctrl, Alt (alias Opt), Shift.

Key names: AZ, 09, F1F25, Space, Enter/Return, Escape/Esc, Tab, Backspace, Delete/Del, Insert, Home, End, PageUp, PageDown, Left, Right, Up, Down, and the punctuation keys [ ] / ; , . - = `. Modifier and key names are case-insensitive.

Collisions

A chord already claimed by another binding is rejected, logged, and the losing entry keeps its default. The check runs across both namespaces. Rebinding both sides in the same file works fine. Collisions are judged against the final assignment, not the built-in defaults.

On Windows the Super key is OS-reserved, so the built-in Cmd-based defaults are remapped: Cmd becomes Ctrl+Shift, Cmd+Shift becomes Ctrl+Alt, and so on. Bindings you write in the config file are used verbatim, no remapping, so write the chord you actually want to press.

workspace.* commands

The table lists the macOS and Linux default first, then the Windows chord.

Command Default (macOS / Linux) / Windows
workspace.splitVertical Cmd+D / Ctrl+Shift+D
workspace.splitHorizontal Cmd+Shift+D / Ctrl+Alt+D
workspace.closePane Cmd+Shift+W / Ctrl+Alt+W
workspace.nextPane Cmd+] / Ctrl+Shift+]
workspace.prevPane Cmd+[ / Ctrl+Shift+[
workspace.resizeLeft Cmd+Ctrl+Left / Ctrl+Alt+Shift+Left
workspace.resizeRight Cmd+Ctrl+Right / Ctrl+Alt+Shift+Right
workspace.resizeUp Cmd+Ctrl+Up / Ctrl+Alt+Shift+Up
workspace.resizeDown Cmd+Ctrl+Down / Ctrl+Alt+Shift+Down
workspace.newTab Cmd+T / Ctrl+Shift+T
workspace.closeTab Cmd+Ctrl+W / Ctrl+Alt+Shift+W
workspace.moveTabLeft Cmd+Alt+[ / Alt+Shift+[
workspace.moveTabRight Cmd+Alt+] / Alt+Shift+]
workspace.nextTab Cmd+Shift+] / Ctrl+Alt+]
workspace.prevTab Cmd+Shift+[ / Ctrl+Alt+[
workspace.tab1workspace.tab9 Cmd+1Cmd+9 / Ctrl+Shift+1Ctrl+Shift+9
workspace.toggleRecording Cmd+Shift+R / Ctrl+Alt+R
workspace.save Cmd+S / Ctrl+Shift+S
workspace.toggleBroadcast Cmd+Shift+I / Ctrl+Alt+I
workspace.chooseTheme Cmd+Shift+T / Ctrl+Alt+T
workspace.reloadConfig Cmd+Shift+, / Ctrl+Alt+,
workspace.toggleHelp Cmd+/ / Ctrl+Shift+/
workspace.commandPalette Cmd+Shift+P / Ctrl+Alt+P

The overlay* commands are shared by every list overlay, and they route to whichever one is open. They are one registration each on purpose: duplicate shortcuts are rejected, and a rejection drops the rest of the batch.

term.* actions

Action Default
term.copy Cmd+C / Ctrl+Shift+C
term.paste Cmd+V / Ctrl+Shift+V
term.find Cmd+F
term.toggle-regex Ctrl+R, in Find
term.next-match Enter, in Find
term.prev-match Shift+Enter, in Find
term.prev-prompt Cmd+Up
term.next-prompt Cmd+Down
term.jump-failure Cmd+Shift+E
term.select-output Cmd+Shift+O
term.scroll-page-up PageUp
term.scroll-page-down PageDown
term.scroll-top Shift+Home
term.scroll-bottom Shift+End
term.font-inc Cmd+=
term.font-dec Cmd+-
term.font-reset Cmd+0
term.copy-mode Cmd+Shift+Space / Ctrl+Shift+Space
term.hints Cmd+Shift+U / Ctrl+Shift+U
term.hints-copy Cmd+Shift+Y / Ctrl+Shift+Y

An override replaces the action's whole default chord list with the single chord you give, and it inherits the action's Shift tolerance. Where Shift is a keyboard artefact rather than meaningful, a stray Shift still matches: rebinding term.find to Cmd+G also answers Cmd+Shift+G. Where Shift picks a direction, like term.next-match versus term.prev-match, it matches exactly.

Several actions only fire in context, and that gate is not part of the binding. Ctrl+C still sends SIGINT when nothing is selected. The Find keys apply only while the search bar is open. term.copy only copies when there is a selection.

Copy mode

term.copy-mode enters a vim-keyed state for scrolling the buffer, selecting text, and copying it without a mouse. While it is active, no key reaches the shell, and incoming output freezes so the text you select cannot scroll away. A status bar across the top shows the key hints.

A click anywhere in the pane leaves copy mode and hands selection back to the mouse.

The copy-mode actions are rebindable like any other. They are left out of the Cmd+/ help overlay on purpose: twenty extra rows in a flat list helps nobody. The full default table is in Usage.

Full example

[font]
family = JetBrainsMono NFM
size   = 13

[general]
theme              = TokyoNight
scrollback         = 20000
bell               = visual
scrollbar          = 6
middle-click-paste = true

[env]
TERM_PROGRAM = Ghostty

[keybindings]
workspace.splitVertical   = Cmd+D
workspace.splitHorizontal = Cmd+Shift+D
term.find                 = Cmd+G
term.toggle-regex         = none

Clone this wiki locally