mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-10-02 16:01:56 +00:00
* feat(tree): keep each workspace's recently closed tabs in the machine tree A workspace now keeps the tabs closed from it in its machine tree (`Workspace::closed`), so reopening one no longer depends on the window that closed it still being open. - `TabCloseRemembered` closes a tab into that list: the tab leaves the workspace as a close takes it (other windows hear `TabClosed`), its panes are stopped with their last screens kept on disk, and their records stay in the pane list. Panes the client held that the tree never recorded are ended outright. - `TabReopen` takes the named or newest entry off the list and answers it with its pane records, for the client to rebuild on fresh shells that open on the old screens. The records it leaves behind are claimed by the successors' seeds instead of being refused as duplicates. - Entries last 24 hours and a workspace keeps the newest 20. The daemon's scrollback keeper expires them on its existing timer, and an entry that goes takes its records and stored screens with it. - Both requests are gated on a new `closed-tabs` hello feature, offered only by a peer that serves a tree and panes, so an older daemon or remote server is never sent them. The field is `#[serde(default)]` and skipped while empty, so older trees load unchanged and an older build reads this one's. Refs #1021 Claude-Session: https://claude.ai/code/session_011mDkkQhwx4RJJBHee3yVpq * feat(tabs): reopen closed tabs from the machine, and a setting for when closing asks Closing a tab now closes it into its workspace's recently-closed list on the machine that holds the workspace, instead of into a list that ended with the window. ⌘⇧T asks that machine for the newest entry, so a closed tab comes back after quitting and relaunching the app, from any window of the workspace, and in remote workspaces whose server keeps the list. - The explicit close (⌘W, the tab's close button, bulk closes) registers the tab with the window's next sync, which turns that tab's `TabClose` into a `TabCloseRemembered`. The machine stops the panes and keeps their screens; the window no longer kills them itself. A tab dragged to another window or never rebuilt still goes out as a plain close. - If the close cannot reach the machine (no `closed-tabs` feature, a window that has not pulled its layout, a sync that fails or is thrown away), the window falls back to what it did before: the tab goes on its own list and its panes are killed from here. - Reopening waits briefly for this window's queued edits to land, so an immediate ⌘⇧T undoes the close it means, then rebuilds the tab through the existing restore: each pane a fresh shell in its old cwd, opening on its last screen, with its shell, SSH target and agent resume facts. The tab keeps its id. The window's own list is used when the machine has nothing. - The home screen offers the next tab ⌘⇧T would reopen, read from the machine mirror, which now tracks this window's remembered closes. A new setting, `confirm_close` (General > Tabs, "Confirm before closing"), decides when closing a tab or pane asks first: `never`, `when-busy` (the default and the old behaviour) or `always`. The SSH "Warn before closing" opt-in is honoured under every mode. Under `always`, Close Other Tabs and Close Tabs to the Right ask once for the whole batch. Refs #1021 Claude-Session: https://claude.ai/code/session_011mDkkQhwx4RJJBHee3yVpq * fix(tree): a remembered close marks its panes' records as no longer live The records stay in the pane list for the reopen, but the panes are stopped right after the close. Left at live: true, `tty7 wait` on a pane of a closed tab read it as a running agentless shell and waited forever instead of reporting it exited. Claude-Session: https://claude.ai/code/session_011mDkkQhwx4RJJBHee3yVpq
243 lines
14 KiB
Plaintext
243 lines
14 KiB
Plaintext
---
|
||
title: "config.json"
|
||
description: "Every key tty7 reads, its type, and its default."
|
||
---
|
||
|
||
| | |
|
||
|---|---|
|
||
| macOS / Linux | `~/.config/tty7/config.json` |
|
||
| Windows | `%APPDATA%\tty7\config.json` |
|
||
| Windows, portable mode | `<folder of tty7-app.exe>\data\config.json` |
|
||
| Override the whole directory | `TTY7_CONFIG_DIR`, or `--config-dir` |
|
||
|
||
Every key is optional — a missing one means its default, so you only write what
|
||
you change. Out-of-range numbers are clamped rather than rejected, and an
|
||
unrecognised enum value falls back to the default with a log line instead of
|
||
failing the file.
|
||
|
||
```json
|
||
{
|
||
"font_family": "JetBrains Mono",
|
||
"font_size": 14,
|
||
"theme_follow_system": true,
|
||
"theme_preset_light": "one_light",
|
||
"theme_preset_dark": "dracula",
|
||
"macos_option_as_alt": true,
|
||
"scrollback_limit": 50000
|
||
}
|
||
```
|
||
|
||
## Where the directory comes from
|
||
|
||
The config directory holds everything an instance keeps, not just
|
||
`config.json`: themes, sessions, scrollback, history, and the background
|
||
server's socket. The first of these wins:
|
||
|
||
1. `--config-dir <dir>` on the command line — `tty7-app.exe --config-dir D:\tty7-data`,
|
||
or `tty7-app --config-dir ~/tty7-alt` elsewhere. The app hands it on to the
|
||
server it starts, so the whole instance moves.
|
||
2. The `TTY7_CONFIG_DIR` environment variable. Unlike a flag it also reaches
|
||
launches you do not control — Explorer's **Open in tty7**, a desktop file,
|
||
the `tty7` command in another terminal.
|
||
3. **Windows portable mode**: a `data` folder next to `tty7-app.exe` in an
|
||
unzipped portable ZIP (the one carrying a `.tty7-portable` file). See
|
||
[Installation](/getting-started/installation).
|
||
4. The platform default in the table above.
|
||
|
||
Two directories are two independent instances, each with its own server and
|
||
sessions.
|
||
|
||
## Typography
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `font_family` | string | `"Hack"` | Primary face. Hack is bundled. |
|
||
| `font_fallbacks` | string[] | platform list | Ordered fallbacks. Stock platform faces are appended to whatever you write. |
|
||
| `font_family_bold` | string | — | A distinct bold face. |
|
||
| `font_family_italic` | string | — | A distinct italic face. |
|
||
| `font_features` | object | — | OpenType tags, e.g. `{"calt": true, "liga": 1}`. Four alphanumeric characters per tag. |
|
||
| `font_thicken` | bool | `true` | macOS: let font smoothing thicken strokes, light text most. `false` draws glyphs at their own weight. Applies after a restart. |
|
||
| `font_size` | number | `15` | Terminal text size in px (4–256). |
|
||
| `line_height` | number | `1.4` | Multiple of the font size (0.5–4). |
|
||
| `ui_font_size` | number | `16` | The interface's root size in px (12–24). |
|
||
| `ui_font_family` | string | — | The face for everything outside the grid. Unset means the system UI font. |
|
||
|
||
[More about fonts →](/customization/fonts)
|
||
|
||
## Theme and window
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `theme_preset` | string | `"light"` | Active theme id. |
|
||
| `theme_follow_system` | bool | `false` | Follow the OS appearance. |
|
||
| `theme_preset_light` | string | `"light"` | Used when following the system. |
|
||
| `theme_preset_dark` | string | `"dark"` | Used when following the system. |
|
||
| `theme_legible_palette` | bool | `true` | Brighten or darken bright ANSI colours that would be unreadable on the background. |
|
||
| `window_opacity` | number | — | 0.2–1.0. Unset means "follow the theme". |
|
||
| `window_blur` | bool | — | Blur behind a translucent window (macOS). Unset means "follow the theme". |
|
||
| `window_backdrop` | enum | `"auto"` | Windows only: `auto`, `blur`, `mica`, `mica-alt`, `acrylic`, `off`. |
|
||
| `dim_inactive_panes` | bool | `true` | Dim panes that are not focused. |
|
||
| `auto_hide_titlebar_buttons` | bool | `false` | Show the title bar's new tab and sidebar buttons only while the pointer is over the bar they sit in. |
|
||
| `startup_mode` | enum | `"normal"` | `normal`, `maximized`, `fullscreen`. |
|
||
| `remember_window_size` | bool | `true` | Reopen at the last size and position. |
|
||
| `restore_session` | bool | `true` | Reopen the last window's tabs, splits, and directories. |
|
||
| `gui_language` | enum | `"en"` | `en`, `zh-CN`, `ja-JP`. Anything else falls back to `en`. |
|
||
|
||
Built-in theme ids: `light`, `one_light`, `catppuccin_latte`, `rose_pine_dawn`,
|
||
`dark`, `dracula`, `harbor`, `one_dark_pro`, `rose_pine`. Your own themes take
|
||
their id from the file name. [More about themes →](/customization/themes)
|
||
|
||
## Tabs, sidebar, panels
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `tab_bar_position` | enum | `"left"` | `left` (sidebar) or `top` (strip). |
|
||
| `new_tab_position` | enum | `"after-current"` | Or `end`. |
|
||
| `confirm_close` | enum | `"when-busy"` | When closing a tab or pane asks first: `never`, `when-busy` (a program or agent is still running), `always` (even an idle shell). `ssh_warn_on_close` is honoured either way. |
|
||
| `sidebar_auto_grouping` | bool | `true` | Groups unpinned tabs by repository (SSH tabs by host). Off: a flat list below the pinned groups, which show either way. Pinned groups and folds are stored with the workspace, not here. |
|
||
| `sidebar_width` | number | `220` | Pixels (100–2000). |
|
||
| `sidebar_collapsed` | bool | `false` | |
|
||
| `right_panel_visible` | bool | `false` | |
|
||
| `right_panel_width` | number | `260` | Pixels (100–2000). |
|
||
| `right_panel_tab` | enum | `"info"` | `info`, `changes`, `files`. |
|
||
| `diff_view` | enum | `"split"` | Or `unified`. Global, not per file. |
|
||
| `document_layout` | enum | `"dock"` | Where an open file or diff is drawn: `dock` beside the terminal, or `fill` over the workspace. What a fresh tab starts as — each tab keeps its own from there. |
|
||
| `document_ratio` | number | `0.5` | The docked column’s share of the terminal column (0.2–0.8). Named widths are `0.333`, `0.5`, `0.667`. |
|
||
| `scm_graph_expanded` | bool | `false` | Whether the history section starts open. |
|
||
| `editor_soft_wrap` | bool | `false` | Whether files open in the code panel with soft wrap on. Follows the status bar's Wrap toggle — whatever it was last left at. |
|
||
| `editor_markdown_preview` | bool | `false` | Whether Markdown files open rendered rather than as source. Follows the Preview / Edit toggle the same way. |
|
||
| `show_tray_icon` | bool | `true` | The tray / menu bar status item. |
|
||
|
||
## Terminal
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `shell` | object | — | `{"program": "fish", "args": ["-l"]}`. Unset uses the platform default. `args` you write are launched verbatim, which also turns off [shell integration](/reference/shell-integration) for that shell — leave them out to keep it. |
|
||
| `custom_shells` | array | `[]` | Extra entries for the new-tab menu: `[{"label": "Ubuntu", "program": "wsl.exe", "args": ["-d", "Ubuntu"]}]`. Launched exactly as written, listed after the detected shells — in the menu's **Other Shells…** list until you have opened it, and in Search Everywhere's Terminals tab as *Shell:* followed by its label. Give an entry arguments and tty7 also skips [shell integration](/reference/shell-integration) for it, so it has no prompt marks, working-directory tracking, or command-finished notifications; an entry with no arguments, on a shell tty7 recognizes, is integrated like the detected row beside it. An entry with no `program` is skipped; with no `label` it is named after its program. |
|
||
| `working_directory` | object | `{"strategy":"inherit"}` | `strategy` is `inherit`, `home`, or `custom`; `path` is used when custom. |
|
||
| `env` | object | `{}` | Extra environment variables for every pane. |
|
||
| `scrollback_limit` | number | `10000` | Lines per pane (100–100,000). New panes only. |
|
||
| `cursor_style` | enum | `"block"` | `block`, `bar`, `underline`. |
|
||
| `prompt_cursor_style` | enum | `"follow"` | The cursor at the shell prompt: `follow`, `block`, `bar`, `underline`. `follow` uses `cursor_style` everywhere; any other value leaves `cursor_style` to the programs the shell runs, e.g. `bar` here with `cursor_style: "block"` gives a bar at the prompt and a block in a TUI that sets no shape of its own. A shell prompt in vi mode keeps its own shapes. |
|
||
| `cursor_blink` | bool | `true` | |
|
||
| `bell` | enum | `"visual"` | `none`, `visual`, `audible`, `both`. |
|
||
| `per_pane_history` | bool | `false` | Give each pane its own shell history file. |
|
||
|
||
## Mouse and scrolling
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `mouse_scroll_multiplier` | number | `1.0` | 0.1–10. |
|
||
| `smooth_scroll` | bool | `true` | Ease each wheel notch. Trackpads unaffected. |
|
||
| `mouse_reporting` | bool | `true` | Let full-screen apps handle clicks and scrolling. |
|
||
| `mouse_zoom_modifier` | enum | `"none"` | Modifier that makes the wheel resize the font: `platform` (⌘ on macOS, Ctrl elsewhere), `ctrl`, `alt`, `none` (the wheel never zooms). |
|
||
| `mouse_hide_while_typing` | bool | `true` | |
|
||
| `focus_follows_mouse` | bool | `false` | |
|
||
|
||
## Input and clipboard
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `prompt_editor` | bool | `true` | tty7 edits the shell prompt. Off sends every key at the prompt to the shell, so its own line editor (ZLE, readline) owns editing. Shell integration stays on. |
|
||
| `tab_completion` | bool | `true` | tty7's completion menu on <kbd>⇥</kbd>. Off hands the key to the shell. Needs `prompt_editor`. |
|
||
| `history_search` | bool | `true` | tty7's fuzzy history on <kbd>⌃ R</kbd>. Off hands the key to the shell. Needs `prompt_editor`. |
|
||
| `smart_select` | bool | `true` | Double-click grabs URLs, paths, bracket pairs, CJK words. |
|
||
| `word_separators` | string | see below | Characters that end a word. Used when smart selection is off. |
|
||
| `copy_on_select` | bool | `false` | |
|
||
| `clipboard_trim_trailing_spaces` | bool | `false` | |
|
||
| `macos_option_as_alt` | bool | `false` | <kbd>⌥</kbd>+key sends the escape chord instead of typing a special character. |
|
||
| `keybindings` | object | `{}` | `{"SplitRight": "cmd-d"}`. [Syntax →](/customization/keybindings) |
|
||
| `keybinding_preset` | string | `"default"` | Or `"tmux"`. |
|
||
| `prefix` | string | `"ctrl-b"` | The tmux preset's prefix. |
|
||
|
||
The default `word_separators` are a comma, a box-drawing bar, a backtick, a
|
||
pipe, a colon, both quote characters, a space, the six bracket characters, the
|
||
angle brackets, and a tab:
|
||
|
||
```json
|
||
{ "word_separators": ",│`|:\"' ()[]{}<>\t" }
|
||
```
|
||
|
||
## Links
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `link_url` | bool | `true` | Underline and open URLs on ⌘/Ctrl-click. |
|
||
| `link_file_open` | string | `internal` | What a file link opens: `internal` (tty7's editor, at the line), `system` (the OS file association), `command`. A file on another machine always uses `internal`. |
|
||
| `link_file_command` | string | — | Command for file links under `link_file_open: command`. `{path}`, `{line}`, `{column}` are substituted; a flag whose value is missing is dropped. |
|
||
| `ssh_loopback_forward` | bool | `true` | Forward the ports a remote pane starts serving, and open its `localhost:PORT` links through those forwards. |
|
||
|
||
## Notifications
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `notify_on_command_finish` | enum | `"unfocused"` | `never`, `unfocused`, `always`. |
|
||
| `notify_threshold_secs` | number | `10` | How long a command must run to qualify (1–3600). |
|
||
|
||
## Agents
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `agent_commands` | object | `{}` | Map a wrapper command to an agent slug: `{"cc": "claude"}`. |
|
||
| `agent_launch` | object | `{}` | The command line quick launch types for an agent, by slug: `{"claude": "claude --dangerously-skip-permissions"}`. Unset agents launch as their bare binary. [More →](/agents/overview#quick-launch) |
|
||
| `agent_frecency` | object | `{}` | Written by tty7: how often and how recently each agent ran, to order quick launch. |
|
||
| `restore_agent_sessions` | bool | `true` | Relaunch an agent conversation when a lost pane is restored. |
|
||
| `install_cli_on_path` | bool | `true` | Put the bundled `tty7` command on PATH at launch. |
|
||
|
||
[Agent slugs →](/agents/overview#your-own-wrapper)
|
||
|
||
## SSH
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `verify_host_keys` | bool | `true` | |
|
||
| `ssh_warn_on_close` | bool | `false` | Confirm before closing a live connection. |
|
||
|
||
### servers.json
|
||
|
||
The saved hosts are not in `config.json`: they live in `servers.json` in the
|
||
same directory, so `config.json` can be synced between machines without taking
|
||
your list of servers along. The file is written with mode `0600` and read as
|
||
forgivingly as `config.json`; if it cannot be parsed, tty7 keeps a copy at
|
||
`servers.json.corrupt` and saves nothing until it is repaired.
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `ssh_profiles` | array | `[]` | Managed from **Settings → SSH**. Secrets live in the OS keychain, never here. |
|
||
| `ssh_profile_frecency` | object | `{}` | How often and how recently each host is used, for ranking. Written by the app. |
|
||
|
||
An older `config.json` that still holds `ssh_profiles` is split the first time
|
||
a newer tty7 reads it: `servers.json` is written first, and only then are the
|
||
two keys removed from `config.json`, leaving the rest of it as it was. If both
|
||
files hold hosts, `servers.json` wins, and the copy in `config.json` is dropped
|
||
the next time settings are saved.
|
||
|
||
Each object in `ssh_profiles` can also set:
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `remote_clipboard_write` | bool | `false` | Allow programs on that SSH host to write PNG, JPEG, GIF, or WebP images to this machine's clipboard with OSC 5522. |
|
||
|
||
## Updates and network
|
||
|
||
| Key | Type | Default | |
|
||
|---|---|---|---|
|
||
| `check_for_updates` | bool | `true` | |
|
||
| `update_channel` | enum | `"stable"` | Or `nightly`. |
|
||
| `auto_download_updates` | bool | `true` | Fetch and verify in the background so installing is a restart. Packages are ~25–30 MB and a check happens every six hours. |
|
||
| `http_proxy` | string | — | For tty7's *own* traffic only — update checks, downloads, remote-server installs. `http://…` or `socks5://…`. Programs in a pane are unaffected. |
|
||
|
||
[Updates →](/reference/updates)
|
||
|
||
## Keys tty7 manages itself
|
||
|
||
`shell_frecency` and `command_frecency` record how often and how recently you
|
||
open a shell or run a command, so the new-tab menu and Search Everywhere can rank
|
||
them. They are written by the app; there is no reason to edit them.
|
||
|
||
<Note>
|
||
If the file cannot be parsed, tty7 starts on defaults, keeps your original at
|
||
`config.json.corrupt`, and logs the reason. It never silently overwrites what
|
||
you wrote.
|
||
</Note>
|