fix: bell indicator stuck after returning to terminal tab

Two issues prevented the bell emoji from clearing reliably:

1. Race on tab return: clearBellState() runs on visibilitychange/focus,
   but the WebSocket has buffered output from while the tab was hidden.
   The terminal processes it immediately, onBell fires, and the bell is
   right back. Fixed with a 1-second suppression window after clearing —
   bells from buffered output are ignored.

2. Bell stays when clicking without typing: the bell only cleared on
   onData (keyboard input). If you clicked the terminal or just switched
   back without typing, the emoji persisted. Added a mousedown listener
   on the terminal element to clear it on any mouse interaction.
This commit is contained in:
GitHub Copilot
2026-03-01 20:26:05 +00:00
parent 5acb468abb
commit 5089aedd6f
2 changed files with 17 additions and 1 deletions
File diff suppressed because one or more lines are too long
+16
View File
@@ -737,7 +737,14 @@ class WebTerminal {
return `webterm:bell:${this.routeKey}`;
}
private bellSuppressUntil = 0;
private setBellActive(): void {
// Suppress bells briefly after returning to the tab so that
// buffered output processed on visibility change doesn't
// immediately re-trigger the indicator we just cleared.
if (Date.now() < this.bellSuppressUntil) return;
if (!this.bellActive) {
this.bellActive = true;
document.title = `${BELL_EMOJI} ${this.baseTitle}`;
@@ -749,6 +756,10 @@ class WebTerminal {
}
private clearBellState(): void {
// Suppress further bells for a short window so that buffered
// output arriving right after focus doesn't re-set the indicator.
this.bellSuppressUntil = Date.now() + 1000;
const key = this.bellStorageKey();
if (key) {
localStorage.removeItem(key);
@@ -846,6 +857,11 @@ class WebTerminal {
this.setBellActive();
});
// Clear bell on any mouse interaction with the terminal
this.addTrackedListener(this.element, "mousedown", () => {
if (this.bellActive) this.clearBellState();
});
// Handle resize
this.terminal.onResize((size) => {
if (this.isValidSize(size.cols, size.rows)) {