mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-10-03 16:01:59 +00:00
* feat(sidebar): keep pinned groups on the workspace and derive the rest Replace the sidebar's hand-made groups with the model from #955: the sidebar groups tabs by repo automatically, and you pin what you want to keep. - Pinned groups (`PinnedGroup`: id, optional name, optional folder, fold) are stored on the workspace in the machine tree, in display order, and a tab points at one by `GroupId`. Everything else is an auto group worked out every frame and never stored: by repo home, or by `user@host` for an SSH pane — native or a shell that ssh'd onward — so `/home/ubuntu` on two machines no longer lands under one header. - A tab whose cwd *enters* a pinned folder joins it (deepest folder wins; a repo home equal to the folder counts, which keeps worktrees with their repo). It is edge-triggered through `EntryWatch`, so a tab dragged out while still inside the folder stays out until it leaves and comes back, and a tab restored at launch is not pulled in by where it already sits. - Groups sync as one `WorkspaceSetGroups` / `GroupsChanged`, pushed only from an edit and adopted from every pull, so a fresh window can never push an empty set over the workspace's. The machine hands tabs naming a dropped group back to auto grouping in the same mutation. Control dialect → v11. - Config: `sidebar_grouping` (three modes) and `sidebar_collapsed_groups` give way to one `sidebar_auto_grouping` toggle; folds live with the workspace. - `tty7 tab ls` reports the pinned group a tab is in (name or folder leaf; JSON carries id, name and folder). * feat(sidebar): draw pinned groups above a divider, with their own gestures The sidebar now reads as two halves: the groups you keep, in the order you put them, then a divider, then the groups it works out (Arc-style). - Pinned headers drag-reorder among themselves (their own reorder surface, so a pinned header cannot be dropped among the derived ones); the order lands on the workspace's group list, not on the tabs. - An auto header carried above the divider is pinned when let go. With nothing pinned yet the divider appears during that drag as a "Drop here to pin" zone, since a hairline at the top of the list is nothing to aim at. - A tab kept in a pinned group and dropped anywhere below the divider goes back to auto grouping; the divider lights to say so. - An empty pinned group stays, with a "+ New Tab" row that opens a tab in its folder (or where ⌘T would, for a label group) and files it there. - Folder groups carry a pin mark that unpins on click and a tooltip with the folder; auto headers show pin and "+" on hover. - Header menus: pinned — Rename, Set Folder… (local workspaces), Use Current Tab's Folder, Clear Folder, New Tab, Unpin (folder groups), Delete. Auto — Pin Group, New Tab. Nothing renames an auto group; nothing pins implicitly. * feat(sidebar): open folders as pinned groups from Finder, the file tree and the palette Every way into a pinned group the design calls for: - Drop a folder from Finder or Explorer onto the sidebar to pin it (a local workspace only — a dropped path is this machine's, and a folder group keeps a directory on the workspace's host). Files are let fall. - "Pin as Group" on a folder in the file tree, on local and remote workspaces alike, since the tree and the group are both on the workspace's host. - Palette "New Group" makes an empty label group and opens its name for typing; "Open Folder as Group…" picks a folder with the system picker, pins it and opens a tab in it. The picker browses this computer, so that one is not offered on a remote workspace. - Tab right-click "Move to Group" lists the pinned groups plus "New Group…", which files the tab in a fresh label group with its name open for typing. Pinning a folder already pinned hands back the group that keeps it rather than making a second one to split its tabs with. * fix(sidebar): let groups that arrive from elsewhere pull no tab into a folder A window draws its first frames before its copy of the workspace's groups lands, so every tab's entry watch recorded "in no folder" — and the groups landing then read as each tab walking into its folder. A restored tab, or one dragged out of its folder group, was pulled back in on every launch. Groups adopted from a pull or from another window's `GroupsChanged` now start every tab's watch over from where it is; only this window's own pin gathers the tabs inside the folder, and says so tab by tab. A tab also goes up with the group it names even when the window does not know that group yet, so a sync in that same gap cannot send every kept tab back to auto grouping. * docs(sidebar): describe pinned and auto groups, and log the change Rewrite the sidebar page's grouping section around "grouped by repo automatically; pin what you want to keep": the divider, folder and label groups, the edge-triggered join, every way to pin, and the header menus. The configuration reference swaps `sidebar_grouping` for `sidebar_auto_grouping`, the CLI reference describes the GROUP column as the pinned group, and the changelog gains an Unreleased entry (#955). * fix(sidebar): file a tab opened by the CLI in a pinned folder into it A tab that reaches a window as TabCreated — from `tty7 tab new` or another window — started its entry watch as a restored tab, so opening one inside a pinned folder left it in the auto group below. It is as new as a tab opened here, and now joins the folder like one; every window that hears of it reaches the same answer. * test(machine): build the group sets in their initializers Clippy's field_reassign_with_default on the two WorkspaceGroups the set-groups test assembles. * fix(sidebar): draw restored tabs in their auto group, and title by repo again Auto groups are not stored, so after a restart every tab sat in Ungrouped until its own repo probe came back, then jumped; before pinned groups the stored repo key put it in place on the first frame. Each tab now carries `last_auto`, the auto group it last resolved to, as a hint: stored with the tab, sent up alongside its group in `TabSetGroup` whenever the live answer moves, and used to draw the tab until the probe answers. The probe always wins and rewrites the hint, and the hint never outranks a pinned group or the folder-entry rule. Another window's hint only fills a gap, so two windows can never bounce a disagreement between them. The workspace's fallback title regained the repo majority it lost: the most common pinned folder first, then the repo most unpinned tabs were last filed under (a worktree counting toward its repo home), then a pane's cwd. * refactor: drop what the new sidebar left unused, and two clippy findings - `TerminalView::native_ssh_cwd` and its helper existed for the sidebar's old folder grouping of native SSH panes; an SSH tab now groups by host, and nothing else read it. - The file tree's context menu takes `cx` instead of `danger` and the new groups flag, back to the argument count it had on main. - A title test builds its workspace in the initializer.
242 lines
14 KiB
Plaintext
242 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. |
|
||
| `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`. |
|
||
| `ssh_tab_title` | enum | `"dynamic"` | What an SSH tab is called: `dynamic` (the title the remote side sets), `profile-name` (the saved host's name, a `~/.ssh/config` alias, or the address typed for a quick connect), or `hostname` (the address dialled). A tab you renamed keeps its name. |
|
||
| `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_diff_preview` | bool | `true` | Clicking a row's `+N −M` opens the diff overlay. |
|
||
| `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 the palette 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`. |
|
||
| `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 | `"platform"` | Modifier that makes the wheel resize the font: `platform` (⌘ on macOS, Ctrl elsewhere), `ctrl`, `alt`, `none`. |
|
||
| `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 the palette 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>
|