Files
l0ng-ai 22e1ab1694 tty7: a GPU-rendered, daemon-backed terminal in pure Rust
tty7 is split into two Rust processes: a persistent daemon that owns the
shells and a GPU-rendered client that talks to it over a local socket.
Because the shells live in the daemon, quitting and reopening the app
leaves the session intact — detach and reattach, no tmux required.

- Persistent sessions — the daemon holds the PTYs and child processes, so
  closing a window or swapping in a new build never takes a shell down.
- Performance — an 11 MB `cat` completes in 95 ms and DOOM-fire renders at
  888 fps; the daemon drains the PTY at device speed off the render path.
- Shell-aware — new tabs and splits open in the current working directory;
  zsh, bash, fish, and PowerShell are set up automatically.
- Enhanced prompt — inline completion, syntax highlighting, history, and
  in-terminal search, with rich flag/subcommand signatures for common tools.
- Tabs, resizable splits, a command palette, click-to-open links, desktop
  notifications, eight themes, and CJK/IME input.

Native builds for macOS, Windows, and Linux.
Built on Zed's gpui and Alacritty's VT core.
2026-07-06 21:54:27 +08:00
..

fig-convert — Fig autocomplete specs → tty7 completion signatures

tty7's per-command completion (flags/subcommands/args with descriptions) is driven by signature data generated from Fig's autocomplete spec corpus (MIT-licensed, community-maintained, hundreds of commands). This is the build-time converter; the runtime consumer is src/terminal/signature.rs.

Why a converter (and not the JS engine)

Fig specs are authored in TypeScript and shipped pre-compiled as ESM on npm. Each spec export defaults an object whose static shape (subcommands, options, args, descriptions, static generator scripts) is exactly what a completion menu needs. The only truly dynamic parts are functions — generator postProcess, custom, filterTerm, generateSpec — which need a JS runtime to evaluate.

We convert ahead of time: run each spec once, snapshot the static shape to JSON, and drop the functions. No JS engine at runtime. (An embedded QuickJS evaluating the functions live would be the alternative; we don't need it.) Dynamic value completion (git checkout <branch>) is recovered later by running a generator's static script and splitting stdout on newlines — the scripts are already captured; only the executor is future work.

Regenerate

# 1. Fetch the compiled spec corpus once (into this dir, gitignored).
npm pack @withfig/autocomplete
tar xzf withfig-autocomplete-*.tgz          # -> package/build/<cmd>.js

# 2. Convert the commands tty7 embeds into assets/completions/.
node convert.mjs --build-dir package/build --out ../../assets/completions git docker

convert.mjs executes each <cmd>.js, walks the object graph keeping only static fields, resolves loadSpec references (e.g. docker compose grafts in the docker-compose spec), and writes minified assets/completions/<cmd>.json. Each command is converted in isolation: one spec that throws is reported () and skipped, never aborting the batch — of Fig's ~716 specs, 715 convert and only a non-command index barrel is skipped. Pass as many command names as you like in one run.

Output schema

Mirrors the serde model in src/terminal/signature.rs one-to-one:

  • root: { name, description, options[], args[], subcommands[] }
  • subcommand: { names[], description, hidden, options[], args[], subcommands[] }
  • option: { names[], description, args[], required, repeatable, hidden }
  • arg: { name, description, optional, variadic, template[], suggestions[], generators[] }
  • suggestion: { names[], description }
  • generator: { script[] } (static shell command only; postProcess dropped)

Scope & rollout

Signatures are read from disk, not embedded: signature::spec_source resolves a completions/ directory (inside the macOS .app bundle's Resources, beside the executable on Linux/Windows, or the in-tree assets/completions for cargo run/tests) and lazily loads <cmd>.json by command name. So adding a command is just dropping its JSON into assets/completions/ — the packaging scripts copy the whole directory into each bundle, and no recompile or binary bloat is involved. A big spec is ~300500 KiB; the full corpus is ~700 commands, so ship whatever subset you want rather than all of it if bundle size matters.

The in-tree set is ~95 everyday commands (version control, container/k8s, language package managers, cloud CLIs, shell/sysadmin tools) — ~5.5 MB, curated from the corpus. The two outliers aws (~51 MB) and gcloud (~18 MB) are deliberately excluded; add them only if you also trim them. To add a command, run convert.mjs with its name (step 2 above) and commit the resulting JSON.