paneflow.json is a per-user JSON file. All keys are optional, and
{} is a valid config. The Rust config loader and runtime resolvers are
the source of behavior; the published JSON Schema
mirrors the public keys for editor validation and autocomplete.
Unknown top-level keys are ignored by the runtime but flagged by the schema in editors. A valid save is reloaded by the config watcher. During hot reload, invalid JSON keeps the previous valid config instead of broadcasting defaults. Startup reads an invalid file as defaults and logs a warning.
File locations
| Platform | Path |
|---|---|
| Linux and macOS | ~/.paneflow/paneflow.json |
| Windows | %USERPROFILE%\.paneflow\paneflow.json |
PANEFLOW_HOME moves the whole directory (config, session, worktrees, cache,
helper binaries). Configuration found at the previous locations
(~/.config/paneflow, ~/Library/Application Support/paneflow,
%APPDATA%\paneflow) is copied into ~/.paneflow on first launch.
Editor schema
Add this at the top of the file for autocomplete in VS Code, Zed, JetBrains IDEs, Helix, and other JSON Schema-aware editors:
{
"$schema": "https://github.com/arthjean/paneflow/raw/main/schemas/paneflow.schema.json",
"$schemaVersion": "1.0.0"
}Top-level keys
| Key | Type | Default | Applied | Notes |
|---|---|---|---|---|
$schema | string | none | Editor only | Points editors to the published schema. Ignored by Paneflow. |
$schemaVersion | string | 1.0.0 | Startup/reload | Unknown versions warn but do not block loading. |
default_shell | string/null | Platform shell fallback | New terminal | Unix: configured -> $SHELL -> /bin/sh. Windows: configured -> pwsh.exe -> powershell.exe -> %ComSpec% -> C:\Windows\System32\cmd.exe -> cmd.exe. |
theme | string/null | Paneflow Dark | Hot reload | The light or dark variant of one preset: Paneflow, Vercel, Claude, Cursor, Tailwind. Pre-preset names (One Dark, PaneFlow Light, Vercel, Claude, Cursor) still resolve. Runtime lookup is case-insensitive. |
theme_mode | string/null | dark | Hot reload | light and dark force the matching bundled theme; system follows the OS window appearance. |
font_family | string/null | bundled JetBrainsMono Nerd Font | Hot reload cache | Accepts .PaneflowMono, JetBrainsMono NF, JetBrainsMono NFM, .PaneflowSans, embedded font names, or installed monospace families. |
font_fallbacks | string array/null | GPUI fallback stack | Hot reload cache | Ordered extra fallback families for glyphs missing from font_family. |
font_size | number/null | 13.0 | Hot reload cache | Points, valid range 8.0 to 32.0; invalid values fall back to 13.0. |
font_weight | string/null | normal | Hot reload cache | One of thin, extra_light, light, semi_light, normal, medium, semi_bold, bold, extra_bold, black, extra_black. |
line_height | number/null | 1.0 | Hot reload cache | Multiplier of the font's own line height (ascent, descent, and line gap), valid range 0.8 to 2.5. The cell is rounded to whole device pixels. |
cell_width | number/null | 1.0 | Hot reload cache | Multiplier of the font's advance, valid range 0.8 to 2.0. The cell is rounded to whole device pixels. |
unfocused_pane_opacity | number/null | 1.0 | Hot reload | Opacity of panes that do not hold focus, when a workspace has more than one pane. Range 0.15 to 1.0; 1.0 disables the dim. |
reduce_motion | boolean/null | false | Hot reload | Minimizes non-essential interface motion: hover transitions settle instantly and decorative animations render a static frame. |
sidebar_show | object/null | all off | Hot reload | What a session tab row shows beyond its name: branch adds its git branch, diffstat adds its insertion and deletion counts, pr turns the branch icon into a pull-request glyph colored by the request's state when one exists, indent_guide draws a hairline under a workspace's folder icon down its tab rows. The first two read the tab's bound worktree, or its workspace's checkout when the tab is unbound; pr needs the gh CLI and answers for GitHub remotes only. Toggled from the rail's Customize Sidebar menu. |
worktrees | object/null | see below | Hot reload | Where Paneflow keeps the git worktrees it creates for branches and how it cleans them up: dir, auto_remove, keep_limit, for_new_branches. Settings > Worktrees edits the same keys. |
window_decorations | string/null | client | Startup | client draws Paneflow chrome; server delegates to the OS compositor. |
window_backdrop | string/null | auto | Startup | auto, mica, blurred, acrylic, transparent, opaque, or off. On Windows, blurred and acrylic set in the config resolve to auto; only PANEFLOW_WINDOW_BACKDROP applies blur there. PANEFLOW_WINDOW_BACKDROP overrides for one launch. |
windows_terminal_material | boolean/null | false | Window/terminal render | Windows-only terminal background material toggle. The Blended interface style sets it to true, Themed to false. Ignored on other platforms. |
windows_chrome_material | boolean/null | false | Window/chrome render | Windows-only native material in the primary navigation card. The Blended interface style sets it to true, Themed to false. Ignored on other platforms. |
macos_chrome_material | boolean/null | false | Window/chrome render | macOS-only native Sidebar material in the primary navigation card. The Blended interface style sets it to true, Themed to false. Ignored on other platforms. |
linux_terminal_material | boolean/null | false | Window/terminal render | Linux-only translucent terminal card over the window veil, so a compositor blur shows through. The Blended interface style sets it to true, Themed to false. Ignored on other platforms. |
linux_chrome_material | boolean/null | false | Window/chrome render | Linux-only translucent veil behind the sidebar and the title bar, so a compositor blur shows through. The Blended interface style sets it to true, Themed to false. Ignored on other platforms. |
option_as_meta | boolean/null | true on Linux and Windows, false on macOS | Hot reload | Sends Alt/Option as ESC-prefix Meta. On macOS, true makes Option plus a letter send Meta instead of composing a character; keep false when Option should type Unicode characters. A config reload applies it to open terminals. |
shell_integration | boolean/null | true | New terminal | Enables Paneflow shell snippets for OSC 7 CWD and OSC 133 command marks. |
editor | object/null | minimap off, scrollbar on | Hot reload | What the code editor draws beside the text: minimap adds a minimap along the right edge, scrollbar keeps the vertical scrollbar. Toggled from the editor's controls menu; the choice applies to every open file. |
automation | object/null | all off | Hot reload | Background work run after an agent turn: tab_auto_naming summarizes the session's recent exchange into a 2-5 word tab name through the agent's own CLI (claude -p, codex exec, opencode run, pi --print) with tools disabled, at most once per three minutes per session and only when the conversation grew. A name you typed is never replaced; "Reset name" reopens the tab to it. |
submit_paste_delay_ms | integer/null | 70 | IPC send | Floor delay between bracketed paste and Enter for paneflow send --submit. Range 10 to 5000. |
external_editor | string/null | auto | Next open action | Editor that opens file links, including terminal file links: auto, system, zed, cursor, windsurf, code, or visual_studio. auto uses $VISUAL, then $EDITOR, then the first known editor on PATH; system always uses the OS opener. A named editor that is not on PATH falls back to auto. |
shortcuts | object | {} | Hot reload | Maps keystrokes to action names. See shortcuts and actions. |
terminal | object/null | defaults below | Mixed | Namespaced terminal renderer and PTY settings. |
commands | array | [] | Settings/Run | Command palette entries and workspace templates. |
agent_profiles | array | [] | Hot reload | Custom launcher entries that run a built-in agent with extra environment variables and arguments, for example a second Claude Code account through CLAUDE_CONFIG_DIR. Settings > Agents > Profiles edits the same list. |
claude_code_bypass_permissions | boolean/null | false | Next Claude launch | Adds --permission-mode bypassPermissions to the Claude Code launcher. High-risk opt-in. |
ai_unrestricted | boolean/null | false | Per IPC call | Allows trusted conductors to submit to peer panes without PANEFLOW_IPC_SCRIPTING. |
ai_injection_fence | boolean/null | true | Per read call | Wraps surface.read output in an untrusted-output fence by default. |
menu_attention_detection | boolean/null | true | Worker start | Marks a session as needing input when Claude Code or Codex draws a numbered approval menu, which fires no lifecycle hook. Set to false to leave such a session on its hook- or screen-derived state. The worker (paneflow serve) reads it when it starts. |
on_quit | ask, keep, stop, null | ask | At quit | What quitting does while hosted sessions run: ask opens the quit dialog, keep leaves every session running, stop stops them all and shuts the host down. With no live session the app exits and shuts the idle host down. |
sidebar_ended_sessions | 0, 3, 5, 10, null | 5 | Next sidebar render | How many ended sessions a workspace previews in the sidebar before the rest collapse under one "N more ended sessions" row. Nothing is pruned; the cap only controls the preview. |
agent_panel | object/null | defaults below | Agents UI | Agents-view notification preferences. |
telemetry | object/null | { "enabled": null } | Startup/consent | Desktop telemetry consent. PANEFLOW_NO_TELEMETRY=1 overrides it. |
Agent launcher buttons
Each button key is boolean/null. null or omission auto-detects the
CLI binary. false hides the button. true forces it visible.
On Windows, the pane palette, the New pane menu, and the welcome screen offer
only Claude Code, Codex, Amp, Gemini, and Copilot, the runtimes that declare
Windows support, and hide agent_profiles entries based on the others. Linux
and macOS keep every agent below.
| Key | Agent |
|---|---|
claude_code_button_visible | Claude Code |
codex_button_visible | Codex |
opencode_button_visible | OpenCode |
pi_button_visible | Pi |
hermes_agent_button_visible | Hermes Agent |
grok_button_visible | Grok |
amp_button_visible | Amp |
cursor_button_visible | Cursor |
gemini_button_visible | Gemini |
kiro_button_visible | Kiro |
antigravity_button_visible | Antigravity |
copilot_button_visible | Copilot |
codebuddy_button_visible | CodeBuddy |
factory_button_visible | Factory |
qoder_button_visible | Qoder |
openclaw_button_visible | OpenClaw |
deepseek_harness_button_visible | DeepSeek Harness |
muse_button_visible | Muse Code |
terminal
| Key | Type | Default | Applied | Notes |
|---|---|---|---|---|
terminal.ligatures | boolean/null | true | Hot reload cache | Enables programming ligatures when the active font supports them. |
terminal.integrated_glyphs | boolean/null | true | Hot reload | Draws built-in block glyphs as filled quads. |
terminal.color_emoji | boolean/null | true | Hot reload | Uses the platform color-emoji path. |
terminal.cursor_color | string/null | theme cursor | Hot reload/new terminal | #RRGGBB, RRGGBB, #RGB, or RGB. |
terminal.scrollbar | boolean/null | true | New terminal view | Overlay scrollbar shown while scrolling or hovering the right edge of a pane. |
terminal.scrollback_lines | integer/null | 10000 | New terminal | Range 100 to 100000. Cached terminals cap at 1000. |
terminal.cursor_shape | string/null | block | New terminal | vintage, block, beam, underline, double_underline, or hollow. |
terminal.osc52_clipboard | string/null | copy | New terminal view | Available since v0.17.5. copy lets the focused terminal write the system clipboard through OSC 52; off refuses all OSC 52 writes. OSC 52 clipboard reads are always denied. Existing terminal views keep their policy until recreated. |
terminal.cursor_blink | string/null | terminal_controlled | New terminal | on, off, or terminal_controlled. |
terminal.env | object/null | none | New terminal | Environment variables injected into every new terminal. Per-surface env wins. Values are passed through verbatim: no ~ and no $NAME expansion, unlike agent_profiles.*.env. Keys Paneflow sets for every terminal, such as TERM, ZDOTDIR, and the PANEFLOW_* variables, are ignored and logged once. LANG defaults to en_US.UTF-8 only when LANG, LC_ALL, and LC_CTYPE are all unset. |
terminal.scroll_multiplier | number/null | 1.0 | New terminal view | Range 0.1 to 10.0. Ignored in mouse-reporting and alternate-screen scroll paths. |
terminal.minimum_contrast | number/null | Auto (60) | Hot reload | Minimum APCA lightness contrast (Lc) enforced between text and its cell background, on the colors a program chose (truecolor and palette indices 16 to 255). The theme's sixteen ANSI colors, foreground, and background are never corrected. Unset means Auto, which is 60 on every theme; 0 turns the correction off; a negative or non-numeric value means Auto. Range 0 to 90. |
agent_panel
| Key | Type | Default | Notes |
|---|---|---|---|
agent_panel.notify_when_agent_waiting | string/null | Never | PrimaryScreen, AllScreens, or Never. Also governs desktop notifications that terminal programs send with OSC 9 or OSC 777. |
worktrees
Paneflow creates a git worktree when you open a branch from the "New pane"
palette, a tab's context menu, or paneflow up. Nothing is written inside the
checkout: the ownership marker lives in the worktree's own git dir
(.git/worktrees/<name>/ in the main repository), so git status stays
clean and the marker disappears with the worktree. Settings > Worktrees edits
the same keys and lists the worktrees Paneflow manages.
Leaving the branch name empty in the palette creates a detached checkout at
the chosen base, named <base>-<sha7>; "Create branch here…" in the tab's
context menu turns it into a branch later, keeping any uncommitted changes.
A .worktreeinclude file at the repository root lists the git-ignored files
and directories to copy into every new worktree, one path per line relative to
the root, # for comments, trailing / optional for directories. Without the
file, Paneflow copies the top-level .env* files and AGENTS.override.md
when they exist. A file that already exists in the worktree is never
overwritten.
Before a managed worktree is removed, automatically or by hand, its
uncommitted changes are saved as a snapshot commit under
refs/paneflow/snapshots/ in the main repository: tracked edits, new files,
and the branch it was on. Settings > Worktrees lists the snapshots; Restore
recreates the worktree with those changes uncommitted on the same branch,
Delete drops the ref. A clean worktree leaves no snapshot.
| Key | Type | Default | Notes |
|---|---|---|---|
worktrees.dir | string/null | ~/.paneflow/worktrees | Root directory for managed worktrees. Each repository gets a subdirectory named <repo>-<hash>, each branch a directory under it. The default is ~/.paneflow/worktrees on every platform (%USERPROFILE%\.paneflow\worktrees on Windows). ~ expands to the home directory. Worktrees created under an earlier root, including the old <repo>.worktrees/ sibling, keep working where they are. |
worktrees.auto_remove | boolean/null | true | Remove a managed worktree when its workspace closes, and trim the oldest managed worktrees past keep_limit. Uncommitted changes are saved as a snapshot first, and the branch is never deleted. false keeps every worktree until you remove it from Settings > Worktrees or the tab menu. |
worktrees.keep_limit | integer/null | 15 | Number of managed worktrees to keep before the oldest ones that no open tab uses are removed. 0 to 200. |
worktrees.for_new_branches | boolean/null | true | Default state of the Worktree toggle in the New branch form. true creates a worktree for the branch; false switches the workspace checkout to it instead, and the pane opens at the repository root. The toggle rewrites this key, and the switch is refused while an agent is working in that checkout. |
{
"worktrees": {
"dir": "~/worktrees",
"auto_remove": true,
"keep_limit": 15
}
}agent_profiles
agent_profiles is a list. Each entry adds a launcher item that runs one of
the built-in agents with extra environment variables and arguments. Profiles
show up in the pane palette next to the built-in agents, and keep the base agent's status tracking, hooks, and sessions.
Settings > Agents > Profiles edits the same list.
| Key | Type | Default | Notes |
|---|---|---|---|
agent_profiles.*.name | string | required | Label shown in the launcher. |
agent_profiles.*.agent | string | required | Tag of the base agent: claude_code, codex, opencode, pi, hermes, grok, amp, cursor, gemini, kiro, antigravity, copilot, codebuddy, factory, qoder, or openclaw. |
agent_profiles.*.env | object | {} | Environment variables set on the agent process. A leading ~ expands to the home directory, and $NAME, ${NAME} or %NAME% anywhere in a value takes that variable from Paneflow's own environment. A $ or % that names nothing stays literal; a name the environment does not define is refused by the Settings editor and, in a hand-written config, leaves the whole value untouched with a warning in the log. |
agent_profiles.*.args | string array | [] | Extra arguments appended after the base agent's own flags. Each token must be a plain word: letters, digits, -, _, ., =. |
An entry with an unknown agent tag, a blank name, or an unsafe token is skipped with a warning; the other profiles still load.
{
"agent_profiles": [
{
"name": "Claude perso",
"agent": "claude_code",
"env": { "CLAUDE_CONFIG_DIR": "~/.claude-perso" },
"args": ["--model", "opus"]
}
]
}commands
commands entries back both simple shell commands and Settings >
Workspaces templates. A command entry must have name; workspace and
command are mutually exclusive.
| Key | Type | Default | Notes |
|---|---|---|---|
commands[].name | string | required | Display name. Must not be blank. |
commands[].description | string/null | none | Human-readable description. |
commands[].keywords | string array | [] | Fuzzy-search keywords. |
commands[].workspace | object/null | none | Workspace template. |
commands[].command | string/null | none | Simple shell command string. |
Workspace template keys:
| Key | Type | Default | Notes |
|---|---|---|---|
commands[].workspace.name | string/null | command name | Workspace display name. |
commands[].workspace.cwd | string/null | current cwd | Default workspace directory. |
commands[].workspace.layout_preset | string/null | none | even_h, even_v, main_vertical, or tiled. |
commands[].workspace.color | string/null | none | Six-digit hex accent color, no leading #. |
commands[].workspace.layout | object/null | preset layout | Layout tree root. |
Layout keys:
| Key | Type | Notes |
|---|---|---|
commands[].workspace.layout.type | string | pane or split. |
commands[].workspace.layout.surfaces | array | Required for pane nodes. |
commands[].workspace.layout.direction | string | Required for split: horizontal or vertical. |
commands[].workspace.layout.ratio | number/null | Legacy binary split ratio, range 0.1 to 0.9. Ignored when ratios exists. |
commands[].workspace.layout.ratios | number array/null | Per-child ratios for N-ary layouts. Must match child count and sum near 1.0. |
commands[].workspace.layout.children | array | Required for split; minimum two layout nodes. |
Surface keys inside a pane:
| Key | Type | Default | Notes |
|---|---|---|---|
commands[].workspace.layout.surfaces[].surface_type | string/null | terminal | Currently only terminal surfaces render. |
commands[].workspace.layout.surfaces[].name | string/null | derived | Surface tab name. |
commands[].workspace.layout.surfaces[].custom_name | string/null | none | User-assigned name that survives restart. |
commands[].workspace.layout.surfaces[].command | string/null | shell | Command to run when the surface is created. |
commands[].workspace.layout.surfaces[].prompt | string/null | none | Prompt to prefill after launching an agent command. |
commands[].workspace.layout.surfaces[].cwd | string/null | workspace cwd | Per-surface working directory. Relative paths resolve against workspace cwd. |
commands[].workspace.layout.surfaces[].path | string/null | none | File opened by a markdown surface. Ignored by terminal surfaces. |
commands[].workspace.layout.surfaces[].env | object/null | none | Extra environment variables. Wins over terminal.env on collision. |
commands[].workspace.layout.surfaces[].focus | boolean/null | false | Gives this surface initial focus. |
commands[].workspace.layout.surfaces[].scrollback | string/null | none | Saved plain-text scrollback restored with the surface. |
commands[].workspace.layout.surfaces[].agent | string/null | none | Stable tag of the agent CLI last detected in the surface. |
commands[].workspace.layout.surfaces[].font_size | number/null | global font_size | Per-surface font-size override, range 8.0 to 32.0. |
commands[].workspace.layout.surfaces[].session | string/null | none | UUID of the persistent host session the terminal reattaches to on restore. Written by Paneflow. |
telemetry
| Key | Type | Default | Notes |
|---|---|---|---|
telemetry.enabled | boolean/null | null | null means unanswered, true means opted in, false means opted out. |
Complete example
{
"$schema": "https://github.com/arthjean/paneflow/raw/main/schemas/paneflow.schema.json",
"$schemaVersion": "1.0.0",
"theme": "One Dark",
"font_family": "JetBrainsMono Nerd Font",
"font_size": 13,
"line_height": 1.0,
"cell_width": 1.0,
"windows_chrome_material": false,
"macos_chrome_material": false,
"terminal": {
"ligatures": true,
"scrollback_lines": 10000,
"cursor_shape": "block"
},
"agent_panel": {
"notify_when_agent_waiting": "PrimaryScreen"
},
"commands": [
{
"name": "API + Claude",
"description": "Open the API project with Claude and tests",
"workspace": {
"name": "API",
"cwd": "~/projects/api",
"layout_preset": "even_h",
"layout": {
"type": "pane",
"surfaces": [
{
"name": "Claude",
"agent": "claude_code",
"command": "claude",
"prompt": "Review the API changes"
}
]
}
}
}
]
}