Skip to content

Surface terminal bells on tabs, and update tab titles immediately - #1881

Open
aymanbagabas wants to merge 5 commits into
raphamorim:mainfrom
aymanbagabas:bell-tab-indicator
Open

aymanbagabas wants to merge 5 commits into
raphamorim:mainfrom
aymanbagabas:bell-tab-indicator

Conversation

@aymanbagabas

Copy link
Copy Markdown
Contributor

Two related tab-strip changes: a bell indicator, and the title latency that made it look broken.

Bell indicator

A BEL from a background tab left no trace: the audio bell fired (on the platforms that default to it) and that was the whole notification. If the tab wasn't focused, nothing said which one rang.

RioEvent::Bell now carries the route_id of the pane that rang, so the tab strip can flag the tab it came from with a 🔔 next to the title. The mark is only set for background tabs — the focused one is already in front of the user — and is cleared on the first frame its tab is shown, whatever brought it to the front (click, keybinding, tab close).

The ringing pane is looked up across every tab, not just the focused one, so a bell from a background split of a background tab still lands on the right tab.

The mark is drawn as its own text run rather than being prefixed onto the title: the UI text layer resolves one font per draw from the run's first char, so gluing them together would shape the whole title in the emoji font. Its width is subtracted from the title budget, so a ringing tab ellipsizes instead of overflowing its island.

Gated behind bell.tab-indicator, default true:

[bell]
tab-indicator = true

Immediate tab titles

While testing the above, the mark took seconds to appear. The cause is older than the bell work: tab titles were only recomputed by a 2s poll, itself gated behind a second 2s throttle, so an OSC 0/2 title change could take seconds to reach the tab strip even though the PTY had already reported it.

RioEvent::Title now carries a route_id too, and the tab title is recomputed when the event arrives. The poll stays for the variables no event announces ({{program}}, paths) and now reports whether it changed anything, so it can repaint. Both paths mark the frame dirty — the tab strip is chrome, and a bare redraw request is dropped by the any_panel_dirty present gate.

The title template regex is compiled once now that it renders per event instead of per tick.

Testing

  • bell_marks_background_tabs_only_and_clears_on_focus covers the tab flagging: background tabs get marked, the focused tab does not, unknown routes are a no-op, and focusing clears it.
  • bell_emits_bell_event_for_its_route drives a real 0x07 byte through the processor, so the C0 dispatch and the route id it carries stay covered.
  • cargo test -p rioterm -p rio-backend -p rio-vt passes (925 tests), cargo clippy --workspace --all-targets is clean.
  • Verified by hand on macOS: printf '\a' from a background tab marks that tab immediately and the mark disappears on focus; printf '\033]0;TITLE\007' from a background tab retitles it immediately with no interaction.

aymanbagabas and others added 5 commits August 17, 2026 11:26
RioEvent::Bell now carries the ringing pane's route_id, so the tab strip
can flag the tab it came from with a bell glyph. The mark is set only for
background tabs and cleared on the next frame the tab is focused.

Gated behind bell.tab-indicator (default true).

Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Tab titles were only recomputed by a 2s poll, itself gated behind a
second 2s throttle, so an OSC 0/2 title change could take seconds to
reach the tab strip even though the PTY had already reported it.

RioEvent::Title now carries the route id of the pane that changed, so
the tab title is recomputed on arrival. The poll stays for the variables
no event announces ({{program}}, paths) and now repaints when it finds a
change; both paths mark the frame dirty, since the tab strip is chrome
and a bare redraw request is dropped by the present gate.

The title template regex is compiled once now that it renders per event
rather than per tick.

Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Drives a real 0x07 byte through the processor so the C0 dispatch and the
route id the event carries stay covered, not just the handler call.

Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants