fix(terminal): update ghostty-web to a build that clears recycled rows

ghostty-web 0.4.0 hands rows that scroll into view out of recycled WASM
page memory without clearing them, so after a tab or project switch the
new emulator showed the previous terminal's text (upstream #138). The fix
landed only in prereleases, so pin 0.4.0-next.20 and carry the local
block-glyph rendering patch over to the new dist file.

Verified in a production build: creating an emulator after disposing a
full one no longer exposes its rows, and switching between two projects
with live output in each keeps every terminal's content to itself.
This commit is contained in:
Bohdan Triapitsyn
2026-09-06 22:57:43 +03:00
parent c2f36fb5e7
commit eb6f7b0904
5 changed files with 66 additions and 11 deletions
@@ -0,0 +1,55 @@
diff --git a/node_modules/ghostty-web/dist/ghostty-web.js b/node_modules/ghostty-web/dist/ghostty-web.js
index 0000000000000000000000000000000000000000..1111111111111111111111111111111111111111 100644
--- a/node_modules/ghostty-web/dist/ghostty-web.js
+++ b/node_modules/ghostty-web/dist/ghostty-web.js
@@ -1844,7 +1844,7 @@
A.flags & G.FAINT && (this.ctx.globalAlpha = 0.5);
const s = C, h = I + this.metrics.baseline;
let N;
- if (A.grapheme_len > 0 && ((k = this.currentBuffer) != null && k.getGraphemeString) ? N = this.currentBuffer.getGraphemeString(g, Q) : N = String.fromCodePoint(A.codepoint || 32), this.ctx.fillText(N, s, h), A.flags & G.FAINT && (this.ctx.globalAlpha = 1), A.flags & G.UNDERLINE) {
+ if (A.grapheme_len > 0 && ((k = this.currentBuffer) != null && k.getGraphemeString) ? N = this.currentBuffer.getGraphemeString(g, Q) : N = A.codepoint == null || A.codepoint <= 0 || A.codepoint > 1114111 || A.codepoint >= 55296 && A.codepoint <= 57343 ? " " : String.fromCodePoint(A.codepoint), this.renderBlockChar(A.codepoint || 32, C, I, D) || this.ctx.fillText(N, s, h), A.flags & G.FAINT && (this.ctx.globalAlpha = 1), A.flags & G.UNDERLINE) {
const t = I + this.metrics.baseline + 2;
this.ctx.strokeStyle = this.ctx.fillStyle, this.ctx.lineWidth = 1, this.ctx.beginPath(), this.ctx.moveTo(C, t), this.ctx.lineTo(C + D, t), this.ctx.stroke();
}
@@ -1864,6 +1864,41 @@
}
}
}
+ renderBlockChar(A, B, g, E) {
+ const C = this.metrics.height, I = E / 2, D = C / 2;
+ switch (A) {
+ case 9600: this.ctx.fillRect(B, g, E, D); return !0;
+ case 9601: this.ctx.fillRect(B, g + C * 7 / 8, E, C / 8); return !0;
+ case 9602: this.ctx.fillRect(B, g + C * 3 / 4, E, C / 4); return !0;
+ case 9603: this.ctx.fillRect(B, g + C * 5 / 8, E, C * 3 / 8); return !0;
+ case 9604: this.ctx.fillRect(B, g + D, E, D); return !0;
+ case 9605: this.ctx.fillRect(B, g + C * 3 / 8, E, C * 5 / 8); return !0;
+ case 9606: this.ctx.fillRect(B, g + C / 4, E, C * 3 / 4); return !0;
+ case 9607: this.ctx.fillRect(B, g + C / 8, E, C * 7 / 8); return !0;
+ case 9608: this.ctx.fillRect(B, g, E, C); return !0;
+ case 9609: this.ctx.fillRect(B, g, E * 7 / 8, C); return !0;
+ case 9610: this.ctx.fillRect(B, g, E * 3 / 4, C); return !0;
+ case 9611: this.ctx.fillRect(B, g, E * 5 / 8, C); return !0;
+ case 9612: this.ctx.fillRect(B, g, I, C); return !0;
+ case 9613: this.ctx.fillRect(B, g, E * 3 / 8, C); return !0;
+ case 9614: this.ctx.fillRect(B, g, E / 4, C); return !0;
+ case 9615: this.ctx.fillRect(B, g, E / 8, C); return !0;
+ case 9616: this.ctx.fillRect(B + I, g, I, C); return !0;
+ case 9620: this.ctx.fillRect(B, g, E, C / 8); return !0;
+ case 9621: this.ctx.fillRect(B + E * 7 / 8, g, E / 8, C); return !0;
+ case 9622: this.ctx.fillRect(B, g + D, I, D); return !0;
+ case 9623: this.ctx.fillRect(B + I, g + D, I, D); return !0;
+ case 9624: this.ctx.fillRect(B, g, I, D); return !0;
+ case 9625: this.ctx.fillRect(B, g, I, C); this.ctx.fillRect(B + I, g + D, I, D); return !0;
+ case 9626: this.ctx.fillRect(B, g, I, D); this.ctx.fillRect(B + I, g + D, I, D); return !0;
+ case 9627: this.ctx.fillRect(B, g, E, D); this.ctx.fillRect(B, g + D, I, D); return !0;
+ case 9628: this.ctx.fillRect(B, g, E, D); this.ctx.fillRect(B + I, g + D, I, D); return !0;
+ case 9629: this.ctx.fillRect(B + I, g, I, D); return !0;
+ case 9630: this.ctx.fillRect(B + I, g, I, D); this.ctx.fillRect(B, g + D, I, D); return !0;
+ case 9631: this.ctx.fillRect(B + I, g, I, C); this.ctx.fillRect(B, g + D, I, D); return !0;
+ default: return !1;
+ }
+ }
/**
* Render cursor
*/