mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 16:02:20 +00:00
docs: add a Mintlify documentation site
38 pages under docs/, written against the source rather than the README: config keys and their clamps from core::config, default keybindings from ui::keymap, every CLI verb and flag from tty7-cli, agent aliases and hook/fork/resume support from core::cli_agent, and Settings paths taken from the actual en-US strings. docs/features.md and its zh-CN translation are retired — everything in them now lives in a page of its own, plus the two things they carried that nothing else did (IME input, the performance notes). README and README.zh-CN point at docs/ instead. Screenshots and videos are placeholders for now: docs/images/placeholder.svg with a caption naming what each shot should be.
This commit is contained in:
@@ -54,8 +54,11 @@ Native builds for each platform on [**Releases**](https://github.com/l0ng-ai/tty
|
||||
| **CLI + Skills** | bundled `tty7` CLI · [agent skill](skills/tty7/SKILL.md) · pane/workspace control · real PTY commands · output, process, port, and agent status |
|
||||
| **SSH** | native russh stack: profiles with keychain secrets · SFTP panel · port forwarding · jump hosts · one-time, unprivileged `tty7-server` install |
|
||||
|
||||
Terminal and keybinding reference: [docs/features.md](docs/features.md). The agent-facing CLI
|
||||
interface is documented in [skills/tty7/SKILL.md](skills/tty7/SKILL.md).
|
||||
Full documentation lives in [**`docs/`**](docs/) —
|
||||
[keyboard shortcuts](docs/reference/keyboard-shortcuts.mdx) ·
|
||||
[config.json](docs/reference/configuration.mdx) ·
|
||||
[CLI reference](docs/cli/reference.mdx). The agent-facing CLI interface is also
|
||||
documented in [skills/tty7/SKILL.md](skills/tty7/SKILL.md).
|
||||
|
||||
Install the skill with:
|
||||
|
||||
|
||||
+4
-1
@@ -54,7 +54,10 @@
|
||||
| **CLI + Skills** | 安装包自带 `tty7` CLI · [agent skill](skills/tty7/SKILL.md) · pane/工作区控制 · 真实 PTY 命令 · 输出、进程、端口和 agent 状态 |
|
||||
| **SSH** | 原生 russh 栈:profile 凭据进 keychain · SFTP 面板 · 端口转发 · 跳板机 · 一次无 sudo 安装 `tty7-server` |
|
||||
|
||||
终端和快捷键参考:[docs/features.zh-CN.md](docs/features.zh-CN.md)。面向 agent 的 CLI 接口见
|
||||
完整文档在 [**`docs/`**](docs/)(英文)——
|
||||
[快捷键](docs/reference/keyboard-shortcuts.mdx) ·
|
||||
[config.json](docs/reference/configuration.mdx) ·
|
||||
[CLI 参考](docs/cli/reference.mdx)。面向 agent 的 CLI 接口另见
|
||||
[skills/tty7/SKILL.md](skills/tty7/SKILL.md)。
|
||||
|
||||
通过以下命令安装 skill:
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: "Orchestrating agents"
|
||||
description: "One agent opening a pane for another, waiting on it, and reading the result."
|
||||
---
|
||||
|
||||
Once an agent's status is a thing a program can ask about, one agent can run
|
||||
another. tty7 gives that loop a primitive instead of leaving it to screen
|
||||
scraping.
|
||||
|
||||
## The loop
|
||||
|
||||
```bash
|
||||
# 1. give the worker a pane
|
||||
PANE=$(tty7 split --v)
|
||||
|
||||
# 2. hand it a task
|
||||
tty7 send "$PANE" 'claude -p "add tests for the parser"' --enter
|
||||
|
||||
# 3. sleep until it needs you or finishes
|
||||
tty7 wait "$PANE" --until waiting,done --changed --timeout 600
|
||||
|
||||
# 4. read what happened
|
||||
tty7 capture "$PANE" --plain
|
||||
|
||||
# 5. clean up
|
||||
tty7 pane close "$PANE"
|
||||
```
|
||||
|
||||
That is the whole shape. The interesting step is the third.
|
||||
|
||||
## `tty7 wait`
|
||||
|
||||
```bash
|
||||
tty7 wait [%PANE] [--until STATE,…] [--changed] [--timeout SECS] [--interval MS]
|
||||
```
|
||||
|
||||
Blocks until the pane's agent reaches one of the states you named.
|
||||
|
||||
| Flag | Default | |
|
||||
|---|---|---|
|
||||
| `--until` | `waiting,done,exit` | Which states end the wait: `idle`, `working`, `waiting`, `done`, `exit` |
|
||||
| `--changed` | off | Ignore the state the pane was *already* in — only wake on one it moved into after the wait began |
|
||||
| `--timeout` | none | Give up after this many seconds, exiting 124 |
|
||||
| `--interval` | 500 ms | How often to poll |
|
||||
|
||||
Exit codes are made for scripts:
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `0` | A state you asked for was reached |
|
||||
| `124` | Timed out — the `timeout(1)` convention, so "not yet" is distinguishable from "broken" |
|
||||
| `1` | The worker died first; the JSON says `"status": "exit"` |
|
||||
|
||||
The reply carries the agent's own message and its native session id, so a
|
||||
wake-up is directly actionable.
|
||||
|
||||
### Why `--changed` matters
|
||||
|
||||
The status the server keeps is a **level, not an event**. `done` stands until
|
||||
the next turn begins; `waiting` stands until the agent moves again.
|
||||
|
||||
So a `wait` issued immediately after a `send` can answer with the *previous*
|
||||
turn's state, before the worker has even read the input. `--changed` refuses the
|
||||
state the pane was already in, which is what every round after the first needs.
|
||||
Without it, the JSON's `stale` flag tells you whether that happened.
|
||||
|
||||
## Watching everything at once
|
||||
|
||||
```bash
|
||||
tty7 agents # every agent on the machine: pane, agent, status, message
|
||||
tty7 agents --json # the same, parseable
|
||||
```
|
||||
|
||||
If you are an agent yourself, you are in that list too.
|
||||
|
||||
## The Claude Code skill
|
||||
|
||||
**Settings → Agents** has a switch that installs
|
||||
`~/.claude/skills/tty7-orchestration` — a skill teaching a *primary* Claude Code
|
||||
agent this whole delegation loop: open a worker pane, send it one bounded task,
|
||||
`wait` on it, answer what it asks, collect the result, close the pane.
|
||||
|
||||
It is a skill rather than a global instruction on purpose. Only its one-line
|
||||
description rides in context until something reaches for it, and worker agents
|
||||
never inherit orchestration authority.
|
||||
|
||||
Uninstalling removes the file tty7 wrote and refuses to touch one it did not, so
|
||||
a hand-written skill that happens to share the directory name survives.
|
||||
|
||||
## Rules of the road
|
||||
|
||||
<Warning>
|
||||
The panes on a machine are somebody's real work, and some of them are other
|
||||
agents mid-task. Treat anything you did not create as read-only.
|
||||
</Warning>
|
||||
|
||||
- **Never `send` into a pane you did not open.** Check `tty7 agents` first.
|
||||
- **Never close a pane, tab, or workspace you did not create.**
|
||||
- **Never `server stop` or `server restart`.** Every pane on the machine dies
|
||||
with it.
|
||||
- **Clean up what you did create** — `tty7 pane close %83` when you are done.
|
||||
|
||||
The full agent-facing contract is in [the skill](/cli/agent-skill).
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: "Coding agents"
|
||||
description: "What tty7 does around Claude Code, Codex, and 16 others — without ever wrapping them."
|
||||
---
|
||||
|
||||
tty7 recognises coding agents running in a pane and builds around them. It does
|
||||
not wrap them, proxy them, or replace their interface: the agent you start is
|
||||
the agent you get, running in a normal PTY, with its own UI. tty7 adds the
|
||||
things a terminal is in a position to add — who is running where, what they
|
||||
need, and what changed.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: a sidebar of agent sessions across several repos, each with a brand avatar and status dot">
|
||||
<img src="/images/placeholder.svg" alt="Agent sessions in the tty7 sidebar" />
|
||||
</Frame>
|
||||
|
||||
## Which agents
|
||||
|
||||
Eighteen CLIs are recognised on sight, by the command running in the pane:
|
||||
|
||||
| Agent | Command |
|
||||
|---|---|
|
||||
| Claude Code | `claude`, `claude-code` |
|
||||
| Codex | `codex`, `codex-cli` |
|
||||
| Gemini | `gemini`, `gemini-cli` |
|
||||
| Copilot | `copilot` |
|
||||
| Cursor | `cursor-agent` |
|
||||
| Amp | `amp` |
|
||||
| OpenCode | `opencode` |
|
||||
| Aider | `aider`, `aider-chat` |
|
||||
| Goose | `goose` |
|
||||
| Droid | `droid` |
|
||||
| Grok | `grok` |
|
||||
| Qwen Code | `qwen`, `qwen-code` |
|
||||
| Auggie | `auggie` |
|
||||
| Hermes | `hermes` |
|
||||
| Vibe | `vibe`, `vibe-acp` |
|
||||
| Antigravity | `agy`, `antigravity` |
|
||||
| Pi | `pi` |
|
||||
| Oh My Pi | `omp` |
|
||||
|
||||
Detection sees through the usual disguises: a full path, a `.cmd` or `.exe` on
|
||||
Windows, leading environment assignments, and an interpreter in front
|
||||
(`node .../claude/cli.js`).
|
||||
|
||||
### Your own wrapper
|
||||
|
||||
If you launch agents through a wrapper script, map its name to an agent in
|
||||
`config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_commands": {
|
||||
"cc": "claude",
|
||||
"work": "codex"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The key is your command's name; the value is one of the slugs above (`claude`,
|
||||
`codex`, `gemini`, `aider`, `amp`, `opencode`, `copilot`, `cursor`, `goose`,
|
||||
`droid`, `pi`, `auggie`, `hermes`, `vibe`, `antigravity`, `grok`, `qwen`,
|
||||
`omp`).
|
||||
|
||||
## What you get for free
|
||||
|
||||
Just by running an agent in a pane:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Brand avatars" icon="id-badge">
|
||||
The tab chip and sidebar row show which agent runs where, so ten tabs stay
|
||||
legible.
|
||||
</Card>
|
||||
<Card title="Git context" icon="code-branch">
|
||||
The branch and working-tree diff on the row, refreshed as the agent works.
|
||||
</Card>
|
||||
<Card title="Session resume" icon="rotate-left">
|
||||
A pane lost to a reboot relaunches the conversation, carrying its original
|
||||
flags. [More →](/agents/sessions)
|
||||
</Card>
|
||||
<Card title="Context feed" icon="paper-plane">
|
||||
Palette commands that hand the current selection or the repo's `git diff` to
|
||||
the running agent as a prompt.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## What needs a hook
|
||||
|
||||
Live status — **working**, **needs your input**, **done** — comes from the agent
|
||||
itself, over a channel tty7 installs into that agent's configuration. It powers
|
||||
the status dots, the notifications, the tray icon, and `tty7 wait`.
|
||||
|
||||
Installing takes one click per agent under **Settings → Agents**.
|
||||
[Status and notifications →](/agents/status)
|
||||
|
||||
## Where to go next
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Status and notifications" icon="circle-dot" href="/agents/status">
|
||||
Hooks, status dots, the tray icon.
|
||||
</Card>
|
||||
<Card title="Sessions" icon="code-branch" href="/agents/sessions">
|
||||
Resume, fork, and copying a session id.
|
||||
</Card>
|
||||
<Card title="Orchestration" icon="diagram-project" href="/agents/orchestration">
|
||||
One agent driving another with `tty7 wait`.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "Agent sessions"
|
||||
description: "Resuming a conversation after a reboot, forking a live one, and getting at the session id."
|
||||
---
|
||||
|
||||
Coding agents keep their own conversation history, addressed by a session id.
|
||||
Because tty7's hooks learn that id, it can do three things with it.
|
||||
|
||||
## Resume after a restart
|
||||
|
||||
When the server goes away — a reboot, a crash, a deliberate restart — the shells
|
||||
go with it. Panes that were running an agent relaunch the conversation on
|
||||
restore instead of coming back to a bare prompt:
|
||||
|
||||
```bash
|
||||
claude --dangerously-skip-permissions --resume 8f3c…
|
||||
```
|
||||
|
||||
The original launch flags are replayed, so the pane comes back the way you
|
||||
started it, not the way the defaults would.
|
||||
|
||||
Supported for Claude Code, Codex, Gemini, OpenCode, Amp, Cursor, Copilot, Grok,
|
||||
Pi, and Oh My Pi. Turn it off with `restore_agent_sessions: false`.
|
||||
|
||||
<Note>
|
||||
Resume needs the agent's hooks installed, since the session id comes from
|
||||
them. [Installing hooks →](/agents/status)
|
||||
</Note>
|
||||
|
||||
## Fork a live session
|
||||
|
||||
Forking branches a running conversation into a second, independent one. The
|
||||
original keeps going untouched; both continue separately from the same history.
|
||||
|
||||
Right-click a **pane** to fork into a split — the menu offers a placement —
|
||||
or right-click the **tab or sidebar row** to open the fork in a new tab.
|
||||
|
||||
| Agent | What tty7 runs |
|
||||
|---|---|
|
||||
| Claude Code | `claude --resume <id> --fork-session` |
|
||||
| Codex | `codex fork <id>` |
|
||||
| Grok | `grok --resume <id> --fork-session` |
|
||||
| OpenCode | `opencode --session <id> --fork` |
|
||||
| Oh My Pi | `omp --fork <id>` |
|
||||
|
||||
It is the agent's own fork command, run in a new pane — nothing is copied by
|
||||
tty7 itself.
|
||||
|
||||
<Warning>
|
||||
A fork duplicates the whole transcript in the agent's session store, so
|
||||
forking repeatedly costs real disk. A pane on a
|
||||
[remote machine](/remote/workspaces) cannot fork, because the command would
|
||||
run against the local agent.
|
||||
</Warning>
|
||||
|
||||
## Copy the session id
|
||||
|
||||
**Copy Session ID** — in the pane's right-click menu, beside *Copy Working
|
||||
Directory*, and in the command palette — puts the agent's native id on the
|
||||
clipboard. Paste it into `codex resume`, a bug report, or another tool.
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: "Status and notifications"
|
||||
description: "Installing the hooks, reading the dots, and being told when an agent needs you."
|
||||
---
|
||||
|
||||
An agent working for two minutes and an agent that stopped ninety seconds ago
|
||||
waiting for permission look identical from outside. tty7 fixes that by letting
|
||||
the agent say which one it is.
|
||||
|
||||
## Installing the hooks
|
||||
|
||||
**Settings → Agents** lists every agent that can report status, with an
|
||||
**Install** button beside each:
|
||||
|
||||
| Agent | |
|
||||
|---|---|
|
||||
| Claude Code · Codex · Copilot · OpenCode · Pi · Grok · Oh My Pi | Hooks available |
|
||||
| Gemini · Aider · Amp · Cursor · Goose · Droid · Auggie · Hermes · Vibe · Antigravity · Qwen Code | Detected and labelled, but no status channel yet |
|
||||
|
||||
Installing writes into that agent's own configuration directory and can be
|
||||
undone from the same row — the button becomes **Uninstall**, and reads
|
||||
**Outdated** with an **Update** when tty7 ships a newer hook.
|
||||
|
||||
<Note>
|
||||
The hooks only do anything inside tty7. Running the same agent in another
|
||||
terminal is unaffected.
|
||||
</Note>
|
||||
|
||||
Connected [remote machines](/remote/workspaces) get their own row, so an agent
|
||||
running on a dev box reports status to the window you are watching it from.
|
||||
|
||||
## The status dot
|
||||
|
||||
Every tab chip and sidebar row carries a dot:
|
||||
|
||||
| Dot | Meaning |
|
||||
|---|---|
|
||||
| 🔵 **Blue** | Working |
|
||||
| 🟠 **Amber** | Needs your input — a permission prompt, a question |
|
||||
| 🟢 **Green** | Done with this turn |
|
||||
|
||||
<Frame caption="Placeholder — screenshot: three sidebar rows, one working, one waiting, one done">
|
||||
<img src="/images/placeholder.svg" alt="Agent status dots" />
|
||||
</Frame>
|
||||
|
||||
The same three states are what `tty7 agents` reports as `running` / `waiting` /
|
||||
`idle`, and what [`tty7 wait`](/agents/orchestration) blocks on.
|
||||
|
||||
## Notifications
|
||||
|
||||
Two, both following your **Settings → Window & Tabs → Notifications** policy:
|
||||
|
||||
- **"needs your permission…"** the moment an agent blocks on you
|
||||
- **"finished after 42s"** at the end of a turn
|
||||
|
||||
Which means that by default — *When unfocused* — you are told the instant you
|
||||
are the bottleneck, and left alone while you are watching.
|
||||
|
||||
## The tray icon
|
||||
|
||||
tty7 keeps a status item in the system tray (menu bar on macOS). It flips to an
|
||||
attention state the moment *any* agent anywhere needs input, so you can see it
|
||||
without the window in front of you.
|
||||
|
||||
Its menu lists every agent pane with its brand avatar and status dot — click one
|
||||
to reveal it — and also holds the notification policy switch and **Quit and Stop
|
||||
Server…**.
|
||||
|
||||
Turn it off with **Settings → Window & Tabs → Show tray icon**
|
||||
(`show_tray_icon: false`).
|
||||
|
||||
## Sending an agent some context
|
||||
|
||||
Two command-palette entries hand what is in front of you to the agent running in
|
||||
the pane, as a ready-made prompt:
|
||||
|
||||
| Command | Sends |
|
||||
|---|---|
|
||||
| **Agent: Send Selection** | The current terminal selection |
|
||||
| **Agent: Send Git Diff for Review** | The repository's `git diff` |
|
||||
|
||||
If nothing recognisable is running, tty7 says *"No running coding agent found"*
|
||||
rather than typing into your shell.
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: "The agent skill"
|
||||
description: "Teaching a coding agent to use tty7 properly — including when not to."
|
||||
---
|
||||
|
||||
The CLI is only half of the story. An agent has to know *when* reaching for a
|
||||
pane beats running a command, and — more importantly — which panes it must not
|
||||
touch. That is what the skill is for.
|
||||
|
||||
## Installing it
|
||||
|
||||
```bash
|
||||
npx skills add l0ng-ai/tty7
|
||||
```
|
||||
|
||||
The source lives at
|
||||
[`skills/tty7/`](https://github.com/l0ng-ai/tty7/tree/main/skills/tty7) in the
|
||||
repository: a `SKILL.md` and a full command reference.
|
||||
|
||||
<Note>
|
||||
This is separate from the **orchestration** skill installed from **Settings →
|
||||
Agents**, which teaches a *primary* agent to delegate to worker agents. This
|
||||
one teaches any agent to drive tty7 at all.
|
||||
[Orchestration →](/agents/orchestration)
|
||||
</Note>
|
||||
|
||||
## What it teaches
|
||||
|
||||
### When to use a pane instead of a plain command
|
||||
|
||||
The Bash-style tool an agent already has is right for anything that starts, does
|
||||
its job, and exits. A pane is right when:
|
||||
|
||||
- **It should not block.** A dev server, a watcher, `tail -f`, a long test run.
|
||||
- **It is interactive or stateful.** A REPL, `ssh`, a database shell — anything
|
||||
where you send, read, then send again. A pane keeps the session alive between
|
||||
turns; a one-shot call cannot.
|
||||
- **It needs a real TTY.** Programs that detect a pipe and change behaviour —
|
||||
colour, progress bars, TUIs, `top`, raw mode.
|
||||
- **The user should be able to watch.** Anything in a pane shows up live in
|
||||
their window. That is often the whole point.
|
||||
- **You are being asked about something you did not start.** "What's running in
|
||||
that pane?", "why is port 3000 taken?", "what are my agents doing?"
|
||||
|
||||
### The safety rules
|
||||
|
||||
<Warning>
|
||||
The panes on this machine are the user's real work, and some of them are other
|
||||
coding agents mid-task. Anything the agent did not create is read-only.
|
||||
</Warning>
|
||||
|
||||
- **Never `send` into a pane you did not open.** Keystrokes land in the middle
|
||||
of whatever is happening there. Check `tty7 agents` first.
|
||||
- **Never close a pane, tab, or workspace you did not create.**
|
||||
- **Never `server stop` or `server restart`.** Every pane on the machine dies
|
||||
with the server, including yours.
|
||||
- **Never `tty7 server start` on your own initiative** when `doctor` says the
|
||||
server is unreachable — starting one the user did not ask for changes what
|
||||
their GUI attaches to. Tell them instead.
|
||||
- **Clean up what you did create.** `tty7 pane close %83` when the scratch pane
|
||||
is done with.
|
||||
|
||||
### The reliable idioms
|
||||
|
||||
Rather than screen-scraping, the skill points agents at the primitives that
|
||||
actually answer the question:
|
||||
|
||||
```bash
|
||||
# is it finished? — when only the depth-0 shell is left, yes
|
||||
tty7 procs %83 --json
|
||||
|
||||
# the answer, not the view
|
||||
tty7 send "$PANE" 'cargo test > /tmp/t.log 2>&1; echo $? > /tmp/t.rc' --enter
|
||||
|
||||
# wake up exactly when the other agent needs something
|
||||
tty7 wait %3 --until waiting,done --changed --timeout 600
|
||||
```
|
||||
|
||||
## For humans writing their own tooling
|
||||
|
||||
The same material is worth reading even if you are not an agent — it is the
|
||||
shortest description of how to use tty7 as a job runner. Start with the
|
||||
[CLI overview](/cli/overview), then the
|
||||
[command reference](/cli/reference).
|
||||
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: "The tty7 command"
|
||||
description: "Driving the workbench from a script, a Makefile, or another agent."
|
||||
---
|
||||
|
||||
`tty7` is a thin, non-interactive client of the tty7 server. Every verb runs and
|
||||
exits; `--json` makes the output machine-readable. **The GUI does not have to be
|
||||
running** — the server is what owns the panes.
|
||||
|
||||
It ships inside every installer and is put on PATH at launch, so it works from
|
||||
any terminal on the machine. [Installation →](/getting-started/installation#the-tty7-command)
|
||||
|
||||
## Start with `doctor`
|
||||
|
||||
```bash
|
||||
tty7 doctor
|
||||
```
|
||||
|
||||
One table that answers everything you need before doing anything else: whether a
|
||||
server is reachable, whether its wire dialect matches this binary, and whether
|
||||
`TTY7_CONFIG_DIR` / `TTY7_WS` / `TTY7_PANE` are set — that is, whether you are
|
||||
running *inside* a tty7 pane.
|
||||
|
||||
Being inside a pane matters because the address-taking verbs (`split`, `send`,
|
||||
`capture`, `procs`, `pane close`) default to `$TTY7_PANE`, and `run --keep`
|
||||
files its pane into `$TTY7_WS`. Outside one you must name a target, and the
|
||||
error says so rather than guessing.
|
||||
|
||||
## Addresses
|
||||
|
||||
| Shape | Means | Stable? |
|
||||
|---|---|---|
|
||||
| `%42` | A pane | **Yes** — a pane keeps its id for its whole life |
|
||||
| `@7` | A tab, numbered across the whole machine in tree order | **No** — it shifts whenever any workspace or tab appears or disappears |
|
||||
| `api` · `76698a44` · a full UUID | A workspace, by name, unique id prefix, or id | Yes |
|
||||
|
||||
Re-resolve `@N` immediately before using it. Pane and workspace ids are safe to
|
||||
remember.
|
||||
|
||||
## Two ways to run something
|
||||
|
||||
### Blocking, with a real exit code
|
||||
|
||||
```bash
|
||||
tty7 run -- cargo test # streams to stdout, exits with cargo's code
|
||||
tty7 run --cwd /path -- make
|
||||
tty7 run --keep -- cargo build # leaves the pane behind as a new tab
|
||||
```
|
||||
|
||||
The closest thing to running the command yourself — the difference is that it
|
||||
gets a real PTY (so colour, progress bars, and TUIs behave), and that you can
|
||||
watch it happen in the window.
|
||||
|
||||
<Note>
|
||||
Everything after `--` belongs to the child: `tty7 run -- cargo test --keep`
|
||||
passes `--keep` to cargo, not to tty7.
|
||||
</Note>
|
||||
|
||||
### Non-blocking: a pane you talk to over time
|
||||
|
||||
This is the one worth reaching for. Get a pane, give it work, come back.
|
||||
|
||||
```bash
|
||||
PANE=$(tty7 split --v) # or --h; prints "%83"
|
||||
tty7 send "$PANE" 'npm run dev' --enter
|
||||
# ... later
|
||||
tty7 capture "$PANE" --plain
|
||||
tty7 pane close "$PANE"
|
||||
```
|
||||
|
||||
If you are not inside a tty7 pane there is nothing to split, so make your own
|
||||
place to work:
|
||||
|
||||
```bash
|
||||
tty7 new --json /path/to/repo # {"id": "...", "pane": 83}
|
||||
```
|
||||
|
||||
## Reading a pane
|
||||
|
||||
```bash
|
||||
tty7 capture %83 --plain
|
||||
```
|
||||
|
||||
`capture` returns what the server stored. Without `--plain` that is the raw
|
||||
bytes, escapes and all. With `--plain` those bytes are replayed through a real
|
||||
terminal grid and you get the text that produced — which is not the same as
|
||||
stripping escapes yourself:
|
||||
|
||||
- A line the shell wrapped at the pane width comes back as **one** line
|
||||
- A progress bar that rewrote itself with `\r` reads as its **final** value
|
||||
- Cursor addressing puts text **where the program put it**, so a TUI's screen
|
||||
lands where it was drawn
|
||||
|
||||
Use `--plain` whenever a human would want to read the output.
|
||||
|
||||
<Warning>
|
||||
A screen is a rectangle. Whatever scrolled off the top is gone, and an exit
|
||||
code was never on it. When you want the *answer* rather than the *view*, have
|
||||
the shell write it somewhere clean:
|
||||
|
||||
```bash
|
||||
tty7 send "$PANE" 'cargo test > /tmp/t.log 2>&1; echo $? > /tmp/t.rc' --enter
|
||||
```
|
||||
</Warning>
|
||||
|
||||
## Knowing when something finished
|
||||
|
||||
```bash
|
||||
tty7 procs %83
|
||||
```
|
||||
|
||||
The process tree inside the pane, indented by depth, with `*` on the foreground
|
||||
process — plus the ports those processes are listening on. **When the only entry
|
||||
left is the depth-0 shell, the command is done.** That is far more reliable than
|
||||
grepping the screen for a sentinel that can wrap or echo twice.
|
||||
|
||||
For agents specifically, use [`tty7 wait`](/agents/orchestration) instead of
|
||||
polling.
|
||||
|
||||
## Looking around
|
||||
|
||||
```bash
|
||||
tty7 ls # every workspace: tabs, panes, who's attached
|
||||
tty7 ws tree api # one workspace as a tree
|
||||
tty7 pane ls --all # every pane, including orphans no workspace holds
|
||||
tty7 agents # every coding agent and its status
|
||||
tty7 status # server pid, uptime, pane count, build, socket
|
||||
tty7 machine ls # this machine plus any linked remotes
|
||||
tty7 events # stream server events until interrupted
|
||||
```
|
||||
|
||||
`--json` on any of them, `-q` to suppress success output (errors still print).
|
||||
|
||||
## Remote machines
|
||||
|
||||
```bash
|
||||
tty7 -m devbox ls
|
||||
tty7 -m devbox run -- cargo test
|
||||
```
|
||||
|
||||
`-m` routes over a link the local server already holds. It will not dial a fresh
|
||||
connection — connect from the GUI first.
|
||||
[Remote workspaces →](/remote/workspaces)
|
||||
|
||||
<Card title="Full command reference" icon="book" href="/cli/reference">
|
||||
Every verb, flag, and JSON shape.
|
||||
</Card>
|
||||
@@ -0,0 +1,253 @@
|
||||
---
|
||||
title: "Command reference"
|
||||
description: "Every verb, its flags, and the JSON it emits under --json."
|
||||
---
|
||||
|
||||
## Global flags
|
||||
|
||||
Accepted anywhere on the line, before or after the subcommand.
|
||||
|
||||
| Flag | Effect |
|
||||
|---|---|
|
||||
| `-m, --machine <MACHINE>` | Route to a linked machine over the local server's existing link. Matches the full link key (`me@devbox:22`) or the bare host (`devbox`). SSH links only; a down link, or a jump/proxy chain, is refused with a reason rather than dialled fresh. |
|
||||
| `--json` | One JSON object on stdout instead of the human table. |
|
||||
| `-q, --quiet` | No output on success. Errors still go to stderr. |
|
||||
|
||||
## Environment
|
||||
|
||||
Set inside every tty7 pane, inherited by anything launched from one.
|
||||
|
||||
| Variable | Meaning |
|
||||
|---|---|
|
||||
| `TTY7_PANE` | This pane's id, e.g. `71` or `%71` (both accepted). Default target of `split`, `send`, `capture`, `procs`, `pane close`. |
|
||||
| `TTY7_WS` | This pane's workspace id. Default for `run --keep`, `tab new`, `ws tree`. |
|
||||
| `TTY7_CONFIG_DIR` | The server's config dir — how the CLI finds the right server. You never pass a socket path. |
|
||||
|
||||
Outside a tty7 shell, address-taking verbs fail with
|
||||
`not inside a tty7 shell — pass an explicit %pane/@tab/workspace`.
|
||||
|
||||
## Exit codes
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `0` | Success |
|
||||
| `1` | The command failed; one line on stderr, prefixed `tty7:` |
|
||||
| `2` | Usage error — unknown verb, missing argument, bad type |
|
||||
| `124` | `tty7 wait` timed out (the `timeout(1)` convention) |
|
||||
| `141` | Unix only: the reader hung up — piping into `head -1`, say — and SIGPIPE ended it, exactly as it ends `cat`. Not a failure. Windows reports 0 for the same thing, having no signal to imitate. |
|
||||
| *other* | Only from `tty7 run`, which passes the child's exit code through |
|
||||
|
||||
If `run` cannot learn the child's code it prints a note to stderr and exits 1
|
||||
with `"exit_code_known": false` in the JSON — that is how you tell a real 1 from
|
||||
a stand-in.
|
||||
|
||||
## Top-level verbs
|
||||
|
||||
### `tty7 ls`
|
||||
|
||||
Same as `ws ls`. Table: `WORKSPACE NAME TABS PANES ATTACHED`.
|
||||
JSON: `{"workspaces":[{"id","name","tabs","panes","attached"}]}`.
|
||||
|
||||
`ATTACHED` names the host holding the workspace — a GUI window, or another
|
||||
client — and is `-` when nobody is.
|
||||
|
||||
### `tty7 run [--keep] [--cwd DIR] [--ws WORKSPACE] -- CMD...`
|
||||
|
||||
Spawns a pane running `CMD`, streams its output to stdout, waits, and exits with
|
||||
its code. The command must come after `--`.
|
||||
|
||||
- `--keep` leaves the pane alive as a new tab afterwards. Needs a workspace, so
|
||||
it requires `--ws` or `$TTY7_WS` — without one it is an error, not a silent
|
||||
fallback.
|
||||
- `--cwd` sets the working directory. `--ws` also sets the pane's `TTY7_WS`.
|
||||
- Interrupting `run` can leave the pane behind as an orphan — see
|
||||
`pane ls --all`.
|
||||
|
||||
JSON: `{"pane","exit","exit_code_known","kept"}`, printed **after** the streamed
|
||||
output. The combined stream is not valid JSON — read the last line.
|
||||
|
||||
### `tty7 new [PATH] [--open]`
|
||||
|
||||
Creates a workspace plus its first tab and shell, at `PATH` if given. Prints the
|
||||
workspace id. JSON: `{"id","pane","opened"}`.
|
||||
|
||||
`--open` also puts a window on it, if a GUI is running on this machine. Without
|
||||
it the workspace still appears in the switcher; it just waits to be opened.
|
||||
|
||||
### `tty7 split [%PANE] (--v|--h) [--ratio R]`
|
||||
|
||||
Alias of `pane split`. Splits `%PANE` (default `$TTY7_PANE`), spawning a shell
|
||||
in the same cwd. Exactly one axis is required — `--v`/`--vertical` puts the new
|
||||
pane below, `--h`/`--horizontal` to the right. `--ratio` (default `0.5`) is the
|
||||
share kept by the *existing* pane. Prints `%NN`. JSON: `{"pane"}`.
|
||||
|
||||
### `tty7 send [%PANE] TEXT [--enter]`
|
||||
|
||||
Types `TEXT` into the pane as keystrokes; `--enter` appends CR. With one
|
||||
argument the text is the argument and the pane comes from `$TTY7_PANE` — but a
|
||||
lone `%42` is rejected as a missing-text error rather than typed.
|
||||
JSON: `{"pane","sent","enter"}`.
|
||||
|
||||
### `tty7 capture [%PANE] [--plain] [--scrollback]`
|
||||
|
||||
The pane's replay. Two independent choices:
|
||||
|
||||
**How much** — the newest scrollback segment by default, the whole ring with
|
||||
`--scrollback`. The ring splits into segments on resize, so for a pane that was
|
||||
never resized the two are identical.
|
||||
|
||||
**In what form** — without `--plain`, the stored bytes with ANSI escapes intact,
|
||||
decoded as UTF-8 (invalid bytes become U+FFFD). With `--plain`, those bytes
|
||||
replayed through a terminal grid and printed as the text they produced.
|
||||
|
||||
Either way it is a snapshot, not a stream: it collects the replay, settles for
|
||||
~300 ms, and returns. Call it again for a newer one.
|
||||
JSON: `{"pane","text"}`.
|
||||
|
||||
### `tty7 procs [%PANE]`
|
||||
|
||||
The process tree inside the pane, indented by depth, `*` on the foreground
|
||||
process — then a second table of ports those processes are listening on. Prints
|
||||
`nothing running in this pane` when both are empty.
|
||||
|
||||
JSON: `{"procs":[{"pid","name","depth","foreground"}],"ports":[{"port","pid","name"}]}`.
|
||||
|
||||
### `tty7 agents`
|
||||
|
||||
Every pane running a recognised coding agent. Table:
|
||||
`PANE AGENT STATUS MESSAGE`, status one of `running` / `waiting` / `idle`.
|
||||
JSON: `{"agents":[...]}`.
|
||||
|
||||
### `tty7 wait [%PANE] [--until STATE,…] [--changed] [--timeout SECS] [--interval MS]`
|
||||
|
||||
Blocks until the pane's agent reaches one of the named states.
|
||||
|
||||
| Flag | Default | |
|
||||
|---|---|---|
|
||||
| `--until` | `waiting,done,exit` | `idle`, `working`, `waiting`, `done`, `exit` |
|
||||
| `--changed` | off | Only wake on a state the pane moved into *after* the wait began |
|
||||
| `--timeout` | none | Give up after N seconds, exiting `124` |
|
||||
| `--interval` | `500` | Poll interval in ms (50–3,600,000) |
|
||||
|
||||
The reply carries the agent's message and native session id. The JSON's `stale`
|
||||
flag says whether the answer might belong to the previous turn.
|
||||
[Orchestration →](/agents/orchestration)
|
||||
|
||||
### `tty7 events`
|
||||
|
||||
Streams server events until interrupted, one per line — pane exits, agent status
|
||||
changes, workspace preemption, layout deltas. `--json` makes it NDJSON. Blocks
|
||||
forever; run it with a timeout or in the background.
|
||||
|
||||
### `tty7 status`
|
||||
|
||||
Same as `server status`: pid, uptime, pane count, dialect versions, build,
|
||||
socket path. JSON is the `ServerStatus` object itself (`pid`, `uptime_secs`,
|
||||
`panes`, `control_version`, `protocol_version`, `build`, `socket`).
|
||||
|
||||
### `tty7 doctor`
|
||||
|
||||
The install check: the three environment variables, whether the server answers,
|
||||
whether its control and protocol versions match this binary, pid/uptime/panes,
|
||||
and how many machine links exist. Adds a note when you are not inside a tty7
|
||||
shell.
|
||||
|
||||
JSON: `{"context":{"config_dir","workspace","pane"},"server":{"reachable","dialect_ok","build","status","routes"}}`
|
||||
— the context fields are booleans, not values.
|
||||
|
||||
## `ws` — workspaces
|
||||
|
||||
Address a workspace by name, by full id, or by a unique id prefix (the 8-char
|
||||
prefix `tty7 ls` prints). An ambiguous name or prefix is an error that lists the
|
||||
candidates.
|
||||
|
||||
| Command | Effect | JSON |
|
||||
|---|---|---|
|
||||
| `ws ls` | Every workspace | `{"workspaces":[...]}` |
|
||||
| `ws tree [WORKSPACE]` | One workspace as a tree: tabs, split axes and ratios, panes with cwds | The whole workspace object: `{"id","name","last_active","tabs":[{"id","name","sidebar_group","root",…}]}` |
|
||||
| `ws new [NAME]` | An empty workspace (no tab, no pane) | `{"id","name"}` |
|
||||
| `ws rename WORKSPACE NAME` | Name or rename | `{"id","name"}` |
|
||||
| `ws rm WORKSPACE` | Delete the workspace | `{"removed"}` |
|
||||
| `ws attach WORKSPACE` | Become its controlling client | `{"attached","took_over_from"}` |
|
||||
| `ws detach WORKSPACE` | Let go without interrupting anything | `{"detached"}` |
|
||||
|
||||
<Warning>
|
||||
`ws rm` does **not** kill the panes it held — they keep running as orphans
|
||||
with no workspace. Find them with `pane ls --all` and close them one by one.
|
||||
</Warning>
|
||||
|
||||
Prefer `tty7 new <path>` over `ws new` when you want something usable: `ws new`
|
||||
leaves an empty workspace you then have to populate, while `tty7 new --json`
|
||||
hands back both ids at once.
|
||||
|
||||
The `root` node in `ws tree --json` is externally tagged, so a leaf is
|
||||
`{"Leaf":{"pane":31}}` and a split is `{"Split":{"axis","ratio","a","b"}}` with
|
||||
`a`/`b` nested the same way.
|
||||
|
||||
## `tab` — tabs
|
||||
|
||||
`@N` numbers tabs across the **whole machine** in tree order, densely from `@1`.
|
||||
The numbering shifts whenever any workspace or tab is created or removed, so
|
||||
resolve it immediately before use. A full tab UUID also works: `@<uuid>`.
|
||||
|
||||
| Command | Effect | JSON |
|
||||
|---|---|---|
|
||||
| `tab ls [WORKSPACE]` | Tabs of a workspace | `{"workspace","tabs":[{"ordinal","id","name","label","agent","group","panes":[…]}]}` |
|
||||
| `tab new [WORKSPACE] [--cwd DIR]` | Add a tab with a fresh shell | `{"tab","pane"}` |
|
||||
| `tab close @TAB` | Close the tab and every pane in it | `{"closed"}` |
|
||||
| `tab rename @TAB NAME` | Name or rename | `{"tab","name"}` |
|
||||
| `tab move @TAB INDEX` | Reposition within its workspace | `{"tab","to"}` |
|
||||
|
||||
`GROUP` is the heading the GUI's sidebar files the tab under, shown by its last
|
||||
segment. Read-only from here: with the default repo grouping the GUI recomputes
|
||||
it from the tab's working directory.
|
||||
|
||||
`label` falls back through the best evidence available — the name if someone set
|
||||
one, else the agent running there, else the last segment of the cwd, else the
|
||||
foreground process. `name` stays literal, so a script can tell a real name from
|
||||
a stand-in.
|
||||
|
||||
## `pane` — panes
|
||||
|
||||
| Command | Effect | JSON |
|
||||
|---|---|---|
|
||||
| `pane ls [WORKSPACE]` | Panes with their workspace, tab, cwd, live flag | `{"panes":[…]}` |
|
||||
| `pane ls --all` | The server's whole pane registry, including orphans | `{"panes":[…],"orphans":N}` |
|
||||
| `pane split …` | Identical to top-level `split` | `{"pane"}` |
|
||||
| `pane close [%PANE]` | Close the pane; its shell is hung up | `{"closed"}` |
|
||||
|
||||
`--all` is the one that shows leaks. Each entry is
|
||||
`{"pane","workspace","orphan","owner","title","cwd","live"}`: `owner` is
|
||||
`tty7-cli` for panes this CLI spawned, and `orphan: true` means no workspace
|
||||
holds it. An interrupted `run` and a removed workspace both leave orphans here.
|
||||
|
||||
`title` is usually the running command — `claude`, `nvim`, `cargo` — which makes
|
||||
`pane ls --all --json` a quick way to find "the pane running X".
|
||||
|
||||
## `machine` — remotes
|
||||
|
||||
`machine ls` lists the local machine plus every link the server holds:
|
||||
`MACHINE KIND CONNECTED`. JSON: `{"machines":[{"key","kind","connected"}]}`.
|
||||
|
||||
## `server` — the daemon
|
||||
|
||||
| Command | Effect |
|
||||
|---|---|
|
||||
| `server status` | Same as `tty7 status` |
|
||||
| `server logs` | Tail the server log; prints the path, and says so when logging was never enabled (`TTY7_LOG=info` before the server starts) |
|
||||
| `server start` | Bring up a server on this machine |
|
||||
| `server stop` | Stop it — **every pane on the machine dies** |
|
||||
| `server restart` | Stop, then start — same consequence |
|
||||
|
||||
<Warning>
|
||||
Do not run `start`, `stop`, or `restart` on someone else's behalf. They change
|
||||
or destroy what the user's GUI is attached to.
|
||||
</Warning>
|
||||
|
||||
## Not implemented yet
|
||||
|
||||
These parse and then exit 1 with an explanation:
|
||||
|
||||
- `ws stop` — the control dialect has no workspace-stop request yet
|
||||
- `machine connect` / `machine disconnect` — use the GUI's connection manager
|
||||
- bare `tty7 <path>` (launch or focus the GUI)
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: "Fonts"
|
||||
description: "The bundled default, fallback chains, ligatures, and why CJK needs a word."
|
||||
---
|
||||
|
||||
**Settings → Appearance → Typography** covers the everyday choices; the rest is
|
||||
`config.json`.
|
||||
|
||||
| Setting | Default | |
|
||||
|---|---|---|
|
||||
| **Font family** | Hack | Picked from fonts installed on your system |
|
||||
| **Font size** | 15 px | The terminal grid |
|
||||
| **Interface font size** | 16 px | Everything outside the grid (12–24) |
|
||||
| **Line height** | 1.4 | A multiple of the font size |
|
||||
| **Bold font** / **Italic font** | — | Distinct faces, when you want them |
|
||||
| **Font ligatures** | off | Contextual alternates stay off unless you ask |
|
||||
|
||||
## Hack is bundled
|
||||
|
||||
The default font ships inside the binary. It renders identically on every
|
||||
machine without relying on a system install, so a fresh laptop looks like the
|
||||
one you set up last year.
|
||||
|
||||
## Fallbacks
|
||||
|
||||
`font_family` is the primary face; `font_fallbacks` is an ordered list tried in
|
||||
turn for anything the primary lacks.
|
||||
|
||||
```json
|
||||
{
|
||||
"font_family": "JetBrains Mono",
|
||||
"font_fallbacks": ["Maple Mono NF CN", "PingFang SC", "Apple Color Emoji"]
|
||||
}
|
||||
```
|
||||
|
||||
The defaults name faces the host OS actually ships — PingFang SC and Apple Color
|
||||
Emoji on macOS, Microsoft YaHei and Segoe UI Emoji on Windows, Noto on Linux.
|
||||
Those stock names are appended to whatever list you write, too, so a
|
||||
`config.json` copied from another platform still resolves.
|
||||
|
||||
## OpenType features
|
||||
|
||||
`font_features` passes tags straight through to the shaper:
|
||||
|
||||
```json
|
||||
{
|
||||
"font_features": { "calt": true, "liga": 1, "ss01": true, "zero": false }
|
||||
}
|
||||
```
|
||||
|
||||
A tag must be four alphanumeric characters; `true`/`false` map to `1`/`0`.
|
||||
Anything malformed is skipped with a log line rather than failing the whole
|
||||
config.
|
||||
|
||||
## CJK and the two-column grid
|
||||
|
||||
<Info>
|
||||
A cell is one advance of the primary face, and a wide (CJK) character is
|
||||
pinned to exactly two of them. A CJK fallback sits flush in its slot only if
|
||||
its ideographs advance **twice** the primary's Latin advance.
|
||||
</Info>
|
||||
|
||||
Bundled Hack advances 0.60205em, so a two-column slot is 1.2041em — while every
|
||||
stock CJK face (Microsoft YaHei, PingFang SC, Noto Sans CJK) advances 1.0em.
|
||||
Those glyphs get left-aligned in the slot, leaving a ~0.2em gap on the right of
|
||||
every character.
|
||||
|
||||
[Maple Mono NF CN](https://github.com/subframe7536/maple-font) is tried first on
|
||||
every platform for exactly this reason: 0.6em Latin, 1.2em CJK, an exact
|
||||
two-cell fit against Hack. It is referenced by name only, never bundled (~20 MB
|
||||
per weight) — install it and tty7 picks it up with no config change.
|
||||
|
||||
If you want CJK set *tight* rather than merely even, change the **primary** face
|
||||
instead. One that advances 0.5em — Sarasa Mono SC, say — makes two columns
|
||||
exactly 1.0em.
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: "Keybindings"
|
||||
description: "Rebinding anything, chord sequences, and the tmux preset."
|
||||
---
|
||||
|
||||
**Settings → Keybindings** (<kbd>⌘ ,</kbd>) lists every shortcut in the app,
|
||||
grouped the same way the command palette is.
|
||||
|
||||
## Rebinding
|
||||
|
||||
Click a shortcut and press the new keys. It saves after a brief pause.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Press keys | Set the binding |
|
||||
| Press more keys | Chain a sequence — <kbd>⌃ B</kbd> then <kbd>X</kbd> |
|
||||
| <kbd>Esc</kbd> | Cancel |
|
||||
| <kbd>⌫</kbd> | Remove the last key — or, pressed first, reset the shortcut to its default |
|
||||
|
||||
**Restore all defaults** at the bottom undoes every rebinding at once. There is
|
||||
no undo for that one.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the Keybindings page mid-capture, showing “Press keys…”">
|
||||
<img src="/images/placeholder.svg" alt="Rebinding a shortcut" />
|
||||
</Frame>
|
||||
|
||||
## Actions with no default key
|
||||
|
||||
Some actions ship deliberately unbound, because there is no obvious key left to
|
||||
take: pane resize and swap, workspace selection, most git commands, SFTP, and
|
||||
the panel tabs. They are all in the command palette, and all bindable here.
|
||||
|
||||
## Editing `config.json` instead
|
||||
|
||||
```json
|
||||
{
|
||||
"keybindings": {
|
||||
"SplitRight": "cmd-d",
|
||||
"ResizePaneLeft": "ctrl-alt-left",
|
||||
"ToggleSftp": "cmd-shift-u"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The syntax is modifiers joined by `-`, then the key. Chords are separated by a
|
||||
space.
|
||||
|
||||
| Token | Means |
|
||||
|---|---|
|
||||
| `secondary` | <kbd>⌘</kbd> on macOS, <kbd>Ctrl</kbd> elsewhere |
|
||||
| `cmd` · `ctrl` · `alt` · `shift` | Literal modifiers |
|
||||
| `ctrl-b n` | A two-key sequence |
|
||||
|
||||
An unknown action name or an invalid keystroke is skipped with a warning in the
|
||||
log rather than breaking the rest of your bindings.
|
||||
|
||||
The full action list is on the [keyboard shortcuts](/reference/keyboard-shortcuts)
|
||||
page.
|
||||
|
||||
## The tmux preset
|
||||
|
||||
**Settings → Keybindings → Preset → tmux** remaps pane and tab actions onto a
|
||||
prefix — <kbd>⌃ B</kbd> by default, changeable in the **Prefix** field beside
|
||||
it.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⌃ B</kbd> <kbd>C</kbd> · <kbd>X</kbd> | New tab · close tab |
|
||||
| <kbd>⌃ B</kbd> <kbd>%</kbd> · <kbd>"</kbd> | Split right · split down |
|
||||
| <kbd>⌃ B</kbd> <kbd>←→↑↓</kbd> | Move focus |
|
||||
| <kbd>⌃ B</kbd> <kbd>⌃ ←→↑↓</kbd> | Resize the pane |
|
||||
| <kbd>⌃ B</kbd> <kbd>O</kbd> · <kbd>;</kbd> | Next pane · previous pane |
|
||||
| <kbd>⌃ B</kbd> <kbd>{</kbd> · <kbd>}</kbd> | Swap with the previous · next pane |
|
||||
| <kbd>⌃ B</kbd> <kbd>Z</kbd> | Zoom the pane |
|
||||
| <kbd>⌃ B</kbd> <kbd>N</kbd> · <kbd>P</kbd> | Next tab · previous tab |
|
||||
| <kbd>⌃ B</kbd> <kbd>1</kbd>…<kbd>9</kbd> | Jump to a tab |
|
||||
|
||||
Two details that make it livable:
|
||||
|
||||
- A **bare prefix** reaches the shell after about a second, so <kbd>⌃ B</kbd>
|
||||
still works as "back one character" when you meant it.
|
||||
- **Prefix plus an unbound key** is passed straight through to the terminal, so
|
||||
a tmux binding you did not remap still lands in whatever is running.
|
||||
|
||||
## Some non-obvious defaults
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⇧ ⏎</kbd> · <kbd>⌥ ⏎</kbd> | Insert a newline at the prompt instead of submitting (`InsertNewline`) |
|
||||
| <kbd>⌘ ⇧ ⏎</kbd> | Zoom the focused pane |
|
||||
| <kbd>⌘ ⇧ E</kbd> | Toggle the code panel |
|
||||
| <kbd>⌘ ⇧ R</kbd> | Restart the SSH session in this pane |
|
||||
| <kbd>⌘ ⇧ O</kbd> | Workspace switcher |
|
||||
| <kbd>⌘ ⇧ N</kbd> | New workspace |
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: "Settings"
|
||||
description: "What lives in each section, and how the settings file works."
|
||||
---
|
||||
|
||||
<kbd>⌘ ,</kbd> opens Settings. There is a search box at the top that matches
|
||||
setting names *and* keywords, which is usually faster than remembering which
|
||||
section something is in.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the Settings window with the section list on the left">
|
||||
<img src="/images/placeholder.svg" alt="tty7 Settings" />
|
||||
</Frame>
|
||||
|
||||
## The eight sections
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Appearance" icon="palette" href="/customization/themes">
|
||||
Theme, sync with system, typography, cursor, transparency, language.
|
||||
</Card>
|
||||
<Card title="Terminal" icon="terminal">
|
||||
Shell and start directory, scrollback and scrolling, mouse, bell, per-pane
|
||||
history.
|
||||
</Card>
|
||||
<Card title="Input" icon="keyboard">
|
||||
Prompt features, selection & clipboard, keyboard (Option as Meta), links.
|
||||
</Card>
|
||||
<Card title="SSH" icon="server" href="/remote/ssh">
|
||||
Hosts, defaults, security, and every per-profile field.
|
||||
</Card>
|
||||
<Card title="Agents" icon="robot" href="/agents/status">
|
||||
Hook installation per agent and per machine, the orchestration skill, the
|
||||
CLI on PATH.
|
||||
</Card>
|
||||
<Card title="Window & Tabs" icon="window-maximize">
|
||||
Startup window, tab bar position and grouping, notifications, tray icon.
|
||||
</Card>
|
||||
<Card title="Keybindings" icon="command" href="/customization/keybindings">
|
||||
Every shortcut, the tmux preset, the prefix.
|
||||
</Card>
|
||||
<Card title="About" icon="circle-info" href="/reference/updates">
|
||||
Version, update channel, and the updater.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## The settings file
|
||||
|
||||
Everything the Settings window writes goes to one file:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| macOS / Linux | `~/.config/tty7/config.json` |
|
||||
| Windows | `%APPDATA%\tty7\config.json` |
|
||||
|
||||
Set `TTY7_CONFIG_DIR` to point the whole directory — config, themes, state —
|
||||
somewhere else.
|
||||
|
||||
You can edit the file by hand; a handful of options exist only there. See the
|
||||
[configuration reference](/reference/configuration) for every key, its type, and
|
||||
its default.
|
||||
|
||||
### How it handles mistakes
|
||||
|
||||
The file is written atomically, and read forgivingly:
|
||||
|
||||
- **A missing key** means the default — you only have to write what you change.
|
||||
- **An out-of-range number** is clamped into its band, not rejected.
|
||||
- **An unrecognised enum value** falls back to the default with a log line,
|
||||
rather than failing the whole file.
|
||||
- **An unparseable file** is not overwritten. tty7 starts on defaults, keeps a
|
||||
copy at `config.json.corrupt`, and says so in the log.
|
||||
|
||||
<Note>
|
||||
A UTF-8 BOM at the start of the file is tolerated, which matters if you edited
|
||||
it in a Windows editor.
|
||||
</Note>
|
||||
|
||||
## Language
|
||||
|
||||
**Settings → Appearance → Language** switches the interface between English,
|
||||
简体中文, and 日本語. The choice is explicit — the system language is never
|
||||
inferred — and CLI output stays English regardless, so agent and script
|
||||
integrations keep a stable surface.
|
||||
|
||||
```json
|
||||
{ "gui_language": "zh-CN" }
|
||||
```
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: "Themes"
|
||||
description: "Nine built-ins, your own YAML themes, iTerm2 imports, and a colour editor."
|
||||
---
|
||||
|
||||
**Settings → Appearance → Theme** — or **Change Theme…** in the command
|
||||
palette — opens the theme picker.
|
||||
|
||||
## Built in
|
||||
|
||||
| Light | Dark |
|
||||
|---|---|
|
||||
| Light *(default)* · One Light · Catppuccin Latte · Rosé Pine Dawn | Dark · Dracula · Harbor · One Dark Pro · Rosé Pine |
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the theme picker with light and dark sections">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 theme picker" />
|
||||
</Frame>
|
||||
|
||||
## Following the system
|
||||
|
||||
Turn on **Sync with system** and pick a theme for each appearance. tty7 follows
|
||||
the OS live — no restart, no reload.
|
||||
|
||||
```json
|
||||
{
|
||||
"theme_follow_system": true,
|
||||
"theme_preset_light": "one_light",
|
||||
"theme_preset_dark": "dracula"
|
||||
}
|
||||
```
|
||||
|
||||
## Legible bright colours
|
||||
|
||||
Some palettes put a bright ANSI colour so close to their own background that it
|
||||
disappears. **Legible bright colors** (on by default) brightens or darkens those
|
||||
just enough to be readable. Turn it off with `theme_legible_palette: false` if
|
||||
you want the palette exactly as authored.
|
||||
|
||||
## Transparency
|
||||
|
||||
**Settings → Appearance → Transparency**:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Opacity** | 0.2–1.0, applied to every theme. *Follow theme* hands the decision back to the theme's own `opacity`. |
|
||||
| **Blur** | Blurs whatever is behind a translucent window (macOS). |
|
||||
| **Background material** | Windows only: *Auto*, *Blur*, *Mica*, *Mica Alt*, *Acrylic*, *Off*. Only the presets your Windows build supports are listed. |
|
||||
|
||||
## Writing your own
|
||||
|
||||
**Open themes folder** in Settings takes you to:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| macOS / Linux | `~/.config/tty7/themes/` |
|
||||
| Windows | `%APPDATA%\tty7\themes\` |
|
||||
|
||||
Drop a `.yaml` file in and it appears in the picker. The file name is the
|
||||
theme's id; `name` is what is shown.
|
||||
|
||||
```yaml
|
||||
name: "Midnight"
|
||||
background: "#0d1117"
|
||||
foreground: "#c9d1d9"
|
||||
accent: "#3fdd8c"
|
||||
cursor: "#3fdd8c"
|
||||
selection: "#264f78"
|
||||
opacity: 0.95
|
||||
blur: true
|
||||
ansi:
|
||||
normal: ["#484f58", "#ff7b72", "#3fb950", "#d29922", "#58a6ff", "#bc8cff", "#39c5cf", "#b1bac4"]
|
||||
bright: ["#6e7681", "#ffa198", "#56d364", "#e3b341", "#79c0ff", "#d2a8ff", "#56d4dd", "#f0f6fc"]
|
||||
```
|
||||
|
||||
Everything except `background`, `foreground`, `accent`, and `ansi` is optional.
|
||||
|
||||
### Gradients and images
|
||||
|
||||
`background` also takes two colours:
|
||||
|
||||
```yaml
|
||||
background: { top: "#0d1117", bottom: "#161b22" }
|
||||
# or
|
||||
background: { left: "#0d1117", right: "#161b22" }
|
||||
```
|
||||
|
||||
And a theme can carry an image behind the terminal:
|
||||
|
||||
```yaml
|
||||
background_image:
|
||||
path: "/Users/me/Pictures/wall.jpg"
|
||||
opacity: 0.25
|
||||
```
|
||||
|
||||
The Settings panel has a picker for both, so you rarely have to write this by
|
||||
hand.
|
||||
|
||||
## Editing in the app
|
||||
|
||||
Select a built-in theme and hit **Duplicate to edit** — built-ins are read-only,
|
||||
so the editor works on your copy. From there you get every colour, including the
|
||||
sixteen ANSI slots, plus the background image controls. Changes are written back
|
||||
to your themes folder as YAML.
|
||||
|
||||
## Importing from iTerm2
|
||||
|
||||
Drop an `.itermcolors` file into the themes folder and tty7 reads it directly —
|
||||
no conversion step.
|
||||
|
||||
<Note>
|
||||
A theme file tty7 could not load is listed in Settings under **Not loaded from
|
||||
the themes folder**, with the reason, rather than silently ignored.
|
||||
</Note>
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "tty7",
|
||||
"description": "A terminal workbench: persistent sessions, remote work, agents.",
|
||||
"colors": {
|
||||
"primary": "#0FA968",
|
||||
"light": "#3FDD8C",
|
||||
"dark": "#0FA968"
|
||||
},
|
||||
"favicon": "/favicon.ico",
|
||||
"logo": {
|
||||
"light": "/logo/logo.svg",
|
||||
"dark": "/logo/logo.svg",
|
||||
"href": "https://github.com/l0ng-ai/tty7"
|
||||
},
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting started",
|
||||
"pages": [
|
||||
"index",
|
||||
"getting-started/installation",
|
||||
"getting-started/first-launch",
|
||||
"getting-started/concepts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "The window",
|
||||
"pages": [
|
||||
"window/tabs-and-splits",
|
||||
"window/sidebar",
|
||||
"window/command-palette",
|
||||
"window/search",
|
||||
"window/side-panel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "The terminal",
|
||||
"pages": [
|
||||
"terminal/prompt",
|
||||
"terminal/history",
|
||||
"terminal/selection-and-clipboard",
|
||||
"terminal/links",
|
||||
"terminal/mouse-and-scrolling"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Coding agents",
|
||||
"pages": [
|
||||
"agents/overview",
|
||||
"agents/status",
|
||||
"agents/sessions",
|
||||
"agents/orchestration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Remote work",
|
||||
"pages": [
|
||||
"remote/ssh",
|
||||
"remote/sftp",
|
||||
"remote/port-forwarding",
|
||||
"remote/workspaces"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Git",
|
||||
"pages": [
|
||||
"git/source-control",
|
||||
"git/diffs",
|
||||
"git/worktrees"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Customization",
|
||||
"pages": [
|
||||
"customization/settings",
|
||||
"customization/themes",
|
||||
"customization/fonts",
|
||||
"customization/keybindings"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "CLI",
|
||||
"groups": [
|
||||
{
|
||||
"group": "The tty7 command",
|
||||
"pages": [
|
||||
"cli/overview",
|
||||
"cli/reference",
|
||||
"cli/agent-skill"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Reference",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Reference",
|
||||
"pages": [
|
||||
"reference/configuration",
|
||||
"reference/keyboard-shortcuts",
|
||||
"reference/shell-integration",
|
||||
"reference/updates",
|
||||
"reference/privacy",
|
||||
"reference/troubleshooting"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "GitHub",
|
||||
"href": "https://github.com/l0ng-ai/tty7"
|
||||
},
|
||||
{
|
||||
"label": "Discord",
|
||||
"href": "https://discord.gg/s3dethqz2V"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Download",
|
||||
"href": "https://github.com/l0ng-ai/tty7/releases"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"github": "https://github.com/l0ng-ai/tty7",
|
||||
"discord": "https://discord.gg/s3dethqz2V"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -1,158 +0,0 @@
|
||||
# Features
|
||||
|
||||
<sub>English · [简体中文](features.zh-CN.md)</sub>
|
||||
|
||||
## Input
|
||||
|
||||
- **Ghost suggestions** — your history completes the whole line as you type; <kbd>→</kbd> to accept
|
||||
- **Explained tab completion** — every flag and subcommand with its description, for ~100 common commands; when tty7 has nothing to offer the Tab falls through to your shell's own completion, and the whole feature can be turned off (Settings → Input → Prompt, or `tab_completion` in `config.json`)
|
||||
- **Syntax highlighting** — as you type, nothing to install
|
||||
- **Fuzzy history search** — <kbd>⌃ R</kbd> shows what you ran, where, and whether it failed; turn it off (Settings → Input → Prompt, or `history_search` in `config.json`) and <kbd>⌃ R</kbd> goes to your shell instead, so an fzf / percol binding keeps working
|
||||
- **History from day one** — your existing shell history works as-is and carries across sessions
|
||||
- **Line editing** — click to place the caret, mouse selection, word motion, undo
|
||||
- **Multi-line editing** — wrapped and multi-line commands edit in place; the grid shifts to keep the caret visible. <kbd>⇧ ⏎</kbd> · <kbd>⌥ ⏎</kbd> insert a newline instead of submitting (rebindable as `InsertNewline`); a plain <kbd>⏎</kbd> submits the whole buffer
|
||||
|
||||
## In the window
|
||||
|
||||
- **Tabs & splits** — always open in the current directory
|
||||
- **Rearrange splits by dragging** — hover a pane and a small grip appears along its top edge; drag it over the layout to put the pane somewhere else in the tab. Dropping on a pane's side goes in beside it — taking an equal share of the row or column it joins, or splitting that pane in half when the side faces across the layout rather than along it — dropping on its middle trades the two panes' places, and carrying it past a pane's outer side — the one facing the window rather than another pane — makes it a full-width or full-height band beside everything else, sized to an even share of what that side already holds — so a pane in the middle of a 2×2 becomes a full-height third column in one drag. The landing lights up while you drag, and only ever lights up when the drop would really change the layout
|
||||
- **Repo-grouped sidebar** — the left tab sidebar groups rows under a header per git repository, non-repo tabs in a trailing *Scratch* section; branch switches and in-repo `cd`s never move a row (`sidebar_grouping` in `config.json`: `repo` default, `none` for a flat list)
|
||||
- **Command palette** <kbd>⌘ P</kbd> · scrollback search <kbd>⌘ F</kbd>
|
||||
- **⌘/Ctrl-click links** (⌘ on macOS, Ctrl on Windows/Linux) · desktop notifications · copy on select (opt-in, Settings → Input → Selection & clipboard)
|
||||
- **Smart double-click selection** — double-click grabs the whole URL, file path, bracket/quote pair, or dictionary-segmented CJK word under the cursor; Shift-click extends a selection (toggle in Settings → Input → Selection & clipboard; word separators via `word_separators` in `config.json`)
|
||||
- **Nine themes, plus your own** — YAML seed themes with solid, gradient, or image backgrounds; iTerm2 `.itermcolors` import; in-app color editor with a background-image picker
|
||||
- **Sync with system** — Settings → Appearance; pick separate light and dark themes and tty7 follows the OS appearance live (`theme_follow_system`, `theme_preset_light` / `theme_preset_dark` in `config.json`)
|
||||
- **Window opacity & blur** — Settings → Appearance → Transparency; applies to every theme, *Follow theme* returns to the theme's own `opacity` / `blur`
|
||||
- **CJK / IME input**
|
||||
- **Windows Explorer menu** — the installer offers *Add “Open in tty7” to the folder context menu* as a setup task, off by default, and the uninstaller always takes it back out. Writing shell verbs is an install-time decision, so there is no runtime setting; a portable-zip install can do it itself with `tty7-app.exe --register-explorer-menu` (or `--unregister-explorer-menu`). Either way the keys land under `HKCU`, so only your own Windows account is affected
|
||||
|
||||
## Fonts
|
||||
|
||||
- **Hack is bundled** — it ships inside the binary, so the default renders identically everywhere without relying on a system install
|
||||
- **Primary + ordered fallbacks** — `font_family` and `font_fallbacks` in `config.json`; optional `font_family_bold` / `font_family_italic` for distinct faces, and `font_features` to pass OpenType features through (contextual ligatures stay off unless you ask for them)
|
||||
- **Platform-aware defaults** — the fallback list names faces the host OS actually ships (PingFang SC / Apple Color Emoji on macOS, Microsoft YaHei / Segoe UI Emoji on Windows, Noto on Linux). Those stock names are appended to a hand-written list too, so a `config.json` written on another platform still resolves
|
||||
|
||||
### CJK and the two-column grid
|
||||
|
||||
A cell is one advance of the primary face, and a wide (CJK) character is pinned
|
||||
to exactly two of them. A CJK fallback therefore sits flush in its slot only if
|
||||
its ideographs advance **twice** the primary's Latin advance.
|
||||
|
||||
Bundled Hack advances 0.60205em, so a two-column slot is 1.2041em — while every
|
||||
stock CJK face (Microsoft YaHei, PingFang SC, Noto Sans CJK) advances 1.0em.
|
||||
Those glyphs get left-aligned in the slot and the leftover ~0.2em lands as a gap
|
||||
on the right of every character.
|
||||
|
||||
[Maple Mono NF CN](https://github.com/subframe7536/maple-font) is tried first on
|
||||
every platform for exactly this reason — 0.6em Latin, 1.2em CJK, an exact
|
||||
two-cell fit against Hack. It is referenced by name only, never bundled (~20MB
|
||||
per weight): install it and tty7 picks it up with no config change.
|
||||
|
||||
For CJK set *tight* rather than merely even, change the primary face instead —
|
||||
one that advances 0.5em (Sarasa Mono SC, say) makes two columns exactly 1.0em.
|
||||
|
||||
## Coding agents
|
||||
|
||||
tty7 recognizes third-party coding agents running in a pane (Claude Code,
|
||||
Codex, Gemini CLI, Aider, Amp, OpenCode, and 12 more) and adds around them —
|
||||
it never wraps or replaces the agent.
|
||||
|
||||
- **Brand avatars** — the tab chip / sidebar row shows which agent runs where; custom wrappers map in via `agent_commands` in `config.json`
|
||||
- **Status dot** — working (blue) / needs your input (amber) / done (green), driven by agent-reported events over an OSC channel; Settings → Agents installs the hooks that feed it (Claude Code, Codex, Copilot CLI, OpenCode, Pi, Grok Build, Oh My Pi)
|
||||
- **Notifications** — "needs your permission…" the moment an agent blocks on you, and "finished after Ns" per turn, honoring your notification policy
|
||||
- **Branch at a glance** — each sidebar row shows its pane's git branch and working-tree diff (`+N −M`), refreshed on `cd` and when a command finishes; clicking the counts opens the diff overlay, and turning that off (Settings → Window & Tabs, or `sidebar_diff_preview: false` in `config.json`) keeps the readout while making it non-clickable
|
||||
- **Session resume** — panes lost to a reboot re-launch their agent conversation on restore, carrying the original launch flags (`claude --dangerously-skip-permissions --resume …`) (`restore_agent_sessions`, on by default)
|
||||
- **Fork session** — branch a live agent conversation into a second, independent one by shelling the agent's own fork command (`codex fork <id>`, `claude --resume <id> --fork-session`, also OpenCode, Grok Build, and Oh My Pi); the original is untouched and both continue separately. Right-click a pane to pick a split placement, or right-click the tab / sidebar row to open the fork in a new tab. Needs the agent's hooks installed, since the fork targets the session id they report; a remote pane can't fork, because the command would run against the local agent — and note a fork copies the whole transcript, so repeated forking costs real disk in the agent's own session store
|
||||
- **Copy Session ID** — put the agent's native session id on the clipboard, beside *Copy Working Directory*, for pasting into `codex resume`, a bug report, or another tool
|
||||
- **Context feed** — palette commands send the current selection or the repo's `git diff` to the running agent as a ready-made prompt
|
||||
- **Tray icon** — a system tray / menu bar item that flips to an attention state the moment any agent needs your input; its menu lists every agent pane (brand avatar + status dot, click to reveal), switches the notification policy, and offers *Quit and Stop Server…* alongside the plain session-keeping quit (`show_tray_icon`, on by default)
|
||||
- **`tty7 wait`** — the CLI's orchestration primitive: block until a pane's agent needs input or finishes its turn (`tty7 wait %3 --until waiting,done --changed --timeout 600`, exit 124 on timeout), so one agent can sleep until its peer blocks on a permission prompt instead of screen-scraping — then `tty7 capture %3 --plain` to read the result. The agent status is a level, not an event, so `--changed` ignores the state the pane was already in when the wait began; without it, the JSON's `stale` flag says whether the answer might belong to the previous turn
|
||||
- **`tty7` on PATH** — the CLI ships inside every installer and is put on PATH at launch, so a script or a coding agent can drive tty7 from any terminal. Inside a tty7 pane it works regardless, since panes inherit the app's environment. On Unix it is a symlink into whichever of `/opt/homebrew/bin`, `/usr/local/bin`, `~/.local/bin`, `~/bin`, `~/.cargo/bin` your PATH already covers; on Windows the install directory is appended to your user PATH, and the uninstaller takes it back out. A `tty7` you installed yourself is left alone, never replaced. Off via Settings → Agents or `install_cli_on_path: false` in `config.json`
|
||||
|
||||
## SSH
|
||||
|
||||
A native Rust SSH stack (russh) is the **only** path — profiles, credentials,
|
||||
and SFTP without shelling out to `ssh`. There is no system-ssh compat mode.
|
||||
|
||||
- **QuickConnect** — type `user@host[:port]` in the palette and connect; IPv6 `[::1]:port` supported
|
||||
- **Saved profiles** — full connection config with passwords / passphrases in the OS keychain, never on disk
|
||||
- **`~/.ssh/config` aliases** — type one to connect (resolved natively — common fields, best-effort — over russh), or import them as profiles in Settings
|
||||
- **GUI auth** — in-pane sheets for password, key passphrase, 2FA, and host-key confirmation (new vs. changed)
|
||||
- **Built-in SFTP** — a slide-in file panel: browse, upload / download, rename / delete / chmod, drag to Finder
|
||||
- **Port forwarding** — Local / Remote / Dynamic, preconfigured or added live, plus ⌘/Ctrl-click `localhost:PORT` to auto-forward
|
||||
- **Jump hosts & proxies** — multi-hop via profile references or `ProxyJump`, ProxyCommand, SOCKS5 / HTTP
|
||||
|
||||
| Entry point | Connects via |
|
||||
|---|---|
|
||||
| Saved profiles · QuickConnect · typed `user@host[:port]` | Native russh — SFTP · keychain · GUI auth · L/R/D forwards |
|
||||
| `~/.ssh/config` aliases | Resolved natively, then russh (`Match`/canonicalize/GSSAPI unsupported — no fallback) |
|
||||
|
||||
## Keybindings
|
||||
|
||||
Keys are shown in macOS notation — on Windows and Linux, read <kbd>⌘</kbd> as
|
||||
<kbd>Ctrl</kbd>. The essentials:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⌘ T</kbd> · <kbd>⌘ W</kbd> · <kbd>⌘ ⇧ T</kbd> | new tab · close tab · reopen closed tab |
|
||||
| <kbd>⌘ 1</kbd>…<kbd>⌘ 9</kbd> | jump to tab 1–9 |
|
||||
| <kbd>⌃ ⇥</kbd> · <kbd>⌃ ⇧ ⇥</kbd> | hold to walk the switcher forwards · backwards; it commits when you let go |
|
||||
| <kbd>⌘ D</kbd> · <kbd>⌘ ⇧ D</kbd> | split right · split down |
|
||||
| <kbd>⌘ ]</kbd> · <kbd>⌘ [</kbd> | next pane · previous pane |
|
||||
| <kbd>⌘ ⌥ ←→↑↓</kbd> | focus the pane in that direction |
|
||||
| <kbd>⌘ ⏎</kbd> · <kbd>⌘ ⇧ ⏎</kbd> | toggle fullscreen · zoom pane |
|
||||
| <kbd>⌘ K</kbd> | clear scrollback |
|
||||
| <kbd>⌘ P</kbd> | command palette |
|
||||
| <kbd>⌘ F</kbd> | search the scrollback |
|
||||
| <kbd>⌃ R</kbd> | fuzzy-search shell history |
|
||||
| <kbd>⌘ +</kbd> · <kbd>⌘ −</kbd> · <kbd>⌘ 0</kbd> | font size up · down · reset |
|
||||
| <kbd>⌘</kbd> + wheel | zoom the font by scrolling over a terminal |
|
||||
|
||||
**Settings → Keybindings** (<kbd>⌘ ,</kbd>) lists every shortcut. Click one,
|
||||
press the new keys (<kbd>Esc</kbd> cancels, <kbd>Backspace</kbd> resets to
|
||||
default), and it takes effect immediately. Pane resize and swap have no default
|
||||
keys — bind them here or run them from the command palette.
|
||||
|
||||
**tmux preset** — remaps pane/tab actions onto a prefix (default <kbd>⌃ B</kbd>):
|
||||
<kbd>⌃ B</kbd> <kbd>C</kbd> opens a tab, <kbd>⌃ B</kbd> <kbd>%</kbd> splits,
|
||||
<kbd>⌃ B</kbd> then an arrow moves focus. A bare prefix reaches the shell after
|
||||
a brief pause; `prefix` + an unbound key passes straight through.
|
||||
|
||||
## Performance notes
|
||||
|
||||
- The PTY is read at device speed and parsed in large batches, off the render path
|
||||
- Hot paths are lock-free — a big `cat` never waits on drawing
|
||||
- The server buffers up to 16 MiB ahead of the window before backpressure applies
|
||||
|
||||
## macOS privacy
|
||||
|
||||
Panes are forked from the bundled executable, so macOS attributes a program's
|
||||
request for a protected resource to tty7.app. tty7 declares the matching TCC
|
||||
usage strings (camera, microphone, contacts, calendar, reminders, photos,
|
||||
location, local network, Bluetooth, speech recognition, Apple Events, system
|
||||
administration) so that program gets the normal one-time prompt instead of
|
||||
being denied outright with no prompt at all.
|
||||
|
||||
Not covered by usage strings:
|
||||
|
||||
- **Full Disk Access** — Apple defines no usage-string key for it. Reaching
|
||||
`~/Library/Mail`, `~/Library/Messages`, `~/Library/Safari` or
|
||||
`~/Library/Containers` needs a manual grant in System Settings.
|
||||
|
||||
Declaring a usage string is not the same as holding the permission: tty7.app
|
||||
itself is granted none of these resources. Every prompt you see belongs to
|
||||
whatever you ran in the pane, and you can revoke it under Privacy & Security.
|
||||
|
||||
## Localization
|
||||
|
||||
The GUI ships English, Simplified Chinese and Japanese strings. Pick one in
|
||||
Settings → Appearance → Language, or in `config.json`:
|
||||
|
||||
```json
|
||||
{ "gui_language": "zh-CN" }
|
||||
```
|
||||
|
||||
`en`, `zh-CN` and `ja-JP` are the only accepted values; anything else falls back
|
||||
to `en`.
|
||||
The choice is explicit — the system language is never inferred. CLI output stays
|
||||
English so agent and script integrations keep a stable, predictable surface.
|
||||
@@ -1,150 +0,0 @@
|
||||
# 功能
|
||||
|
||||
<sub>[English](features.md) · 简体中文</sub>
|
||||
|
||||
## 输入
|
||||
|
||||
- **影子建议** —— 边打字边用你的历史补全整条命令,<kbd>→</kbd> 接受
|
||||
- **带说明的 Tab 补全** —— 每个 flag、每个子命令都带说明,覆盖约 100 个常用命令;tty7 没有候选时 Tab 自动交给 shell 自己的补全,整个功能也可关闭(设置 → 输入 → 提示符,或 `config.json` 里的 `tab_completion`)
|
||||
- **语法高亮** —— 边打边亮,什么都不用装
|
||||
- **模糊历史搜索** —— <kbd>⌃ R</kbd> 看到每条命令在哪跑的、什么时候、有没有失败;关掉它(设置 → 输入 → 提示符,或 `config.json` 里的 `history_search`)后 <kbd>⌃ R</kbd> 直接交给 shell,你绑的 fzf / percol 照常可用
|
||||
- **历史开箱即用** —— 你已有的 shell 历史直接生效,并跨会话延续
|
||||
- **行编辑** —— 点击定位光标、鼠标选区、词级移动、撤销
|
||||
- **多行编辑** —— 折行和多行命令原地编辑;网格自动上移,光标始终可见。<kbd>⇧ ⏎</kbd> · <kbd>⌥ ⏎</kbd> 插入换行而不提交(可改绑,动作名 `InsertNewline`),单独按 <kbd>⏎</kbd> 提交整个缓冲区
|
||||
|
||||
## 窗口
|
||||
|
||||
- **标签页与分屏** —— 永远开在当前目录
|
||||
- **拖动重排分屏** —— 鼠标移到某个 pane 上,它顶边中间会浮出一个小抓手;拖着它在布局里走,就能把这个 pane 挪到标签页内的别处。落在某个 pane 的某一侧=插到它旁边:那一侧要是朝着同一排的邻居,就并入那一排、和它们等分;要是横着切过这一排(没有排可并),才是把那个 pane 一分为二、自己占住那一半。落在它正中=两个 pane 互换位置;继续推到某个 pane 朝着窗口那一侧的外缘(不是朝着另一个 pane 的那侧)=变成贴着窗口某一边、跨满整行或整列的一条,宽度按那条轴上已有的份数均分 —— 2×2 里的一个 pane 一次拖动就能变成通高的第三列(各占三分之一),而不是独占半屏。拖动过程中落点会高亮,且只有当这一放确实会改变布局时才会亮
|
||||
- **侧栏按仓库分组** —— 左侧标签栏按 git 仓库分组、每组一个标题行,不在仓库里的标签归入末尾的 *草稿* 组;切分支、仓库内 `cd` 都不会挪动行(`config.json` 的 `sidebar_grouping`:默认 `repo`,`none` 恢复扁平列表)
|
||||
- **命令面板** <kbd>⌘ P</kbd> · scrollback 搜索 <kbd>⌘ F</kbd>
|
||||
- **⌘ 点击打开链接** · 桌面通知 · 划选即复制(可选,设置 → 输入 → 选择与剪贴板)
|
||||
- **智能双击选中** —— 双击直接选中整条 URL、文件路径、括号/引号对,中文按词典分词出词;Shift 点击扩展选区(设置 → 输入 → 选择与剪贴板可开关;分隔符用 `config.json` 的 `word_separators` 配置)
|
||||
- **9 套主题,也能自定义** — YAML 种子主题,背景支持纯色、渐变或图片;可导入 iTerm2 `.itermcolors`;应用内颜色编辑器带背景图选择
|
||||
- **跟随系统外观** — 设置 → 外观;分别选好浅色和深色主题,tty7 随系统深浅模式实时切换(`config.json` 中的 `theme_follow_system`、`theme_preset_light` / `theme_preset_dark`)
|
||||
- **窗口透明与模糊** — 设置 → 外观 → 透明度;对所有主题生效,*跟随主题* 恢复主题自带的 `opacity` / `blur`
|
||||
- **CJK / 输入法输入**
|
||||
- **Windows 资源管理器右键菜单** —— 安装程序提供 *Add “Open in tty7” to the folder context menu* 这个安装任务,默认不勾选,卸载时一律移除。写 shell verb 是安装期的决定,所以没有运行时开关;用 portable zip 的话可以自己执行 `tty7-app.exe --register-explorer-menu`(或 `--unregister-explorer-menu`)。两种方式写入的键都在 `HKCU` 下,只影响你自己的 Windows 账户
|
||||
|
||||
## 字体
|
||||
|
||||
- **内置 Hack** —— 打包进二进制,默认配置在各平台渲染完全一致,不依赖系统安装
|
||||
- **主字体 + 有序 fallback** —— `config.json` 里的 `font_family` 和 `font_fallbacks`;可选 `font_family_bold` / `font_family_italic` 指定独立字面,`font_features` 透传 OpenType 特性(上下文连字默认关闭)
|
||||
- **默认列表按平台分支** —— fallback 只写宿主系统真正自带的字体(macOS 用 PingFang SC / Apple Color Emoji,Windows 用 Microsoft YaHei / Segoe UI Emoji,Linux 用 Noto)。这些名字也会追加到你手写的列表后面,所以在别的平台写出来的 `config.json` 一样能落地
|
||||
|
||||
### 中文与两列网格
|
||||
|
||||
一个格子等于主字体的一个 advance,宽字符(CJK)被钉死在正好两格上。所以中文
|
||||
fallback 只有在**汉字 advance 等于主字体西文 advance 的两倍**时,才能严丝合缝地
|
||||
填满自己的槽。
|
||||
|
||||
内置 Hack 的 advance 是 0.60205em,两格就是 1.2041em —— 而系统自带的中文字体
|
||||
(Microsoft YaHei、PingFang SC、Noto Sans CJK)全都是 1.0em。这些字形在槽里左
|
||||
对齐,多出来的约 0.2em 就变成每个字右边的一道空隙。
|
||||
|
||||
[Maple Mono NF CN](https://github.com/subframe7536/maple-font) 在所有平台都排在
|
||||
第一位正是因为这个 —— 西文 0.6em、中文 1.2em,对上 Hack 正好两格。它只按名字引
|
||||
用,不打包(每字重约 20MB):装上即生效,不用改配置。
|
||||
|
||||
想让中文排得**紧**而不只是均匀,要换的是主字体:选一个 advance 为 0.5em 的
|
||||
(比如 Sarasa Mono SC 更纱黑体等宽),两格就正好 1.0em。
|
||||
|
||||
## Coding agent
|
||||
|
||||
tty7 能识别 pane 里跑着的第三方 coding agent(Claude Code、Codex、Gemini CLI、
|
||||
Aider、Amp、OpenCode 等共 18 个)并在其外围加功能 —— 绝不包裹或替代 agent 本身。
|
||||
|
||||
- **品牌头像** —— 标签 chip / 侧栏行显示每个 pane 跑的是哪个 agent;自定义包装命令可通过 `config.json` 的 `agent_commands` 映射
|
||||
- **状态点** —— 工作中(蓝)/ 等你输入(琥珀)/ 完成(绿),由 agent 自己上报的 OSC 事件驱动;在 设置 → Agents 一键装好对应 hooks(Claude Code、Codex、Copilot CLI、OpenCode、Pi、Grok Build、Oh My Pi)
|
||||
- **通知** —— agent 卡在等你批准的那一刻弹 "needs your permission…",每轮结束弹 "finished after Ns",遵循你的通知策略
|
||||
- **一眼看分支** —— 侧栏每行显示该 pane 的 git 分支和工作区改动(`+N −M`),`cd` 或命令跑完时自动刷新;点改动数字会打开 diff 浮层,关掉它(设置 → 窗口与标签页,或 `config.json` 的 `sidebar_diff_preview: false`)分支和数字照常显示,只是不再可点
|
||||
- **会话恢复** —— 重启后无法重连的 pane 会自动续上 agent 对话,并带上原始启动 flags(`claude --dangerously-skip-permissions --resume …`;`restore_agent_sessions`,默认开启)
|
||||
- **Fork 会话** —— 直接调 agent 自己的 fork 命令(`codex fork <id>`、`claude --resume <id> --fork-session`,OpenCode、Grok Build 和 Oh My Pi 同样支持),把当前对话分叉成一个独立会话;原会话原封不动,两边各自往下走。在 pane 上右键可选择分屏位置,在标签 / 侧栏行上右键则直接开新标签。需要先装好该 agent 的 hooks(fork 认的是 hooks 上报的 session id);远程 pane 不能 fork,因为命令会跑在本机的 agent 上;另外 fork 会整份复制对话历史,反复 fork 会在 agent 自己的会话目录里占掉不少磁盘
|
||||
- **复制会话 ID** —— 把 agent 的原生 session id 复制到剪贴板,就在 *复制工作目录* 旁边,方便粘进 `codex resume`、bug 报告或别的工具
|
||||
- **上下文回填** —— 面板命令把当前选区或仓库 `git diff` 打包成 prompt 直接喂给正在跑的 agent
|
||||
- **托盘图标** —— 系统托盘 / 菜单栏常驻图标,任何 agent 等你输入时立即切换为提醒态;菜单列出所有 agent pane(品牌头像 + 状态点,点击直达)、可切换通知策略,并在保留会话的普通退出之外提供 *退出并停止服务器…*(`show_tray_icon`,默认开启)
|
||||
- **`tty7 wait`** —— CLI 的编排原语:阻塞到某个 pane 的 agent 等待输入或完成一轮(`tty7 wait %3 --until waiting,done --changed --timeout 600`,超时退出码 124),让一个 agent 睡到同伴卡在权限确认的那一刻,而不是抓屏猜——然后 `tty7 capture %3 --plain` 收结果。agent 状态是电平不是边沿,所以 `--changed` 会忽略 wait 开始时 pane 本来就处在的那个状态;不加它的话,JSON 里的 `stale` 标记会告诉你这个答案是不是上一轮留下的
|
||||
- **`tty7` 上 PATH** —— CLI 随每个安装包一起发布,启动时自动放到 PATH 上,脚本和 coding agent 在任何终端里都能驱动 tty7。tty7 自己的 pane 里则一定可用,因为 pane 继承 app 的环境。Unix 上是往 `/opt/homebrew/bin`、`/usr/local/bin`、`~/.local/bin`、`~/bin`、`~/.cargo/bin` 中你 PATH 已经覆盖的那个目录里放一个软链;Windows 上是把安装目录追加到用户 PATH,卸载时再摘掉。你自己装的 `tty7` 一律保持原样,不会被覆盖。关掉:设置 → Agents,或 `config.json` 里 `install_cli_on_path: false`
|
||||
|
||||
## SSH
|
||||
|
||||
**唯一**路径就是原生 Rust SSH 栈(russh)—— profile、凭据、SFTP 全部内置,
|
||||
不 shell 出 `ssh`,也没有系统 ssh 兼容模式。
|
||||
|
||||
- **QuickConnect** —— 面板里打 `user@host[:port]` 回车即连;支持 IPv6 `[::1]:port`
|
||||
- **保存 profile** —— 完整连接配置,密码 / passphrase 进 OS keychain,不落盘
|
||||
- **`~/.ssh/config` alias** —— 直接输入 alias 即连(原生解析常用字段,尽力而为,走 russh),也可在设置页一键导入为 profile
|
||||
- **GUI 认证** —— pane 内 sheet 输入密码、私钥 passphrase、2FA,并确认主机密钥(新主机 vs 已变更)
|
||||
- **内置 SFTP** —— 滑入式文件面板:浏览、上传 / 下载、重命名 / 删除 / chmod,可拖进 Finder
|
||||
- **端口转发** —— Local / Remote / Dynamic,预配置或运行时增删,外加 ⌘ 点击 `localhost:PORT` 一键转发
|
||||
- **跳板与代理** —— 经 profile 引用或 `ProxyJump` 多跳、ProxyCommand、SOCKS5 / HTTP
|
||||
|
||||
| 入口 | 连接方式 |
|
||||
|---|---|
|
||||
| 保存 profile · QuickConnect · 输入 `user@host[:port]` | 原生 russh —— SFTP · keychain · GUI 认证 · L/R/D 转发 |
|
||||
| `~/.ssh/config` alias | 原生解析后走 russh(`Match`/canonicalize/GSSAPI 不支持,且无回退) |
|
||||
|
||||
## 快捷键
|
||||
|
||||
下表按 macOS 记法书写 —— 在 Windows 和 Linux 上,把 <kbd>⌘</kbd> 读作
|
||||
<kbd>Ctrl</kbd>。最常用的几个:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⌘ T</kbd> · <kbd>⌘ W</kbd> · <kbd>⌘ ⇧ T</kbd> | 新建标签页 · 关闭标签页 · 恢复关闭的标签页 |
|
||||
| <kbd>⌘ 1</kbd>…<kbd>⌘ 9</kbd> | 跳到第 1–9 个标签页 |
|
||||
| <kbd>⌃ ⇥</kbd> · <kbd>⌃ ⇧ ⇥</kbd> | 按住不放在切换面板里向后 · 向前走,松手即切换 |
|
||||
| <kbd>⌘ D</kbd> · <kbd>⌘ ⇧ D</kbd> | 向右分屏 · 向下分屏 |
|
||||
| <kbd>⌘ ]</kbd> · <kbd>⌘ [</kbd> | 下一个窗格 · 上一个窗格 |
|
||||
| <kbd>⌘ ⌥ ←→↑↓</kbd> | 按方向切换焦点窗格 |
|
||||
| <kbd>⌘ ⏎</kbd> · <kbd>⌘ ⇧ ⏎</kbd> | 切换全屏 · 缩放窗格 |
|
||||
| <kbd>⌘ K</kbd> | 清除 scrollback |
|
||||
| <kbd>⌘ P</kbd> | 命令面板 |
|
||||
| <kbd>⌘ F</kbd> | 搜索 scrollback |
|
||||
| <kbd>⌃ R</kbd> | 模糊搜索 shell 历史 |
|
||||
| <kbd>⌘ +</kbd> · <kbd>⌘ −</kbd> · <kbd>⌘ 0</kbd> | 字号增大 · 减小 · 重置 |
|
||||
| <kbd>⌘</kbd> + 滚轮 | 在终端上滚动缩放字号,演示时随手放大 |
|
||||
|
||||
**设置 → 按键绑定**(<kbd>⌘ ,</kbd>)列出全部快捷键。点一行、按下新键即可
|
||||
(<kbd>Esc</kbd> 取消,<kbd>Backspace</kbd> 恢复默认),改完立即生效。窗格缩放与
|
||||
交换默认不绑定键 —— 在这里绑定,或从命令面板执行。
|
||||
|
||||
**tmux 预设** —— 把窗格/标签页操作映射到前缀键(默认 <kbd>⌃ B</kbd>):
|
||||
<kbd>⌃ B</kbd> <kbd>C</kbd> 新建标签页,<kbd>⌃ B</kbd> <kbd>%</kbd> 分屏,
|
||||
<kbd>⌃ B</kbd> 接方向键切换焦点。单独按前缀键会在短暂延迟后送达 shell,
|
||||
`前缀` + 未绑定的键原样透传给终端。
|
||||
|
||||
## 性能说明
|
||||
|
||||
- 以设备速度读取 PTY,在渲染路径之外成批解析
|
||||
- 热路径全程无锁 —— 再大的 `cat` 也不会阻塞在渲染上
|
||||
- 触发背压前,服务器最多可领先窗口缓冲 16 MiB
|
||||
|
||||
## macOS 隐私
|
||||
|
||||
窗格是从 app bundle 里的可执行文件 fork 出来的,所以程序申请受保护资源时,
|
||||
macOS 会把这次请求算到 tty7.app 头上。tty7 声明了对应的 TCC usage strings
|
||||
(摄像头、麦克风、通讯录、日历、提醒、照片、定位、本地网络、蓝牙、语音识别、
|
||||
Apple Events、系统管理),这样程序才能正常弹出一次性授权窗口,而不是连弹窗都
|
||||
没有就被直接拒绝。
|
||||
|
||||
不受 usage strings 覆盖的:
|
||||
|
||||
- **完全磁盘访问** —— 苹果没有为它定义 usage-string 键。要读写
|
||||
`~/Library/Mail`、`~/Library/Messages`、`~/Library/Safari` 或
|
||||
`~/Library/Containers`,需要在「系统设置」中手动授权。
|
||||
|
||||
声明 usage string 不等于持有权限:tty7.app 自己一项都没有拿到。你看到的每个
|
||||
授权弹窗都属于你在窗格里运行的那个程序,也可以在「隐私与安全性」中撤销。
|
||||
|
||||
## 本地化
|
||||
|
||||
GUI 目前提供英文、简体中文和日文三套文案。在「设置 → 外观 → 语言」中选择,或直接改
|
||||
`config.json`:
|
||||
|
||||
```json
|
||||
{ "gui_language": "zh-CN" }
|
||||
```
|
||||
|
||||
只接受 `en`、`zh-CN` 和 `ja-JP` 三个值,其它值一律回落到 `en`。语言必须显式指定,不会
|
||||
去猜系统语言。CLI 输出保持英文,保证 agent、脚本和开发者工作流的输出稳定可预测。
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "Core concepts"
|
||||
description: "Workspaces, tabs, panes — and the background server that owns them all."
|
||||
---
|
||||
|
||||
Four words explain most of tty7. Three of them you can see; the fourth is the
|
||||
reason the other three survive a reboot.
|
||||
|
||||
## Pane
|
||||
|
||||
A **pane** is one terminal: one shell (or one program) attached to one PTY. It
|
||||
is the only thing in tty7 that actually runs something.
|
||||
|
||||
Panes have stable ids — `%42` — for their whole life. That id is what the
|
||||
[CLI](/cli/overview) addresses, and what `$TTY7_PANE` holds inside the pane
|
||||
itself.
|
||||
|
||||
## Tab
|
||||
|
||||
A **tab** is a layout of panes. One pane to start with; split it and the tab
|
||||
holds two, arranged in rows and columns you can drag around.
|
||||
|
||||
Tabs appear in the sidebar (or the top strip, if you move it there). A tab's
|
||||
label is the best evidence tty7 has: a name you set, else the coding agent
|
||||
running in it, else the last segment of its working directory.
|
||||
|
||||
## Workspace
|
||||
|
||||
A **workspace** is a named set of tabs — a project, usually. One window shows
|
||||
one workspace at a time, and <kbd>⌘ ⇧ O</kbd> opens the switcher to move between
|
||||
them or open a second window on another one.
|
||||
|
||||
Workspaces are how tty7 keeps ten repositories from becoming forty
|
||||
indistinguishable tabs. They also travel: a workspace on a remote machine is
|
||||
still a workspace, opened from the same switcher.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the workspace switcher, workspaces on the left, that workspace's tabs on the right">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 workspace switcher" />
|
||||
</Frame>
|
||||
|
||||
## The server
|
||||
|
||||
Here is the part that matters. **The window does not own your shells — a
|
||||
background server does.**
|
||||
|
||||
Quitting tty7 closes the window and leaves that server running. Your build keeps
|
||||
building, your agent keeps working, your SSH session stays up. Open tty7 again
|
||||
and it reattaches to exactly what was there.
|
||||
|
||||
This is also why:
|
||||
|
||||
- **`tty7` works from any terminal.** The CLI talks to the same server. The GUI
|
||||
does not have to be running at all.
|
||||
- **A crash is not a catastrophe.** Panes come back showing what was on them:
|
||||
a capped tail of each pane's output is kept on disk and handed to the pane
|
||||
that reopens on its id.
|
||||
- **Stopping is explicit.** *Quit and Stop Server…* in the tray menu is the only
|
||||
ordinary way to end everything, and it warns you first.
|
||||
|
||||
<Warning>
|
||||
Restarting the server ends every process in every pane on that machine —
|
||||
shells, agents, and SSH sessions alike. Layouts are kept and come back with
|
||||
fresh shells. Never do it on someone else's behalf without asking.
|
||||
</Warning>
|
||||
|
||||
### What survives what
|
||||
|
||||
| | Close a tab | Quit tty7 | Stop the server | Reboot |
|
||||
|---|:--:|:--:|:--:|:--:|
|
||||
| The shell keeps running | ✗ | ✓ | ✗ | ✗ |
|
||||
| The layout comes back | ✗ | ✓ | ✓ | ✓ |
|
||||
| What was on screen comes back | ✗ | ✓ | ✓ | ✓ <sup>1</sup> |
|
||||
| A supported agent session resumes | ✗ | ✓ | ✓ | ✓ |
|
||||
|
||||
<sub><sup>1</sup> A capped tail of each pane, restored once. See
|
||||
[session restore](/reference/troubleshooting#panes-came-back-empty).</sub>
|
||||
|
||||
## Machines
|
||||
|
||||
Everything above exists per **machine**. Your laptop is one; a dev box you
|
||||
connect to over SSH is another, with its own server, its own workspaces, and its
|
||||
own panes.
|
||||
|
||||
The switcher lists them together, and the CLI reaches them with `-m`:
|
||||
|
||||
```bash
|
||||
tty7 -m devbox ls
|
||||
```
|
||||
|
||||
Remote panes run on the remote machine — the files, the repository, the git
|
||||
data, and the process tree are all over there.
|
||||
[Remote workspaces →](/remote/workspaces)
|
||||
|
||||
## The three environment variables
|
||||
|
||||
Every pane exports these, and anything you launch from one inherits them:
|
||||
|
||||
| Variable | What it holds |
|
||||
|---|---|
|
||||
| `TTY7_PANE` | This pane's id — the default target of `tty7 split`, `send`, `capture`, `procs`. |
|
||||
| `TTY7_WS` | This pane's workspace id. |
|
||||
| `TTY7_CONFIG_DIR` | The config directory, which is how the CLI finds the right server. |
|
||||
|
||||
`echo $TTY7_PANE` is the fastest way to tell whether you are inside tty7 at all.
|
||||
|
||||
<Card title="Drive it from a script" icon="terminal" href="/cli/overview">
|
||||
Those ids are the whole interface. The CLI page starts there.
|
||||
</Card>
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: "First launch"
|
||||
description: "The handful of settings worth changing before you start working."
|
||||
---
|
||||
|
||||
Open tty7 and you get a window with one tab and one shell, and a tab sidebar
|
||||
down the left. Everything below is optional — but these are the settings people
|
||||
end up changing anyway, so they are worth five minutes now.
|
||||
|
||||
Open Settings with <kbd>⌘ ,</kbd> (<kbd>Ctrl ,</kbd> on Windows and Linux), or
|
||||
from the command palette (<kbd>⌘ P</kbd> → *Settings*).
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the Settings window, Appearance section">
|
||||
<img src="/images/placeholder.svg" alt="tty7 Settings" />
|
||||
</Frame>
|
||||
|
||||
## 1. Pick a theme
|
||||
|
||||
**Settings → Appearance → Theme.** Nine themes ship built in — Light, One Light,
|
||||
Catppuccin Latte, Rosé Pine Dawn, Dark, Dracula, Harbor, One Dark Pro, and
|
||||
Rosé Pine. The default is **Light**.
|
||||
|
||||
Turn on **Sync with system** to pick a light theme and a dark theme separately;
|
||||
tty7 then follows the OS appearance live.
|
||||
|
||||
Transparency lives on the same page, under **Transparency** — opacity applies to
|
||||
every theme, and *Follow theme* hands the decision back to the theme's own
|
||||
setting. On Windows there is also a **Background material** picker (Mica,
|
||||
Acrylic, and friends).
|
||||
|
||||
[More about themes →](/customization/themes)
|
||||
|
||||
## 2. Choose your shell
|
||||
|
||||
**Settings → Terminal → Shell.** Leave **Program** empty to use the platform
|
||||
default. Otherwise it takes an executable name on PATH or an absolute path
|
||||
(`zsh`, `fish`, `pwsh`, `nu`, `/opt/homebrew/bin/bash`), plus space-separated
|
||||
**Arguments** — `-l` for a login shell, say.
|
||||
|
||||
**Start in** decides what a *fresh* shell opens in: tty7's launch directory
|
||||
(the default), your home folder, or a fixed path. New tabs and splits keep
|
||||
inheriting the active pane's directory either way.
|
||||
|
||||
## 3. macOS only: decide what Option does
|
||||
|
||||
**Settings → Input → Keyboard → Option (⌥) acts as Meta.**
|
||||
|
||||
Off (the default), <kbd>⌥ B</kbd> types `∫`, which is what macOS has always
|
||||
done. On, it sends the escape chord shells expect, so <kbd>⌥ B</kbd> moves back
|
||||
a word and <kbd>⌥ ⌫</kbd> deletes one. Turn it on if you live in readline;
|
||||
leave it off if you type accented characters.
|
||||
|
||||
## 4. If you use coding agents, install the hooks
|
||||
|
||||
**Settings → Agents.** tty7 detects 18 coding CLIs by process name on its own —
|
||||
you get brand avatars and tab labels for free. The *status dots*, the "needs
|
||||
your permission" notifications, and `tty7 wait` all need one more thing: a small
|
||||
hook the agent calls to report what it is doing.
|
||||
|
||||
Click **Install** next to Claude Code, Codex, Copilot CLI, OpenCode, Pi, Grok
|
||||
Build, or Oh My Pi. It writes into that agent's own config directory and can be
|
||||
removed from the same row.
|
||||
|
||||
[More about agents →](/agents/status)
|
||||
|
||||
## 5. Know what Quit does
|
||||
|
||||
Plain **Quit** closes the window and leaves the background server running.
|
||||
Your shells, builds, and agent turns keep going, and reopening tty7 reattaches
|
||||
to them.
|
||||
|
||||
To actually stop everything, use **Quit and Stop Server…** from the tray icon's
|
||||
menu. It says so plainly before it does it: anything still running in your
|
||||
shells is terminated, while your tabs and layout are kept and reopen with fresh
|
||||
shells.
|
||||
|
||||
<Note>
|
||||
This is why there is no tmux in the picture. The persistence is not a feature
|
||||
of your shell setup — it belongs to the server underneath.
|
||||
[Core concepts →](/getting-started/concepts)
|
||||
</Note>
|
||||
|
||||
## 6. Tune the notifications
|
||||
|
||||
**Settings → Window & Tabs → Notifications.** By default tty7 posts a desktop
|
||||
notification when a foreground command that ran longer than 10 seconds
|
||||
finishes — but only while the window is unfocused. Set **Notify on command
|
||||
finish** to *Never* or *Always*, and move the threshold if 10 seconds is the
|
||||
wrong number for your work.
|
||||
|
||||
Agent notifications ("needs your permission…", "finished after 42s") follow the
|
||||
same policy.
|
||||
|
||||
## 7. Coming from tmux?
|
||||
|
||||
**Settings → Keybindings → Preset → tmux** remaps pane and tab actions onto a
|
||||
prefix, <kbd>⌃ B</kbd> by default. <kbd>⌃ B</kbd> <kbd>C</kbd> opens a tab,
|
||||
<kbd>⌃ B</kbd> <kbd>%</kbd> splits, <kbd>⌃ B</kbd> then an arrow moves focus.
|
||||
|
||||
A bare prefix reaches the shell after about a second, and prefix plus an unbound
|
||||
key passes straight through — so a tmux binding you did not remap still lands in
|
||||
whatever is running.
|
||||
|
||||
[More about keybindings →](/customization/keybindings)
|
||||
|
||||
## Where things live
|
||||
|
||||
| | macOS / Linux | Windows |
|
||||
|---|---|---|
|
||||
| Settings file | `~/.config/tty7/config.json` | `%APPDATA%\tty7\config.json` |
|
||||
| Custom themes | `~/.config/tty7/themes/` | `%APPDATA%\tty7\themes\` |
|
||||
|
||||
Everything in the Settings window writes to `config.json`, and you can edit it
|
||||
by hand instead — see the [configuration reference](/reference/configuration).
|
||||
Set `TTY7_CONFIG_DIR` to move the whole directory somewhere else.
|
||||
|
||||
## Next
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Core concepts" icon="cube" href="/getting-started/concepts">
|
||||
Workspaces, tabs, panes, and the server that owns them.
|
||||
</Card>
|
||||
<Card title="The prompt" icon="terminal" href="/terminal/prompt">
|
||||
Suggestions, completion, and history search — the part you touch most.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,151 @@
|
||||
---
|
||||
title: "Installation"
|
||||
description: "Native builds for macOS, Windows, and Linux — plus building from source."
|
||||
---
|
||||
|
||||
Every release publishes native builds on
|
||||
[**GitHub Releases**](https://github.com/l0ng-ai/tty7/releases). There is no
|
||||
runtime to install first: fonts are embedded in the binary, and the Linux
|
||||
AppImage bundles its own X11/Wayland/font libraries.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="macOS">
|
||||
Download the DMG that matches your Mac and drag **tty7** into Applications.
|
||||
|
||||
| Mac | File |
|
||||
|---|---|
|
||||
| Apple silicon (M1 and later) | `tty7-<version>-macos-arm64.dmg` |
|
||||
| Intel | `tty7-<version>-macos-x86_64.dmg` |
|
||||
|
||||
Builds are signed with a Developer ID certificate and notarized by Apple, so
|
||||
Gatekeeper opens them without a right-click dance.
|
||||
|
||||
<Note>
|
||||
Builds are produced on macOS 14 and macOS 15. macOS 14 (Sonoma) or later
|
||||
is the tested range.
|
||||
</Note>
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows">
|
||||
Two shapes, both x86-64:
|
||||
|
||||
| File | Use it when |
|
||||
|---|---|
|
||||
| `tty7-<version>-windows-x86_64-setup.exe` | You want a normal install with Start-menu entries and an uninstaller. |
|
||||
| `tty7-<version>-windows-x86_64.zip` | You want it portable — unzip anywhere and run `tty7-app.exe`. |
|
||||
|
||||
The installer offers one optional setup task, off by default: **Add "Open in
|
||||
tty7" to the folder context menu**. It writes shell verbs under `HKCU`, so
|
||||
only your own Windows account is affected, and the uninstaller always takes
|
||||
them back out.
|
||||
|
||||
A portable install can add or remove the same entries itself:
|
||||
|
||||
```powershell
|
||||
tty7-app.exe --register-explorer-menu
|
||||
tty7-app.exe --unregister-explorer-menu
|
||||
```
|
||||
|
||||
<Note>
|
||||
The Windows package also carries a Linux `tty7-server` binary so a WSL
|
||||
distro can be served locally instead of downloading one. See
|
||||
[Remote workspaces](/remote/workspaces).
|
||||
</Note>
|
||||
</Tab>
|
||||
|
||||
<Tab title="Linux">
|
||||
| File | Use it when |
|
||||
|---|---|
|
||||
| `tty7-<version>-linux-x86_64.AppImage` | Almost always. `chmod +x` and run — the X11, Wayland, xkb, and font libraries are bundled, so it works on Fedora, Arch, Debian and friends, not just Ubuntu. |
|
||||
| `tty7-<version>-linux-x86_64.tar.gz` | You would rather unpack the plain binary and place it yourself. |
|
||||
|
||||
```bash
|
||||
chmod +x tty7-*-linux-x86_64.AppImage
|
||||
./tty7-*-linux-x86_64.AppImage
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## The `tty7` command
|
||||
|
||||
Every installer ships the `tty7` CLI beside the app, and the app puts it on your
|
||||
PATH the first time it launches. That is what lets a script — or a coding agent
|
||||
in some other terminal — open panes and read them back.
|
||||
|
||||
- **On Unix** it is a symlink into whichever of `/opt/homebrew/bin`,
|
||||
`/usr/local/bin`, `~/.local/bin`, `~/bin`, or `~/.cargo/bin` your PATH already
|
||||
covers.
|
||||
- **On Windows** the install directory is appended to your user PATH, and the
|
||||
uninstaller removes it again.
|
||||
|
||||
A `tty7` you installed yourself — a `cargo install` build, a package manager's
|
||||
copy — is never replaced. To turn the whole thing off, uncheck **Settings →
|
||||
Agents → Install the tty7 command on PATH**.
|
||||
|
||||
<Tip>
|
||||
Inside a tty7 pane the CLI works regardless of PATH, because panes inherit the
|
||||
app's environment.
|
||||
</Tip>
|
||||
|
||||
## Updating
|
||||
|
||||
tty7 checks for updates every six hours and can update itself: **Settings →
|
||||
About → Check now**, then **Update and relaunch**. Releases are downloaded and
|
||||
verified in the background so applying one is just a restart.
|
||||
|
||||
Pick **Stable** or **Nightly** under **Settings → About → Update channel**. See
|
||||
[Updates and channels](/reference/updates) for what each feed publishes and how
|
||||
switching behaves.
|
||||
|
||||
## Building from source
|
||||
|
||||
You need a stable Rust toolchain. The build is a plain `cargo build`; the app
|
||||
binary is `tty7-app`.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="macOS / Windows">
|
||||
```bash
|
||||
git clone https://github.com/l0ng-ai/tty7
|
||||
cd tty7
|
||||
cargo build --release
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Linux">
|
||||
gpui resolves its X11/Wayland/font backends through `pkg-config` at build
|
||||
time, so the development packages have to be present:
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y pkg-config cmake clang \
|
||||
libxkbcommon-dev libxkbcommon-x11-dev \
|
||||
libfontconfig1-dev libfreetype6-dev \
|
||||
libwayland-dev libx11-dev libxcb1-dev \
|
||||
libzstd-dev libssl-dev libkrb5-dev
|
||||
cargo build --release
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Warning>
|
||||
A source build does not update itself, and it will not replace an installed
|
||||
copy's server. If you run both, see
|
||||
[Troubleshooting](/reference/troubleshooting).
|
||||
</Warning>
|
||||
|
||||
## Uninstalling
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="macOS">
|
||||
Quit tty7 (use **Quit and Stop Server** from the tray menu so the background
|
||||
server stops too), then drag the app to the Trash. Your settings live in
|
||||
`~/.config/tty7` and are left alone; delete that folder to remove them.
|
||||
</Accordion>
|
||||
<Accordion title="Windows">
|
||||
Use **Add or remove programs**. The uninstaller removes the PATH entry and
|
||||
any Explorer context-menu keys it added. Settings live in
|
||||
`%APPDATA%\tty7`.
|
||||
</Accordion>
|
||||
<Accordion title="Linux">
|
||||
Delete the AppImage or the unpacked directory. Settings live in
|
||||
`~/.config/tty7`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: "Diffs"
|
||||
description: "The diff overlay: side-by-side or unified, from the sidebar or the panel."
|
||||
---
|
||||
|
||||
## Opening one
|
||||
|
||||
| From | How |
|
||||
|---|---|
|
||||
| The sidebar | Click a row's `+N −M` counts |
|
||||
| Source Control | **Open Changes** on a file, or click the row |
|
||||
| History | Click a file inside a commit's detail view |
|
||||
|
||||
The overlay covers the window; <kbd>Esc</kbd> closes it.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the diff overlay, side-by-side, several files">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 diff overlay" />
|
||||
</Frame>
|
||||
|
||||
## Side-by-side or unified
|
||||
|
||||
**Toggle Unified / Side-by-Side Diff** in the command palette switches between
|
||||
the two. The choice is global — one setting for every diff, the same call VS
|
||||
Code's `diffEditor.renderSideBySide` makes — and persists as `diff_view` in
|
||||
`config.json`.
|
||||
|
||||
## What it shows
|
||||
|
||||
- Every changed file, with its status and `+N −M`
|
||||
- **Untracked files** as a preview of their contents, up to 4 MB — past that the
|
||||
card says the read failed rather than showing a silently cut-off file
|
||||
- A commit's files, when the diff came from the history
|
||||
|
||||
Two limits keep a huge diff from becoming a huge wait:
|
||||
|
||||
| Limit | Value | What happens |
|
||||
|---|---|---|
|
||||
| Files rendered | 300 | *"Showing the first 300 of N changes."* |
|
||||
| Lines before auto-collapse | 400 per file | Big files start collapsed; expand the ones you care about |
|
||||
|
||||
Both are stated in the overlay when they apply — nothing is dropped silently.
|
||||
|
||||
## Turning the sidebar shortcut off
|
||||
|
||||
If you would rather the sidebar's counts not be clickable, turn off **Settings →
|
||||
Window & Tabs → Open diff preview from sidebar counts**
|
||||
(`sidebar_diff_preview: false`). The branch and counts stay on the row; they
|
||||
just stop opening the overlay.
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: "Source control"
|
||||
description: "Stage, commit, branch, and push from the panel beside your terminal."
|
||||
---
|
||||
|
||||
The **Source Control** tab of the [side panel](/window/side-panel) (<kbd>⌘ J</kbd>)
|
||||
is a full git client for whichever repository the focused pane is in. It follows
|
||||
the pane: `cd` into another repository and the panel switches with you.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the Source Control panel with staged and unstaged groups and a commit box">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 source control panel" />
|
||||
</Frame>
|
||||
|
||||
## Changes
|
||||
|
||||
Files are grouped by what git thinks of them:
|
||||
|
||||
| Group | |
|
||||
|---|---|
|
||||
| **Merge Changes** | Conflicts, with *Resolve Conflict* and *Mark as Resolved* |
|
||||
| **Staged Changes** | What the next commit will contain |
|
||||
| **Changes** | Modified but not staged |
|
||||
| **Untracked** | New files |
|
||||
|
||||
Each row has **Stage Changes**, **Unstage Changes**, **Discard Changes**, and
|
||||
**Open Changes** — which opens the [diff](/git/diffs). Group-level *Stage All*,
|
||||
*Unstage All*, and *Discard All* sit on the headers, and the destructive ones
|
||||
confirm first.
|
||||
|
||||
## Committing
|
||||
|
||||
Write the message in the box at the top and pick a commit action:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Commit** | Commit what is staged |
|
||||
| **Commit All** | Stage everything, then commit |
|
||||
| **Commit (Amend)** | Replace the last commit — confirms first, because anyone who already has it has to reconcile |
|
||||
| **Commit & Push** | Commit, then push |
|
||||
| **Commit & Sync** | Commit, then pull and push |
|
||||
|
||||
<kbd>⌘ ⏎</kbd> commits while the caret is in the message box. **Stash All** is
|
||||
there too.
|
||||
|
||||
## Branches and remotes
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Checkout to…** | Switch branches, with a search box; offers **Stash & Switch** when the tree is dirty |
|
||||
| **Create Branch…** | From here, or from any commit in the history |
|
||||
| **Publish Branch** | For a branch with no upstream yet |
|
||||
| **Sync Changes** | Pull, then push |
|
||||
| **Push** · **Pull** · **Fetch** | Individually |
|
||||
|
||||
All of these are also in the command palette under **Git**, so they are
|
||||
bindable.
|
||||
|
||||
When a repository is mid-operation — merging, rebasing, cherry-picking,
|
||||
reverting, bisecting, applying — the panel says so instead of pretending
|
||||
everything is normal.
|
||||
|
||||
## History
|
||||
|
||||
**Git: Toggle Commit History** (or the *History* section header) opens the
|
||||
commit graph: branches drawn as lanes, a filter box, **Current Branch** or
|
||||
**All Branches**, and *Load more* at the bottom.
|
||||
|
||||
Click a commit for its detail view — message, parents, and the files it touched,
|
||||
each openable as a diff. From a commit's menu:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Checkout Commit** · **Create Branch Here…** | Move to it |
|
||||
| **Cherry Pick** · **Revert Commit** | Apply or undo it here |
|
||||
| **Reset (Soft / Mixed / Hard)** | Move the branch to it — Hard confirms, since commits after it fall off the branch and uncommitted changes are discarded |
|
||||
| **Copy Commit SHA** | |
|
||||
|
||||
The history section starts collapsed and remembers whether you opened it
|
||||
(`scm_graph_expanded`).
|
||||
|
||||
## In the sidebar
|
||||
|
||||
You do not have to open the panel to know where you stand: every
|
||||
[sidebar row](/window/sidebar) carries its pane's branch and a `+N −M` count of
|
||||
the working tree, refreshed on `cd` and when a command finishes. Clicking the
|
||||
counts opens the diff overlay.
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: "Worktrees"
|
||||
description: "An isolated checkout on a fresh branch, in one dialog and one tab."
|
||||
---
|
||||
|
||||
Running two agents on the same repository at once means they fight over the
|
||||
working tree. A git worktree is the fix, and tty7 makes it a single dialog.
|
||||
|
||||
## Creating one
|
||||
|
||||
**New Worktree Tab…** — in the command palette, the tab's right-click menu, and
|
||||
the application menu — asks three things:
|
||||
|
||||
| Field | Default |
|
||||
|---|---|
|
||||
| **Worktree Name** | A fresh name that does not collide with an existing branch or directory |
|
||||
| **New Branch** | The same name, editable |
|
||||
| **Start From** | The branch you are currently on |
|
||||
|
||||
Each field opens on a suggestion you can accept or type straight over.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the New Worktree Tab dialog">
|
||||
<img src="/images/placeholder.svg" alt="Creating a worktree" />
|
||||
</Frame>
|
||||
|
||||
Confirm and tty7 creates the worktree, opens a tab in it, and starts a shell
|
||||
there. The [sidebar](/window/sidebar) files it under the same repository group as
|
||||
its parent, on its own branch.
|
||||
|
||||
## Where they go
|
||||
|
||||
Worktrees land inside the repository, under:
|
||||
|
||||
```
|
||||
<repo>/.tty7/worktrees/<name>
|
||||
```
|
||||
|
||||
`<repo>/.tty7/.gitignore` is created with `*` in it the first time, so the
|
||||
directory never shows up as an untracked mess in your own repository.
|
||||
|
||||
## Removing one
|
||||
|
||||
Closing a worktree tab offers to remove the worktree with it:
|
||||
|
||||
- **Clean tree** — *Remove Worktree* or *Keep*.
|
||||
- **Dirty tree** — the dialog says so, and removing requires the explicit
|
||||
*Discard Changes & Remove*.
|
||||
|
||||
Nothing is removed silently, and *Keep* leaves the worktree on disk for `git
|
||||
worktree list` to find later.
|
||||
|
||||
<Tip>
|
||||
Pair this with [agent sessions](/agents/sessions): a worktree per agent means
|
||||
two Claude Codes can work on the same repository without stepping on each
|
||||
other's files.
|
||||
</Tip>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 675" width="1200" height="675" role="img" aria-label="Screenshot placeholder">
|
||||
<rect width="1200" height="675" rx="12" fill="#0f1113"/>
|
||||
<rect x="1" y="1" width="1198" height="673" rx="12" fill="none" stroke="#2a2e33" stroke-width="2"/>
|
||||
<g fill="#3FDD8C" opacity="0.9">
|
||||
<circle cx="44" cy="40" r="7"/>
|
||||
<circle cx="68" cy="40" r="7" opacity="0.45"/>
|
||||
<circle cx="92" cy="40" r="7" opacity="0.25"/>
|
||||
</g>
|
||||
<line x1="0" y1="76" x2="1200" y2="76" stroke="#2a2e33" stroke-width="2"/>
|
||||
<text x="600" y="340" text-anchor="middle" fill="#5c646d" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="34">screenshot coming soon</text>
|
||||
<text x="600" y="386" text-anchor="middle" fill="#3a4046" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="20">tty7 docs</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 881 B |
+105
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: "tty7"
|
||||
sidebarTitle: "Introduction"
|
||||
description: "A terminal workbench: persistent sessions, remote work, agents."
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/hero.webp" alt="tty7 showing a sidebar of agent sessions across several repositories" />
|
||||
</Frame>
|
||||
|
||||
tty7 is a terminal you can leave running. Close the window, reboot the machine,
|
||||
walk to a different laptop — the shells you started are still there, and so are
|
||||
the coding agents you left working in them.
|
||||
|
||||
It is written in Rust, renders on the GPU through Zed's
|
||||
[gpui](https://github.com/zed-industries/zed), and parses VT with Alacritty's
|
||||
terminal core. In practice that means roughly twice the throughput of Alacritty,
|
||||
Ghostty, or Kitty on a big `cat`, and a frame rate that does not fall over when
|
||||
something floods the screen.
|
||||
|
||||
## What makes it different
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Sessions that outlive the app" icon="power-off" href="/getting-started/concepts">
|
||||
A background server owns your shells, not the window. Quit tty7 and your
|
||||
builds keep building. No tmux to learn or configure.
|
||||
</Card>
|
||||
<Card title="Editor-grade input" icon="keyboard" href="/terminal/prompt">
|
||||
Ghost suggestions from your history, tab completion that explains each flag,
|
||||
syntax highlighting, click-to-place-caret, real multi-line editing.
|
||||
</Card>
|
||||
<Card title="Agent-aware" icon="robot" href="/agents/overview">
|
||||
18 coding CLIs are recognised on sight. Per-pane status dots, notifications
|
||||
when one needs you, git context, and session resume after a reboot.
|
||||
</Card>
|
||||
<Card title="Remote work that feels local" icon="server" href="/remote/workspaces">
|
||||
A native Rust SSH stack with profiles, SFTP, and port forwarding — plus
|
||||
remote workspaces where files, repos, and panes all stay on the far machine.
|
||||
</Card>
|
||||
<Card title="Git where you are looking" icon="code-branch" href="/git/source-control">
|
||||
Branch and diff counts on every sidebar row, a source control panel, a diff
|
||||
overlay, and worktrees in one dialog.
|
||||
</Card>
|
||||
<Card title="Scriptable" icon="terminal" href="/cli/overview">
|
||||
A bundled `tty7` CLI that opens panes, sends keys, reads screens, and blocks
|
||||
until an agent needs you — so scripts and agents can drive the workbench.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Start here
|
||||
|
||||
<Steps>
|
||||
<Step title="Install it">
|
||||
Native builds for macOS, Windows, and Linux.
|
||||
[Installation →](/getting-started/installation)
|
||||
</Step>
|
||||
<Step title="Set it up">
|
||||
Five minutes of settings that pay for themselves.
|
||||
[First launch →](/getting-started/first-launch)
|
||||
</Step>
|
||||
<Step title="Learn the three words">
|
||||
Workspace, tab, pane — and the server underneath them.
|
||||
[Core concepts →](/getting-started/concepts)
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## How fast, exactly
|
||||
|
||||
Same machine, same day, same 155×40 grid — Apple M1 Pro, macOS 26.3.1,
|
||||
five-run averages.
|
||||
|
||||
| | **tty7** | Alacritty | Ghostty | Kitty |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Plaintext I/O — 11 MB `cat` <sub>(lower is better)</sub> | **95 ms** | 239 ms | 179 ms | 185 ms |
|
||||
| [DOOM-fire](https://github.com/const-void/DOOM-fire-zig) frame rate <sub>(higher is better)</sub> | **888 fps** | 485 fps | 552 fps | 617 fps |
|
||||
| Cold-launch memory | 116 MB <sup>1</sup> | 105 MB | 128 MB | 130 MB |
|
||||
|
||||
<sub><sup>1</sup> GUI 105 MB plus the persistent server at 11 MB.</sub>
|
||||
|
||||
The methodology and a one-command reproduction live in
|
||||
[`scripts/bench/`](https://github.com/l0ng-ai/tty7/tree/main/scripts/bench).
|
||||
|
||||
Three decisions account for most of it:
|
||||
|
||||
- **The PTY is read at device speed** and parsed in large batches, off the
|
||||
render path — so drawing never throttles reading.
|
||||
- **The hot paths are lock-free.** A big `cat` never waits on the renderer.
|
||||
- **The server buffers up to 16 MiB** ahead of the window before backpressure
|
||||
applies, which is enough that a flood finishes writing while the window is
|
||||
still catching up.
|
||||
|
||||
## Getting help
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Discord" icon="discord" href="https://discord.gg/s3dethqz2V">
|
||||
Ask a question, show what you built.
|
||||
</Card>
|
||||
<Card title="Issues" icon="github" href="https://github.com/l0ng-ai/tty7/issues">
|
||||
Bugs and feature requests.
|
||||
</Card>
|
||||
<Card title="Changelog" icon="list" href="https://github.com/l0ng-ai/tty7/blob/main/CHANGELOG.md">
|
||||
Everything that shipped, release by release.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg width="120" height="120" viewBox="8 6 80 80" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- "Duo" mark on transparent — same geometry as app-icon.svg, no tile. -->
|
||||
<rect x="28" y="16" width="58" height="46" rx="12" fill="#3FDD8C" opacity="0.8"/>
|
||||
<rect x="10" y="30" width="58" height="46" rx="12" fill="#17171A"/>
|
||||
<path d="M24 42 L35 53 L24 64" fill="none" stroke="#ECEAE4" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 456 B |
@@ -0,0 +1,183 @@
|
||||
---
|
||||
title: "config.json"
|
||||
description: "Every key tty7 reads, its type, and its default."
|
||||
---
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| macOS / Linux | `~/.config/tty7/config.json` |
|
||||
| Windows | `%APPDATA%\tty7\config.json` |
|
||||
| Override the whole directory | `TTY7_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
|
||||
}
|
||||
```
|
||||
|
||||
## 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_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). |
|
||||
|
||||
[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`. |
|
||||
| `sidebar_grouping` | enum | `"repo"` | Or `none` for a flat list. |
|
||||
| `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. |
|
||||
| `scm_graph_expanded` | bool | `false` | Whether the history section starts open. |
|
||||
| `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. |
|
||||
| `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_hide_while_typing` | bool | `true` | |
|
||||
| `focus_follows_mouse` | bool | `false` | |
|
||||
|
||||
## Input and clipboard
|
||||
|
||||
| Key | Type | Default | |
|
||||
|---|---|---|---|
|
||||
| `tab_completion` | bool | `true` | tty7's completion menu on <kbd>⇥</kbd>. Off hands the key to the shell. |
|
||||
| `history_search` | bool | `true` | tty7's fuzzy history on <kbd>⌃ R</kbd>. Off hands the key to the shell. |
|
||||
| `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_command` | string | — | Command for file links. `{path}`, `{line}`, `{column}` are substituted; a flag whose value is missing is dropped. |
|
||||
| `ssh_loopback_forward` | bool | `false` | Open `localhost:PORT` links through a temporary forward when the pane is in SSH. |
|
||||
|
||||
## 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"}`. |
|
||||
| `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 | |
|
||||
|---|---|---|---|
|
||||
| `ssh_profiles` | array | `[]` | Managed from **Settings → SSH**. Secrets live in the OS keychain, never here. |
|
||||
| `verify_host_keys` | bool | `true` | |
|
||||
| `ssh_warn_on_close` | bool | `false` | Confirm before closing a live connection. |
|
||||
|
||||
## 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
|
||||
|
||||
`ssh_profile_frecency` and `command_frecency` record how often and how recently
|
||||
you use a profile or command, so the pickers 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>
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: "Keyboard shortcuts"
|
||||
description: "Every default binding, plus the action names for rebinding."
|
||||
---
|
||||
|
||||
**Settings → Keybindings** (<kbd>⌘ ,</kbd>) is the live version of this page —
|
||||
it shows what *your* copy is bound to. This is the shipped default.
|
||||
|
||||
## Tabs and workspaces
|
||||
|
||||
| Action | macOS | Windows / Linux |
|
||||
|---|---|---|
|
||||
| New Tab | <kbd>⌘ T</kbd> | <kbd>Ctrl ⇧ T</kbd> |
|
||||
| Close Pane / Tab | <kbd>⌘ W</kbd> | <kbd>Ctrl ⇧ W</kbd> |
|
||||
| Reopen Closed Tab | <kbd>⌘ ⇧ T</kbd> | <kbd>Alt ⇧ T</kbd> |
|
||||
| Next Tab · Previous Tab | <kbd>⌃ ⇥</kbd> · <kbd>⌃ ⇧ ⇥</kbd> | same |
|
||||
| Go to Tab 1–9 | <kbd>⌘ 1</kbd>…<kbd>⌘ 9</kbd> | <kbd>Alt 1</kbd>…<kbd>Alt 9</kbd> |
|
||||
| New Workspace | <kbd>⌘ ⇧ N</kbd> | <kbd>Ctrl ⇧ N</kbd> |
|
||||
| Switch Workspace | <kbd>⌘ ⇧ O</kbd> | <kbd>Ctrl ⇧ O</kbd> |
|
||||
|
||||
## Panes
|
||||
|
||||
| Action | macOS | Windows / Linux |
|
||||
|---|---|---|
|
||||
| Split Right | <kbd>⌘ D</kbd> | <kbd>Ctrl ⇧ D</kbd> |
|
||||
| Split Down | <kbd>⌘ ⇧ D</kbd> | <kbd>Ctrl Alt ⇧ D</kbd> |
|
||||
| Next Pane · Previous Pane | <kbd>⌘ ]</kbd> · <kbd>⌘ [</kbd> | <kbd>Ctrl ⇧ ]</kbd> · <kbd>Ctrl ⇧ [</kbd> |
|
||||
| Focus Pane Left / Right / Up / Down | <kbd>⌘ ⌥ ←→↑↓</kbd> | <kbd>Alt ←→↑↓</kbd> |
|
||||
| Zoom Pane | <kbd>⌘ ⇧ ⏎</kbd> | <kbd>Ctrl ⇧ ⏎</kbd> |
|
||||
| Enter Full Screen | <kbd>⌘ ⏎</kbd> | <kbd>F11</kbd> |
|
||||
|
||||
## View
|
||||
|
||||
| Action | macOS | Windows / Linux |
|
||||
|---|---|---|
|
||||
| Command Palette | <kbd>⌘ P</kbd> | <kbd>Ctrl ⇧ P</kbd> |
|
||||
| Toggle Left Sidebar | <kbd>⌘ B</kbd> | <kbd>Ctrl ⇧ B</kbd> |
|
||||
| Toggle Right Panel | <kbd>⌘ J</kbd> | <kbd>Ctrl ⇧ J</kbd> |
|
||||
| Toggle Code Panel | <kbd>⌘ ⇧ E</kbd> | <kbd>Ctrl ⇧ E</kbd> |
|
||||
| Font Size Up · Down · Reset | <kbd>⌘ +</kbd> · <kbd>⌘ −</kbd> · <kbd>⌘ 0</kbd> | <kbd>Ctrl +</kbd> · <kbd>Ctrl −</kbd> · <kbd>Ctrl 0</kbd> |
|
||||
| Zoom the font | <kbd>⌘</kbd> + wheel | <kbd>Ctrl</kbd> + wheel |
|
||||
|
||||
## Terminal
|
||||
|
||||
| Action | macOS | Windows / Linux |
|
||||
|---|---|---|
|
||||
| Find in Terminal | <kbd>⌘ F</kbd> | <kbd>Ctrl ⇧ F</kbd> |
|
||||
| Find Next · Previous | <kbd>⌘ G</kbd> · <kbd>⌘ ⇧ G</kbd> | <kbd>F3</kbd> · <kbd>⇧ F3</kbd> |
|
||||
| Clear Scrollback | <kbd>⌘ K</kbd> | <kbd>Ctrl ⇧ K</kbd> |
|
||||
| Copy · Paste | <kbd>⌘ C</kbd> · <kbd>⌘ V</kbd> | <kbd>Ctrl ⇧ C</kbd> · <kbd>Ctrl ⇧ V</kbd> · <kbd>⇧ Insert</kbd> |
|
||||
| Insert Newline (at the prompt) | <kbd>⇧ ⏎</kbd> · <kbd>⌥ ⏎</kbd> | same |
|
||||
| Fuzzy history search | <kbd>⌃ R</kbd> | same |
|
||||
| Accept ghost suggestion | <kbd>→</kbd> | same |
|
||||
| Completion menu | <kbd>⇥</kbd> | same |
|
||||
|
||||
## Git and SSH
|
||||
|
||||
| Action | macOS | Windows / Linux |
|
||||
|---|---|---|
|
||||
| Commit (caret in the message box) | <kbd>⌘ ⏎</kbd> | <kbd>Ctrl ⏎</kbd> |
|
||||
| Save (in the editor) | <kbd>⌘ S</kbd> | <kbd>Ctrl S</kbd> |
|
||||
| Restart SSH Session | <kbd>⌘ ⇧ R</kbd> | <kbd>Ctrl ⇧ R</kbd> |
|
||||
|
||||
## Application
|
||||
|
||||
| Action | macOS | Windows / Linux |
|
||||
|---|---|---|
|
||||
| Settings | <kbd>⌘ ,</kbd> | <kbd>Ctrl ,</kbd> |
|
||||
| Keyboard Shortcuts | <kbd>⌘ /</kbd> | — |
|
||||
| Hide tty7 · Hide Others · Minimize | <kbd>⌘ H</kbd> · <kbd>⌘ ⌥ H</kbd> · <kbd>⌘ M</kbd> | — |
|
||||
| Quit | <kbd>⌘ Q</kbd> | <kbd>Ctrl ⇧ Q</kbd> |
|
||||
|
||||
## Actions with no default key
|
||||
|
||||
All of these are in the command palette, and all are bindable under **Settings →
|
||||
Keybindings**:
|
||||
|
||||
| Group | Actions |
|
||||
|---|---|
|
||||
| Tabs | `RenameTab` · `NewWorktreeTab` · `CloseOtherTabs` · `CloseTabsToTheRight` · `CopyWorkingDirectory` · `MarkTabUnread` · `ToggleTabSidebar` |
|
||||
| Panes | `ResizePaneLeft/Right/Up/Down` · `SwapPaneNext` · `SwapPanePrev` |
|
||||
| Workspaces | `SelectWorkspace1`…`SelectWorkspace9` · `RenameWorkspace` · `StopWorkspace` · `DeleteWorkspace` |
|
||||
| Agents | `ForkAgentSession` (+ `Right` / `Left` / `Down` / `Up`) · `CopyAgentSessionId` |
|
||||
| Git | `ScmStageAll` · `ScmUnstageAll` · `ScmDiscardAll` · `ScmCommitAmend` · `ScmRefresh` · `ScmSync` · `ScmPush` · `ScmPull` · `ScmFetch` · `ScmCheckoutBranch` · `ScmCreateBranch` · `ScmToggleGraph` · `ToggleDiffViewMode` |
|
||||
| Panels | `ShowRightPanelInfo` · `ShowRightPanelChanges` · `ShowRightPanelFiles` |
|
||||
| SSH | `ToggleSftp` · `ShowSshForwards` · `OpenSshProfiles` |
|
||||
| Application | `About` · `CheckForUpdates` · `OpenDocumentation` · `OpenDiscord` · `ReportIssue` · `ShowAll` · `ZoomWindow` |
|
||||
|
||||
## Rebinding syntax
|
||||
|
||||
```json
|
||||
{
|
||||
"keybindings": {
|
||||
"ResizePaneLeft": "ctrl-alt-left",
|
||||
"ToggleSftp": "secondary-shift-u",
|
||||
"ScmSync": "ctrl-b s"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`secondary` means <kbd>⌘</kbd> on macOS and <kbd>Ctrl</kbd> elsewhere. A space
|
||||
separates the steps of a chord.
|
||||
[More →](/customization/keybindings)
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "Privacy and permissions"
|
||||
description: "What macOS asks you, why, and what tty7 itself holds."
|
||||
---
|
||||
|
||||
## Why macOS asks tty7 for permission
|
||||
|
||||
Panes are forked from tty7's own bundled executable, so when a program you run
|
||||
asks macOS for a protected resource, macOS attributes the request to **tty7.app**
|
||||
— not to the program.
|
||||
|
||||
If tty7 declared no usage strings, that request would be **denied outright with
|
||||
no prompt at all**, and the program would look broken for no visible reason.
|
||||
|
||||
So tty7 declares the matching usage strings, and you get the normal one-time
|
||||
prompt:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Devices" icon="camera">
|
||||
Camera · microphone · Bluetooth · location · motion
|
||||
</Card>
|
||||
<Card title="Personal data" icon="address-book">
|
||||
Contacts · calendars · reminders · photo library
|
||||
</Card>
|
||||
<Card title="System" icon="gear">
|
||||
Local network · Apple Events · speech recognition · system administration
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Warning>
|
||||
**Declaring a usage string is not the same as holding the permission.**
|
||||
tty7.app itself is granted none of these. Every prompt you see belongs to
|
||||
whatever you ran in the pane, and you can revoke it under **System Settings →
|
||||
Privacy & Security**.
|
||||
</Warning>
|
||||
|
||||
### Full Disk Access
|
||||
|
||||
Apple defines no usage-string key for it. Reaching `~/Library/Mail`,
|
||||
`~/Library/Messages`, `~/Library/Safari`, or `~/Library/Containers` needs a
|
||||
manual grant in **System Settings → Privacy & Security → Full Disk Access**.
|
||||
|
||||
## What leaves your machine
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Update checks** | A request to the GitHub releases API every six hours, plus the download when you accept one. Turn it off with `check_for_updates: false`. |
|
||||
| **Remote server installs** | Downloading a `tty7-server` binary for a machine you connected to — or, for WSL, copying the one already bundled with your install. |
|
||||
| **Everything else** | Nothing. There is no telemetry, no analytics, and no account. |
|
||||
|
||||
Both of the above honour `http_proxy`. [Updates →](/reference/updates#proxies)
|
||||
|
||||
## What is stored, and where
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Settings, themes, window state | `~/.config/tty7/` (`%APPDATA%\tty7\` on Windows) |
|
||||
| SSH passwords and key passphrases | The **OS keychain** — never `config.json`, never plain text on disk |
|
||||
| Pane scrollback tails | `<config>/scrollback/*.bin`, mode `0600` on Unix and behind the config directory's ACL on Windows. 256 KiB per pane, kept only until something can no longer ask for it: closing a pane deletes its file at once, a restore consumes it, and a periodic pass collects the rest. |
|
||||
| Shell history | Your shell's own file, exactly as before — unless you turned on per-pane history, which merges back into it. |
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: "Shell integration"
|
||||
description: "What tty7 injects into your shell, and what it buys you."
|
||||
---
|
||||
|
||||
A terminal that only sees bytes cannot tell a prompt from output, or a finished
|
||||
command from a hung one. tty7's shell integration closes that gap: the shell
|
||||
reports where prompts begin, what was submitted, what it exited with, and where
|
||||
it is.
|
||||
|
||||
**You do not install it.** It is injected when the pane's shell starts, and
|
||||
removes itself from the equation if you run the same shell elsewhere.
|
||||
|
||||
## Which shells
|
||||
|
||||
| Shell | How it is injected |
|
||||
|---|---|
|
||||
| **zsh** | A throwaway `ZDOTDIR` whose files source yours first, then tty7's. Your `TTY7_USER_ZDOTDIR` is preserved. |
|
||||
| **bash** | An rcfile that sources your own first. |
|
||||
| **fish** | A `-C` init command. |
|
||||
| **PowerShell** | An encoded init command that wraps your existing `prompt` function and PSReadLine's line reader. |
|
||||
| **WSL** *(Windows)* | The distro's shell is bootstrapped with the same scripts. |
|
||||
| **Remote panes** | The same three POSIX shells, bootstrapped over the SSH connection. Toggle per profile with **Settings → SSH → Session → Shell integration**. |
|
||||
|
||||
`TTY7_SHELL_INTEGRATION` is set once it is active, and guards against a second
|
||||
injection when shells nest.
|
||||
|
||||
<Note>
|
||||
A shell launched with custom arguments is left alone for bash and PowerShell,
|
||||
because tty7's injection would conflict with the flags you chose.
|
||||
</Note>
|
||||
|
||||
## What it reports
|
||||
|
||||
| Signal | Sequence | Used for |
|
||||
|---|---|---|
|
||||
| Prompt begins / input begins | `OSC 133;A`, `133;B` | The [prompt layer](/terminal/prompt): suggestions, completion, multi-line editing |
|
||||
| Command submitted | `OSC 133;C` | Knowing a command is running; agent detection on Windows, where ConPTY exposes no foreground process group |
|
||||
| Command finished, with exit code | `OSC 133;D` | The "finished after 42s" notification, failure marks in [history](/terminal/history) |
|
||||
| Working directory | `OSC 7` | New tabs and splits opening in the right place, the sidebar's repo grouping, the git branch readout |
|
||||
| Editing mode (vi / emacs) | `OSC 133;V` | Matching tty7's key handling to your shell's mode |
|
||||
| Window title | `OSC 0` | Tab labels |
|
||||
|
||||
## What turns off without it
|
||||
|
||||
Run a shell tty7 does not integrate with, and everything below still works —
|
||||
it just falls back to less precise sources:
|
||||
|
||||
- Ghost suggestions, the completion menu, and <kbd>⌃ R</kbd>'s fuzzy history
|
||||
- "Command finished" notifications and the failure marks in history search
|
||||
- Exact working-directory tracking (tty7 falls back to inspecting the process)
|
||||
|
||||
Panes, splits, scrollback, search, SSH, and the CLI are unaffected.
|
||||
|
||||
## Per-pane history
|
||||
|
||||
When `per_pane_history` is on, the integration is also what makes it work. It
|
||||
runs *after* your own rc file — which is the only reason it can: `$HISTFILE` is
|
||||
yours to set, wherever you like, and nothing outside the shell knew where it
|
||||
pointed until then.
|
||||
|
||||
The sequence is: seed the pane's private file from your real history so it does
|
||||
not start blank, record how much was seeded, repoint `$HISTFILE`, and merge
|
||||
everything past that mark back when the pane closes.
|
||||
|
||||
[More about history →](/terminal/history#one-history-or-one-per-pane)
|
||||
|
||||
## Remote shells
|
||||
|
||||
For a remote workspace or an SSH pane, the same scripts are sent over the
|
||||
connection at login, so a remote pane reports its cwd, exit codes, and prompt
|
||||
marks exactly like a local one. Turn it off for a particular host under that
|
||||
profile's **Advanced → Session**.
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
title: "Troubleshooting"
|
||||
description: "The things that go wrong, and what they actually mean."
|
||||
---
|
||||
|
||||
## Start here
|
||||
|
||||
```bash
|
||||
tty7 doctor
|
||||
```
|
||||
|
||||
One table: whether the server is reachable, whether its wire dialect matches
|
||||
your binary, the three environment variables, pid/uptime/panes, and how many
|
||||
machine links exist. Most of what follows is a specific answer this gives you.
|
||||
|
||||
## `tty7: command not found`
|
||||
|
||||
The CLI is put on PATH the first time the app launches. If it is missing:
|
||||
|
||||
- Check **Settings → Agents → Install the tty7 command on PATH** is on.
|
||||
- On Unix it symlinks into whichever of `/opt/homebrew/bin`, `/usr/local/bin`,
|
||||
`~/.local/bin`, `~/bin`, `~/.cargo/bin` your PATH already covers — if none of
|
||||
those are on your PATH, add one.
|
||||
- On Windows the install directory is appended to your user PATH, which needs a
|
||||
new shell to take effect.
|
||||
- A `tty7` you installed yourself is never replaced, so an old one earlier in
|
||||
PATH will win.
|
||||
|
||||
Inside a tty7 pane it works regardless, since panes inherit the app's
|
||||
environment.
|
||||
|
||||
## The server is unreachable
|
||||
|
||||
`tty7 doctor` says so, and the GUI cannot open panes.
|
||||
|
||||
Start it with `tty7 server start`. If you are an agent or a script,
|
||||
**do not** — tell the user instead. Starting a server they did not ask for
|
||||
changes what their GUI attaches to.
|
||||
|
||||
For logs:
|
||||
|
||||
```bash
|
||||
TTY7_LOG=info # must be set before the server starts
|
||||
tty7 server logs
|
||||
```
|
||||
|
||||
## Panes came back empty
|
||||
|
||||
A crash, a `kill -9`, or a reboot takes the shells with it — that part is
|
||||
unavoidable. The *screens* should come back: tty7 keeps a capped tail of each
|
||||
pane's output (256 KiB) and hands it to the pane that reopens on that id.
|
||||
|
||||
It is consumed once. If a pane was restored, then closed, then reopened, the
|
||||
second time there is nothing left to restore — that is by design, not a bug.
|
||||
|
||||
## "The background server is still running <build>"
|
||||
|
||||
tty7 updated in place, so the app is new and your panes are still served by the
|
||||
previous build. Restarting the server picks up the new one and **ends every
|
||||
process in every pane**. There is no hurry — do it when your panes are idle.
|
||||
[Updates →](/reference/updates)
|
||||
|
||||
## A remote machine will not connect
|
||||
|
||||
| Message | What it means |
|
||||
|---|---|
|
||||
| *"running an old tty7 server that this copy cannot talk to"* | The server there predates your client's protocol. Let tty7 update it — this ends every session on that machine. |
|
||||
| *"running a newer tty7 server than this copy"* | Update tty7 here instead, or replace the server there. |
|
||||
| *"answered, but not as a tty7 server"* | Something else is listening, or the binary is not what tty7 expects. |
|
||||
| *"tty7 no longer has a way to reach <machine>"* | The SSH link dropped. Reconnect from the switcher. |
|
||||
|
||||
`tty7 -m <machine>` never dials a fresh connection by design — it uses a link
|
||||
the local server already holds. Connect from the GUI first.
|
||||
|
||||
## <kbd>⇥</kbd> or <kbd>⌃ R</kbd> is not doing what I expect
|
||||
|
||||
Both are switches, and turning one off hands the key straight back to your
|
||||
shell:
|
||||
|
||||
- **Settings → Input → Prompt → Tab completion**
|
||||
- **Settings → Input → Prompt → History search**
|
||||
|
||||
If they do nothing at all in a particular pane, the shell there probably has no
|
||||
[shell integration](/reference/shell-integration) — nushell, elvish, xonsh and
|
||||
friends run fine but do not get the prompt layer.
|
||||
|
||||
## <kbd>⌥ B</kbd> types `∫` instead of moving a word
|
||||
|
||||
That is macOS's default. Turn on **Settings → Input → Keyboard → Option (⌥) acts
|
||||
as Meta**.
|
||||
|
||||
## CJK characters have a gap on the right
|
||||
|
||||
Your CJK fallback advances 1.0em while the primary face advances 0.60205em, so
|
||||
the glyph does not fill its two-column slot. Install
|
||||
[Maple Mono NF CN](https://github.com/subframe7536/maple-font) — it is already
|
||||
first in the fallback chain and fits Hack exactly — or change the primary face.
|
||||
[The full explanation →](/customization/fonts#cjk-and-the-two-column-grid)
|
||||
|
||||
## A theme in my themes folder is not showing up
|
||||
|
||||
Settings lists it under **Not loaded from the themes folder**, with the reason.
|
||||
Usually a missing required key: `background`, `foreground`, `accent`, and `ansi`
|
||||
(with eight `normal` and eight `bright` entries) are all mandatory.
|
||||
|
||||
## My `config.json` edits did nothing
|
||||
|
||||
If the file cannot be parsed, tty7 starts on defaults and keeps your original at
|
||||
`config.json.corrupt` — check for that file. Otherwise:
|
||||
|
||||
- An out-of-range number is **clamped**, not applied literally.
|
||||
- An unrecognised enum value falls back to the default with a log line.
|
||||
- `scrollback_limit` applies to **new** panes only.
|
||||
- An unknown action name in `keybindings` is skipped with a warning.
|
||||
|
||||
## Selecting text inside vim / less selects the app's own thing
|
||||
|
||||
Hold <kbd>⇧</kbd> while dragging to keep the gesture local, or turn off
|
||||
**Settings → Terminal → Mouse → Report mouse to apps**.
|
||||
|
||||
## `tty7 capture … | head -1` printed a Rust panic
|
||||
|
||||
An old build's behaviour when the reader hangs up. The data you asked for still
|
||||
arrived. On such a build, redirect to a file and slice the file instead of
|
||||
piping into `head`. Current builds exit `141` on Unix, which is exactly what
|
||||
`cat` does.
|
||||
|
||||
## Still stuck
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Discord" icon="discord" href="https://discord.gg/s3dethqz2V">
|
||||
Ask — someone has probably hit it.
|
||||
</Card>
|
||||
<Card title="Report an issue" icon="github" href="https://github.com/l0ng-ai/tty7/issues/new">
|
||||
Include `tty7 doctor` output and your platform.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: "Updates"
|
||||
description: "How tty7 updates itself, and what the two channels mean."
|
||||
---
|
||||
|
||||
**Settings → About** is where everything lives: the version you are on, the
|
||||
channel you follow, and the button that installs what is waiting.
|
||||
|
||||
## How it works
|
||||
|
||||
<Steps>
|
||||
<Step title="Check">
|
||||
tty7 checks at launch and every six hours after that. Turn it off entirely
|
||||
with `check_for_updates: false`, or check on demand with **Check now**.
|
||||
</Step>
|
||||
<Step title="Download and verify, in the background">
|
||||
A found release is fetched and verified before you are asked to do anything
|
||||
— which turns "spend five minutes downloading" into "press restart".
|
||||
Nothing is ever *installed* without an explicit choice; the staged package
|
||||
waits in Settings.
|
||||
|
||||
Turn this off on a metered connection: the packages run 25–30 MB.
|
||||
(`auto_download_updates: false`)
|
||||
</Step>
|
||||
<Step title="Update and relaunch">
|
||||
A dedicated `tty7-updater` helper verifies the release checksum, the bundle
|
||||
version, and — on macOS — the code-signing requirement, before replacing the
|
||||
installation. If the relaunch fails, it puts the previous copy back.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
Declining an update defers it rather than retiring it; it comes back later.
|
||||
|
||||
## Channels
|
||||
|
||||
**Settings → About → Update channel.**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Stable** *(default)* | Published releases. Reads `/releases/latest`, which excludes prereleases. |
|
||||
| **Nightly** | Rebuilt from the latest code every night — newer, but not release-tested. Reads the rolling `nightly` tag. |
|
||||
|
||||
The channel is a property of your **installation**, not something inferred from
|
||||
how version numbers sort. Neither feed can hand the other an update, so a
|
||||
Nightly is never walked back onto Stable by an update it did not ask for, and an
|
||||
installation only changes channel when you change it.
|
||||
|
||||
Switching channels invalidates what the old feed produced: the staged package,
|
||||
the deferred prompt, and any transfer still in flight.
|
||||
|
||||
<Note>
|
||||
A stable release outranks every dated build of its core version, which is how
|
||||
switching back to Stable *graduates* rather than downgrades.
|
||||
</Note>
|
||||
|
||||
## Platform notes
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="macOS">
|
||||
The new GUI reuses a running local server when its wire protocol is
|
||||
compatible, so your shells survive the update. An incompatible server keeps
|
||||
its shells too and raises an explicit keep-or-restart prompt.
|
||||
</Accordion>
|
||||
<Accordion title="Windows">
|
||||
Windows cannot replace a running daemon's image, so the install path stops
|
||||
the service first — and the dialog says so before you agree.
|
||||
|
||||
An all-users `C:\Program Files` install cannot be updated in place and keeps
|
||||
the release-page fallback instead. Running Setup as the signed-in user would
|
||||
either install a second copy beside the real one, or put a bare UAC prompt
|
||||
in front of someone whose GUI just vanished.
|
||||
</Accordion>
|
||||
<Accordion title="Linux">
|
||||
AppImage and tarball installs are replaced by downloading the new file.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## "The background server is still running <build>"
|
||||
|
||||
If Settings tells you this, tty7 was updated in place: the app is the new build,
|
||||
but your panes are still served by the previous one. Restarting the server picks
|
||||
up the new one — **and ends every process running in your panes**, shells,
|
||||
agents, and SSH sessions alike.
|
||||
|
||||
There is no hurry. Pick a moment when your panes are idle.
|
||||
|
||||
## Proxies
|
||||
|
||||
Update checks and downloads resolve a proxy from, in order:
|
||||
|
||||
1. `http_proxy` in `config.json` — `http://127.0.0.1:7890` or
|
||||
`socks5://127.0.0.1:1080`
|
||||
2. The platform system proxy (Windows registry, macOS `SCDynamicStore`)
|
||||
3. `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY`
|
||||
|
||||
Programs running in a pane are deliberately unaffected — they inherit their
|
||||
proxy from their own environment, as in any other terminal.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: "Port forwarding"
|
||||
description: "Local, remote, and dynamic forwards — preconfigured or added mid-session."
|
||||
---
|
||||
|
||||
## The three kinds
|
||||
|
||||
| | What it does |
|
||||
|---|---|
|
||||
| **Local** (`L`) | A port on this machine reaches a service on the remote side |
|
||||
| **Remote** (`R`) | A port on the remote machine reaches a service here |
|
||||
| **Dynamic** (`D`) | A SOCKS proxy on this machine, routed through the connection |
|
||||
|
||||
## Adding one to a profile
|
||||
|
||||
**Settings → SSH →** a profile **→ Port forwarding → + Add rule**. Rules saved
|
||||
here open with the connection, every time.
|
||||
|
||||
A Local or Remote rule needs a listen port and a target; a Dynamic rule needs
|
||||
only the listen port. An incomplete rule tells you so rather than being saved
|
||||
half-configured.
|
||||
|
||||
Each rule takes an optional description — *"what it's for"* — because six months
|
||||
later `8080 → 3000` explains nothing.
|
||||
|
||||
## Adding one mid-session
|
||||
|
||||
*SSH: Port Forwarding* in the command palette opens the **Forwards** panel for
|
||||
the current connection. Add a rule there and it starts immediately; remove it
|
||||
and it stops. These live only as long as the session unless you save them into
|
||||
the profile.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the Forwards panel with a local and a dynamic rule">
|
||||
<img src="/images/placeholder.svg" alt="The forwards panel" />
|
||||
</Frame>
|
||||
|
||||
## The one-click shortcut
|
||||
|
||||
<kbd>⌘</kbd>-clicking a `localhost:PORT` link inside an SSH pane can open a
|
||||
temporary forward for exactly that port and then open the browser — turn on
|
||||
**Settings → Input → Links → Forward SSH loopback links**.
|
||||
|
||||
That is the right tool for "let me look at this dev server once". For something
|
||||
you use every day, put it in the profile.
|
||||
|
||||
## Jump hosts and proxies
|
||||
|
||||
Multi-hop connections are configured per profile:
|
||||
|
||||
- **Jump host** — point at another saved profile, or use a `ProxyJump` chain
|
||||
- **ProxyCommand** — an arbitrary transport command, with `%h`, `%p`, `%r`
|
||||
substituted
|
||||
- **SOCKS5 proxy** / **HTTP proxy** — `host:port`, under **Advanced → Proxies**
|
||||
|
||||
<Note>
|
||||
These proxy settings are for reaching the SSH server. tty7's *own* network
|
||||
traffic — update checks, release downloads, remote-server installs — uses
|
||||
`http_proxy` in `config.json`, the system proxy, or the `HTTP_PROXY` family.
|
||||
Programs running in a pane are unaffected either way; they inherit their proxy
|
||||
from their own environment, as in any terminal.
|
||||
</Note>
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: "SFTP"
|
||||
description: "A file browser for the machine on the other end of the connection."
|
||||
---
|
||||
|
||||
While a pane is in an SSH session, *SSH: Remote Files* (command palette) slides
|
||||
a file panel in over it. It is a real SFTP client on the same connection — no
|
||||
second login, no second password.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the SFTP panel over a pane, with a transfer in progress">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 SFTP panel" />
|
||||
</Frame>
|
||||
|
||||
## Browsing
|
||||
|
||||
The panel opens on the remote home directory. **Go to Shell Directory** in the
|
||||
overflow menu jumps it to wherever the pane's shell currently is, which is
|
||||
usually where you actually want to be.
|
||||
|
||||
Right-click a row for **Open**, **Follow Symlink**, **Rename**, **chmod…**, and
|
||||
delete. The overflow menu adds **New Folder**, **New File**, **Upload…**, and
|
||||
**Refresh**.
|
||||
|
||||
## Transferring
|
||||
|
||||
| Direction | How |
|
||||
|---|---|
|
||||
| Download | Drag a file out of the panel into Finder or Explorer |
|
||||
| Upload | **Upload…**, or drag files into the panel |
|
||||
|
||||
Uploads are written under a temporary name and renamed into place at the end, so
|
||||
a half-finished file never looks like a finished one. A name that is already
|
||||
taken asks before replacing.
|
||||
|
||||
**Transfer History** in the overflow menu shows every transfer with its
|
||||
progress, and lets you cancel one in flight. The panel header summarises what is
|
||||
happening — *"2 transferring · 64%"*.
|
||||
|
||||
## Permissions
|
||||
|
||||
**chmod…** takes an octal mode (`755`, `600`). The current mode is shown in the
|
||||
row's editor before you change it.
|
||||
|
||||
<Tip>
|
||||
For files in a [remote workspace](/remote/workspaces) you can often skip SFTP
|
||||
entirely — the Files panel reads and writes across the link directly, and the
|
||||
built-in editor saves back to the remote machine.
|
||||
</Tip>
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
title: "SSH"
|
||||
description: "A native Rust SSH stack: quick connects, saved profiles, keychain credentials, jump hosts."
|
||||
---
|
||||
|
||||
tty7 speaks SSH itself, over [russh](https://github.com/Eugeny/russh). It never
|
||||
shells out to the `ssh` binary, and there is no compatibility mode that does.
|
||||
|
||||
That is what makes the rest possible: credentials in the OS keychain,
|
||||
[SFTP](/remote/sftp) in a side panel, [port forwards](/remote/port-forwarding)
|
||||
you can add mid-session, and authentication prompts drawn as sheets in the pane
|
||||
instead of a password echoing into your shell.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: an SSH connection sheet asking for a key passphrase inside a pane">
|
||||
<img src="/images/placeholder.svg" alt="Connecting over SSH in tty7" />
|
||||
</Frame>
|
||||
|
||||
## Four ways to connect
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="QuickConnect — type an address">
|
||||
Open the palette (<kbd>⌘ P</kbd>) and type an address. IPv6 works with
|
||||
brackets.
|
||||
|
||||
```
|
||||
me@devbox
|
||||
me@devbox:2222
|
||||
me@[2001:db8::1]:22
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="A saved profile">
|
||||
Profiles live in **Settings → SSH → Hosts**. Start typing the name in the
|
||||
palette, or open the *SSH: Manage Profiles…* command.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="An alias from ~/.ssh/config">
|
||||
Type an alias you already have and tty7 resolves it natively — common fields,
|
||||
best effort — then connects over russh. **Settings → SSH → Import from
|
||||
~/.ssh/config** turns aliases into real profiles.
|
||||
|
||||
<Note>
|
||||
`Match`, `canonicalize*`, and GSSAPI directives are not supported, and
|
||||
there is no fallback to the system `ssh` when one appears.
|
||||
</Note>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="A remote workspace">
|
||||
The same connection can host whole workspaces on the far machine rather than
|
||||
a single shell. [Remote workspaces →](/remote/workspaces)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Profiles
|
||||
|
||||
**Settings → SSH → Hosts** holds the full connection config. The basics:
|
||||
|
||||
| Field | |
|
||||
|---|---|
|
||||
| **Name** | A label for this connection |
|
||||
| **Host** | Hostname or IP |
|
||||
| **User** | Login user — blank resolves at connect time |
|
||||
| **Auth** | *Auto* (tries every applicable method), *Password*, *Key*, *Agent*, or *2FA* |
|
||||
| **Jump host** | Another profile, or a `ProxyJump` chain |
|
||||
| **Port forwarding** | Rules opened with the connection |
|
||||
|
||||
**Defaults** at the top of the list is inherited by every host, so a setting you
|
||||
want everywhere is set once.
|
||||
|
||||
Passwords and key passphrases go in the **OS keychain**, never in
|
||||
`config.json` and never on disk in plain text. **Forget Password** in a
|
||||
profile's menu removes the stored one.
|
||||
|
||||
### Advanced
|
||||
|
||||
Behind **Advanced** on a profile, grouped:
|
||||
|
||||
| Group | Fields |
|
||||
|---|---|
|
||||
| **Authentication** | Identity files (one path per line, `%h`/`%r` expand), agent forwarding |
|
||||
| **Proxies** | ProxyCommand (`%h`/`%p`/`%r` substituted), SOCKS5 proxy, HTTP proxy |
|
||||
| **Algorithms** | KEX algorithms, ciphers, MACs, host-key algorithms, compression |
|
||||
| **Connection** | Keepalive interval and count, connect timeout, X11 forwarding |
|
||||
| **Session** | Shell integration, login scripts, skip banner |
|
||||
|
||||
Everything blank means "the library default", so you only fill in what you
|
||||
actually need to override.
|
||||
|
||||
## Authentication prompts
|
||||
|
||||
Password, key passphrase, and 2FA prompts appear as sheets inside the pane, with
|
||||
a **Remember (keychain)** option where it makes sense.
|
||||
|
||||
## Host keys
|
||||
|
||||
Host keys are verified against `known_hosts` by default. A first connection asks
|
||||
you to confirm the fingerprint; a **changed** key is a much louder prompt that
|
||||
makes you type `yes` to override, because that is what a changed key deserves.
|
||||
|
||||
**Settings → SSH → Security → Verify host keys** turns verification off
|
||||
entirely. It is on for a reason.
|
||||
|
||||
Also under Security: **Warn before closing** a live connection, off by default.
|
||||
|
||||
## Reconnecting
|
||||
|
||||
<kbd>⌘ ⇧ R</kbd> — or *SSH: Reconnect* in the palette — restarts the session in
|
||||
the current pane. Useful after a laptop sleeps or a network changes.
|
||||
|
||||
## What is not supported
|
||||
|
||||
- No fallback to the system `ssh` binary
|
||||
- No `Match`, `canonicalize*`, or GSSAPI directives from `~/.ssh/config`
|
||||
- Kerberos `gssapi-with-mic` is offered by the desktop app for managed
|
||||
connections, but is not part of the `~/.ssh/config` resolution path
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: "Remote workspaces"
|
||||
description: "Whole workspaces hosted on another machine — files, repos, panes, and git all stay over there."
|
||||
---
|
||||
|
||||
An SSH pane runs one shell on a remote machine. A **remote workspace** goes
|
||||
further: tty7 runs a server on the far machine, and the whole workbench points
|
||||
at it. Tabs, splits, the file tree, the git panel, the diff overlay, the process
|
||||
list — all of it is the remote machine's, rendered here.
|
||||
|
||||
Nothing is synced or copied. The repository stays where it is.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: a remote workspace open, sidebar showing remote repos, with the machine name in the strip">
|
||||
<img src="/images/placeholder.svg" alt="A remote workspace in tty7" />
|
||||
</Frame>
|
||||
|
||||
## Connecting
|
||||
|
||||
<Steps>
|
||||
<Step title="Open the switcher">
|
||||
<kbd>⌘ ⇧ O</kbd>. Machines are listed alongside your local workspaces —
|
||||
*This Computer* first, then every saved SSH profile and, on Windows, every
|
||||
WSL distribution.
|
||||
</Step>
|
||||
<Step title="Pick a machine">
|
||||
tty7 connects over the same SSH stack as everything else, so profiles,
|
||||
keychain credentials, and jump hosts all apply.
|
||||
</Step>
|
||||
<Step title="Approve the server install, once">
|
||||
The first connection asks:
|
||||
|
||||
> tty7 will write its server binary to *devbox* so this machine can host
|
||||
> workspaces there. Nothing else on *devbox* is touched, and no sudo is
|
||||
> used.
|
||||
|
||||
It shows the exact path, version, size, source, and SHA-256 before you
|
||||
agree. Later upgrades on that machine install silently.
|
||||
</Step>
|
||||
<Step title="Open a workspace">
|
||||
From then on the machine's workspaces are in the switcher, and a new one
|
||||
opens like a local one.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## What gets installed
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **What** | A single static `tty7-server` binary |
|
||||
| **Where** | `~/.local/share/tty7/bin/tty7-server-c<control>p<protocol>` |
|
||||
| **Privileges** | None. No sudo, nothing outside your home directory |
|
||||
| **Hosts** | Linux, x86-64 or aarch64 |
|
||||
|
||||
The binary is named after the wire dialect it speaks, so a client and a server
|
||||
that disagree never quietly half-work — tty7 installs the matching one instead.
|
||||
|
||||
On Windows, a WSL distribution is handed the Linux server the installer already
|
||||
shipped, so a WSL workspace needs no network access at all.
|
||||
|
||||
## Reattaching
|
||||
|
||||
Remote workspaces are the point at which persistence pays off twice: the panes
|
||||
survive on the remote machine whether or not your laptop is awake, and you can
|
||||
reattach from a different client entirely.
|
||||
|
||||
A strip along the top of the window says what the connection is doing —
|
||||
*connecting*, *reconnecting (attempt 3)*, *disconnected*, or *taken over by
|
||||
someone else*. Reconnection is automatic; a workspace another client has claimed
|
||||
says so by name rather than fighting over it.
|
||||
|
||||
## Keeping the server current
|
||||
|
||||
Two dialogs you may meet:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Update tty7's server on “devbox”?">
|
||||
The machine is serving sessions from a build whose protocol this client
|
||||
cannot speak. tty7 has already installed a matching server, but the one
|
||||
already running is the one your sessions are on. **Update Server** replaces
|
||||
it and **ends every session it is hosting** — including ones this window is
|
||||
not showing. Cancel leaves the machine exactly as it is.
|
||||
</Accordion>
|
||||
<Accordion title="Restart tty7's server on “devbox”?">
|
||||
Same consequence, deliberately: every shell on that machine ends. Workspaces
|
||||
and layouts are kept and come back with fresh shells.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Warning>
|
||||
Both of these end other people's work if the machine is shared. tty7 spells
|
||||
out what will happen before either one runs — read it.
|
||||
</Warning>
|
||||
|
||||
## What a remote pane cannot do
|
||||
|
||||
- **Fork an agent session.** The fork command would run against the *local*
|
||||
agent, so tty7 does not offer it.
|
||||
- **Move very large files through the Files panel.** Drag-and-drop across the
|
||||
link is capped at what one control frame can carry; past that the panel tells
|
||||
you to use [SFTP](/remote/sftp).
|
||||
|
||||
## From the CLI
|
||||
|
||||
```bash
|
||||
tty7 machine ls # this machine plus every link the server holds
|
||||
tty7 -m devbox ls # route any command to a linked machine
|
||||
tty7 -m devbox run -- cargo test
|
||||
```
|
||||
|
||||
`-m` matches the full link key (`me@devbox:22`) or just the host. It uses a link
|
||||
the local server *already* holds — it will not dial a fresh connection, and it
|
||||
says so rather than guessing. Connect from the switcher first.
|
||||
|
||||
[CLI overview →](/cli/overview)
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "History"
|
||||
description: "Fuzzy history search, and whether each pane gets its own."
|
||||
---
|
||||
|
||||
## Fuzzy search with <kbd>⌃ R</kbd>
|
||||
|
||||
<kbd>⌃ R</kbd> opens a fuzzy search over what you have actually run. Type any
|
||||
fragment — the letters do not have to be adjacent — and the list narrows.
|
||||
|
||||
Each row carries the context the plain shell version throws away:
|
||||
|
||||
- **where** you ran it, so `npm run dev` from three repositories is three
|
||||
distinct entries
|
||||
- **when**, as a relative time
|
||||
- **whether it failed**, from the exit code
|
||||
|
||||
Ranking mixes frequency with recency, and commands you ran in the *current*
|
||||
directory are pushed up — the thing you want is usually the thing you last did
|
||||
here.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: ⌃R open, matching rows showing directory, time, and a failed exit">
|
||||
<img src="/images/placeholder.svg" alt="Fuzzy history search" />
|
||||
</Frame>
|
||||
|
||||
<kbd>⏎</kbd> puts the command on the prompt. <kbd>Esc</kbd> closes without
|
||||
touching it.
|
||||
|
||||
### Handing <kbd>⌃ R</kbd> back
|
||||
|
||||
If you already have an fzf, percol, atuin or McFly binding you like, turn off
|
||||
**Settings → Input → Prompt → History search** (`history_search: false`).
|
||||
<kbd>⌃ R</kbd> then goes to the shell, and whatever you bound there keeps
|
||||
working.
|
||||
|
||||
## Where the history comes from
|
||||
|
||||
Your existing shell history file, as-is. Nothing is imported or converted, there
|
||||
is no separate store to warm up, and a history written outside tty7 shows up
|
||||
immediately.
|
||||
|
||||
## One history, or one per pane
|
||||
|
||||
By default every pane shares your shell's history file, which is what a terminal
|
||||
has always done: a command typed in one pane is available in the next.
|
||||
|
||||
**Settings → Terminal → Give each pane its own shell history**
|
||||
(`per_pane_history: true`) changes that. Each pane gets a private history file:
|
||||
|
||||
- **seeded** from your real history when the pane opens, so it is not blank
|
||||
- **merged back** into your real history when the pane closes, so nothing typed
|
||||
is lost
|
||||
|
||||
Useful when several agents or several tasks are running side by side and you do
|
||||
not want their commands interleaved in your <kbd>↑</kbd> key. Off by default,
|
||||
because someone who has not asked for it would experience the change as their
|
||||
history mysteriously forgetting the other window.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "Links"
|
||||
description: "Opening URLs, files, and localhost ports straight out of the terminal."
|
||||
---
|
||||
|
||||
Hold <kbd>⌘</kbd> (<kbd>Ctrl</kbd> on Windows and Linux) and links under the
|
||||
pointer underline; click to open one.
|
||||
|
||||
## URLs
|
||||
|
||||
Anything that looks like a URL is detected, including one the shell wrapped
|
||||
across two lines — tty7 stitches it back together before opening it.
|
||||
|
||||
Turn detection off with **Settings → Input → Links → Detect URLs**
|
||||
(`link_url: false`).
|
||||
|
||||
## Files
|
||||
|
||||
A file path in the output — a compiler error, a test failure, a `grep -n` hit —
|
||||
opens in your default application for that file type.
|
||||
|
||||
To send it somewhere specific instead, set **Settings → Input → Links → Open
|
||||
files with**. The command runs with placeholders substituted:
|
||||
|
||||
```
|
||||
code --goto {path}:{line}:{column}
|
||||
zed {path}:{line}
|
||||
herdr edit {path} --line={line}
|
||||
```
|
||||
|
||||
`{path}`, `{line}`, and `{column}` are filled in from the link. A flag whose
|
||||
value is not available is dropped rather than passed empty — so
|
||||
`--line={line}` simply disappears when the link had no line number. Leave the
|
||||
field blank to go back to the default application.
|
||||
|
||||
The same setting is `link_file_command` in `config.json`.
|
||||
|
||||
## localhost ports
|
||||
|
||||
<kbd>⌘</kbd>-clicking `localhost:3000` opens it in your browser, which is only
|
||||
useful if the server is on this machine.
|
||||
|
||||
When the pane is inside an SSH session it usually is not. Turn on **Settings →
|
||||
Input → Links → Forward SSH loopback links** (`ssh_loopback_forward: true`) and
|
||||
tty7 opens a temporary port forward through that connection first, so the link
|
||||
reaches the server on the remote machine.
|
||||
|
||||
<Tip>
|
||||
For a forward you want to keep, set one up properly instead —
|
||||
[port forwarding](/remote/port-forwarding).
|
||||
</Tip>
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: "Mouse, scrolling, and the bell"
|
||||
description: "How the pointer, the wheel, and ^G behave — and how to change each."
|
||||
---
|
||||
|
||||
## Scrolling
|
||||
|
||||
The wheel and trackpad scroll the pane's scrollback. All of it lives under
|
||||
**Settings → Terminal → Scrolling**:
|
||||
|
||||
| Setting | Default | What it does |
|
||||
|---|---|---|
|
||||
| **Scrollback** | 10,000 lines | How much history each pane keeps, up to 100,000. Applies to new panes. |
|
||||
| **Scroll speed** | 1.0 | A multiplier on wheel scrolling (0.1–10). |
|
||||
| **Smooth scrolling** | On | Eases each wheel notch into place over a few frames instead of jumping the whole way. Trackpads scroll continuously already and are unaffected. |
|
||||
|
||||
<kbd>⌘</kbd> plus the wheel zooms the font instead of scrolling.
|
||||
|
||||
## The pointer
|
||||
|
||||
Under **Settings → Terminal → Mouse**:
|
||||
|
||||
| Setting | Default | What it does |
|
||||
|---|---|---|
|
||||
| **Focus follows mouse** | Off | Hovering a pane focuses it without a click. |
|
||||
| **Hide mouse while typing** | On | The pointer disappears as you type and returns on the next move. |
|
||||
| **Report mouse to apps** | On | Full-screen programs — vim, tmux, `htop` — get clicks and scroll events themselves. Hold <kbd>⇧</kbd> to keep a gesture local and select text instead. |
|
||||
|
||||
<Tip>
|
||||
If selecting text inside `vim` or `less` grabs the app's own selection instead
|
||||
of yours, hold <kbd>⇧</kbd> while you drag.
|
||||
</Tip>
|
||||
|
||||
## Font size
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⌘ +</kbd> · <kbd>⌘ −</kbd> | Bigger · smaller |
|
||||
| <kbd>⌘ 0</kbd> | Back to the configured size |
|
||||
| <kbd>⌘</kbd> + wheel | Zoom by scrolling over a terminal |
|
||||
|
||||
The base size is **Settings → Appearance → Typography → Font size**, 15 px by
|
||||
default. The rest of the interface has its own size — **Interface font size**,
|
||||
16 px, adjustable from 12 to 24 — so you can scale the chrome without touching
|
||||
the terminal grid, or the other way round.
|
||||
|
||||
## The bell
|
||||
|
||||
**Settings → Terminal → Bell → Terminal bell** decides what `^G` does:
|
||||
|
||||
| Mode | Behaviour |
|
||||
|---|---|
|
||||
| **Off** | Nothing |
|
||||
| **Visual** *(default)* | A brief flash |
|
||||
| **Audible** | The system sound |
|
||||
| **Both** | Flash and sound |
|
||||
|
||||
## Command-finished notifications
|
||||
|
||||
**Settings → Window & Tabs → Notifications** posts a desktop notification when a
|
||||
foreground command finishes:
|
||||
|
||||
- **Notify on command finish** — *Never*, *When unfocused* (default), or
|
||||
*Always*
|
||||
- **Notify threshold** — how long a command has to run to qualify, 10 seconds by
|
||||
default
|
||||
|
||||
Coding agents use the same policy for their own notifications.
|
||||
[Agent status →](/agents/status)
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
title: "The prompt"
|
||||
description: "Ghost suggestions, tab completion that explains itself, syntax highlighting, and real multi-line editing."
|
||||
---
|
||||
|
||||
tty7 puts an editor at the shell prompt. Nothing to install, no plugin to source
|
||||
— the moment a supported shell starts in a pane, the prompt behaves like this.
|
||||
|
||||
<Frame caption="Placeholder — video: typing a command with a ghost suggestion, accepting it, then opening the completion menu">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 prompt" />
|
||||
</Frame>
|
||||
|
||||
## Ghost suggestions
|
||||
|
||||
As you type, the rest of the line is filled in from your history, greyed out
|
||||
ahead of the cursor.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>→</kbd> | Accept the whole suggestion |
|
||||
| Keep typing | The suggestion narrows |
|
||||
| <kbd>Esc</kbd> or anything that does not match | It disappears |
|
||||
|
||||
Your existing shell history is what feeds it — there is no separate database to
|
||||
build up first, and it carries across sessions and reboots.
|
||||
|
||||
## Tab completion, with descriptions
|
||||
|
||||
<kbd>⇥</kbd> opens a completion menu that knows what it is offering:
|
||||
|
||||
- **Commands** from your PATH and your shell's builtins
|
||||
- **Files and directories**, with `cd`, `pushd`, `popd`, and `rmdir` offering
|
||||
directories only
|
||||
- **Flags and subcommands** with their descriptions, for about 100 common
|
||||
commands — `git`, `cargo`, `docker`, `kubectl`, `npm`, `brew` and the rest
|
||||
- **Values** where a flag only takes certain ones
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the completion menu on `git c`, each subcommand with its description">
|
||||
<img src="/images/placeholder.svg" alt="Explained tab completion" />
|
||||
</Frame>
|
||||
|
||||
When tty7 has nothing useful to offer, the <kbd>⇥</kbd> falls through to your
|
||||
shell's own completion, so a carefully configured zsh setup is not lost.
|
||||
|
||||
To hand <kbd>⇥</kbd> back to the shell entirely, turn off **Settings → Input →
|
||||
Prompt → Tab completion** (`tab_completion` in `config.json`).
|
||||
|
||||
## Syntax highlighting
|
||||
|
||||
The line you are typing is coloured as you type it: the command, its flags, its
|
||||
arguments, paths, quoted strings, operators, comments. It is a fast tokenizer,
|
||||
not a shell parser — it never changes what gets run.
|
||||
|
||||
## Line editing
|
||||
|
||||
The prompt behaves like a text field, because it is one:
|
||||
|
||||
- **Click to place the caret** anywhere in the line
|
||||
- **Select with the mouse**, drag to extend
|
||||
- **Word motion** and word delete
|
||||
- **Undo**
|
||||
|
||||
Everything readline does still works — this sits on top, it does not replace it.
|
||||
|
||||
<Tip>
|
||||
On macOS, turn on **Settings → Input → Keyboard → Option (⌥) acts as Meta** if
|
||||
you want <kbd>⌥ B</kbd> / <kbd>⌥ F</kbd> to move by word instead of typing
|
||||
`∫` and `ƒ`.
|
||||
</Tip>
|
||||
|
||||
## Typing with an IME
|
||||
|
||||
Pinyin, Kana, Hangul and the rest work in a pane the way they do in a text
|
||||
field: the composition is drawn in place at the cursor and only the committed
|
||||
text reaches the program.
|
||||
|
||||
Two rules decide who gets a keystroke:
|
||||
|
||||
- **A plain printable key goes to the IME.** A key held with <kbd>⌃</kbd>,
|
||||
<kbd>⌘</kbd>, <kbd>fn</kbd>, or <kbd>⌥</kbd> does not — those are chords, not
|
||||
characters.
|
||||
- **A program that asks for every key gets every key.** When something turns on
|
||||
the kitty keyboard protocol's report-all-keys mode, the IME steps aside so the
|
||||
program sees raw input.
|
||||
|
||||
<Note>
|
||||
On macOS with **Option (⌥) acts as Meta** turned on, <kbd>⌥</kbd> chords
|
||||
bypass the IME entirely, so <kbd>⌥ B</kbd> reaches your shell as meta-b
|
||||
instead of being eaten as a dead key.
|
||||
</Note>
|
||||
|
||||
Rendering CJK well is a separate question — see
|
||||
[fonts and the two-column grid](/customization/fonts#cjk-and-the-two-column-grid).
|
||||
|
||||
## Multi-line commands
|
||||
|
||||
A command that wraps, or one you deliberately break across lines, edits in
|
||||
place. The grid shifts to keep the caret visible instead of scrolling the whole
|
||||
screen away.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⇧ ⏎</kbd> · <kbd>⌥ ⏎</kbd> | Insert a newline instead of submitting |
|
||||
| <kbd>⏎</kbd> | Submit the whole buffer, however many lines it is |
|
||||
|
||||
The newline key is rebindable as `InsertNewline` under **Settings →
|
||||
Keybindings**.
|
||||
|
||||
## Which shells
|
||||
|
||||
The prompt features arrive through tty7's shell integration, which is injected
|
||||
automatically — nothing to add to your rc file — for **zsh**, **bash**,
|
||||
**fish**, **PowerShell**, and **WSL**. Other shells (nushell, elvish, xonsh, and
|
||||
the rest) run perfectly well in a pane; they simply do not get the prompt layer.
|
||||
|
||||
The integration is also what reports the working directory, the exit code of
|
||||
each command, and where prompts begin — which is what the sidebar's branch
|
||||
readout, the "command finished" notification, and `tty7 procs` are built on.
|
||||
|
||||
[How shell integration works →](/reference/shell-integration)
|
||||
|
||||
## Turning it off
|
||||
|
||||
Both prompt features are switches, and turning one off hands its key straight
|
||||
back to the shell:
|
||||
|
||||
| Setting | Key it releases |
|
||||
|---|---|
|
||||
| **Settings → Input → Prompt → Tab completion** | <kbd>⇥</kbd> → your shell's completion |
|
||||
| **Settings → Input → Prompt → History search** | <kbd>⌃ R</kbd> → your shell's reverse-i-search, or your fzf binding |
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Selection and clipboard"
|
||||
description: "Smart double-click, copy on select, and the settings around them."
|
||||
---
|
||||
|
||||
## Selecting
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Drag | Select a range |
|
||||
| Double-click | Select the thing under the cursor — see below |
|
||||
| Triple-click | Select the line |
|
||||
| <kbd>⇧</kbd>-click | Extend the current selection to where you clicked |
|
||||
| <kbd>⌘ A</kbd> *(macOS)* | Select all — also in the right-click menu and the command palette on every platform |
|
||||
|
||||
### Smart double-click
|
||||
|
||||
A double-click does not just grab a word bounded by spaces. It works out what
|
||||
you are pointing at:
|
||||
|
||||
| Under the cursor | What you get |
|
||||
|---|---|
|
||||
| A URL | The whole URL — including one the shell wrapped across two lines |
|
||||
| A file path | The whole path |
|
||||
| An email address | The whole address |
|
||||
| A bracket or quote | The matching pair, and everything between them |
|
||||
| CJK text | The word, segmented by dictionary rather than by character |
|
||||
|
||||
Turn it off with **Settings → Input → Selection & clipboard → Smart selection**.
|
||||
With it off, double-click falls back to plain word selection using the
|
||||
`word_separators` list from `config.json` — by default:
|
||||
|
||||
```
|
||||
,│`|:"' ()[]{}<>⇥
|
||||
```
|
||||
|
||||
## Copying and pasting
|
||||
|
||||
| | macOS | Windows / Linux |
|
||||
|---|---|---|
|
||||
| Copy | <kbd>⌘ C</kbd> | <kbd>Ctrl ⇧ C</kbd> |
|
||||
| Paste | <kbd>⌘ V</kbd> | <kbd>Ctrl ⇧ V</kbd> · <kbd>⇧ Insert</kbd> |
|
||||
|
||||
Two settings shape what lands where, both under **Settings → Input → Selection
|
||||
& clipboard**:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Copy on select" icon="clipboard">
|
||||
Selecting with the mouse copies immediately, no <kbd>⌘ C</kbd>. Off by
|
||||
default.
|
||||
</Card>
|
||||
<Card title="Trim trailing spaces on copy" icon="scissors">
|
||||
Strips trailing whitespace from every copied line — useful when copying out
|
||||
of a TUI that pads to the pane width. Off by default.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
Pasting multiple lines sends them as typed input, exactly as any terminal
|
||||
does. If the shell supports bracketed paste it will treat the block as one
|
||||
paste rather than running each line.
|
||||
</Note>
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "Command palette"
|
||||
description: "One key for every action in the app — including the ones with no shortcut."
|
||||
---
|
||||
|
||||
<kbd>⌘ P</kbd> opens the command palette. Type to filter, <kbd>↑</kbd>
|
||||
<kbd>↓</kbd> to move, <kbd>⏎</kbd> to run.
|
||||
|
||||
Every action tty7 can perform is in here, whether or not it has a keybinding —
|
||||
which makes it the fastest way to reach the ones that deliberately ship unbound,
|
||||
like pane resize and swap.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the command palette open over a terminal, grouped results">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 command palette" />
|
||||
</Frame>
|
||||
|
||||
## What is in it
|
||||
|
||||
Results are grouped, and the groups are the map of the app:
|
||||
|
||||
| Group | Examples |
|
||||
|---|---|
|
||||
| **Tabs & Panes** | New Tab · New Worktree Tab… · Split Right · Zoom Pane · Focus Pane Left · Resize Pane Up · Swap Pane Next · Reopen Closed Tab · Copy Working Directory · Fork Session |
|
||||
| **Workspaces** | New Workspace · Switch Workspace… · Rename Workspace… · Stop Workspace… · Delete Workspace… |
|
||||
| **View** | Show/Hide Left Sidebar · Show/Hide Right Panel · Show Code Panel · Tab Bar: Move to Top · Right Panel: Info / Changes / Files · Change Theme… · Enter Full Screen · Toggle Unified / Side-by-Side Diff |
|
||||
| **Git** | Commit · Stage All Changes · Unstage All · Discard All · Checkout to… · Create Branch… · Sync · Push · Pull · Fetch · Toggle Commit History |
|
||||
| **Terminal** | Clear Scrollback · Find in Terminal… · Find Next / Previous · Copy · Cut · Paste · Select All |
|
||||
| **SSH** | Add Connection… · Manage Profiles… · Reconnect · Remote Files · Port Forwarding |
|
||||
| **Agents** | Send Selection · Send Git Diff for Review · Copy Session ID |
|
||||
| **Application** | Settings… · Keyboard Shortcuts · Check for Updates… · Documentation · Join the Discord · Report an Issue… · Restart Server… |
|
||||
|
||||
Entries that do something destructive say so under the name — *Delete
|
||||
Workspace…* is subtitled "ends its shells and forgets the layout", *Restart
|
||||
Server…* is "ends every running shell; layout is kept".
|
||||
|
||||
## Connecting from the palette
|
||||
|
||||
Type an SSH address and the palette offers to connect to it:
|
||||
|
||||
```
|
||||
me@devbox
|
||||
me@devbox:2222
|
||||
[::1]:22
|
||||
```
|
||||
|
||||
Saved profiles and `~/.ssh/config` aliases show up the same way — start typing
|
||||
the name. [SSH →](/remote/ssh)
|
||||
|
||||
## Sending context to an agent
|
||||
|
||||
Two palette commands hand what is in front of you to the coding agent running in
|
||||
the pane, as a ready-made prompt:
|
||||
|
||||
- **Agent: Send Selection** — the current selection.
|
||||
- **Agent: Send Git Diff for Review** — the repository's `git diff`.
|
||||
|
||||
If no agent is running, tty7 says so rather than typing into your shell.
|
||||
[Agents →](/agents/overview)
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "Search"
|
||||
description: "Finding text in the scrollback, and everything else the search boxes cover."
|
||||
---
|
||||
|
||||
## In the terminal
|
||||
|
||||
<kbd>⌘ F</kbd> opens the find bar over the focused pane and searches its whole
|
||||
scrollback, not just the visible screen.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⏎</kbd> · <kbd>⌘ G</kbd> | Next match |
|
||||
| <kbd>⇧ ⏎</kbd> · <kbd>⌘ ⇧ G</kbd> | Previous match |
|
||||
| <kbd>Esc</kbd> | Close the find bar |
|
||||
|
||||
Two toggles sit in the bar:
|
||||
|
||||
- **Match case** — off by default, so a lowercase query matches anything.
|
||||
- **Use regular expression** — the query becomes a regex. An invalid pattern is
|
||||
shown as an error rather than silently matching nothing.
|
||||
|
||||
Matches are highlighted in place and the view scrolls to each one as you step
|
||||
through. On Windows and Linux the shortcuts are <kbd>Ctrl ⇧ F</kbd> to open,
|
||||
<kbd>F3</kbd> and <kbd>⇧ F3</kbd> to step.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the find bar with matches highlighted in the scrollback">
|
||||
<img src="/images/placeholder.svg" alt="Searching the scrollback" />
|
||||
</Frame>
|
||||
|
||||
<Tip>
|
||||
How much there is to search is **Settings → Terminal → Scrolling →
|
||||
Scrollback** — 10,000 lines per pane by default, up to 100,000. The change
|
||||
applies to new panes.
|
||||
</Tip>
|
||||
|
||||
## Everywhere else
|
||||
|
||||
tty7 leans on the same pattern in a lot of places. All of them are
|
||||
type-to-filter, no button to press:
|
||||
|
||||
| Where | What it searches |
|
||||
|---|---|
|
||||
| <kbd>⌘ P</kbd> | Commands — and SSH addresses you type in full |
|
||||
| <kbd>⌘ ⇧ O</kbd> | Workspaces, tabs, and machines |
|
||||
| <kbd>⌃ R</kbd> | Your shell history, fuzzily — [see History](/terminal/history) |
|
||||
| Settings search box | Every setting, by name and by keyword |
|
||||
| Files panel | Files under the workspace root |
|
||||
| Theme picker | Built-in and custom themes |
|
||||
| Font picker | Fonts installed on your system |
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "The side panel"
|
||||
description: "Info, Source Control, and Files — plus the built-in editor."
|
||||
---
|
||||
|
||||
<kbd>⌘ J</kbd> opens a panel on the right of the window with three tabs. It is
|
||||
hidden by default; whichever tab you leave it on is where it opens next time.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the side panel showing the Info tab beside a terminal">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 side panel" />
|
||||
</Frame>
|
||||
|
||||
## Info
|
||||
|
||||
Everything tty7 knows about the focused pane, in one column:
|
||||
|
||||
| Section | What it shows |
|
||||
|---|---|
|
||||
| **Session** | working directory, shell, SSH connection, git branch, `+N −M` changes, and the coding agent with its status (idle / working / waiting / done) |
|
||||
| **Processes** | the process tree inside the pane, with the foreground process marked |
|
||||
| **Ports** | every port those processes are listening on |
|
||||
|
||||
The working directory row has **Reveal in Finder** / **Open Folder** beside it.
|
||||
The ports section is the quickest answer to "what is this pane serving, and
|
||||
where" — the same data `tty7 procs` prints.
|
||||
|
||||
## Source Control
|
||||
|
||||
The git panel for the focused pane's repository: staged changes, unstaged
|
||||
changes, untracked files, and merge conflicts, each in its own group. Write a
|
||||
message, commit, and push without leaving the window.
|
||||
|
||||
[Source control →](/git/source-control)
|
||||
|
||||
## Files
|
||||
|
||||
A file tree rooted at the pane's working directory, with git status decorations
|
||||
on every row and a search box at the top.
|
||||
|
||||
- **Click a file** to open it in the built-in editor.
|
||||
- **Drag a file out** to Finder or Explorer to copy it there.
|
||||
- **Drag files in** from the desktop to copy them into the folder under the
|
||||
cursor. A folder row takes them itself, a file row stands in for the folder
|
||||
holding it, and the empty space below the tree means the top of it. Folders
|
||||
come in whole, the executable bit survives, and a name that is already taken
|
||||
is asked about rather than replaced.
|
||||
|
||||
Both directions work over a [remote workspace](/remote/workspaces) too, reading
|
||||
on one machine and writing on the other, up to the size one control frame can
|
||||
carry — past that the panel tells you to use [SFTP](/remote/sftp).
|
||||
|
||||
## The editor
|
||||
|
||||
<kbd>⌘ ⇧ E</kbd> toggles the code panel; clicking a file in the Files tab opens
|
||||
it there. It is a real editor — syntax highlighting, line and column readout,
|
||||
wrap toggle, and a Markdown preview — meant for the edit you would otherwise
|
||||
have opened `vim` for.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⌘ S</kbd> | Save |
|
||||
| <kbd>Esc</kbd> | Back to the terminal |
|
||||
|
||||
Files are watched on disk: a change underneath you is picked up, and closing
|
||||
with unsaved edits asks before discarding them. Files over 4 MB and anything
|
||||
that looks binary are refused with a note rather than opened badly.
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: "The sidebar"
|
||||
description: "Tabs grouped by repository, with branch, diff counts, and agent status on every row."
|
||||
---
|
||||
|
||||
The left sidebar is tty7's tab bar, and it is the default because a vertical row
|
||||
has room for things a horizontal chip does not: the repository a tab belongs to,
|
||||
the branch it is on, how much has changed there, and what a coding agent in it
|
||||
is doing.
|
||||
|
||||
<kbd>⌘ B</kbd> shows and hides it. Drag its right edge to resize.
|
||||
|
||||
<Frame caption="Placeholder — screenshot: the sidebar with two repo groups, agent avatars and status dots, branch and +N −M counts">
|
||||
<img src="/images/placeholder.svg" alt="The tty7 tab sidebar" />
|
||||
</Frame>
|
||||
|
||||
## Grouped by repository
|
||||
|
||||
Rows sit under a header per git repository, with everything else collected in a
|
||||
trailing **Scratch** section. The grouping follows the tab's working directory,
|
||||
not its history — switching branches or `cd`-ing around inside a repository
|
||||
never moves a row out from under its header.
|
||||
|
||||
**Settings → Window & Tabs → Sidebar grouping** switches between *By repo* (the
|
||||
default) and *Flat*.
|
||||
|
||||
## What a row tells you
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Who is running there" icon="robot">
|
||||
A brand avatar when a coding agent is in the tab, plus a status dot —
|
||||
blue for working, amber for needs-your-input, green for done.
|
||||
</Card>
|
||||
<Card title="Where it is" icon="code-branch">
|
||||
The pane's git branch, refreshed on `cd` and whenever a command finishes.
|
||||
</Card>
|
||||
<Card title="What changed" icon="plus-minus">
|
||||
The working-tree diff as `+N −M`. Click the counts to open the
|
||||
[diff overlay](/git/diffs).
|
||||
</Card>
|
||||
<Card title="Whether you have looked" icon="circle-dot">
|
||||
An unread marker on tabs that produced output while you were elsewhere.
|
||||
*Mark as Unread* is in the right-click menu.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
If you would rather the counts not be clickable, turn off **Settings → Window &
|
||||
Tabs → Open diff preview from sidebar counts**. The branch and the numbers stay;
|
||||
they simply stop opening the overlay.
|
||||
|
||||
## Rearranging
|
||||
|
||||
Drag a row to reorder it, or drag a whole group header to move the group.
|
||||
Dropping a row into another group moves the tab there.
|
||||
|
||||
## Naming
|
||||
|
||||
Almost no tab has a name of its own, so the label falls back through the best
|
||||
evidence available, in order:
|
||||
|
||||
1. a name you set (right-click → **Rename Tab…**)
|
||||
2. the coding agent running in the tab — "Claude Code"
|
||||
3. the last segment of the working directory
|
||||
4. the foreground process
|
||||
|
||||
That order is also what `tty7 tab ls` reports as `label`, with `name` left
|
||||
literal so a script can tell a real name from a stand-in.
|
||||
|
||||
## The switcher
|
||||
|
||||
<kbd>⌘ ⇧ O</kbd> opens the workspace switcher: every workspace on every machine
|
||||
you are connected to on the left, that workspace's tabs on the right. Type to
|
||||
filter both, <kbd>⇥</kbd> to cross into the tab column, <kbd>⏎</kbd> to open.
|
||||
|
||||
From here you can also rename a workspace, open one in a new window, stop one,
|
||||
or connect to a machine you have a profile for.
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: "Tabs and splits"
|
||||
description: "Opening, arranging, and rearranging the panes in a tab."
|
||||
---
|
||||
|
||||
<Note>
|
||||
Keys are written in macOS notation. On Windows and Linux, read <kbd>⌘</kbd> as
|
||||
<kbd>Ctrl ⇧</kbd> for most window actions — the exact chords are on the
|
||||
[keyboard shortcuts](/reference/keyboard-shortcuts) page.
|
||||
</Note>
|
||||
|
||||
## Tabs
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⌘ T</kbd> | New tab |
|
||||
| <kbd>⌘ W</kbd> | Close the tab (or the focused pane, if the tab has more than one) |
|
||||
| <kbd>⌘ ⇧ T</kbd> | Reopen the tab you just closed |
|
||||
| <kbd>⌘ 1</kbd> … <kbd>⌘ 9</kbd> | Jump to tab 1–9 |
|
||||
| <kbd>⌃ ⇥</kbd> · <kbd>⌃ ⇧ ⇥</kbd> | Hold to walk the switcher forwards or backwards; it commits when you let go |
|
||||
|
||||
A new tab always opens in the current pane's directory. Where it lands in the
|
||||
list is **Settings → Window & Tabs → New tab position** — *After current* by
|
||||
default, or *At end*.
|
||||
|
||||
Right-click a tab for the rest: rename, close others, close to the right, copy
|
||||
the working directory, mark unread, and — when a coding agent is running there —
|
||||
fork its session.
|
||||
|
||||
## Splits
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| <kbd>⌘ D</kbd> | Split right |
|
||||
| <kbd>⌘ ⇧ D</kbd> | Split down |
|
||||
| <kbd>⌘ ]</kbd> · <kbd>⌘ [</kbd> | Next pane · previous pane |
|
||||
| <kbd>⌘ ⌥ ← → ↑ ↓</kbd> | Focus the pane in that direction |
|
||||
| <kbd>⌘ ⇧ ⏎</kbd> | Zoom the focused pane to fill the tab |
|
||||
| <kbd>⌘ ⏎</kbd> | Fullscreen the window |
|
||||
|
||||
A split inherits the current pane's working directory, so splitting inside a
|
||||
repository keeps you in the repository.
|
||||
|
||||
Resizing and swapping panes have no default keys — bind them under **Settings →
|
||||
Keybindings**, or run them from the command palette (*Resize Pane Left*, *Swap
|
||||
Pane Next*, and friends).
|
||||
|
||||
Inactive panes are dimmed slightly so the focused one is obvious. Turn that off
|
||||
with **Settings → Appearance → Dim inactive panes**.
|
||||
|
||||
## Rearranging by dragging
|
||||
|
||||
Hover a pane and a small grip appears along its top edge. Drag it to move that
|
||||
pane somewhere else in the tab.
|
||||
|
||||
<Frame caption="Placeholder — video: dragging a pane by its grip, with the landing highlighted">
|
||||
<img src="/images/placeholder.svg" alt="Dragging a pane to a new position" />
|
||||
</Frame>
|
||||
|
||||
Where you drop it decides what happens:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="On a pane's side">
|
||||
The pane goes in **beside** that one. If it is facing a neighbour in the
|
||||
same row or column, it joins that row and takes an equal share of it. Only
|
||||
when it faces across the layout — where there is no row to join — does it
|
||||
split that pane in half.
|
||||
</Accordion>
|
||||
<Accordion title="On a pane's middle">
|
||||
The two panes **trade places**.
|
||||
</Accordion>
|
||||
<Accordion title="Past a pane's outer side">
|
||||
The band beyond the outermost edge — the side facing the window rather than
|
||||
another pane — makes the dragged pane a full-width or full-height band
|
||||
beside everything else, sized to an even share of what that side already
|
||||
holds. A pane in the middle of a 2×2 becomes a full-height *third* column in
|
||||
one drag, rather than taking half the window.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
The landing lights up while you drag, and only ever lights up when the drop
|
||||
would actually change the layout.
|
||||
|
||||
## Closing something that is busy
|
||||
|
||||
Closing a pane or tab with a command still running asks first, and says what is
|
||||
running: *"cargo is still running. Closing ends it."* When a coding agent is
|
||||
mid-turn it says that instead — *"Claude Code is still working. Closing ends its
|
||||
turn."*
|
||||
|
||||
## Where the tabs live
|
||||
|
||||
**Settings → Window & Tabs → Tab bar position** puts tabs in a vertical sidebar
|
||||
on the left (the default) or a horizontal strip on top. The sidebar has room for
|
||||
things a strip does not — git branch, diff counts, agent status — so most of
|
||||
[its own page](/window/sidebar) is about that.
|
||||
|
||||
<kbd>⌘ B</kbd> toggles the sidebar; <kbd>⌘ J</kbd> toggles the
|
||||
[side panel](/window/side-panel) on the right.
|
||||
Reference in New Issue
Block a user