From d4acf2fa45ab2a5a9f1147b6fb2b36cd8656d871 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:39:43 -0700 Subject: [PATCH 01/50] fix(browser): use radix 10 for padding parseInt in Mouse coords parseInt without a radix can mis-parse leading-zero padding values as octal. Match AccessibilityManager and document rowCount JSDoc typo. Co-authored-by: Cursor --- src/browser/input/Mouse.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index c40a7cc782..5f73743fbd 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -6,8 +6,8 @@ export function getCoordsRelativeToElement(window: Pick, event: {clientX: number, clientY: number}, element: HTMLElement): [number, number] { const rect = element.getBoundingClientRect(); const elementStyle = window.getComputedStyle(element); - const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left')); - const topPadding = parseInt(elementStyle.getPropertyValue('padding-top')); + const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'), 10); + const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'), 10); return [ event.clientX - rect.left - leftPadding, event.clientY - rect.top - topPadding @@ -22,7 +22,7 @@ export function getCoordsRelativeToElement(window: Pick Date: Sun, 31 May 2026 21:55:20 -0700 Subject: [PATCH 02/50] fix(common): pass radix 10 to parseInt in getSafariVersion Safari major version parsing used parseInt without an explicit radix. Other call sites in the repo use base 10; add the radix for consistency and to avoid octal interpretation of leading-zero strings. Co-authored-by: Cursor --- src/common/Platform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 09ba097e6c..9ccf8bda83 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -41,7 +41,7 @@ export function getSafariVersion(): number { if (majorVersion === null || majorVersion.length < 2) { return 0; } - return parseInt(majorVersion[1]); + return parseInt(majorVersion[1], 10); } // Find the users platform. We use this to interpret the meta key From f04679a42ef4dadd3ba919377550d98a11cce29e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:12:26 -0700 Subject: [PATCH 03/50] fix(common): pass radix 10 when parsing rgb() components in Color rgba/rgb CSS parsing used parseInt without a radix on captured digit groups. Add base 10 explicitly, matching getSafariVersion and other call sites in the codebase. Co-authored-by: Cursor --- src/common/Color.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/Color.ts b/src/common/Color.ts index aa3ca00aba..d416d670e4 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -177,9 +177,9 @@ export namespace css { // Formats: rgb() or rgba() const rgbaMatch = css.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/); if (rgbaMatch) { - $r = parseInt(rgbaMatch[1]); - $g = parseInt(rgbaMatch[2]); - $b = parseInt(rgbaMatch[3]); + $r = parseInt(rgbaMatch[1], 10); + $g = parseInt(rgbaMatch[2], 10); + $b = parseInt(rgbaMatch[3], 10); $a = Math.round((rgbaMatch[5] === undefined ? 1 : parseFloat(rgbaMatch[5])) * 0xFF); return channels.toColor($r, $g, $b, $a); } From 878e3fc52d3cf2313b3d5775663e8286f431b38f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:32:13 -0700 Subject: [PATCH 04/50] fix(browser): pass radix 10 when parsing Chrome major version Wheel event DPR factoring parses Chrome/(\d+) with parseInt without a radix. Add base 10 explicitly, matching getSafariVersion and other version parsing in the codebase. Co-authored-by: Cursor --- src/browser/scrollable/mouseEvent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index 6be8963e26..f6fca88f17 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -217,7 +217,7 @@ export class StandardWheelEvent { let shouldFactorDPR: boolean = false; if (platform.isChrome) { const chromeVersionMatch = navigator.userAgent.match(/Chrome\/(\d+)/); - const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1]) : 123; + const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1], 10) : 123; shouldFactorDPR = chromeMajorVersion <= 122; } From b0f6aed4a1a7ff27609b15d77f683a8d4923b0b7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 23:03:46 -0700 Subject: [PATCH 05/50] fix(common): pass radix 10 when parsing OSC color indices InputHandler parsed palette indices from OSC color sequences with parseInt without a radix after validating digits-only slots. Use base 10 for set/report and restore color index parsing. Co-authored-by: Cursor --- src/common/InputHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 9d725cd1b8..0b12dd8e1d 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -3065,7 +3065,7 @@ export class InputHandler extends Disposable implements IInputHandler { const idx = slots.shift() as string; const spec = slots.shift() as string; if (/^\d+$/.exec(idx)) { - const index = parseInt(idx); + const index = parseInt(idx, 10); if (isValidColorIndex(index)) { if (spec === '?') { event.push({ type: ColorRequestType.REPORT, index }); @@ -3228,7 +3228,7 @@ export class InputHandler extends Disposable implements IInputHandler { const slots = data.split(';'); for (let i = 0; i < slots.length; ++i) { if (/^\d+$/.exec(slots[i])) { - const index = parseInt(slots[i]); + const index = parseInt(slots[i], 10); if (isValidColorIndex(index)) { event.push({ type: ColorRequestType.RESTORE, index }); } From 99550840d24b8e43e0a6d2dfca73d7fe93b09b4f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:43:31 -0700 Subject: [PATCH 06/50] fix(fit): add radix and zero fallback for parent width parsing Align parent width parsing with the height fix: parseInt with radix 10 and || 0 inside Math.max so non-numeric CSS width does not yield NaN cols in proposeDimensions. Co-authored-by: Cursor --- addons/addon-fit/src/FitAddon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-fit/src/FitAddon.ts b/addons/addon-fit/src/FitAddon.ts index 3db988acd5..fab52dfdea 100644 --- a/addons/addon-fit/src/FitAddon.ts +++ b/addons/addon-fit/src/FitAddon.ts @@ -74,8 +74,8 @@ export class FitAddon implements ITerminalAddon, IFitApi { : (this._terminal.options.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH)); const parentElementStyle = _getComputedStyle(this._terminal.element.parentElement); - const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); - const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'))); + const parentElementHeight = Math.max(0, parseInt(parentElementStyle.getPropertyValue('height'), 10) || 0); + const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'), 10) || 0); const elementStyle = _getComputedStyle(this._terminal.element); const elementPadding = { top: parseInt(elementStyle.getPropertyValue('padding-top')), From a2cd1ab29c6a72c25bb953c687f773280c2157ff Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:46:40 -0700 Subject: [PATCH 07/50] fix(fit): parse terminal padding with radix 10 and zero fallback proposeDimensions summed padding-top/bottom/left/right via bare parseInt, so non-numeric padding values produced NaN and broke available width/height. Use radix 10 and || 0 on each side, consistent with parent element size parsing. Co-authored-by: Cursor --- addons/addon-fit/src/FitAddon.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/addon-fit/src/FitAddon.ts b/addons/addon-fit/src/FitAddon.ts index fab52dfdea..75bbe9caa7 100644 --- a/addons/addon-fit/src/FitAddon.ts +++ b/addons/addon-fit/src/FitAddon.ts @@ -78,10 +78,10 @@ export class FitAddon implements ITerminalAddon, IFitApi { const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'), 10) || 0); const elementStyle = _getComputedStyle(this._terminal.element); const elementPadding = { - top: parseInt(elementStyle.getPropertyValue('padding-top')), - bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')), - right: parseInt(elementStyle.getPropertyValue('padding-right')), - left: parseInt(elementStyle.getPropertyValue('padding-left')) + top: parseInt(elementStyle.getPropertyValue('padding-top'), 10) || 0, + bottom: parseInt(elementStyle.getPropertyValue('padding-bottom'), 10) || 0, + right: parseInt(elementStyle.getPropertyValue('padding-right'), 10) || 0, + left: parseInt(elementStyle.getPropertyValue('padding-left'), 10) || 0 }; const elementPaddingVer = elementPadding.top + elementPadding.bottom; const elementPaddingHor = elementPadding.right + elementPadding.left; From f34d0a255c8944543e345ab8b9b9a9b0eed5855c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:50:32 -0700 Subject: [PATCH 08/50] fix(image): pass radix 10 in IIPHandler dimension parsing _dim() parsed percent, pixel, and cell dimension strings with parseInt without an explicit radix. Use base 10 for all three paths, consistent with other numeric parsing fixes in the repo. Co-authored-by: Cursor --- addons/addon-image/src/IIPHandler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index 5e15c52771..56f5cdf352 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -222,8 +222,8 @@ export class IIPHandler implements IOscHandler, IResetHandler { private _dim(s: string, total: number, cdim: number): number { if (s === 'auto') return 0; - if (s.endsWith('%')) return parseInt(s.slice(0, -1)) * total / 100; - if (s.endsWith('px')) return parseInt(s.slice(0, -2)); - return parseInt(s) * cdim; + if (s.endsWith('%')) return parseInt(s.slice(0, -1), 10) * total / 100; + if (s.endsWith('px')) return parseInt(s.slice(0, -2), 10); + return parseInt(s, 10) * cdim; } } From a1a8d1abae74aa11988ec48385d88a3aa3eccdc1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:57:02 -0700 Subject: [PATCH 09/50] fix(image): pass radix 10 when parsing kitty graphics numeric keys KittyGraphicsTypes parsed numeric control values with parseInt without a radix. Use base 10 explicitly for format, id, dimensions, and offset fields. Co-authored-by: Cursor --- addons/addon-image/src/kitty/KittyGraphicsTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts index 448ebcc051..4288dd0a41 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts @@ -168,7 +168,7 @@ export function parseKittyCommand(data: string): IKittyCommand { cmd.deleteSelector = value; continue; } - const numValue = parseInt(value); + const numValue = parseInt(value, 10); switch (key) { case KittyKey.FORMAT: cmd.format = numValue; break; case KittyKey.ID: cmd.id = numValue; break; From d407bf561f5edde2955955a6067523b62043bea3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 23:00:26 -0700 Subject: [PATCH 10/50] fix(webgl): pass radix 10 for SVG arc flag parsing CustomGlyphRasterizer parsed large-arc and sweep flags with parseInt without a radix when drawing SVG arc paths. Use base 10 for 0/1 flags. Co-authored-by: Cursor --- addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 357d512d24..137cca7e3e 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -221,8 +221,8 @@ function drawPathDefinitionCharacter( const rx = parseFloat(args[0]) * deviceCellWidth; const ry = parseFloat(args[1]) * deviceCellHeight; const xAxisRotation = parseFloat(args[2]) * Math.PI / 180; - const largeArcFlag = parseInt(args[3]); - const sweepFlag = parseInt(args[4]); + const largeArcFlag = parseInt(args[3], 10); + const sweepFlag = parseInt(args[4], 10); const x = xOffset + parseFloat(args[5]) * deviceCellWidth; const y = yOffset + parseFloat(args[6]) * deviceCellHeight; drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y); From c961408bf7ef733afff29c8de87d48bc72f31a64 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:31:29 -0700 Subject: [PATCH 11/50] fix(browser): use startY in horizontalDirection row helper call horizontalDirection called moveToRequestedRow with targetX as the first argument, but that parameter is the starting row (startY). resetStartingRow and moveToRequestedCol already pass startY correctly; only this copy-paste mistake passed a column index where a row was expected. That could miscompute wrapped-row offsets and emit the wrong horizontal arrow key sequence when building selection/cursor movement strings. Co-authored-by: Cursor --- src/browser/input/MoveToCell.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index b5ddc7ca2b..9cc27088dd 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -156,7 +156,7 @@ function wrappedRowsForRow(currentRow: number, bufferService: IBufferService): n */ function horizontalDirection(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): Direction { let startRow; - if (moveToRequestedRow(targetX, targetY, bufferService, applicationCursor).length > 0) { + if (moveToRequestedRow(startY, targetY, bufferService, applicationCursor).length > 0) { startRow = targetY - wrappedRowsForRow(targetY, bufferService); } else { startRow = startY; From 3f3f6abfb90ebd0367e0e894e16aa48fd6c62a39 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:33:19 -0700 Subject: [PATCH 12/50] fix(browser): parenthesize document fallback for CoreBrowserService Nullish coalescing binds tighter than the conditional operator, so the previous expression was parsed as (this._document ?? windowDefined) ? window.document : null. When this._document was set (e.g. parent ownerDocument or documentOverride), the ternary still returned window.document instead of the configured document. CoreBrowserService.mainDocument is used by AccessibilityManager, viewport styles, and decoration renderers; CharSizeService already received this._document directly. Add parentheses so the fallback applies only when _document is nullish. Co-authored-by: Cursor --- src/browser/CoreBrowserTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index f7e20b9141..5b65a93dba 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -505,7 +505,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea, parent.ownerDocument.defaultView ?? window, // Force unsafe null in node.js environment for tests - this._document ?? (typeof window !== 'undefined') ? window.document : null as any + this._document ?? ((typeof window !== 'undefined') ? window.document : null as any) )); this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService); From 536eeb4db09c4787e3197c76e08aef09cc12ce09 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:34:13 -0700 Subject: [PATCH 13/50] refactor(browser): remove unreachable coords guard in getCoords getCoordsRelativeToElement always returns a two-element tuple, so the array is always truthy and if (!coords) could never run. hasValidCharSize already handles the only early-exit path before coordinates are computed. Co-authored-by: Cursor --- src/browser/input/Mouse.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index 5f73743fbd..ba8bddaa21 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -37,10 +37,6 @@ export function getCoords(window: Pick, event: Pick< } const coords = getCoordsRelativeToElement(window, event, element); - if (!coords) { - return undefined; - } - coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth); coords[1] = Math.ceil(coords[1] / cssCellHeight); From 1dd113b38a49dbb030ad52f8171254ba88ac5ff2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:38:07 -0700 Subject: [PATCH 14/50] fix(browser): reset TimeBasedDebouncer state on dispose dispose() cleared the pending timeout but left _refreshTimeoutID and _additionalRefreshRequested set. After dispose, a stale timeout id or trailing-refresh flag could confuse a new instance or tests. Match RenderDebouncer by clearing the timeout id and reset the trailing flag. Co-authored-by: Cursor --- src/browser/TimeBasedDebouncer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 0860e7927e..959c77a4e2 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -31,7 +31,9 @@ export class TimeBasedDebouncer implements IRenderDebouncer { public dispose(): void { if (this._refreshTimeoutID) { clearTimeout(this._refreshTimeoutID); + this._refreshTimeoutID = undefined; } + this._additionalRefreshRequested = false; } public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { From 1a01e9634f694c9cf4d9898a75d67c418148af12 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:50:16 -0700 Subject: [PATCH 15/50] fix(parser): use correct grammar in Params validation errors Replace "values lesser than -1" with "values less than -1" in addParam and addSubParam throw messages. "Lesser" is not standard English for numeric comparisons; tests assert the exact message. Co-authored-by: Cursor --- src/common/parser/Params.test.ts | 4 ++-- src/common/parser/Params.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/parser/Params.test.ts b/src/common/parser/Params.test.ts index 3bd67d3975..a30b1376c6 100644 --- a/src/common/parser/Params.test.ts +++ b/src/common/parser/Params.test.ts @@ -199,13 +199,13 @@ describe('Params', () => { it('reject params lesser -1', () => { const params = new Params(); params.addParam(-1); - assert.throws(() => params.addParam(-2), 'values lesser than -1 are not allowed'); + assert.throws(() => params.addParam(-2), 'values less than -1 are not allowed'); }); it('reject subparams lesser -1', () => { const params = new Params(); params.addParam(-1); params.addSubParam(-1); - assert.throws(() => params.addSubParam(-2), 'values lesser than -1 are not allowed'); + assert.throws(() => params.addSubParam(-2), 'values less than -1 are not allowed'); assert.deepEqual(params.toArray(), [-1, [-1]]); }); it('clamp parsed params', () => { diff --git a/src/common/parser/Params.ts b/src/common/parser/Params.ts index 6986a48bac..9b6c3f8554 100644 --- a/src/common/parser/Params.ts +++ b/src/common/parser/Params.ts @@ -162,7 +162,7 @@ export class Params implements IParams { return; } if (value < -1) { - throw new Error('values lesser than -1 are not allowed'); + throw new Error('values less than -1 are not allowed'); } this._subParamsIdx[this.length] = this._subParamsLength << 8 | this._subParamsLength; this.params[this.length++] = value > Constants.MAX_VALUE ? Constants.MAX_VALUE : value; @@ -185,7 +185,7 @@ export class Params implements IParams { return; } if (value < -1) { - throw new Error('values lesser than -1 are not allowed'); + throw new Error('values less than -1 are not allowed'); } this._subParams[this._subParamsLength++] = value > Constants.MAX_VALUE ? Constants.MAX_VALUE : value; this._subParamsIdx[this.length - 1]++; From 4500485e7993d0956423356770fb777b9bf3ee2b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:52:33 -0700 Subject: [PATCH 16/50] fix(parser): validate ESC prefix byte range with correct precedence _identifier() checked custom prefix bytes with `res && 0x3c > res || res > 0x3f`, which parsed as `(res && 0x3c > res) || (res > 0x3f)` because && binds tighter than ||. A prefix of NUL (0x00) skipped both clauses and was accepted even though only 0x3c..0x3f are valid. Use an explicit range test: res < 0x3c || res > 0x3f. Mirror the same fix in ApcParser and DcsParser test identifier helpers. Co-authored-by: Cursor --- src/common/parser/ApcParser.test.ts | 2 +- src/common/parser/DcsParser.test.ts | 2 +- src/common/parser/EscapeSequenceParser.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/parser/ApcParser.test.ts b/src/common/parser/ApcParser.test.ts index b27b400395..563ddbeabd 100644 --- a/src/common/parser/ApcParser.test.ts +++ b/src/common/parser/ApcParser.test.ts @@ -21,7 +21,7 @@ function identifier(id: IFunctionIdentifier): number { throw new Error('only one byte as prefix supported'); } res = id.prefix.charCodeAt(0); - if (res && 0x3c > res || res > 0x3f) { + if (res < 0x3c || res > 0x3f) { throw new Error('prefix must be in range 0x3c .. 0x3f'); } } diff --git a/src/common/parser/DcsParser.test.ts b/src/common/parser/DcsParser.test.ts index 459bc89612..5fa7b94167 100644 --- a/src/common/parser/DcsParser.test.ts +++ b/src/common/parser/DcsParser.test.ts @@ -22,7 +22,7 @@ function identifier(id: IFunctionIdentifier): number { throw new Error('only one byte as prefix supported'); } res = id.prefix.charCodeAt(0); - if (res && 0x3c > res || res > 0x3f) { + if (res < 0x3c || res > 0x3f) { throw new Error('prefix must be in range 0x3c .. 0x3f'); } } diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index a0c97b6104..c12ca3446d 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -342,7 +342,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP throw new Error('only one byte as prefix supported'); } res = id.prefix.charCodeAt(0); - if (res && 0x3c > res || res > 0x3f) { + if (res < 0x3c || res > 0x3f) { throw new Error('prefix must be in range 0x3c .. 0x3f'); } } From 98d7c25bd5476ae39773fee378ac48053fa98ceb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:44:30 -0700 Subject: [PATCH 17/50] fix(browser): cancel trailing debounce timeout on immediate refresh TimeBasedDebouncer can schedule a trailing setTimeout when refresh requests are throttled. If the debounce window later elapses and an immediate refresh runs, that pending timeout was left armed and could fire afterward, causing a duplicate _innerRefresh (e.g. extra live region updates for screen readers). Clear any pending timeout and reset _additionalRefreshRequested before running the immediate refresh path. Co-authored-by: Cursor --- src/browser/TimeBasedDebouncer.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 959c77a4e2..f0dd682e29 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -49,7 +49,12 @@ export class TimeBasedDebouncer implements IRenderDebouncer { // enough time to pass before refreshing again. const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { - // Enough time has lapsed since the last refresh; refresh immediately + // Enough time has elapsed since the last refresh; refresh immediately + if (this._refreshTimeoutID !== undefined) { + clearTimeout(this._refreshTimeoutID); + this._refreshTimeoutID = undefined; + this._additionalRefreshRequested = false; + } this._lastRefreshMs = refreshRequestTime; this._innerRefresh(); } else if (!this._additionalRefreshRequested) { From 69b11a1a48f721c30ba9a79a324a6355212cf681 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:00:22 -0700 Subject: [PATCH 18/50] fix(browser): treat rAF id 0 as scheduled in RenderDebouncer dispose(), addRefreshCallback(), and refresh() used truthiness on _animationFrame, so a requestAnimationFrame handle of 0 would not be cancelled or deduplicated. Compare against undefined instead, matching other timer checks in the browser layer. Co-authored-by: Cursor --- src/browser/RenderDebouncer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index f6ea59bf65..fcfdaa5eca 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -23,7 +23,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { } public dispose(): void { - if (this._animationFrame) { + if (this._animationFrame !== undefined) { this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame); this._animationFrame = undefined; } @@ -31,7 +31,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { public addRefreshCallback(callback: FrameRequestCallback): number { this._refreshCallbacks.push(callback); - if (!this._animationFrame) { + if (this._animationFrame === undefined) { this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); } return this._animationFrame; @@ -46,7 +46,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - if (this._animationFrame) { + if (this._animationFrame !== undefined) { return; } From 80468eeb4cbf1269c8c7fe0d858a9c9c4bfcfbbc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:04:23 -0700 Subject: [PATCH 19/50] fix(browser): clear idle blink timer when handle is 0 _clearIdleTimer() used truthiness on _idleTimeout, so a setTimeout handle of 0 would not be cleared. Compare against undefined, consistent with RenderDebouncer rAF handling. Co-authored-by: Cursor --- src/browser/renderer/dom/DomRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index de08ac0ac4..383cf30252 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -694,7 +694,7 @@ class CursorBlinkStateManager { } private _clearIdleTimer(): void { - if (this._idleTimeout) { + if (this._idleTimeout !== undefined) { this._coreBrowserService.window.clearTimeout(this._idleTimeout); this._idleTimeout = undefined; } From bc26cc16c1ad1fe7f66db302b93008825262c550 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:14:29 -0700 Subject: [PATCH 20/50] fix(browser): place imports before module const in TimeBasedDebouncer RENDER_DEBOUNCE_THRESHOLD_MS was declared above the import, which breaks ES module conventions and can confuse tooling. Move the import to the top of the module after the license header. Co-authored-by: Cursor --- src/browser/TimeBasedDebouncer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index f0dd682e29..27b692bae8 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -3,10 +3,10 @@ * @license MIT */ -const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second - import { IRenderDebouncer } from './Types'; +const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second + /** * Debounces calls to update screen readers to update at most once configurable interval of time. */ From bc42be20740bcbded0771ebe0acfbcfe6c686652 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:37:23 -0700 Subject: [PATCH 21/50] docs(common): correct ST sequence metadata and comment typos - EscapeSequenceParser: ESC ST is ESC backslash (0x1B 0x5C), not ESC plus a double-quote character; align @vt metadata with C1 ST on the following line. - Color.contrastRatio: document l2 as the second luminance argument. - Marker.dispose: fix "change" -> "chance" in dispose listener comment. - Buffer.clear: fix possessive "it's" -> "its" in JSDoc. Co-authored-by: Cursor --- src/common/Color.ts | 2 +- src/common/buffer/Buffer.ts | 2 +- src/common/buffer/Marker.ts | 2 +- src/common/parser/EscapeSequenceParser.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/Color.ts b/src/common/Color.ts index d416d670e4..b8a9d8d60e 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -373,7 +373,7 @@ export function toPaddedHex(c: number): string { /** * Gets the contrast ratio between two relative luminance values. * @param l1 The first relative luminance. - * @param l2 The first relative luminance. + * @param l2 The second relative luminance. * @see https://www.w3.org/TR/WCAG20/#contrast-ratiodef */ export function contrastRatio(l1: number, l2: number): number { diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index be3df09b1f..f41b9da67f 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -142,7 +142,7 @@ export class Buffer extends Disposable implements IBuffer { } /** - * Clears the buffer to it's initial state, discarding all previous data. + * Clears the buffer to its initial state, discarding all previous data. */ public clear(): void { this._stringCache.clear(); diff --git a/src/common/buffer/Marker.ts b/src/common/buffer/Marker.ts index ed5124adde..c21575919c 100644 --- a/src/common/buffer/Marker.ts +++ b/src/common/buffer/Marker.ts @@ -30,7 +30,7 @@ export class Marker implements IMarker { } this.isDisposed = true; this.line = -1; - // Emit before super.dispose such that dispose listeners get a change to react + // Emit before super.dispose such that dispose listeners get a chance to react this._onDispose.fire(); dispose(this._disposables); this._disposables.length = 0; diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index a0c97b6104..868ab574fe 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -18,7 +18,7 @@ import { ApcParser } from './ApcParser'; // @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." // @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." // @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." -// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." +// @vt: #Y ESC ST "String Terminator" "ESC \\" "Terminator used for string type sequences." // @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." // @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." // @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." From b45167e00b8656917789e27404cefc98fd5a5f7f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:38:08 -0700 Subject: [PATCH 22/50] docs: fix comment typos in ThemeService and Constants Co-authored-by: Cursor --- src/browser/services/ThemeService.ts | 2 +- src/common/buffer/Constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/services/ThemeService.ts b/src/browser/services/ThemeService.ts index 9b39b99adf..e12e5e2b32 100644 --- a/src/browser/services/ThemeService.ts +++ b/src/browser/services/ThemeService.ts @@ -132,7 +132,7 @@ export class ThemeService extends Disposable implements IThemeService { colors.ansi[i + 16] = parseColor(theme.extendedAnsi[i], DEFAULT_ANSI_COLORS[i + 16]); } } - // Clear our the cache + // Clear the cache this._contrastCache.clear(); this._halfContrastCache.clear(); this._updateRestoreColors(); diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts index 5ce075cf78..592b5258ba 100644 --- a/src/common/buffer/Constants.ts +++ b/src/common/buffer/Constants.ts @@ -24,7 +24,7 @@ export const NULL_CELL_CODE = 0; /** * Whitespace cell. * This is meant as a replacement for empty cells when needed - * during rendering lines to preserve correct aligment. + * during rendering lines to preserve correct alignment. */ export const WHITESPACE_CELL_CHAR = ' '; export const WHITESPACE_CELL_WIDTH = 1; From 7b05c8a1b2d14645347f4dbda2878a8e9533b309 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:51:02 -0700 Subject: [PATCH 23/50] docs: fix comment and JSDoc spelling typos Correct "occurred" in IMouseEvent and keydown JSDoc, remove duplicate "This" in EscapeSequenceParser ZDM comment, use "elapsed" instead of "lapsed" in TimeBasedDebouncer, and fix "event" in Keyboard comment. Co-authored-by: Cursor --- src/browser/CoreBrowserTerminal.ts | 2 +- src/browser/TimeBasedDebouncer.ts | 2 +- src/common/Types.ts | 2 +- src/common/input/Keyboard.ts | 2 +- src/common/parser/EscapeSequenceParser.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index f7e20b9141..d089c62c04 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -102,7 +102,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { private _keyDownHandled: boolean = false; /** - * Records whether a keydown event has occured since the last keyup event, i.e. whether a key + * Records whether a keydown event has occurred since the last keyup event, i.e. whether a key * is currently "pressed". */ private _keyDownSeen: boolean = false; diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 0860e7927e..6c92aad24d 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -47,7 +47,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer { // enough time to pass before refreshing again. const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { - // Enough time has lapsed since the last refresh; refresh immediately + // Enough time has elapsed since the last refresh; refresh immediately this._lastRefreshMs = refreshRequestTime; this._innerRefresh(); } else if (!this._additionalRefreshRequested) { diff --git a/src/common/Types.ts b/src/common/Types.ts index 9e1481fd3c..b73fc8413b 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -181,7 +181,7 @@ export interface ICoreMouseEvent { x: number; y: number; /** - * Button the action occured. Due to restrictions of the tracking protocols + * Button the action occurred. Due to restrictions of the tracking protocols * it is not possible to report multiple buttons at once. * Wheel is treated as a button. * There are invalid combinations of buttons and actions possible diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 2c3ca28cd8..2f8de1963c 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -46,7 +46,7 @@ export function evaluateKeyboardEvent( // Whether to cancel event propagation (NOTE: this may not be needed since the event is // canceled at the end of keyDown cancel: false, - // The new key even to emit + // The new key event to emit key: undefined }; const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 868ab574fe..9fffbcb4ff 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -243,7 +243,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { * * This parser is currently hardcoded to operate in ZDM (Zero Default Mode) * as suggested by the original parser, thus empty parameters are set to 0. - * This this is not in line with the latest ECMA-48 specification + * This is not in line with the latest ECMA-48 specification * (ZDM was part of the early specs and got completely removed later on). * * Other than the original parser from vt100.net this parser supports From 3a4f9e235d739f7c821eeb3737defd65e5b14ea4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:38:39 -0700 Subject: [PATCH 24/50] docs: fix "transferred" spelling in CONTRIBUTING.md Correct typo in the note about moving support questions to GitHub discussions. Co-authored-by: Cursor --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe61ad265c..b20229f1c2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ The preferred way to report bugs or request features is to use It's pretty common for maintainers of large open source projects to suffer from burnout, especially when needing to triage a large number of incoming issues instead of actually building things. Here are some of the steps we take to try mitigate this: -- Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transfered there without further comment and core maintainers may or may not participate in discussions. +- Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transferred there without further comment and core maintainers may or may not participate in discussions. - Issues are strictly for well defined features or bugs that are _actionable_. - Sometimes features are out of scope. A common example of this is a niche feature that the pricipal implementation ([VS Code](https://code.visualstudio.com/)) won't leverage and therefore would be difficult to maintain and likely suffer from bitrot. The reporter may not agree with this, but you could always create an addon if that works or maintain your own fork if it comes to that. - If a feature does not have a clear way forward or needs more discussion it may be closed ro moved to a discussion. From e12f007597c8c5d288bb39817b8967208a18009f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:53:21 -0700 Subject: [PATCH 25/50] docs(buffer): fix Content enum names in Constants JSDoc The Content bitmask JSDoc examples used camelCase names that do not exist on the enum (codepointMask, isCombined, hasContent, widthMask, widthShift). Update examples to match the real members: CODEPOINT_MASK, IS_COMBINED_MASK, HAS_CONTENT_MASK, WIDTH_MASK, and WIDTH_SHIFT as used throughout BufferLine and CellData. Co-authored-by: Cursor --- src/common/buffer/Constants.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts index 592b5258ba..c02dba50d9 100644 --- a/src/common/buffer/Constants.ts +++ b/src/common/buffer/Constants.ts @@ -36,18 +36,18 @@ export const WHITESPACE_CELL_CODE = 32; export const enum Content { /** * bit 1..21 codepoint, max allowed in UTF32 is 0x10FFFF (21 bits taken) - * read: `codepoint = content & Content.codepointMask;` - * write: `content |= codepoint & Content.codepointMask;` + * read: `codepoint = content & Content.CODEPOINT_MASK;` + * write: `content |= codepoint & Content.CODEPOINT_MASK;` * shortcut if precondition `codepoint <= 0x10FFFF` is met: * `content |= codepoint;` */ CODEPOINT_MASK = 0x1FFFFF, /** - * bit 22 flag indication whether a cell contains combined content - * read: `isCombined = content & Content.isCombined;` - * set: `content |= Content.isCombined;` - * clear: `content &= ~Content.isCombined;` + * bit 22 flag indicating whether a cell contains combined content + * read: `isCombined = content & Content.IS_COMBINED_MASK;` + * set: `content |= Content.IS_COMBINED_MASK;` + * clear: `content &= ~Content.IS_COMBINED_MASK;` */ IS_COMBINED_MASK = 0x200000, // 1 << 21 @@ -55,19 +55,19 @@ export const enum Content { * bit 1..22 mask to check whether a cell contains any string data * we need to check for codepoint and isCombined bits to see * whether a cell contains anything - * read: `isEmpty = !(content & Content.hasContent)` + * read: `isEmpty = !(content & Content.HAS_CONTENT_MASK)` */ HAS_CONTENT_MASK = 0x3FFFFF, /** * bit 23..24 wcwidth value of cell, takes 2 bits (ranges from 0..2) - * read: `width = (content & Content.widthMask) >> Content.widthShift;` - * `hasWidth = content & Content.widthMask;` + * read: `width = (content & Content.WIDTH_MASK) >> Content.WIDTH_SHIFT;` + * `hasWidth = content & Content.WIDTH_MASK;` * as long as wcwidth is highest value in `content`: - * `width = content >> Content.widthShift;` - * write: `content |= (width << Content.widthShift) & Content.widthMask;` + * `width = content >> Content.WIDTH_SHIFT;` + * write: `content |= (width << Content.WIDTH_SHIFT) & Content.WIDTH_MASK;` * shortcut if precondition `0 <= width <= 3` is met: - * `content |= width << Content.widthShift;` + * `content |= width << Content.WIDTH_SHIFT;` */ WIDTH_MASK = 0xC00000, // 3 << 22 WIDTH_SHIFT = 22 From cf1484d7fd43ed32cbeacc69713a23046d523ca1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:54:19 -0700 Subject: [PATCH 26/50] docs(browser): fix incomplete and typo comments in Mouse.ts getCoords had a truncated comment ("if there are no valid") and a typo ("addition" vs "additional") in the selection precision note. Complete the char-size guard comment and fix the selection wording. Co-authored-by: Cursor --- src/browser/input/Mouse.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index 5f73743fbd..9ea1d60199 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -31,7 +31,7 @@ export function getCoordsRelativeToElement(window: Pick, event: Pick, element: HTMLElement, colCount: number, rowCount: number, hasValidCharSize: boolean, cssCellWidth: number, cssCellHeight: number, isSelection?: boolean): [number, number] | undefined { - // Coordinates cannot be measured if there are no valid + // Coordinates cannot be measured if there is no valid character size. if (!hasValidCharSize) { return undefined; } @@ -45,7 +45,7 @@ export function getCoords(window: Pick, event: Pick< coords[1] = Math.ceil(coords[1] / cssCellHeight); // Ensure coordinates are within the terminal viewport. Note that selections - // need an addition point of precision to cover the end point (as characters + // need an additional point of precision to cover the end point (as characters // cover half of one char and half of the next). coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[1] = Math.min(Math.max(coords[1], 1), rowCount); From e2d1420ff763c5aa347dc1323631991c6d78468c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:57:19 -0700 Subject: [PATCH 27/50] docs(common): fix throughput typo in WriteBuffer comment Correct "throughtput" to "throughput" in the FPS vs throughput tradeoff note inside WriteBuffer. Co-authored-by: Cursor --- src/common/input/WriteBuffer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 85774363ba..0b4619690a 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -267,7 +267,7 @@ export class WriteBuffer extends Disposable { * this condition here, also the renderer has no way to spot nonsense updates either. * FIXME: A proper fix for this would track the FPS at the renderer entry level separately. * - * If favoring of FPS shows bad throughtput impact, use the following instead. It favors + * If favoring of FPS shows bad throughput impact, use the following instead. It favors * throughput by eval'ing `startTime` upfront pulling at least one more chunk into the * current microtask queue (executed before setTimeout). */ From 5ef80014139c726ad661f57ec59ae1ab970f3860 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:58:21 -0700 Subject: [PATCH 28/50] docs(common): fix activeVersion onChange JSDoc wording The onChange event comment said "activate version changed" while the property is activeVersion. Clarify that the event fires when the active Unicode version changes. Co-authored-by: Cursor --- src/common/services/Services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index cd1bc9cd4f..32e67f1813 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -350,7 +350,7 @@ export interface IUnicodeService { readonly versions: string[]; /** Currently active version. */ activeVersion: string; - /** Event triggered, when activate version changed. */ + /** Event triggered when the active version changes. */ readonly onChange: IEvent; /** From fd13893687acd77f4acd0d6179ad8de39ebb5a8b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:59:22 -0700 Subject: [PATCH 29/50] docs(buffer): fix possessive typo in Windows conpty comment Change "it's view" to "its view" in the comment explaining why new rows are appended on Windows when using conpty. Co-authored-by: Cursor --- src/common/buffer/Buffer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index f41b9da67f..c7d094a43c 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -197,7 +197,7 @@ export class Buffer extends Disposable implements IBuffer { for (let y = this._rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { if (this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) { - // Just add the new missing rows on Windows as conpty reprints the screen with it's + // Just add the new missing rows on Windows as conpty reprints the screen with its // view of the world. Once a line enters scrollback for conpty it remains there this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } else { From 26e771d0756dcd15f9498031e7aad862b691e751 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:09:34 -0700 Subject: [PATCH 30/50] docs(common): fix grammar in Unicode and platform comments Use "a Unicode" instead of "an Unicode" on IUnicodeService.register, and "user's platform" instead of "users platform" above the platform detection exports. Co-authored-by: Cursor --- src/common/Platform.ts | 2 +- src/common/services/Services.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 9ccf8bda83..a034ca69c2 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -44,7 +44,7 @@ export function getSafariVersion(): number { return parseInt(majorVersion[1], 10); } -// Find the users platform. We use this to interpret the meta key +// Find the user's platform. We use this to interpret the meta key // and ISO third level shifts. // http://stackoverflow.com/q/19877924/577598 export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 32e67f1813..8f0aeeed15 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -344,7 +344,7 @@ export type UnicodeCharWidth = 0 | 1 | 2; export const IUnicodeService = createDecorator('UnicodeService'); export interface IUnicodeService { serviceBrand: undefined; - /** Register an Unicode version provider. */ + /** Register a Unicode version provider. */ register(provider: IUnicodeVersionProvider): void; /** Registered Unicode versions. */ readonly versions: string[]; From 8854430df4c08b58a312129bf824f685c787b856 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:11:27 -0700 Subject: [PATCH 31/50] docs(parser): fix sub-parameter limit name in Params JSDoc addSubParam documents a cap on stored sub parameters; the limit is enforced via maxSubParamsLength, not subParamsLength (the current count). Co-authored-by: Cursor --- src/common/parser/Params.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/parser/Params.ts b/src/common/parser/Params.ts index 6986a48bac..76dbe2b3da 100644 --- a/src/common/parser/Params.ts +++ b/src/common/parser/Params.ts @@ -172,7 +172,7 @@ export class Params implements IParams { * Add a sub parameter value. * The sub parameter is automatically associated with the last parameter value. * Thus it is not possible to add a subparameter without any parameter added yet. - * `Params` only stores up to `subParamsLength` sub parameters, any later + * `Params` only stores up to `maxSubParamsLength` sub parameters, any later * sub parameter will be ignored. */ public addSubParam(value: number): void { From 2db34c49554bb4292bea9ea2aaec3e321316bbb4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:13:26 -0700 Subject: [PATCH 32/50] docs(common): fix main thread spelling in TaskQueue comment Change "mainthread" to "main thread" in the workload scheduling note. Co-authored-by: Cursor --- src/common/TaskQueue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index 9e15c823a5..b1bcf023a5 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -8,7 +8,7 @@ import type { ILogService } from './services/Services'; interface ITaskQueue { /** * Adds a task to the queue which will run in a future idle callback. - * To avoid perceivable stalls on the mainthread, tasks with heavy workload + * To avoid perceivable stalls on the main thread, tasks with heavy workload * should split their work into smaller pieces and return `true` to get * called again until the work is done (on falsy return value). */ From be3dd80171748116cc0fc9cfecb4df733dc8ffb8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:35:46 -0700 Subject: [PATCH 33/50] docs(parser): fix JSDoc typos in IFunctionIdentifier Correct "representation" and "registration" spelling in the comment describing how handler identifiers are encoded for EscapeSequenceParser. Co-authored-by: Cursor --- src/common/parser/Types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/parser/Types.ts b/src/common/parser/Types.ts index 7306cdf766..f7c8ae2d4b 100644 --- a/src/common/parser/Types.ts +++ b/src/common/parser/Types.ts @@ -234,8 +234,8 @@ export interface IApcParser extends ISubParser Date: Sun, 31 May 2026 20:38:51 -0700 Subject: [PATCH 34/50] docs(addons): fix comment typos in unicode-graphemes and web-links Correct grapheme/handling spelling in UnicodeGraphemesAddon header and everything/addresses typos in WebLinksAddon URL matcher comments. Co-authored-by: Cursor --- addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts | 2 +- addons/addon-web-links/src/WebLinksAddon.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts index 574a36cc8b..09a0735717 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts @@ -2,7 +2,7 @@ * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT * - * UnicodeVersionProvider for V15 with grapeme cluster handleing. + * UnicodeVersionProvider for V15 with grapheme cluster handling. */ import type { Terminal, ITerminalAddon, IUnicodeHandling } from '@xterm/xterm'; diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 3328a3c3fd..5e9f6a0ef8 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -7,13 +7,13 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; -// consider everthing starting with http:// or https:// +// consider everything starting with http:// or https:// // up to first whitespace, `"` or `'` as url // NOTE: The repeated end clause is needed to not match a dangling `:` // resembling the old (...)*([^:"\'\\s]) final path clause // additionally exclude early + final: // - unsafe from rfc3986: !*'() -// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) +// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 addresses, also allow ~) // also exclude as finals: // - final interpunction like ,.!? // - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) From a47e4905e6d2f7f8098f23da01a763e1c8500a7a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:53:52 -0700 Subject: [PATCH 35/50] docs(addon-webgl): fix comment typos in TextureAtlas and GlyphRenderer Correct "transferred", remove duplicate "is", and use "one-off" instead of "once of" in developer-facing comments. Co-authored-by: Cursor --- addons/addon-webgl/src/GlyphRenderer.ts | 2 +- addons/addon-webgl/src/TextureAtlas.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 650fc451a4..4117525c48 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -220,7 +220,7 @@ export class GlyphRenderer extends Disposable { public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { // Since this function is called for every cell (`rows*cols`), it must be very optimized. It // should not instantiate any variables unless a new glyph is drawn to the cache where the - // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for + // slight slowdown is acceptable for the developer ergonomics provided as it's a one-off for // each glyph. this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); } diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index dab0b1c85f..31db49a7fb 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -69,7 +69,7 @@ export class TextureAtlas implements ITextureAtlas { private _overflowSizePage: AtlasPage | undefined; private _tmpCanvas: HTMLCanvasElement; - // A temporary context that glyphs are drawn to before being transfered to the atlas. + // A temporary context that glyphs are drawn to before being transferred to the atlas. private _tmpCtx: CanvasRenderingContext2D; private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 }; @@ -152,7 +152,7 @@ export class TextureAtlas implements ITextureAtlas { } private _createNewPage(): AtlasPage { - // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a + // Try merge the set of the 4 most used pages of the largest size. This is deferred to a // microtask to ensure it does not interrupt textures that will be rendered in the current // animation frame which would result in blank rendered areas. This is actually not that // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. From 58fc09ff0e33017b87525b4d599767a0ef8e2594 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:05:21 -0700 Subject: [PATCH 36/50] docs(webgl): remove duplicate word in context lost README The webglcontextlost section repeated "fired" ("fires the ... event fired on the canvas"). Drop the extra word. Co-authored-by: Cursor --- addons/addon-webgl/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-webgl/README.md b/addons/addon-webgl/README.md index 9d89863f31..47a03b72ef 100644 --- a/addons/addon-webgl/README.md +++ b/addons/addon-webgl/README.md @@ -23,7 +23,7 @@ See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon- ### Handling Context Loss -The browser may drop WebGL contexts for various reasons like OOM or after the system has been suspended. There is an API exposed that fires the `webglcontextlost` event fired on the canvas so embedders can handle it however they wish. An easy, but suboptimal way, to handle this is by disposing of WebglAddon when the event fires: +The browser may drop WebGL contexts for various reasons like OOM or after the system has been suspended. There is an API exposed that fires the `webglcontextlost` event on the canvas so embedders can handle it however they wish. An easy, but suboptimal way, to handle this is by disposing of WebglAddon when the event fires: ```ts const terminal = new Terminal(); From 3494f6b5b0c31bcaf2bf52927749efe714d84952 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:02 +0000 Subject: [PATCH 37/50] docs: fix additional comment typos across renderer and selection (addons only) Co-authored-by: Daniel Imms --- addons/addon-webgl/src/WebglRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 264bfda6ee..d26c6d25ee 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -667,7 +667,7 @@ export class WebglRenderer extends Disposable implements IRenderer { // The the size of the canvas on the page. It's important that this rounds to nearest integer // and not ceils as browsers often have floating point precision issues where // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when - // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too + // it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too // large for the canvas element size. this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); From fa5eb33cdb141762042410cb80bacd8087cbea45 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:02 +0000 Subject: [PATCH 38/50] fix: small comment, error string, and guard cleanups (addons only) Co-authored-by: Daniel Imms --- addons/addon-serialize/src/SerializeAddon.ts | 2 +- addons/addon-webgl/src/TextureAtlas.ts | 2 +- addons/addon-webgl/src/WebglAddon.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 0aad8e1d6a..26ede1c418 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -173,7 +173,7 @@ class StringSerializeHandler extends BaseSerializeHandler { private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell(); private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell(); protected _rowEnd(row: number, isLastRow: boolean): void { - // if there is colorful empty cell at line end, whe must pad it back, or the the color block + // if there is colorful empty cell at line end, we must pad it back, or the color block // will missing if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { // use clear right to set background. diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 31db49a7fb..53d2d056f4 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -1125,7 +1125,7 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol // were covered (fg=#8ae234, bg=#c4a000). const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); - // Set alpha channel of relevent pixels to 0 + // Set alpha channel of relevant pixels to 0 let isEmpty = true; for (let offset = 0; offset < imageData.data.length; offset += 4) { // Check exact match diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index 624c9ea386..f61206b138 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -40,7 +40,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi }; const gl = document.createElement('canvas').getContext('webgl2', contextAttributes) as IWebGL2RenderingContext; if (!gl) { - throw new Error('Webgl2 is only supported on Safari 16 and above'); + throw new Error('WebGL2 is only supported on Safari 16 and above'); } } super(); From 1faa41eac1585260786e87cb0db2ca2aea88d620 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:03 +0000 Subject: [PATCH 39/50] docs: fix typos in typings, tests, and core comments (addons only) Co-authored-by: Daniel Imms --- addons/addon-clipboard/typings/addon-clipboard.d.ts | 2 +- addons/addon-webgl/typings/addon-webgl.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-clipboard/typings/addon-clipboard.d.ts b/addons/addon-clipboard/typings/addon-clipboard.d.ts index f8e024ac34..f5a892e215 100644 --- a/addons/addon-clipboard/typings/addon-clipboard.d.ts +++ b/addons/addon-clipboard/typings/addon-clipboard.d.ts @@ -82,7 +82,7 @@ declare module '@xterm/addon-clipboard' { /** * The clipboard provider interface that enables xterm.js to access the browser clipboard. * - * Note this this clipboard provider does not implement own cut buffers as xterm does, + * Note that this clipboard provider does not implement own cut buffers as xterm does, * but redirect the selection parameter always to navigator.clipboard. */ export class BrowserClipboardProvider implements IClipboardProvider{ diff --git a/addons/addon-webgl/typings/addon-webgl.d.ts b/addons/addon-webgl/typings/addon-webgl.d.ts index 37b6b8a953..37acbb8a91 100644 --- a/addons/addon-webgl/typings/addon-webgl.d.ts +++ b/addons/addon-webgl/typings/addon-webgl.d.ts @@ -23,12 +23,12 @@ declare module '@xterm/addon-webgl' { public readonly onChangeTextureAtlas: IEvent; /** - * An event that is fired when the a new page is added to the texture atlas. + * An event that is fired when a new page is added to the texture atlas. */ public readonly onAddTextureAtlasCanvas: IEvent; /** - * An event that is fired when the a page is removed from the texture atlas. + * An event that is fired when a page is removed from the texture atlas. */ public readonly onRemoveTextureAtlasCanvas: IEvent; From 0b590dacd64d489669eb2b73edd44d40ab72c6ce Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:04 +0000 Subject: [PATCH 40/50] docs: fix comment typos in BufferReflow, Types, and addons (addons only) Co-authored-by: Daniel Imms --- addons/addon-serialize/test/SerializeAddon.test.ts | 2 +- addons/addon-webgl/src/WebglRenderer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index fb42d807dc..c8dba3039b 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -192,7 +192,7 @@ test.describe('SerializeAddon', () => { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ - sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence + sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags at the end, serialize will use \x1b[0m to clear instead of the specific disable sequence sgr(INVERSE) + line, sgr(BOLD) + line, sgr(UNDERLINED) + line, diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index d26c6d25ee..2f38e03cb9 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -664,7 +664,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; - // The the size of the canvas on the page. It's important that this rounds to nearest integer + // The size of the canvas on the page. It's important that this rounds to nearest integer // and not ceils as browsers often have floating point precision issues where // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when // it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too From be38252e37ecfa1f86f82e8a02543d8ea2d9bed4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:04 +0000 Subject: [PATCH 41/50] docs: fix comment typos in Constants, DomRenderer, and addons (addons only) Co-authored-by: Daniel Imms --- addons/addon-ligatures/src/index.ts | 2 +- addons/addon-serialize/src/SerializeAddon.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-ligatures/src/index.ts b/addons/addon-ligatures/src/index.ts index fb8e85e739..2fc664f5ba 100644 --- a/addons/addon-ligatures/src/index.ts +++ b/addons/addon-ligatures/src/index.ts @@ -21,7 +21,7 @@ const CACHE_SIZE = 100000; /** * Enable ligature support for the provided Terminal instance. To function - * properly, this must be called after `open()` is called on the therminal. If + * properly, this must be called after `open()` is called on the terminal. If * the font currently in use supports ligatures, the terminal will automatically * start to render them. * @param term Terminal instance from xterm.js diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 26ede1c418..e1728feb21 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -221,7 +221,7 @@ class StringSerializeHandler extends BaseSerializeHandler { // you can't use control sequence to move cursor to (x === row) (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + // so we mark it as invalid to simplify the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) ) { isValid = true; @@ -233,7 +233,7 @@ class StringSerializeHandler extends BaseSerializeHandler { isNextRowFirstCharDoubleWidth && (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + // so we mark it as invalid to simplify the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) && equalBg(thisRowLastSecondChar, nextRowFirstChar) ) { From 92a1bfcf2e7f06523153a68bd2e73f7156619f7d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:06:29 -0700 Subject: [PATCH 42/50] test(search): send real CSI CUP in #3834 null-char case The integration test intended to move the cursor forward with ESC [ C but wrote the literal string \x1b[C due to double escaping. Use a single-escaped '\x1b[CHi Hi' so the terminal receives the control sequence and creates the null cell the scenario describes. Co-authored-by: Cursor --- addons/addon-search/test/SearchAddon.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-search/test/SearchAddon.test.ts b/addons/addon-search/test/SearchAddon.test.ts index a4ccb6c0ef..89f0ff4fa3 100644 --- a/addons/addon-search/test/SearchAddon.test.ts +++ b/addons/addon-search/test/SearchAddon.test.ts @@ -501,7 +501,7 @@ test.describe('Search Tests', () => { window.search.onDidChangeResults(e => window.calls.push(e)); `); // Move cursor forward 1 time to create a null character, as opposed to regular whitespace - await ctx.proxy.write('\\x1b[CHi Hi'); + await ctx.proxy.write('\x1b[CHi Hi'); strictEqual(await ctx.page.evaluate(`window.search.findPrevious('h', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); deepStrictEqual(await ctx.page.evaluate('window.calls'), [ { resultCount: 2, resultIndex: 1 } From a9a2a8e3c166a05bb13bbab0f812ca01de8f0297 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:28:51 -0700 Subject: [PATCH 43/50] test(search): reject readFile errors in #2444 fixture loader beforeAll always resolved the readFile promise with data even when err was set, which led to calling toString on undefined. Reject the promise on err so fixture load failures surface clearly. Use res/rej callback names to avoid shadowing path.resolve. Co-authored-by: Cursor --- addons/addon-search/test/SearchAddon.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-search/test/SearchAddon.test.ts b/addons/addon-search/test/SearchAddon.test.ts index 89f0ff4fa3..3438772a85 100644 --- a/addons/addon-search/test/SearchAddon.test.ts +++ b/addons/addon-search/test/SearchAddon.test.ts @@ -417,7 +417,7 @@ test.describe('Search Tests', () => { test.describe('#2444 wrapped line content not being found', () => { let fixture: string; test.beforeAll(async () => { - fixture = (await new Promise(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data)))).toString(); + fixture = (await new Promise((res, rej) => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => err ? rej(err) : res(data)))).toString(); if (process.platform !== 'win32') { fixture = fixture.replace(/\n/g, '\n\r'); } From 8e7e4f16e5cf5487afdc366762ac115e1cb90108 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:10:27 -0700 Subject: [PATCH 44/50] test(parser): fix typo in EscapeSequenceParser test title Rename the mocha case "inital states" to "initial states". Co-authored-by: Cursor --- src/common/parser/EscapeSequenceParser.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index f684da2c84..73238d0398 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -243,7 +243,7 @@ describe('EscapeSequenceParser', () => { p = new TestEscapeSequenceParser(tansitions); assert.deepEqual(p.transitions, tansitions); }); - it('inital states', () => { + it('initial states', () => { assert.equal(parser.initialState, ParserState.GROUND); assert.equal(parser.currentState, ParserState.GROUND); assert.equal(parser.osc, ''); From 9a70816dad388d7d09ab2716d28b0a190221080b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 1 Jun 2026 07:42:15 -0700 Subject: [PATCH 45/50] test(addon-attach): correct Playwright suite name in AttachAddon tests The integration test file used test.describe('Search Tests'), copied from addon-search. That mislabels attach-addon results in Playwright reports and makes the suite harder to filter or debug. Rename the block to 'AttachAddon' so the describe name matches the addon under test. No test logic changed. Validated with: npm run build && npm run esbuild, npm run test-unit, npm run esbuild-demo-client && npm run esbuild-demo-server && npm run test-integration, npm run package, npm run package-headless. Co-authored-by: Cursor --- addons/addon-attach/test/AttachAddon.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-attach/test/AttachAddon.test.ts b/addons/addon-attach/test/AttachAddon.test.ts index b49d9f3379..ee9b22c2ee 100644 --- a/addons/addon-attach/test/AttachAddon.test.ts +++ b/addons/addon-attach/test/AttachAddon.test.ts @@ -15,7 +15,7 @@ test.beforeAll(async ({ browser }) => { }); test.afterAll(async () => await ctx.page.close()); -test.describe('Search Tests', () => { +test.describe('AttachAddon', () => { test.beforeEach(async () => { await ctx.proxy.reset(); From 967d3f59e64e819637340972107e912a437a6113 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:08 +0000 Subject: [PATCH 46/50] docs(addon-image): fix README spelling and wording README spelling and wording fixes only; no code changes. Co-authored-by: Daniel Imms --- addons/addon-image/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/addon-image/README.md b/addons/addon-image/README.md index 80c88de8e0..3b636d922a 100644 --- a/addons/addon-image/README.md +++ b/addons/addon-image/README.md @@ -90,7 +90,7 @@ terminal.loadAddon(imageAddon); therefore palette animations cannot be used. - **SIXEL Raster Attributes Handling** - If raster attributes were found in the SIXEL data (level 2), the image will always be truncated to the given height/width extend. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extend. Both is discouraged by the >30 years old specification. + If raster attributes were found in the SIXEL data (level 2), the image will always be truncated to the given height/width extent. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extent. Both is discouraged by the >30 years old specification. Currently the SIXEL implementation of the addon does not take custom pixel sizes into account, a SIXEL pixel will map 1:1 to a screen pixel. @@ -149,9 +149,9 @@ The addon provides the following API endpoints to retrieve raw image data as can ### Memory Usage -The addon does most image processing in Javascript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, lets look at the data flow and processing steps: -- Incomming image data chunk at `term.write` (terminal) - `term.write` might stock up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time. +The addon does most image processing in JavaScript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, let's look at the data flow and processing steps: +- Incoming image data chunk at `term.write` (terminal) + `term.write` might stack up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time. - Sequence Parser (terminal) The parser operates on a buffer containing up to 2^17 codepoints (~0.5 MB). - Sequence Handler - Chunk Decoding (addon) @@ -177,18 +177,18 @@ const totalActive = storageBytes + decodingBytes; ``` Note that browsers have offloading tricks for rarely touched memory segments, esp. `storageBytes` might not directly translate into real memory usage. Usage peaks will happen during active decoding of multiple big images due to the need of 2 full pixel buffers at the same time, which cannot be offloaded. Thus you may want to keep an eye on `pixelLimit` under limited memory conditions. -Further note that the formulas above do not respect the Javascript object's overhead. Compared to the raw buffer needs the book keeping by these objects is rather small (<<5%). +Further note that the formulas above do not respect the JavaScript object's overhead. Compared to the raw buffer needs the bookkeeping by these objects is rather small (<<5%). _Why should I care about memory usage at all?_ -Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the Javascript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure. +Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the JavaScript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure. _How can I adjust the memory usage?_ - `pixelLimit` - A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage is only temporarily peaking, a rather high value should work even with multiple terminals in one page. + A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage only peaks temporarily, a rather high value should work even with multiple terminals in one page. - `storageLimit` - A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for memory peaking for decoding. + A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for peak memory use during decoding. - `sixelSizeLimit | iipSizeLimit | kittySizeLimit` - Constructor settings. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken image sequences at an earlier phase, thus may stop the invoked decoders from entering memory intensive tasks for potentially invalid data. + Constructor settings. This has only a small impact on peak memory during decoding. It is meant to avoid processing of overly big or broken image sequences at an earlier phase, thus may stop the invoked decoders from entering memory intensive tasks for potentially invalid data. ### Terminal Interaction From 096707d7363cb7085e568eecf96e7f2449a75381 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:18 +0000 Subject: [PATCH 47/50] test: fix typos in test titles and comments Apply test-only hunks from docs typo fixes: SharedRendererTests duplicate word, SerializeAddon workaround comment, and mouse range spelling in MouseService and InputHandler tests. Co-authored-by: Daniel Imms --- addons/addon-serialize/test/SerializeAddon.test.ts | 2 +- src/browser/services/MouseService.test.ts | 2 +- src/common/InputHandler.test.ts | 2 +- test/playwright/SharedRendererTests.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index fb42d807dc..c8dba3039b 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -192,7 +192,7 @@ test.describe('SerializeAddon', () => { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ - sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence + sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags at the end, serialize will use \x1b[0m to clear instead of the specific disable sequence sgr(INVERSE) + line, sgr(BOLD) + line, sgr(UNDERLINED) + line, diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index 6d760ff0a7..b4655a1a1d 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -176,7 +176,7 @@ describe('MouseService _triggerMouseEvent', () => { for (let i = 0; i < bufferService.cols; ++i) { assert.equal(trigger({ col: i, row: 0, x: 0, y: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true); if (i > 222) { - // supress mouse reports if we are out of addressible range (max. 222) + // suppress mouse reports if we are out of addressable range (max. 222) assert.deepEqual(toBytes(reports.pop()), []); } else { assert.deepEqual(toBytes(reports.pop()), [0x1b, 0x5b, 0x4d, 0x20, i + 33, 0x21]); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 3cfc9e019d..8f0109727e 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -1241,7 +1241,7 @@ describe('InputHandler', () => { await inputHandler.parseP('\x1b[e'); assert.deepEqual(getCursor(bufferService), [8, 5]); }); - describe('should clamp cursor into addressible range', () => { + describe('should clamp cursor into addressable range', () => { it('CUF', async () => { bufferService.buffer.x = 10000; bufferService.buffer.y = 10000; diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index db5c4c0734..1983fd4108 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -980,7 +980,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void }); test.describe('selectionInactiveBackground', async () => { - test('should render the the inactive selection when not focused', async () => { + test('should render the inactive selection when not focused', async () => { const theme: ITheme = { selectionBackground: '#FF000080', selectionInactiveBackground: '#0000FF80' From 6426573f455eba613655a641e5e90da4b3dc48e6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:30:04 +0000 Subject: [PATCH 48/50] docs(addon-web-links): shorten comment to satisfy max-len after typo fix Co-authored-by: Daniel Imms --- addons/addon-web-links/src/WebLinksAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 5e9f6a0ef8..6ea1bc8d95 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -13,7 +13,7 @@ import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; // resembling the old (...)*([^:"\'\\s]) final path clause // additionally exclude early + final: // - unsafe from rfc3986: !*'() -// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 addresses, also allow ~) +// - unsafe chars from rfc1738: {}|\^~[]` (minus [] for ipv6 addresses, also allow ~) // also exclude as finals: // - final interpunction like ,.!? // - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) From 94958c2489ec7ec9c16c1c51ecab9f4bb416dce8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 1 Jun 2026 08:33:02 -0700 Subject: [PATCH 49/50] Fix whitespace --- src/common/parser/EscapeSequenceParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 9fffbcb4ff..87844113ae 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -18,7 +18,7 @@ import { ApcParser } from './ApcParser'; // @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." // @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." // @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." -// @vt: #Y ESC ST "String Terminator" "ESC \\" "Terminator used for string type sequences." +// @vt: #Y ESC ST "String Terminator" "ESC \\" "Terminator used for string type sequences." // @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." // @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." // @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." From 454e0749c267e88ace5c3604f513cff6d9ea1589 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 16:15:01 +0000 Subject: [PATCH 50/50] fix(browser): use ??= in RenderDebouncer for lint Co-authored-by: Daniel Imms --- src/browser/RenderDebouncer.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index fcfdaa5eca..eeadbe0186 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -31,9 +31,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { public addRefreshCallback(callback: FrameRequestCallback): number { this._refreshCallbacks.push(callback); - if (this._animationFrame === undefined) { - this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); - } + this._animationFrame ??= this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); return this._animationFrame; }