mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
6f842c300746d810ef57f47841bdb49f0861d2b2
* fix(file-tree): stop a watcher event repainting a window with nothing to draw
Issue #243 made two claims. The flicker was fixed independently on main by
4814d94 "fix(remote): keep file-tree listings on screen while they refresh",
which reached the same mechanism separately and went further on two axes. This
is only the other claim — the one in the issue's title, and the one still true
on main: `file_tree_apply_fs_events` ended in an unconditional `cx.notify()`, so
every batch from a watcher that is recursive over the root repainted the whole
window. The overwhelming majority of those batches name a directory the tree has
never listed and does not show: `.git` internals, build output, `node_modules`.
A window with nothing new to draw was being asked to draw, several times a
second, for as long as anything under the root was being written.
* `invalidate_dir` reports whether it reached anything the tree holds, and a
batch that reached nothing returns without repainting.
* What it did reach is re-read from the callback itself, rather than by
notifying in order to buy the paint that would have re-read it.
* `land_load` returns `Landed { superseded, changed }` and the caller repaints
only on `changed`, so a file rewritten in a directory on screen costs no
frames. `TreeEntry` gains `PartialEq` for that comparison.
* The `.gitignore` whole-cache branch is scoped by `gitignore_reaches_tree`:
patterns at `D/.gitignore` govern `D` and below, so unless a tracked directory
sits under `D` there is nothing to refresh. Its `cx.notify()` is deliberately
kept when the branch *is* taken — `invalidate_all` restarts the search and
only a paint re-walks it. `InFlight::pending_keys` is new, for the in-flight
half of that test.
Measured, not asserted. `Tty7App::render` feeds a per-thread draw counter under
`cfg(test)`, and gpui's test build redraws dirty windows from inside
`flush_effects` — so a headless window plus that counter answers "does this
reach render idle?" with no compositor and without the reporter's Wayland
session. Supporting seams: `test_window::harness` / `harness_with_pane` and
`terminal::view::quiet_test_pane`. `render_probe::arm` takes a draw budget so a
repaint-loop regression fails the test instead of hanging the suite — the loop
lives inside one `flush_effects` call and nothing outside it can interrupt it.
Render idle is measured as "stops drawing", not "never draws again": settling
legitimately costs a last frame as the final listing lands, so the count is
taken over a second interval once the first has absorbed that tail. Confirmed
rather than assumed — the count is 1 at 3s and still 1 at 12s.
Numbers, each taken by reverting the hunk it belongs to and re-running:
five writes under an unlisted directory 5 draws -> 0
five rewrites of a displayed file 10 draws -> 0
five .gitignore writes under node_modules 10 draws -> 0
marks left unread after a gitignore refresh 1 -> 0
The four controls — a settled panel on a non-empty, an empty and a hidden-only
directory, and a real change still arriving — pass either way, which is the
point: they were never the bug. `untracked_paths_leave_no_bookkeeping_behind`
also passes either way, because main's `invalidate_dir` already only marks what
is cached; it is a guard, not a fix.
Two things stated rather than faked. The end-to-end through a real OS watcher
did not survive: the watcher moved into the host layer, which a synthetic
`file_tree_apply_fs_events` cannot drive. For the same reason the gitignore test
asserts the marking and the re-read rather than the recomputed `ignored` flags,
which the host owns — and says so where it stops.
This commit replaces four earlier ones on this branch, squashed because they
were written against the single-host `file_tree` that no longer exists and could
not be replayed onto it. What each contributed:
* 744a08d fix(file-tree): stop the watcher blanking the panel and repainting for
nothing — the original. Its flicker half is dropped in favour of 4814d94; its
render-idle half is what this commit is.
* a88544c no-mistakes(review): scope gitignore refresh to the tree and cover
in-flight loads — the `gitignore_reaches_tree` scoping, kept. Its other half,
covering in-flight loads in the whole-cache refresh, is not carried: main's
`invalidate_all` already stales them via `InFlight::invalidate_all`.
* 0ff3b64 no-mistakes(document): correct stale watcher comment in file_tree docs
— folded into the doc comments here.
* f0e33a4 no-mistakes(document): drop tracked AGENTS.md, move render-probe caveat
into source — the deletion is moot on this base (the file was never added),
and the caveat it relocated is in `render_probe`'s doc comment.
Separately and deliberately not fixed here: on macOS the watcher reports paths
through `/private/var` while the cache is keyed by the root as handed in, so a
root reached via a symlink never matches and its changes are missed. Pre-existing
and invisible on Linux; the tests canonicalize around it and say so.
1484 tests pass across the workspace, fmt clean, clippy unchanged from
origin/main's baseline at 57 warnings. The app was not built, launched or driven;
visual acceptance is the owner's.
Refs #243.
* no-mistakes(review): correct watch scope claims, repaint on moved root, skip hidden-panel reads
* no-mistakes(review): exclude the SFTP column from the tree-drawn gate, fix stale docs
Also carries the correction the earlier messages on this branch owe the reader.
The first commit's message (and the CHANGELOG entry it shipped with) asserted
that the file tree watches its root **recursively**, and justified the whole
change on the traffic that supposedly produced: `.git` internals, build output,
everything under `node_modules`. That was wrong, not merely imprecise. The watch
is non-recursive — `sync_watch`'s own doc says so, it covers roots plus expanded
directories, and `WatchedDirs::translate` enforces it per backend. The recursive
watcher belonged to the older single-host design; the premise was carried across
the port to the host-keyed tree without being re-checked.
What is actually reachable, and all this now claims: the tree hears about a
change in a directory it is *displaying*, and a file's contents being rewritten
reports exactly as loudly as a file appearing. Comparing the re-read against
what is already on screen is what stops that repainting a window with nothing
new to draw.
Measured against that reachable case only, by reverting the comparison and
re-running: five rewrites of a file in a displayed directory cost 10 frames and
now cost 0. The figures the first message quoted for writes under an unlisted
directory and for `.gitignore` writes under `node_modules` are withdrawn — the
tests behind them synthesised watcher events this watch cannot deliver, so they
described scenarios the system cannot produce. Those tests have been removed or
reframed as guards on the predicate rather than evidence of a live symptom.
The other corrections in this round:
* A `.git` create or delete cleared the repo-root cache and then took the new
early return, so nothing re-resolved it and a moved repository root no longer
re-rooted the tree on an idle window. That regression came in with this change
and is closed.
* The watcher-driven re-read was not gated on the tree being drawn, so a hidden
panel did filesystem work it never used to do. It is gated now.
* That gate then assumed the Files tab always draws the local tree, which it
does not: a connected native-SSH pane substitutes the SFTP browser and the
local tree is never rendered. The predicate accounts for that too.
* Two stale statements of the recursive premise survived the first sweep, in
`assets.rs` and `code_editor.rs`, and are corrected.
The SFTP-substitution case is covered by a test at the predicate; the
substitution itself is a render-path branch with no headless seam, so what is
asserted is the predicate's answer rather than the panel's output.
* no-mistakes(review): gate watcher re-read on listings drawn, fix stale docs
* no-mistakes(review): delete withdrawn recursive-watch CHANGELOG entry duplicated by rebases
---------
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
tty7
A terminal workbench: shells, sessions, SSH, coding agents.
Pure Rust · GPU rendering on Zed's gpui · VT core from Alacritty
English · 简体中文
Why
- Fast — ~2× the throughput of Alacritty, Ghostty, or Kitty (benchmarks)
- Sessions persist — quit or reboot; your shells keep running, no tmux
- Editor-grade input — completion, syntax highlighting, history search built in; zero config for zsh, bash, fish, PowerShell
- Agent-aware — recognizes Claude Code & co. in a pane: status, notifications, session resume
Install
Native builds for each platform on Releases:
| macOS | …-macos-arm64.dmg · …-x86_64.dmg |
drag into Applications |
| Windows | …-setup.exe · portable ….zip |
|
| Linux | …-x86_64.AppImage |
chmod +x and run — x11/wayland libs bundled |
What's inside
| Input | ghost suggestions from history · explained tab completion · syntax highlighting · multi-line editing · click places the caret · ⌃ R fuzzy history |
| Window | tabs & splits · ⌘ P palette · ⌘ F scrollback search · nine themes · IME |
| Coding agents | per-pane agent detection (~17 CLIs): status dot, notifications, branch + diff, resume after reboot, tray icon that signals "needs your input" |
| SSH | native russh stack: profiles with keychain secrets, SFTP panel, port forwarding, jump hosts |
Details for every row: docs/features.md. Keybindings: ⌘ , opens Settings — browse and remap everything, tmux preset included (full list).
Benchmarks
Same machine, same day, same 155×40 grid — Apple M1 Pro, macOS 26.3.1, five-run averages (2026-07-04):
| tty7 | Alacritty | Ghostty | Kitty | |
|---|---|---|---|---|
Plaintext IO — 11 MB cat (lower = better) |
95 ms | 239 ms | 179 ms | 185 ms |
| DOOM-fire frame rate (higher = better) | 888 fps | 485 fps | 552 fps | 617 fps |
| Cold-launch memory | 116 MB¹ | 105 MB | 128 MB | 130 MB |
¹ GUI 105 MB + the persistent daemon 11 MB.
Methodology and one-command reproduction: scripts/bench/.
Built on gpui and alacritty_terminal · Apache-2.0 · Discord · Changelog
Description
A terminal workbench in pure Rust: shells, persistent sessions, SSH, coding agents. GPU-rendered on Zed's gpui, VT core from Alacritty.
ai-agentsalacrittyclaude-codecoding-agentcross-platformdaemondeveloper-toolsgpugpuilinuxmacosptyrustshellsshterminalterminal-emulatorterminal-multiplexertmuxwindows
Readme
Apache-2.0
34 MiB
Languages
Rust
99.1%
Shell
0.5%
PowerShell
0.2%
Inno Setup
0.1%