feat(terminal): track, draw, and assert on the window title - #91
Open
Ayman Bagabas (aymanbagabas) wants to merge 3 commits into
Open
feat(terminal): track, draw, and assert on the window title#91Ayman Bagabas (aymanbagabas) wants to merge 3 commits into
Ayman Bagabas (aymanbagabas) wants to merge 3 commits into
Conversation
A program announces what it is doing by setting the window title with `OSC 0` or `OSC 2`, and shell-use threw that away. An agent driving a terminal could see what a program printed but not what it called itself, which is exactly what long-running tools report progress through. The emulator now tracks the title, and it is reachable everywhere the other terminal properties are: `state`, `get title`, `expect title`, and `wait title`, in the CLI and in both bindings. Screenshots draw it in the title bar. An empty title reports as none rather than as a blank one. Programs clear the title that way on exit, so keeping the two apart would only give every caller the same special case to write. Widths are measured in terminal columns, not characters. A CJK title is half as many characters as columns, so counting characters drew a snapshot frame four columns out of true and, in the centred title bar, spilled the title over the window controls at both ends. Snapshots only record the title when asked with `--include-title`. A shell prompt routinely sets it to a username, hostname, and absolute path, so recording it by default would pin every stored baseline to one machine and make it change on `cd` while the screen stayed the same. The title stack (`CSI 22 t` / `CSI 23 t`) comes free with this, since alacritty implements a pop as setting the title it popped; there is a test rather than any code for it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Windows ConPTY gives a session the program's path as its window title as soon as it opens, so asserting a fresh session reports none failed there while the feature itself worked. That a fresh emulator has no title, and that an empty one resets rather than storing a blank, are claims about the emulator rather than about the platform. Both are already pinned in the conformance suite, which runs without a PTY and against every backend. The end-to-end test keeps to what only it can prove: that a title set by a real program in a real shell arrives intact, and that it replaced whatever the session started with. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Ayman Bagabas (aymanbagabas)
marked this pull request as ready for review
August 5, 2026 00:44
Member
Author
|
cpendery (@cpendery) ready for review when you have a moment. Branches off latest GitHub will not let me request you as a reviewer directly (no push access here), hence the mention. |
cpendery (cpendery)
left a comment
Member
There was a problem hiding this comment.
nit: can we also update the bindings readmes with the new methods?
…ally Review follow-ups on the window title. Width is measured over the whole string with `UnicodeWidthStr` rather than by summing characters. A sequence can be narrower than its parts: an emoji joined by zero-width joiners renders as one glyph, and a base character followed by a variation selector or keycap mark is one unit. Summing per character called a family emoji eight columns where a terminal draws two, which drew a frame six columns too wide. Truncation now finds its cut by measuring real prefixes, so the result is exactly as wide as it measured even when the cut lands inside a sequence. The full title is recorded alongside the frame. The border still shows a title shortened to fit, which is readable but lossy: two long titles differing only past the cut recorded identically and passed for each other, which is the wrong behaviour for an assertion. Attributes now carry `title` and nest the colour shifts under `colors`, so a stored snapshot taken with `--include-colors` needs re-recording once. The grid and the title are read under one lock. Taking them separately let the reader thread advance the terminal in between, pairing a grid from one moment with a title from another: a shell writes its prompt and then sets its title, so a snapshot of a screen that never changed again could still come out different each time. Screenshots had the same split and are fixed with it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Member
Author
|
Thanks cpendery (@cpendery) — all four addressed in 996c5f9, replies inline on each thread.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A program announces what it is doing by setting the window title with
OSC 0orOSC 2, and shell-use threw that away. An agent driving a terminal could see what a program printed but not what it called itself — which is exactly where long-running tools report progress, and how a shell says which command is running.The emulator now tracks the title, and it is reachable everywhere the other terminal properties already are.
Before / after
Same session, same command, screenshotted by the same
screenshot --out:The title is drawn centred in the title bar, clamped so it can never run under the window controls, and truncated with an ellipsis when it does not fit. No title leaves the bar exactly as it was, so every screenshot taken without one is byte-identical to before.
Surfaces
state.title,get title,expect title, andwait title, plusget_title/wait_title/expect_titlein the Python and JS bindings. All four take--regex,--not, and--timeoutwhere the sibling text commands do.Widths are counted in columns, not characters
A terminal lays out by column. A CJK glyph is one
charbut two columns; a combining mark is onecharbut none. Counting characters drew the snapshot frame four columns out of true, and in the centred title bar it sized the title at half its real width so it spilled over the window controls at both ends.One shared
truncate_to_columnsinterminal/cell.rsserves both. This is the only reasonunicode-widthis now named inCargo.toml:alacritty_terminalalready pulls the identical 0.2.2, so declaring it added one line toCargo.lockand zero new packages. Neitheralacritty_terminalnorvtere-exports it, and hand-rolling East-Asian-width tables would be both reinvention and wrong on emoji and combining marks — the tests cover🚀 buildande\u{301}clairfor exactly that reason.An empty title means reset
Programs clear the title by sending an empty one, so
OSC 2;reports asNonerather than as a title that happens to be blank. Keeping the two apart would only hand every caller the same special case to write.Snapshots record the title only when asked
expect snapshot --include-titleputs it in the frame's top border; the default leaves it out.This is deliberate and worth a look during review. A stock Ubuntu
.bashrcsetsPS1='\[\e]0;\u@\h: \w\a\]…', so a normal shell writes its own title on every prompt. Recorded by default, that is:which pins the baseline to one username, hostname, and absolute path, and changes on
cdwhile the screen stays identical. Off by default, a snapshot with no title is byte-identical to one taken before this PR, so every stored baseline keeps passing.Free
The title stack (
CSI 22 tpush,CSI 23 tpop) works, because alacritty implements a pop as setting the title it popped and that routes through the same event. There is a test for it rather than any code.Notes for review
Event::Title/Event::ResetTitlein the existingCaptureProxyrather than read back off the terminal, because alacritty exposes no way to read it:Term.titleandTerm.colorsare both private fields, but onlycolorshas a public getter (pub fn colors(&self) -> &Colors). There is noTerm::title(), andset_titleis aHandlersetter. Alacritty's own frontend consumes the same events for the same reason.OSC 1(icon name) is deliberately not supported: vte never dispatches it, and with a single drawn title there is nothing for a separate icon name to do.</text><script>cannot inject an element.