Theming & Config
The one config file — ~/.tmux-ide/config.json — that colors every surface and binds every key
Theming & config
tmux-ide has one global config file: ~/.tmux-ide/config.json (override the
path with the TMUX_IDE_CONFIG environment variable). It holds the keymap, the
theme, and the behavior toggles for notifications, restore, updates, and
worktrees.
The theme tokens are semantic, not per-surface: a single theme block
colors the tmux chrome (status bar, pane chips, actions menu, cheat sheet) and
the TUI widgets. Re-theming the whole product is a one-file edit plus a re-adopt.
Every field is optional. A missing or malformed file falls back to the built-in defaults, and any individual field that's missing or mistyped falls back to its own default — the parser never throws. Config is read once per process, so changes take effect on the next launch (a re-adopt spawns fresh processes).
You can edit it interactively with the config panel (prefix v / ⌥,) or
tmux-ide popup config.
Full default
{
"keys": {
"popup": "M-p",
"home": "M-h",
"cheatsheet": "M-k",
"menu": "M-m",
"sidebar": "M-b",
"panels": { "explorer": "M-e", "changes": "M-g", "config": "M-," }
},
"theme": {
"accent": "colour75",
"muted": "colour240",
"fg": "colour250",
"status": {
"blocked": "colour203",
"working": "colour221",
"done": "colour111",
"idle": "colour114",
"unknown": "colour244"
},
"glyphs": { "active": "●", "inactive": "○" }
},
"updater": { "tickMs": 2000, "snapshotEvery": 15 },
"notifications": { "toast": true, "macos": false },
"restore": { "resumeAgents": false },
"updates": { "check": true },
"welcome": { "show": true },
"worktrees": { "dir": "" }
}keys
Each entry is the root-table Alt fast-path for a surface, as a tmux key
name (M- is Meta/⌥). tmux-ide also binds a prefix twin for every surface —
the reliable path that works under every keyboard protocol — derived from these
same entries, so you configure both by editing one value.
| Field | Alt default | Prefix twin | Opens |
|---|---|---|---|
home | M-h | prefix h | Home cockpit from anywhere |
popup | M-p | prefix j | Session switcher |
cheatsheet | M-k | prefix k | Cheat sheet |
menu | M-m | prefix u | Actions menu |
sidebar | M-b | prefix b | Sidebar nav column |
panels.explorer | M-e | prefix e | File explorer panel |
panels.changes | M-g | prefix g | Git changes panel |
panels.config | M-, | prefix v | Config editor panel |
The Alt binds can be swallowed while an agent pane has focus (agents may switch
the terminal's key encoding via the kitty keyboard protocol); tmux-ide registers
kitty-encoded fallbacks, but coverage varies by terminal. The prefix twins are
immune by construction — see
The dock & keys.
theme
Semantic tokens, applied to both the tmux chrome and the widgets. Values are
tmux colors (colourN or #rrggbb).
| Field | Default | Meaning |
|---|---|---|
accent | colour75 | Primary / brand accent |
muted | colour240 | Dim / secondary text |
fg | colour250 | Default foreground |
status.blocked | colour203 | Blocked agent |
status.working | colour221 | Working agent |
status.done | colour111 | Done agent |
status.idle | colour114 | Idle agent |
status.unknown | colour244 | No signal |
glyphs.active | ● | Filled state glyph |
glyphs.inactive | ○ | Hollow state glyph |
TUI-owned surfaces
The full-screen TUI owns its chrome instead of inheriting whatever colours a child program happens to leave in the terminal. New and migrated surfaces use a theme pipeline with three boundaries:
@tmux-ide/contractsdefines renderer-neutral roles for canvas, panels, headers, text strength, borders, status, selection, density, shape, motion, typography and focus.- The OpenTUI host projects those roles to
RGBAonce and follows the renderer's light/dark mode whentheme.modeissystem. - Each migrated product surface consumes a named recipe such as
sidebar,application-bar,bottom-dockorcommand-palette. Components choose a role by purpose rather than carrying a private palette.
Navigation labels use the sidebar recipe's readable text rung, selection labels
use selectionText, and tertiary metadata alone uses muted. Critical
foreground/background pairs are contrast-checked at the OpenTUI projection
boundary. A low-contrast custom value falls back through the owned text ladder
instead of making navigation disappear.
The built-in muted greys deliberately use stable extended xterm-256 entries, not ANSI base slot 8. Base slots are terminal-profile aliases and can be nearly black even when the source RGB looked grey. Mirrored tmux pane contents are the exception: their ANSI colours belong to the program running in that pane and remain untouched.
updater
The chrome tick loop and snapshot cadence.
| Field | Default | Meaning |
|---|---|---|
tickMs | 2000 | Milliseconds between ticks |
snapshotEvery | 15 | Write a restore snapshot every N ticks |
notifications
| Field | Default | Meaning |
|---|---|---|
toast | true | In-tmux toast on blocked / done |
macos | false | Also post a native macOS notification |
restore
| Field | Default | Meaning |
|---|---|---|
resumeAgents | false | Revive Claude conversations on restore (--resume-agents) |
See Restore & resume.
updates
| Field | Default | Meaning |
|---|---|---|
check | true | Check for a newer tmux-ide version |
welcome
| Field | Default | Meaning |
|---|---|---|
show | true | Allow the first-run welcome card. Set false to suppress it |
worktrees
| Field | Default | Meaning |
|---|---|---|
dir | "" | Base dir for worktree checkouts. Empty → sibling <repo>-worktrees; a relative path resolves against the repo |
See Worktrees.
Desktop design system
The desktop app has its own token layer, separate from the tmux chrome above:
sfora's oklch zinc system. Colour, elevation and shape are authored in one place
— the token block in apps/desktop-renderer/src/styles.css — and every
component reads a semantic role rather than a value.
Two layers. The system (--sf-*) holds the zinc ladder, the shadow ladder
and the accents. The mapping re-points the roles the shell consumes
(--tmux-ide-*, --desktop-*) at that system, which is why changing the
product's identity does not touch a single component.
The architecture is planes, not pages
Depth is the identity. Four planes, back to front:
| Plane | Light | Dark | In tmux-ide |
|---|---|---|---|
| Sidebar | 96.3% | 12% | The fleet sidebar, titlebar, dock |
| Content, inset | 98.7% | 14.05% | The canvas the windows live on |
| Grey section | 96.76% | 27.41% | Grouped and section surfaces |
| White item | 100% | 16.8% | Window cards, dialogs, popovers |
All zinc-hued — hue 286, whisper chroma. Never a blue-grey, never a warm one.
Elevation is a ring, not a blur
Structure comes from the plane plus a crisp 1px ring, not from borders everywhere. The visible edge is what makes a card read against the plane behind it; a near-zero ring dissolves into the canvas, and on a floating surface the ring draws the silhouette sharper than any border colour can. In dark mode the ring flips to a lit top edge, which reads as a card's white border against a darker canvas.
Six named tokens carry every surface — --sf-shadow-surface (resting cards),
-field (inputs), -overlay (dialogs, menus, palette), -tooltip, -panel
(floating toolbars), -inset-track (inset wells). There are no ad-hoc shadows
in the renderer. Hairline borders survive only as genuine dividers, at
--paper-hairline, which resolves to one device pixel — 0.5px on retina.
Colour means one thing each
Primary is metallic zinc, not blue: buttons and primary actions are
--sf-primary. This is the single most look-defining value in the system.
Three blues, deliberately distinct, and never interchangeable:
- the lit-blue gradient (
--sf-accent-blue→--sf-accent-blue-dark) is the selected nav or fleet row, and nothing else; - anchor
#6187feis focus rings and text selection, and nothing else. It ships no P3 variant, so it stays sRGB; --sf-app-blueis reserved for macOS-system-adjacent affordances and is deliberately unused.
Semantics are never decorative: --sf-valid is agent work and success,
--sf-invalid is errors and blocked, --sf-warning is attention and staleness.
Status glyphs, chips, readiness rungs and degraded surfaces draw from these and
nothing else. Section accents (blue, green, amber, purple) are for identity
— eyebrows and section marks — and are never used for status.
Two rungs are derived rather than transcribed, both forced by contrast:
light-mode -ink twins for the semantic tones (#00c454 is 2.23:1 on white and
#f6c30f is 1.58:1 — neither can carry 11px text or a 6px status dot, which
needs 3:1 as a non-text component), and a metadata text rung, because sfora's
tertiary rung is 3.55:1 on the sidebar plane and this product spends it on text
that must be read. All 112 ink-on-plane pairs clear their floor in both modes.
Type and shape
The Board-UI composite ramp — headline 16/22, body 14/20, body-2 13/18,
caption-1 12/16 at +0.15px, caption-2 11/15 at +0.2px — drawn in the system
face (-apple-system, SF on macOS) rather than bundling Inter. That is a
deliberate deviation from sfora: the ramp carries the look, SF is metrically
close, and the native face is what stops the app reading as a web page in a
window. Chrome sits mostly at body-2 and caption-1, like a real Mac app;
terminals keep their mono stack.
Radii follow sfora's ladder: controls at 8px, cards and nav rows on the 10px
2lg step.
High contrast
The system steps aside when the OS asks for more contrast: the mapping is scoped
to [data-increased-contrast="false"], and in high-contrast mode the theme
resolver owns the focus, selection, attention and danger colours. The trade-off
is that user and project theme overrides only reach those roles while high
contrast is on.