From 2fe3fd13a164f956d0c6a2365fa89feaf4366074 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 6 Jun 2026 09:15:09 -0700 Subject: [PATCH] fix(addon-webgl): skip missing buffer lines in _updateModel (#5991) * fix(addon-webgl): skip missing buffer lines in _updateModel _updateModel force-unwrapped buffer.lines.get(row), which can be undefined during trim or resize. That threw and left stale glyphs on rows below the gap. Clear the WebGL model row and glyph cells, reset blink state, and continue with remaining viewport rows, matching DomRenderer and ImageStorage handling. Co-authored-by: Cursor * refactor(addon-webgl): extract _nullModelCell helper Co-authored-by: Daniel Imms --------- Co-authored-by: Cursor Co-authored-by: Daniel Imms --- addons/addon-webgl/src/WebglRenderer.ts | 27 +++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 2f38e03cb..a951efba5 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -456,7 +456,18 @@ export class WebglRenderer extends Disposable implements IRenderer { for (y = start; y <= end; y++) { row = y + terminal.buffer.ydisp; - line = terminal.buffer.lines.get(row)!; + const bufferLine = terminal.buffer.lines.get(row); + if (!bufferLine) { + this._model.lineLengths[y] = 0; + for (x = 0; x < terminal.cols; x++) { + j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL; + modelUpdated = true; + this._nullModelCell(x, y, j, 0, 0, 0); + } + this._setRowBlinkState(y, false); + continue; + } + line = bufferLine; let rowHasBlinkingCells = false; this._model.lineLengths[y] = 0; isCursorRow = cursorY === row; @@ -587,13 +598,9 @@ export class WebglRenderer extends Disposable implements IRenderer { // Null out non-first cells for (x++; x <= lastCharX; x++) { j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL; - this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0); - this._model.cells[j] = NULL_CELL_CODE; // Don't re-resolve the cell color since multi-colored ligature backgrounds are not // supported - this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg; - this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg; - this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext; + this._nullModelCell(x, y, j, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext); } x--; // Go back to the previous update cell for next iteration } @@ -607,6 +614,14 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateTextBlinkState(); } + private _nullModelCell(x: number, y: number, cellIndex: number, bg: number, fg: number, ext: number): void { + this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, bg, fg, ext, NULL_CELL_CHAR, 0, 0); + this._model.cells[cellIndex] = NULL_CELL_CODE; + this._model.cells[cellIndex + RenderModelConstants.BG_OFFSET] = bg; + this._model.cells[cellIndex + RenderModelConstants.FG_OFFSET] = fg; + this._model.cells[cellIndex + RenderModelConstants.EXT_OFFSET] = ext; + } + private _resetBlinkingRowState(): void { this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false); this._rowHasBlinkingCellsCount = 0;