Without an autocomplete hint, focusing a terminal on iOS/iPadOS Safari
pops the AutoFill accessory (passwords / credit cards / contacts) for
the hidden textarea. The sibling hints (autocorrect, autocapitalize,
spellcheck) are already set; this adds the missing one.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
TextureAtlas.clearTexture() wipes every page and both cache maps but never
bumped _pageLayoutVersion, so it silently broke the invalidation contract that
#6042 introduced for shared atlases.
The atlas is shared by every terminal with an equal config (CharAtlasCache),
but WebglRenderer.clearTextureAtlas() only clears the *calling* renderer's
model. Every other owner keeps vertex data pointing into the rows that were
just wiped, and GlyphRenderer.beginFrame() has no way to notice, so those
terminals keep drawing from stale texture coords until something else happens
to invalidate them. Once the clearing terminal rasterizes new glyphs into the
freed rows, the siblings render whatever landed there instead of their own
text.
Embedders hit this whenever they force a redraw on one terminal while others
are alive; VS Code calls Terminal.clearTextureAtlas() on OS resume, which is
why a stale terminal there stays garbled even after opening a new one (the new
terminal re-acquires the same cached atlas) and only recovers once the whole
panel is closed and the last owner drops the atlas.
Bump _pageLayoutVersion on clear so sibling renderers rebuild their models on
their next frame, matching what page merges and evictions already do.
The added test fails on master (terminal B's row renders terminal A's flood
glyphs) and passes with the fix.
* fix(addon-search): advance highlight scan by buffer match size
_highlightAllMatches() stepped to the next find() using term.length
and col + 1 when staying on the same row. Search results expose size
in buffer columns (wide glyphs, wrapped matches), which select() and
decorations already use.
Advance with col + size and wrap row/col when crossing the terminal
width so highlight-all does not skip, duplicate, or stop early on
non-ASCII matches.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(addon-search): regression for highlight-all wide char advance by size
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
* test(addon-search): move highlight-all regression to integration tests
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Upgrade actions/checkout, setup-node, upload-artifact, download-artifact,
and codeql-action to their latest major versions that run on Node 24.
Replace NathanFirmo/wait-for-other-job with native needs: build for
test-unit and test-integration jobs.
Fixes#6006
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Remove early return when ybase===0 && y===0 so clear() always
clears markers, resets buffer lines, and refills the viewport.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
* fix(selection): drag-scroll end row matches last visible line
Use ydisp + rows - 1 for selectionEnd when scrolling down during drag,
consistent with finalSelectionEnd for select-all.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
* fix(selection): reset start to origin when start row is trimmed
When handleTrim removes the selection start row, clamp to [0,0] and
refresh; do not keep a stale column on row 0.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
* 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 <cursoragent@cursor.com>
* refactor(addon-webgl): extract _nullModelCell helper
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
* fix(DomRenderer): skip missing buffer lines during row render
renderRows and link underline refresh stopped updating when any
viewport row lacked buffer data, leaving stale DOM on rows below.
Match ImageStorage.render behavior: clear the row element when
present and continue with remaining rows.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(DomRenderer): clear blink state when skipping missing lines
When a viewport row has no buffer line, renderRows clears the row DOM
and continues but did not reset _rowHasBlinkingCells for that row.
That could leave the text blink manager thinking blinking cells remain
after the row was emptied.
Clear blink state for the row when skipping a missing buffer line,
including during link underline refresh.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Character joiner scanning already iterated cells with
getTrimmedLength(), but trailing attribute runs and
_stringRangesToCellRanges used buffer cols. On short lines that
could run joiners on a single trailing cell and extend open ranges
through empty tail columns, affecting ligature/join rendering.
Use trimmed length consistently for trailing-run detection, the
cell-range walk, and the final range end column.
Co-authored-by: Cursor <cursoragent@cursor.com>
Mocha 11.7.x pulls in serialize-javascript 6.0.2, which is affected by
code injection via RegExp.flags and Date.toISOString (CVE incomplete fix).
Force the patched 7.x release via npm overrides.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
* Add InputHandler unit tests for ISO-2022 charset mapping
InputHandler unit tests always used MockCharsetService, so regressions in
print()'s ASCII remapping, G0/G1 designation (ESC ( ) * +), and SO/SI
level switching were never caught at unit level.
Add an ISO-2022 character sets suite wired to real CharsetService and
BufferService that verifies DEC line drawing on G0 (ESC ( 0), G1 after
ESC ) 0 + SO, and charset/glevel restoration via ESC 7 / ESC 8 before
printing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add BufferService.scroll unit tests for isUserScrolling
BufferService.scroll() adjusts ybase and ydisp differently depending on
whether the circular buffer is full and whether isUserScrolling is set,
but no unit test asserted those branches. InputHandler and other callers
use MockBufferService.scroll, which throws, so real scroll viewport logic
was only exercised indirectly.
Add BufferService.test.ts covering full-buffer scroll with the user
scrolled up (ydisp decrements), partial-buffer scroll while scrolled up
(ydisp unchanged), and full-buffer scroll at the viewport bottom
(ydisp tracks ybase).
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add InputHandler unit tests for DECOM cursor positioning
CUP with DEC origin mode (DECOM) offsets rows by the scroll region top
and clamps within DECSTBM margins, but unit tests only toggled the mode
for save/restore. Playwright covered the combination; headless unit tests
did not.
Add a cursor positioning test matching the Playwright DECOM case: with
margins and origin on, CUP places the cursor relative to scrollTop, clamps
to the margin, and uses absolute rows when origin is off.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add WindowsMode unit tests for wrap heuristic edges
ConPTY does not set isWrapped on soft-wrapped lines; updateWindowsModeWrappedState
infers wrapping from the last cell of the previous row. Browser Terminal tests
exercise this only through full writes, with no direct coverage of the helper
or negative cases (trailing space or null).
Add WindowsMode.test.ts asserting isWrapped is set for a full non-space line,
cleared for trailing whitespace, and cleared when the previous line ends empty.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add OscParser.reset unit test for mid-payload abort
OscParser.reset() must call end(false) on active handlers when aborted
during PAYLOAD, but no test invoked reset() directly. EscapeSequenceParser
tests mock OSC, so production reset/cleanup was unverified at unit level.
Add a reset describe block asserting handlers receive END false after a
partial payload, then a subsequent OSC sequence completes normally.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add DcsParser.reset unit test for mid-payload abort
DcsParser.reset() must call unhook(false) on hooked handlers when aborted
mid-DCS, matching OscParser's end(false) cleanup. No DcsParser test called
reset() directly, leaving the abort path unverified at unit level.
Add a reset describe block asserting UNHOOK false after partial put, then a
subsequent DCS sequence completes with UNHOOK true.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add InputHandler print tests for grapheme joining
print() uses UnicodeService.shouldJoin to merge combining marks, but
InputHandler tests never exercised that path. The U+200B infinite-loop
regression test used mocks and had no assertions, so it would pass even
if print() misbehaved.
Add tests for e + combining acute in one parse and across two parses.
Strengthen the U+200B regression test to use BufferService and assert the
buffer does not grow spurious rows.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add CharsetService unit tests for glevel and designation
CharsetService only updates the active charset when setgCharset targets the
current glevel; most InputHandler tests use MockCharsetService instead. A
regression in setgLevel or inactive-slot designation would not be caught
without direct service tests.
Add CharsetService.test.ts covering inactive designation, setgLevel
activation, active-slot updates, and reset clearing state.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add reflowCursorLine unit tests for ConPTY resize behavior
reflowCursorLine lets resize reflow wrapped lines that contain the cursor
(needed when ConPTY does not). No test referenced the option or
reflowLargerGetLinesToRemove's cursor skip branch.
Add BufferReflow tests for skip vs reflow when the cursor is inside a
wrapped block, and Buffer.resize tests showing merge on widen only when
reflowCursorLine is enabled.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add EscapeSequenceParser reset test for async pause recovery
reset() sets ParserStackType.RESET so the next parse continues in GROUND
at chunkPos + 1 without resuming a paused async handler. Existing async
tests only verified reset lifts FAIL after improper continuation, not
mid-chunk recovery with trailing printable data.
Add a test that pauses on async CSI, calls reset(), then continues parsing
and asserts XY is printed once and the stack returns to NONE.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add Buffer.resize unit test for ConPTY row growth
When rows increase on Windows ConPTY, Buffer.resize appends lines without
decrementing ybase or ydisp so the viewport stays stable while ConPTY
repaints. Buffer tests covered the non-Windows scroll-up path but never
passed windowsPty into options.
Add a ConPTY row-growth test asserting ybase, ydisp, and line count when
windowsPty is configured.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add Buffer reflow test for ConPTY buildNumber threshold
Buffer disables reflow on ConPTY when buildNumber is below 21376, but no
unit test asserted that gate. Column shrink without reflow leaves wrapped
content on a single line instead of splitting across rows.
Add a test comparing shrink behavior at build 21375 versus 21376, plus
retain unwrap-on-widen coverage for supported ConPTY builds.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add ApcParser.reset unit test for mid-payload abort
ApcParser.reset() calls end(false) on active handlers when aborted mid-APC,
matching OscParser and DcsParser, but no ApcParser test invoked reset()
directly.
Add a reset describe block asserting END false after partial put and a
clean subsequent APC sequence.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add InputHandler unit test for REP with grapheme clusters
repeatPrecedingCharacter uses parser.precedingJoinState and reprints the
full cluster from the buffer, but only Playwright covered e + combining
acute with CSI b. A regression could break REP for joined sequences without
failing unit tests.
Add a print-suite test asserting CSI 2 b triples e + acute in one parse.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add InputHandler unit tests for REP no-op cases
repeatPrecedingCharacter returns early when parser.precedingJoinState is
zero, including after another escape sequence clears join state. Only the
positive grapheme REP case was unit tested.
Add tests for bare CSI b and for REP following SGR so regressions cannot
spuriously duplicate buffer content.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add DecorationManager unit test for wrapped search matches
DecorationManager splits matches across wrapped rows with per-segment x
and width, but only SearchEngine had unit coverage; decoration geometry
was left to Playwright.
Add DecorationManager.test.ts using a real Terminal, a wrapped-buffer
match from SearchEngine, and assertions on registerDecoration options
for both row segments.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add BufferService.scroll unit test for DECSTBM margins
When scrollTop is non-zero, scroll() shifts lines inside the margin with
shiftElements instead of pushing scrollback. Existing BufferService tests
only covered scrollTop === 0.
Add a test with margins on rows 1–3 that verifies rows outside the region
stay fixed and lines inside the region scroll correctly.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add BufferService.scrollLines unit tests
scrollLines adjusts ydisp and toggles isUserScrolling for viewport
scrolling, but only scroll() had unit coverage. Regressions in the
user-scroll flag or ydisp clamping would affect the whole viewport.
Add tests for scrolling up, clamping at ydisp 0, and clearing
isUserScrolling when reaching the buffer bottom.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add DecorationManager overview ruler dedup unit test
Multiple highlight matches on the same buffer line should only register
one overview ruler marker; later matches on that line omit ruler options.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Assert overview ruler on each line of wrapped search match
Wrapped highlight decorations span two buffer lines; each segment
should register overview ruler options unlike same-line dedup.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(buffer): bound combined-cell copy in copyCellsFrom
When copying a column range, combined-string metadata was copied for
every source column >= srcCol with no upper limit. Partial copies could
pull _combined entries from outside [srcCol, srcCol + length) into the
destination and corrupt cells beyond the copied span.
Only copy combined keys within the same range as the cell data loop.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(buffer): clear stale _combined entry in BufferLine.setCell
setCell updated combined data when IS_COMBINED_MASK was set but left
orphaned _combined[index] entries when writing a non-combined cell.
Delete the map entry in that case so later combined copies and lookups
stay aligned with cell content flags.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(buffer): clear stale extended attrs in BufferLine.setCell
setCell stored extended attributes when HAS_EXTENDED was set but did
not remove orphaned _extendedAttrs[index] when overwriting with a
cell without extended background data. Delete the entry in that case,
matching the combined-cell cleanup added for setCell earlier.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(buffer): clear dest extended attrs in copyCellsFrom
When copying a column range, extended attributes were copied from
source cells with HAS_EXTENDED but stale _extendedAttrs on the
destination were left for cells without extended data. Delete dest
entries in that case, matching setCell behavior and the bounded
_combined copy loop.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(buffer): clear extended attrs in setCellFromCodepoint
The input-handler fast path set extended data when HAS_EXTENDED was
present but left orphaned _extendedAttrs entries when overwriting
with non-extended cells. Delete the map entry in that case, matching
setCell and copyCellsFrom.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(buffer): clear stale combined data in setCellFromCodepoint
When the input handler overwrites a cell that previously held a
grapheme cluster (IS_COMBINED_MASK), setCellFromCodepoint updated
_data and _extendedAttrs but left this._combined[index] intact.
setCell already deletes _combined for non-combined cells; this path
did not, so getString and renderers could show the old cluster string
after a normal codepoint write.
Add delete this._combined[index] at the start of setCellFromCodepoint,
matching setCell's non-combined branch and the extended-attrs cleanup
already performed in the same method.
Co-authored-by: Cursor <cursoragent@cursor.com>
* perf(buffer): only delete stale combined/extended map entries when present
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
* fix(buffer): treat combined/extended maps as flag-gated caches
_flags in _data are authoritative; sparse maps are only read when
IS_COMBINED_MASK or HAS_EXTENDED is set. setCellFromCodepoint no longer
touches _combined. loadCell clears CellData side fields when flags are off.
copyCellsFrom copies map entries per-cell from src flags only.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
PR #5966 switched benchmark imports to ../../src/ paths that do not
resolve at runtime. Restore common/* and browser/* imports (with
benchmark-only tsconfig paths for typechecking) and NODE_PATH=./out
on benchmark npm scripts so emitted requires resolve against out/.
Fixes#5970
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary
Improves reliability and correctness of `bin/` maintenance scripts used for local development and CI.
## Behavioral fixes
- **Exit codes**: `test_integration.js` exits with code 1 when `--suite` names an unknown suite; `test_unit.js` uses a non-zero fallback (`-1`) when the coverage or mocha child process has no status; `esbuild_all.mjs` exits non-zero if any parallel esbuild child fails.
- **Repo root**: `setup-full.mjs` resolves the repository root one directory above `bin/` (was incorrectly using `bin/` as root).
- **esbuild_all**: Propagates child build failures instead of always exiting 0.
- **Integration suites**: When `--suite` is omitted, all configured Playwright suites (core + addons) run; when provided, only the matching suite runs.
- **test_unit.js**: Declares the `files` filter variable so file arguments from `npm run test-unit` work without a ReferenceError.
- **test_mousemodes.js**: Uses the correct cursor-home CSI (`ESC [ H`) when clearing the menu screen.
- **Cleanup**: Removes debug logging from the test-unit coverage path; adds a missing semicolon after the suite regex match.
## Affected scripts
- `bin/test_integration.js`
- `bin/test_unit.js`
- `bin/esbuild_all.mjs`
- `bin/setup-full.mjs`
- `bin/test_mousemodes.js`
## Testing
- Syntax-checked affected scripts with `node --check`
- Verified `node bin/test_integration.js --suite=unknown` exits 1 with an error message
- `npm run lint-changes` (no TypeScript files changed)
<!-- CURSOR_AGENT_PR_BODY_END -->
<div><a href="https://cursor.com/agents/bc-94b28f5d-740c-4cb6-8cd9-40f35bc6086c"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-94b28f5d-740c-4cb6-8cd9-40f35bc6086c"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
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 <Tyriar@users.noreply.github.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
The webglcontextlost section repeated "fired" ("fires the ... event
fired on the canvas"). Drop the extra word.
Co-authored-by: Cursor <cursoragent@cursor.com>
Correct "transferred", remove duplicate "is", and use "one-off"
instead of "once of" in developer-facing comments.
Co-authored-by: Cursor <cursoragent@cursor.com>
Correct "representation" and "registration" spelling in the comment
describing how handler identifiers are encoded for EscapeSequenceParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
addSubParam documents a cap on stored sub parameters; the limit is
enforced via maxSubParamsLength, not subParamsLength (the current count).
Co-authored-by: Cursor <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
- 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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
_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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
LinkComputer rebuilt the search RegExp with (regex.flags || '') + 'g',
which throws if the caller already passed a global regex. Only append
g when it is not already present in regex.flags.
Co-authored-by: Cursor <cursoragent@cursor.com>
_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 <cursoragent@cursor.com>
The merging-pages index sort used (a, b) => a > b ? 1 : -1, which
returns -1 when a === b and violates the comparator contract. Use
(a, b) => a - b for ascending numeric order.
Co-authored-by: Cursor <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
Valid column indices are 0 through cols - 1. The previous check used
startCol > cols, so on an 80-column terminal column 80 was accepted while
column 100 correctly threw. Use >= so the out-of-range guard matches the
documented error for invalid search positions.
Co-authored-by: Cursor <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
_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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
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 <cursoragent@cursor.com>
parseInt without a radix can mis-parse leading-zero padding values as
octal. Match AccessibilityManager and document rowCount JSDoc typo.
Co-authored-by: Cursor <cursoragent@cursor.com>
The import/no-cycle refactor had moved IInsertEvent, IDeleteEvent, and
ICircularList into Types.ts, with CircularList.ts re-exporting the event
types from there. That split the list abstraction from its implementation
and pulled Types into the circular-list dependency graph.
Define IInsertEvent, IDeleteEvent, and ICircularList in CircularList.ts
alongside the CircularList class. CircularList.ts now depends only on
Lifecycle and Event, not Types.ts.
Remove those definitions from Types.ts (and the unused Emitter import).
Update callers to import list types from CircularList:
- buffer/Types.ts (IBuffer.lines)
- DecorationService.ts (buffer line insert/delete handlers)
- browser TestUtils.test.ts (mock buffer lines)
Types.ts no longer imports or re-exports circular-list types, preserving
the acyclic Types -> buffer/CircularList boundary.
Co-authored-by: Cursor <cursoragent@cursor.com>
The import/no-cycle refactor had moved IBuffer, IBufferSet, and related
cell/line types into src/common/Types.ts, with buffer/Types.ts only
re-exporting them. That inverted the natural module boundary and made
buffer code depend on the top-level types hub.
Define buffer-owned types in buffer/Types.ts instead:
- CharData, IExtendedAttrs, IAttributeData, ICellData, IBufferLine
- IMarker, IBuffer, IBufferSet, BufferIndex
Restore UnderlineStyle to buffer/Constants.ts (used by IExtendedAttrs)
and remove those definitions from Types.ts.
buffer/Types.ts imports only what it needs from outside the buffer layer:
ICircularList and ICharset from Types, IDisposable from Lifecycle,
IEvent from Event, and UnderlineStyle from Constants. Types.ts does not
import buffer/Types.ts, so the previous Types <-> buffer cycle stays broken.
Update imports across common, browser, headless, addons, and tests to use
common/buffer/Types for buffer types rather than common/Types.
Co-authored-by: Cursor <cursoragent@cursor.com>
Enable oxlint's import/no-cycle rule (import plugin) so circular module
dependencies are caught in CI, similar to eslint-plugin-import/no-cycle
and biome no-import-cycles discussed in the issue.
Relocate shared types and dependencies to remove cycles under src/common:
- Types.ts: host IInsertEvent, IDeleteEvent, IParams, UnderlineStyle,
IBuffer, and IBufferSet; drop imports from CircularList, buffer/Types,
parser/Types, and Services
- CoreTerminal.ts: define and export ICoreTerminal
- ServiceRegistry.ts: define IServiceIdentifier (break Services registry loop)
- UnicodeService: stop constructing UnicodeV6; CoreTerminal registers the
default v6 provider after service creation
- buffer/Types.ts and parser/Types.ts: re-export moved types for callers
Fixes https://github.com/xtermjs/xterm.js/issues/5897
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the primary lint pipeline with oxlint and oxlint-tsgolint for type-aware TypeScript rules, preserving existing rule behavior from eslint.config.mjs via @oxlint/migrate and targeted jsPlugins (@stylistic/eslint-plugin, eslint-plugin-jsdoc as jsdoc-js, oxlint-plugin-eslint for no-restricted-syntax and object-curly-spacing).
Keep a hybrid ESLint pass (eslint.config.naming.mjs) only for @typescript-eslint/naming-convention until tsgolint implements it (oxc-project/tsgolint#186). Legacy eslint.config.mjs and eslint.config.typings.mjs remain for reference but are no longer used by npm scripts.
Add .oxlintrc.json for src/addons/demo/test and .oxlintrc.typings.json for typings/ (syntax-only, no --type-aware). Update lint, lint-fix, lint-api, and lint-changes to run oxlint first, then naming ESLint. Platform binaries install via oxlint-tsgolint optionalDependencies on Linux, macOS, and Windows.
Fixes https://github.com/xtermjs/xterm.js/issues/5690
Co-authored-by: Cursor <cursoragent@cursor.com>
Follow-up to https://github.com/xtermjs/xterm.js/issues/5896 / PR #5966.
Converting addons to ../../../src/common relative imports broke unit tests:
esbuild emits addon code under addons/*/out-esbuild/ without mirroring the
shared tree, so Node could not load TypeScript sources at runtime
(Cannot find module '../../../src/common/Lifecycle').
Core src/, test/, and headless continue to use relative imports with no
common/* path mappings. Addons keep the prior model until build layout
can share out-esbuild the same way as the main package.
Changes:
- Restore common/* and browser/* paths in addon src/test/benchmark tsconfigs
- Revert addon imports to common/ and browser/ path-mapped specifiers
- Restore NODE_PATH=out-esbuild in bin/test_unit.js for Mocha resolution
- Restore webpack resolve.alias on addons that bundle against out/
Fixes CI test-unit failures on remove-path-mappings-5896.
Co-authored-by: Cursor <cursoragent@cursor.com>
Fixes https://github.com/xtermjs/xterm.js/issues/5896
TypeScript path mappings for common/*, browser/*, and headless/*
were not rewritten in emitted JavaScript, which forced webpack resolve
aliases and NODE_PATH workarounds so runtime could resolve modules.
That added build complexity and broke tooling such as ESLint import/no-cycle.
Changes:
- Convert ~750 imports across src/, addons/, and test/ to relative paths
- Remove common/browser/headless paths and src catch-all mappings from tsconfigs
(keep @xterm/addon-* demo and addon typings paths)
- Remove webpack resolve.alias for common, browser, headless, and vs
- Drop NODE_PATH from unit tests, benchmarks, and VS Code launch config
- Fix addon-image IIPImageStorage to import ./Types after removing catch-all *
Verified: npm run build, esbuild, package, package-headless
Co-authored-by: Cursor <cursoragent@cursor.com>
Add development environment guidance for cloud agents including:
- Demo server startup notes
- Build-before-test requirements
- Note about no external services needed
- tsgo compiler note
- Lint optimization tip
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Remove unnecessary unawaited setViewportSize from ProgressAddon tests,
which was the line reported in flaky CI failures. Use serial mode and
scoped hooks per Playwright shared-page guidance, and guard afterAll
teardown when setup fails.
Fixes#5956
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Extract AltMouseCursorController to bind DOM listeners only when the option and mouse protocol are active.
Co-authored-by: Cursor <cursoragent@cursor.com>
Alt is only used locally to gate passthrough; applications like tmux ignore alt-modified SGR sequences.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the alt-buffer guard in headless registerMarker so behavior
matches the browser terminal. Update headless public API and typings
to return IMarker instead of IMarker | undefined.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
When enabled during mouse events mode, click/drag/move events require the alt
key while wheel events are unaffected. Selection stays enabled by default
and takes precedence over macOptionClickForcesSelection.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Avoid per-call allocation when translating many lines in sequence by
resetting a module-level work StringBuilder before and after each use.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Avoid calling _getCurrentLinkId() twice per codepoint when a hyperlink
is active by reading the link id once before the print loop.
Fixes#5940
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Guard _innerWrite and public write methods after disposal so pending
setTimeout continuations cannot run against a disposed terminal.
Fixes#5936
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Replace existing.push(...bucket) with an indexed loop in
_mergeLineBucket to avoid intermediate array allocations during
buffer trim/insert/delete re-indexing.
Reuse _mergeLineBucket in _handleBufferLinesTrim for consistency.
Fixes#5941
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Align headless Terminal core API with browser CoreBrowserTerminal
and the public registerMarker naming.
Fixes#5938
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Replace map().reduce() with one accumulator loop in
reflowSmallerGetNewLineLengths to avoid allocating an intermediate
array when summing wrapped line trimmed lengths.
Fixes#5942
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
The constructor defined property getters/setters twice on _publicOptions;
the first block was immediately overwritten by the second. Align with the
browser public Terminal implementation.
Fixes#5935
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
The windowsPty option change handler checked buildNumber twice
instead of requiring both backend and buildNumber to be defined.
This aligns with Buffer.ts and ensures ConPTY wrapping heuristics
are evaluated only when both fields are present.
Fixes#5934
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Replace O(n²) string concatenation in the translate loop with
StringBuilder.append, matching the pattern used elsewhere in the project.
Fixes#5939
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Replace PayloadStringBuffer with LimitedStringBuilder from common.
Add thorough StringBuilder and LimitedStringBuilder test coverage.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Remove common/Clone.ts and its tests. Use the native structuredClone API
for cloning default modes, options, and test fixtures.
Fixes#5911
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Replace direct console.log/warn/error calls in the WebGL addon with
ILogService so messages respect the terminal logLevel and custom logger.
Fixesxtermjs/xterm.js#5921
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
configEquals omitted deviceMaxTextureSize, deviceCellWidth, and
deviceCellHeight, which could cause the char atlas cache to reuse an
atlas built for a different GPU texture limit or cell size.
Fixes#5915
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Register vertex array objects for disposal in GlyphRenderer and
RectangleRenderer, matching existing buffer and program cleanup.
Fixes#5918
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Replace repeated string concatenation with a chunk buffer that joins
once on completion. Fixes#5916.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
flushSync() processes pending writes synchronously (e.g. during
CoreTerminal.resize()) but did not emit onWriteParsed, unlike the
async _innerWrite path. Addons listening for parse completion could
miss updates after resize.
Fixes#5912
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Headless clear() now calls buffer.clearAllMarkers() to match the browser
implementation, preventing markers from surviving clear() and pointing at
wrong lines.
Fixes#5910
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Dispose _onRemoveTextureAtlasCanvas in TextureAtlas.dispose() alongside
_onAddTextureAtlasCanvas to avoid leaking listeners when the atlas is torn down.
Fixes#5917
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Register _trimListener via _register(MutableDisposable) so it is disposed
when SelectionService is torn down. Use .value assignment on buffer
activate instead of manual dispose/swap.
Fixes#5914
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Fixes#5913. Blinking is supported in renderers via TextBlinkStateManager,
and bright background colors (100-107) are handled in the SGR loop.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Wrap the onBufferActivate subscription in this._register so it is
disposed when BufferNamespaceApi is disposed, matching the pattern used
elsewhere in the codebase.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Document multi-variant escape handlers with separate /** ... */ lines
instead of multiline blocks.
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Replace multiline-comment-style (starred-block), which also forces
multiline // comments into block comments, with jsdoc/multiline-blocks.
That rule only enforces starred JSDoc layout for /** blocks (no text on
the opening line) and leaves multiline // comments unchanged for
internal documentation.
Fix multiline JSDoc in Types.ts.
Fixes#5899
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Enable multiline-comment-style with starred-block to require JSDoc-style
block comments with content on separate lines from the opening delimiter.
Auto-fix existing violations across the codebase.
Fixes#5899
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Enable @stylistic/space-unary-ops to warn on spaces after unary
operators like ! in conditions. Fix existing violation in SixelHandler.
Fixes#5898
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
Introduce MicrotaskTimer as a TimeoutTimer-style helper that schedules a
single runner via queueMicrotask, with set and cancel but no cancelAndSet.
Use it in DecorationLineCache to batch insert/delete line-index updates after
marker line adjustments, replacing ad-hoc queueMicrotask scheduling.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract the per-line lookup index from DecorationService into an exported
DecorationLineCache class in the same module, with DecorationService listed
first for readability.
Optimize buffer insert/delete maintenance to shift map keys in O(unique
indexed lines) instead of full re-indexing every affected decoration. Only
multi-line decorations that span the mutation point get a full re-index.
Add a direct DecorationLineCache unit test in DecorationService.test.ts.
Co-authored-by: Cursor <cursoragent@cursor.com>
Refs #5176.
forEachDecorationAtCell and getDecorationsAtCell previously scanned every
registered decoration on each call. With thousands of search highlights this
becomes O(total decorations) per cell and blocks rendering during viewport
refreshes.
Maintain a Map of buffer line -> decorations that cover that line. Multi-line
decorations are indexed on every line in their span, so mixed single-line and
multi-line workloads stay fast without falling back to a full scan.
Keep the index aligned with buffer mutations:
- Register and unregister decorations in the line buckets on add/dispose.
- On trim, shift line keys synchronously (before marker handlers run).
- On insert/delete, re-index affected decorations in a microtask after marker
line updates are applied.
Inject IBufferService so line listeners follow the active buffer. Add unit
tests for trim, dispose-on-trim, insert, and dense single-line plus multi-line
lookup. Add DecorationService.benchmark.ts to measure sparse hits and
viewport-sized grid scans with 20k decorations.
Co-authored-by: Cursor <cursoragent@cursor.com>
Per kovidgoyal/kitty#9999, enabling "Report event types" without
"Disambiguate escape codes" doesn't really make sense, and terminals may
choose whatever behavior they think makes sense. However, keys shouldn't
get swallowed (which, after #5791, happens when the !useCsiU branch
cannot handle the event), so instead make "Report event types" imply
"Disambiguate escape codes".
According to spec:
- The Enter, Tab and Backspace keys will not have release events unless
"Report all keys as escape codes" is also set.
- Key events that result in text are reported as plain UTF-8 text, so
events are not supported for them, unless the application requests
"key report" mode.
I checked kitty's behavior. While it does report release events for keys
that result in text as CSI sequences, press and repeat events for these
are reported as plain UTF-8 text. This commit changes Xterm.js to match
this behavior.
Add a parallel array container for execute handlers (codes < 0x18)
alongside the existing object. Setters populate both, the EXE
fast-path reads from the array with nullish coalescing to avoid
branching. ExecuteHandlerType updated to accept optional ident param
to allow the ?? pattern with the fallback handler.
Replaces 10 separate reset()+addParam(0) pairs across the parser with
a single resetZdm() method that inlines both operations, avoiding two
calls to a foreign object per sequence reset.
Remove intermediate byte handling from the fast-path, bail to table
lookup instead. Remove redundant per-step length checks by requiring
i + 2 < length upfront and letting the for-loop guard the rest.
Replace three !== checks (OSC_STRING, DCS_PASSTHROUGH, APC_STRING)
with one < OSC_STRING comparison. States below OSC_STRING are all
safe for the CSI fast-path; states above need the normal table path
for proper subparser abort propagation.
Address reviewer feedback:
- CSI fast-path is now a condition at the top of the codepoint loop
instead of inside CLEAR, avoiding table lookups for the common case
- EXE bytes < 0x18 get a fast-path before the CSI check so control
bytes don't pay the penalty
- PRINT loop unrolling replaced with while-based version that uses
trueish eval and spares individual length checks in the unrolled case
- Removed misplaced PRINT scan-ahead from CSI dispatch
The CSI fast-path check previously ran on every byte of input, causing
3-18% regressions on ESCAPE, OSC, DCS, and other non-CSI sequences.
Moving the check into the CLEAR action handler means it only evaluates
after the state machine already identified an ESC byte. The fast-path
body is extracted into a separate _csiFast method to keep the switch
case body small for V8 optimization.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three related disposable-registration gaps that leak xterm Terminal
instances when the host application unmounts a Terminal. Evidence +
full writeup at juspay/kolu#606.
1. addon-webgl/WebglRenderer._cursorBlinkStateManager was declared
`= new MutableDisposable()` without the `this._register(...)`
wrapper that every sibling MutableDisposable in the class uses.
CursorBlinkStateManager runs a setInterval for the cursor blink
and its `dispose()` correctly clears it — but dispose() was never
called because the disposable wasn't registered. The live timer
kept the renderer (and thus the Terminal) alive past host
unmount.
2. common/TaskQueue.DebouncedIdleTask had no `dispose()` method.
Added one that clears the internal IdleTaskQueue (cancelling any
pending idle callback).
3. browser/services/RenderService._pausedResizeTask (a
DebouncedIdleTask) was not registered. Wrapped with
`this._register(...)` so RenderService.dispose propagates.
Verified in a downstream app (kolu) with a repro that toggles Focus
vs Canvas layout modes:
- Pre-fix: 24/28 disposed xterm Terminals retained past host unmount
+ forced GC.
- Post-fix: 0/28 retained.
Retention chain (before fix):
DOMTimer → ScheduledAction → V8Function
→ CursorBlinkStateManager._renderCallback → WebglRenderer
→ Terminal
Terminal.resize() already no-ops internally when dimensions haven't
changed. The addon's guard prevented resize() from being called at all,
forcing callers using ResizeObserver-based layouts to use perturbation
workarounds (resize to rows-1, then fit) that race with the observer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace `window` with `globalThis` in IdleTaskQueue export.
In Bun and Deno, `isNode` is false but `window` is undefined, causing
a ReferenceError. Using `globalThis` is safe in all JS runtimes and
makes the `isNode` guard unnecessary.
This is the only `in window` expression in code bundled into
@xterm/headless.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Defer Viewport DOM scroll updates while DEC 2026 synchronized output is
active, then flush once rendering resumes so DOM and canvas update
atomically.
Also update demo sizing to avoid relying on removed internal viewport
scrollBarWidth.
Disable desynchronized 2D canvas mode for image layers so resize paint stays synchronized and avoids blank-frame jitter on some platforms.
Signed-off-by: sergiochan <yuheng9211@qq.com>
Track the composition range using the textarea selection and avoid re-sending trailing text that existed before composition started (notably in screenReaderMode).
Fixes#5456
This is likely a race condition during a resize which is hard to say
what the right behavior is. Instead of hard failing by throwing an
error, silently ignore if this happens.
Fixes#5587
1. processLookahead/BacktrackPosition created separate entry objects
for each glyph at each position (O(N^M) with N glyphs, M positions).
Now all glyphs at a position share one entry object.
2. cloneEntry, cloneTree, flattenEntry, and mergeTreeEntry could
infinitely recurse or duplicate work on shared/cyclic references.
Added visited/merged tracking maps to cache results.
Note that parsing CommitMono locks up the renderer for some time still
so there is still a performance issue here, this fixes the OOM though.
This was mostly defensive generated code since the codebase is
unfamiliar to me.
Fixes#5570
Draw calls can now be scaled by the char bounding box instead of the
cell bounding box. This means these character won't be stretched. Both
can also be used at the same like as is the case with 1FBBC RIGHT OPEN
SQUARED DOT.
Fixes#5488
- Uses fontforge + helper bin/convert_svg_to_custom_glyph.js
- Some of these are very large. They'll be much smaller after gzip but
if this becomes a problem we can compile the svgs into a binary format
Fixes#5479
Applications can now detect synchronized output support by querying
CSI ? 2026 $ p and receiving CSI ? 2026 ; <mode> $ y response.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
performance.now() is faster than Date.now() as there's no need to
consult the system clock. The more important part of this is that
performance.now() is monotonic and is guaranteed to never to backwards.
When typing rapidly on Android with physical keyboards, characters often
duplicate on the terminal. This is because physical keyboard events
always go through IME on Android, even for plain English characters, and
rapid typing causes a race condition in the composition helper.
This patch fixes the issue by making sure textarea changes are handled
correctly.
- Enhanced copilot instructions with detailed architecture overview
- Added comprehensive development workflow documentation
- Documented addon development patterns and key examples
- Added project-specific conventions and TypeScript structure
- Included common patterns for parser integration and buffer access
- Added critical implementation details for rendering and events
- Created unit test instructions file with addon testing rules
- Specified preferences for assertions and self-documenting tests
- Remove export from IHighlight and IMultiHighlight interfaces
- Remove export from ISelectedDecoration interface
- Remove export from ISearchPosition interface
- Keep ISearchResult exported as it's used across multiple files
- Make ISelectedDecoration extend IDisposable for consistency
- Remove unnecessary inline comments from unit tests
- Add descriptive assertion messages where appropriate
- Follow coding guidelines for test comment usage
- Extract SearchEngine for core search algorithms
- Extract DecorationManager for visual decorations
- Extract SearchState for state management
- Extract SearchResultTracker for result tracking
- Simplify SearchAddon by delegating to components
- Improve modularity and maintainability
- Move ISearchAddonOptions and ISearchResultChangeEvent to types
- Make noScroll property required in IInternalSearchOptions
- Change exported interfaces to internal scope where appropriate
- Use disposableTimeout instead of manual timeout management
- Replace MutableDisposable pattern for SearchAddon highlight timeout
- Replace SearchLineCache timeout with MutableDisposable approach
- Clean up timeout disposal logic in both classes
- Add SearchLineCache class to cache translated buffer lines
- Integrate caching into SearchAddon for line translation
- Improve search performance by avoiding repeated translation
- Add cache invalidation on terminal resize and writes
- Add comprehensive unit test examples with yarn test-unit
- Include integration test examples with yarn test-integration
- Document test filtering by file path and wildcard patterns
- Explain test runner frameworks (mocha and playwright)
- Convert standalone constants to Constants enum with detailed docs
- Fix spacing issue in SearchAddon class declaration
- Update all references to use Constants enum for consistency
- Export ISearchResultChangeEvent interface in addon-search
- Use proper Event<T> typing instead of inline object
- Improve API consistency and type safety for consumers
This aligns reflowing much closer to how conpty does it. This was always
an issue but only became a big issue recently because conpty 1.22+ opts
to passthrough sequences rather than reprinting aggressively. This means
that the conpty buffer being in sync with the xterm.js buffer is more
important, otherwise the cursor will show up in a seemingly random
position.
The existing reflow appears to differ somewhat in conpty, like it seems
to reflow at the word level, not the character level like xterm.js, but
refining that closer if not worth the effort since conpty may end up
relying on the terminal's buffer in the future[1].
Fixes#5319Fixes#3513
Related #4231
Related microsoft/vscode#241978
[1]: https://github.com/microsoft/terminal/blob/main/doc/specs/%2313000%20-%20In-process%20ConPTY.md
This was a very long standing bug that was hard to nail down a repro case. Turns
out this was because at some point the current link can get cleared and re-evaluated,
I think because of a different fix we made. That caused the object check to fail.
So the fix there is to check link equality, not js object equality.
Additionally I found we don't dispose of old linkifiers when open is called
multiple times.
See microsoft/vscode#230010
Whenever the demo is reloaded on Windows the demo server crashes. This just
ignores the error as I tried logging and adding try/catch around all
interactions with the websocket and couldn't narrow it down.
Symbols taken from https://github.com/powerline/fontpatcher/blob/develop/fonts/powerline-symbols.sfd
The original symbols use height and widith of approx 1060x2048, so I
modified to be roughly 1000x1000 via:
fontforge -lang=ff -c 'Open($1); SelectAll(); UnlinkReference(); Scale(100, 50); Move(0, 300); Export("svg/%n-%e.svg");' powerline-symbols.sfd
Then I edited by hand to round values to approx 2 significant digits,
and then used a script to convert relative SVG commands such as 'l',
'c', 's' to use only 'M', 'L' and 'C' and used expected format with
comma separators rather than spaces, and divide all values by 1000 to
map into a 1x1 space.
Add support to ANSI OSC52 sequence to manipulate selection and clipboard
data. The sequence specs supports multiple clipboard selections but we
only support the common ones, system and primary clipboard selections.
This adds a new event listener to the common terminal module
`onClipboard` to allow external implementations to hook into it.
The addon uses the browser Clipboard API to read/write from and to the
clipboard. The default `ClipboardProvider` uses the browser Clipboard
API. This means it only supports read/write to and from the system
clipboard.
Reference: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
Fixes: xtermjs#3260
Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
2023-09-20 17:46:52 -04:00
716 changed files with 53996 additions and 19031 deletions
description:Run and author benchmark tests in xterm.js. Use when editing benchmark files, working in `*.benchmark.ts`, or when asked how to run the full benchmark suite, a single benchmark file, or a single benchmark case.
---
# Benchmark Run Instructions
## Running Benchmarks
- Full suite: `npm run benchmark`
- Single benchmark file:
- Tree: `npm run benchmark -- -t out-test/benchmark/Event.benchmark.js`
- Run file: `npm run benchmark -- -s "out-test/benchmark/Event.benchmark.js" out-test/benchmark/Event.benchmark.js`
- Single context/case:
- Use `-t` to get the path, then:
-`npm run benchmark -- -s "<path>" out-test/benchmark/Event.benchmark.js`
## Benchmark Case Selection
When writing benchmark instructions:
- Use `RuntimeCase` to measure pure runtime in ms.
- Use `ThroughputRuntimeCase` when measuring throughput in MB/s.
## Notes
- Benchmarks run from built JS in `out-test/benchmark/*.benchmark.js`.
- Keep `NODE_PATH=./out` (handled by the npm script).
description:Write and review xterm.js unit tests with project conventions. Use when editing `*.test.ts` files or when asked for test authoring guidelines.
---
# Unit Test Instructions
When writing unit tests, follow these rules:
- When writing unit tests for addons, always create a real xterm.js instance instead of mocking it.
- Prefer `assert.ok` over `assert.notStrictEqual` when checking something is undefined or not.
- Avoid comments as most tests should be self-documenting.
npm run build && npm run esbuild # Build all TypeScript and bundle
```
**Testing**:
- Unit tests: `npm run test-unit` (Mocha)
- Unit tests filtering to file: `npm run test-unit -- **/fileName.ts
- Per-addon unit tests: `npm run test-unit -- addons/addon-image/out-esbuild/*.test.js`
- Integration tests: `npm run test-integration` (Playwright across Chrome/Firefox/WebKit)
- Integration tests by file: `npm run test-integration -- test/playwright/InputHandler.test.ts`. Never use grep to filter tests, it doesn't work
- Integration tests by addon: `npm run test-integration -- --suite=addon-search`. Suites always follow the format `addon-<something>`
- Lint: `npm run lint` (oxlint with type-aware rules, then ESLint for `naming-convention` only), `npm run lint-api` for `typings/`, `npm run lint-fix` for oxlint auto-fix
- Lint changes: `npm run lint-changes` to lint only changed files, `npm run lint-changes-fix` to fix them
## Addon Development Pattern
All addons follow this structure:
```typescript
exportclassMyAddonimplementsITerminalAddon{
activate(terminal: Terminal):void{
// Called when loaded via terminal.loadAddon()
// Register handlers, access terminal APIs
}
dispose():void{
// Cleanup when addon is disposed
}
}
```
**Key Examples**:
-`addons/addon-fit/`: Terminal sizing
-`addons/addon-webgl/`: GPU-accelerated rendering
-`addons/addon-search/`: Text search functionality
## Project-Specific Conventions
**TypeScript Project Structure**: Uses TypeScript project references (`tsconfig.all.json`) for incremental builds across browser/headless/addons.
**API Design**:
- Browser and headless terminals share the same public API
- Constructor-only options (cols, rows) cannot be changed after instantiation
**Disposable Management**:
- When a disposable object can be replaced over time, prefer a registered `MutableDisposable` over manual dispose/reassign logic.
- Register it on the owning class (for example, `this._register(new MutableDisposable())`) and assign through `.value`; this automatically disposes the previous value and avoids accidentally leaking resources.
**TypeScript Constants**:
- Prefer `const enum` over top-level `const` declarations for primitive constants when appropriate, since values are inlined and avoid runtime property lookups.
**Testing Utilities**: Use `TestUtils.ts` helpers:
-`openTerminal(ctx, options)` for setup
-`pollFor(page, fn, expectedValue)` for async assertions
**Buffer Access**: Read terminal content via buffer API:
```typescript
constline=terminal.buffer.active.getLine(0);
constcell=line?.getCell(0);
```
**Events**: All terminals emit standard events (onData, onResize, onRender) plus platform-specific ones.
## Critical Implementation Details
- Terminal rendering uses either DOM or WebGL renderers
- Buffer lines are immutable; create new instances for modifications
- Character width handling supports Unicode 11+ and grapheme clustering
- Mouse events translate web events to terminal protocols (X10, VT200, etc.)
- Color theming supports both palette and true color modes
## Writing unit tests
- Unit tests live alongside the source code file of the thing it's testing with a .test.ts suffix.
## Cursor Cloud specific instructions
**Demo server**: Start with `npm start` (port 3000). The demo server uses node-pty to spawn real shell sessions over WebSocket. Integration tests auto-start it via Playwright's `webServer` config, so you don't need to start it manually for `npm run test-integration`.
**Build before testing**: Always run `npm run build && npm run esbuild` before `npm run test-unit`. Integration tests also need `npm run esbuild-demo-client` and `npm run esbuild-demo-server`. The update script handles this automatically on session start.
**No external services**: This project has zero external dependencies (no databases, Docker, or APIs). Everything runs locally with Node.js.
**TypeScript compiler**: The project uses `tsgo` (native TypeScript compiler preview) rather than standard `tsc`. It's installed via the `@typescript/native-preview` package.
**Lint only changed files**: Prefer `npm run lint-changes` over `npm run lint` when iterating on code changes — it's significantly faster.
You can find issues to work on by looking at issues labeled with [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). It's a good idea to comment on the issue saying that you're looking into it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute:
- Fork [xterm.js](https://github.com/xtermjs/xterm.js/) ([how to fork a repo](https://help.github.com/articles/fork-a-repo)).
- Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running.
- Make the fix and verify it works in the demo. Be sure to follow thew general code style of the rest of the project.
- If your changes are easy to test or likely to regress in the future, add tests. These could be unit or integration (playwright) tests.
- Submit a pull request ([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
> ![TIP]
> Don't put more than one feature or fix in a single pull request. The smaller pull requests are the easier they are to review and merge.
By contributing code to xterm.js you:
- Agree to license the contributed code under xterm.js' [MIT license](LICENSE).
- Confirm that you have the right to contribute and license the code in question. This means that either you hold all rights on the code, or the rights holder has explicitly granted the right to use it like this, through a compatible open source license or through a direct agreement with you.
## Opening issues
The preferred way to report bugs or request features is to use
[GitHub issues](http://github.com/sourcelair/xterm.js/issues). Before
- Include information about **the browser in which the problem occurred**. Even
if you tested several browsers, and the problem occurred in all of them,
mention this fact in the bug report. Also include browser version numbers and
the operating system that you're on.
- Include information about **the browser in which the problem occurred** or the terminal being used. If you tested several browsers and the problem occurred in all of them, mention this fact in the bug report. Also include browser version numbers and the operating system that you're on.
- Include the version of xterm.js being used, preferably either with the latest `beta` tagged release on npm or reproducing in the demo on the`master` branch.
- Mention precisely what went wrong. What did you expect to happen? What happened instead? Describe the exact steps a maintainer has to take to make the problem occur.
- If the problem can not be reproduced in the [demo of xterm.js](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo), provide an HTML document that demonstrates the problem.
- Be polite and follow [the code of conduct](https://github.com/xtermjs/xterm.js?tab=coc-ov-file#readme).
- Mention which release of xterm.js you're using. Preferably, try also with
the current HEAD of the master branch, to ensure the problem has not already been
fixed.
### Issue triaging philosophy
- Mention precisely what went wrong. What did you expect to happen? What happened instead? Describe the
exact steps a maintainer has to take to make the problem occur.
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:
-If the problem can not be reproduced in the [demo of xterm.js](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo), please provide an HTML document that demonstrates the problem.
-Be polite. Issues with an indignant or belligerent tone tend to be moved to the
bottom of the pile.
-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.
- If a bug is not easily reproducible it may be closed or moved to a discussion. Generally issues that are labeled are something we want to do or has actionable steps to look into further.
## Answering discussion questions
Issues are only meant to track (likely) feature requests and bugs. We use [GitHub Discussions](https://github.com/xtermjs/xterm.js/discussions) for general Q&A as well as discussing possible features. If you want to help out, many questions get asked over at the [discussions page](https://github.com/xtermjs/xterm.js/discussions) which could use an expert as the core team is often stretched thin.
## Contributing code
You can find issues to work on by looking at the [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) issues. It's a good idea to comment on the issue saying that you're taking it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute:
([how to fork a repo](https://help.github.com/articles/fork-a-repo)).
- Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running.
- Make your changes.
- If your changes are easy to test or likely to regress, add tests. Tests go into `test`, directory.
- Follow the general code style of the rest of the project (see below).
- Submit a pull request
([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
Don't put more than one feature/fix in a single pull request.
By contributing code to xterm.js you:
- Agree to license the contributed code under xterm.js' [MIT
license](LICENSE).
- Confirm that you have the right to contribute and license the code
in question. (Either you hold all rights on the code, or the rights
holder has explicitly granted the right to use it like this,
through a compatible open source license or through a direct
agreement with you.)
### Test coverage
One area that always needs attention is improving out unit test coverage, you can view the code coverage report on [Azure Pipelines](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3) by clicking the Code Coverage tab.
Issues are only meant to track (likely) feature requests and bugs. We use [GitHub Discussions](https://github.com/xtermjs/xterm.js/discussions) for general Q&A as well as discussing possible features. If you want to help out, many questions get asked over at the [discussions page](https://github.com/xtermjs/xterm.js/discussions) which could use an expert as the core team is often stretched thin.
Xterm.js is a front-end component written in TypeScript that lets applications bring fully-featured terminals to their users in the browser. It's used by popular projects such as VS Code, Hyper and Theia.
Xterm.js is a frontend component that enables applications to bring fully-featured terminals to their users in the browser. It's used by popular projects such as [VS Code](https://code.visualstudio.com/) (and its forks), [Tabby](https://tabby.sh/) and [Hyper](https://hyper.is/).
## Features
- **Terminal apps just work**: Xterm.js works with most terminal apps such as `bash`, `vim`, and `tmux`, including support for curses-based apps and mouse events.
- **Performant**: Xterm.js is *really* fast, it even includes a GPU-accelerated renderer.
- **Performant**: Xterm.js is *really* fast and includes an optional GPU-accelerated renderer.
- **Rich Unicode support**: Supports CJK, emojis, and IMEs.
- **Self-contained**: Requires zero dependencies to work.
- **Accessible**: Screen reader and minimum contrast ratio support can be turned on.
- **And much more**: Links, theming, addons, well documented API, etc.
- **Self-contained**: The core library has zero dependencies.
- **Accessible**: Screen reader mode and minimum contrast ratio support can be turned on.
- **And much more**: Links, theming, custom glyphs, addons, well documented API, etc.
## What xterm.js is not
- Xterm.js is not a terminal application that you can download and use on your computer.
- Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output).
- Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output) through a library like [node-pty](https://github.com/microsoft/node-pty).
## Getting Started
First, you need to install the module, we ship exclusively through [npm](https://www.npmjs.com/), so you need that installed and then add xterm.js as a dependency by running:
First, you need to install the module. We ship exclusively through [npm](https://www.npmjs.com), so you need that installed and then add [@xterm/xterm](https://www.npmjs.com/package/@xterm/xterm) as a dependency by running:
```bash
npm install xterm
npm install --save @xterm/xterm
```
To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to the head of your HTML page. Then create a `<div id="terminal"></div>` onto which xterm can attach itself. Finally, instantiate the `Terminal` object and then call the `open` function with the DOM object of the `div`.
The recommended way to load xterm.js with the ES module syntax, either using TypeScript or a modern JS tooling. Note that both CommonJS and ES module files are shipped with the npm package.
```javascript
import{Terminal}from'@xterm/xterm';
```
Then instantiate a `Terminal` object, open it on an element and write to it:
```javascript
constterm=newTerminal();
term.open(document.getElementById('terminal'));
term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
```
Most use cases will hook up input and output to a pseudoterminal API such as [node-pty](https://www.npmjs.com/package/node-pty) which will drive the terminal.
```javascript
pty.onData(data=>term.write(data));
term.onData(data=>pty.write(data));
```
Then make sure to also include the `css/xterm.css` file, for example in HTML:
term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
</script>
@@ -44,22 +73,12 @@ To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to t
</html>
```
### Importing
The recommended way to load xterm.js is via the ES6 module syntax:
```javascript
import{Terminal}from'@xterm/xterm';
```
### Addons
⚠️ *This section describes the new addon format introduced in v3.14.0, see [here](https://github.com/xtermjs/xterm.js/blob/3.14.2/README.md#addons) for the instructions on the old format*
Addons are separate modules that extend the `Terminal` by building on the [xterm.js API](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts). To use an addon, you first need to install it in your project:
```bash
npm i -S @xterm/addon-web-links
npm install --save @xterm/addon-web-links
```
Then import the addon, instantiate it and call `Terminal.loadAddon`:
- [`@xterm/addon-serialize`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-serialize): Serializes the terminal's buffer to a VT sequences or HTML
- [`@xterm/addon-serialize`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-serialize): Serializes the terminal's buffer to VT sequences or HTML
- [`@xterm/addon-unicode-graphemes`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode-graphemes): Enhanced unicode support including grapheme clustering (experimental)
- [`@xterm/addon-unicode11`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode11): Updates character widths to their unicode11 values
- [`@xterm/addon-web-fonts`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-fonts): Easily integrate web fonts
- [`@xterm/addon-web-links`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-links): Adds web link detection and interaction
- [`@xterm/addon-webgl`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-webgl): Renders xterm.js using a `canvas` element's webgl2 context
## Browser Support
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Specifically the latest versions of *Chrome*, *Edge*, *Firefox*, and *Safari*.
Xterm.js works seamlessly in [Electron](https://electronjs.org/) apps and may even work on earlier versions of the browsers. These are the versions we strive to keep working.
Since xterm.js is typically implemented as a developer tool, generally only modern evergreen browsers are supported officially. Specifically the latest versions of *Chrome*, *Edge*, *Firefox*, and *Safari*. Xterm.js also works seamlessly in [Electron](https://electronjs.org/) apps and may even work on earlier versions of the browsers. These are the versions we strive to keep working.
### Node.js Support
We also publish [`xterm-headless`](https://www.npmjs.com/package/xterm-headless) which is a stripped down version of xterm.js that runs in Node.js. An example use case for this is to keep track of a terminal's state where the process is running and using the serialize addon so it can get all state restored upon reconnection.
We also publish [`@xterm/headless`](https://www.npmjs.com/package/@xterm/headless) which is a stripped down version of xterm.js that runs headless in Node.js. An example use case for this is to keep track of a terminal's state where the process is running and using the [serialize addon](https://www.npmjs.com/package/@xterm/addon-serialize) so it can get all state restored upon reconnection.
## API
The full API for xterm.js is contained within the [TypeScript declaration file](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), use the branch/tag picker in GitHub (`w`) to navigate to the correct version of the API.
Note that some APIs are marked *experimental*, these are added to enable experimentation with new ideas without committing to support it like a normal [semver](https://semver.org/) API. Note that these APIs can change radically between versions, so be sure to read release notes if you plan on using experimental APIs.
Some APIs may be marked with *experimental*, these are added to enable experimentation with new ideas without committing to support it like a normal [semver](https://semver.org/) API. Note that these APIs can change radically between versions, so be sure to read release notes if you plan on using experimental APIs.
## Releases
Xterm.js follows a monthly release cycle roughly.
All current and past releases are available on this repo's [Releases page](https://github.com/sourcelair/xterm.js/releases), you can view the [high-level roadmap on the wiki](https://github.com/xtermjs/xterm.js/wiki/Roadmap) and see what we're working on now by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones).
Stable releases are done on an as needed basis. All current and past releases are available onthis repo's [releases page](https://github.com/sourcelair/xterm.js/releases), you can see what's planned for upcoming releases looking through the repository [milestones](https://github.com/sourcelair/xterm.js/milestones).
### Beta builds
Our CI releases beta builds to npm for every change that goes into master. Install the latest beta build with:
Beta releases are continuously published off the `master` branch. Install the latest beta build with:
```bash
npm install -S xterm@beta
npm install --save @xterm/xterm@beta
```
These should generally be stable, but some bugs may slip in. We recommend using the beta build primarily to test out new features and to verify bug fixes.
The principal implementation (VS Code) typically uses the latest or near the latest beta build. Generally they are quite stable but can potentially contain bugs or breaking changes. If stability is very important we recommend using the beta build primarily to test out new features and to verify bug fixes, unless you're tracking what's landing and are comfortable taking that risk.
## Contributing
You can read the [guide on the wiki](https://github.com/xtermjs/xterm.js/wiki/Contributing) to learn how to contribute and set up xterm.js for development.
Read [CONTRIBUTING.md](https://github.com/xtermjs/xterm.js/blob/master/CONTRIBUTING.md) to learn how to contribute to the project.
## Real-world uses
Xterm.js is used in several world-class applications to provide great terminal experiences.
Xterm.js is used in many world-class applications to provide great terminal experiences.
- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js.
- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile, and powerful open source code editor that provides an integrated terminal based on xterm.js.
- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js.
- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies.
- [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams.
- [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by xterm.js.
@@ -222,14 +241,34 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**Cloudtutor.io**](https://cloudtutor.io): innovative online learning platform that offers users access to an interactive lab.
- [**Helix Editor Playground**](https://github.com/tomgroenwoldt/helix-editor-playground): Online playground for the terminal based helix editor.
- [**Coder**](https://github.com/coder/coder): Self-Hosted Remote Development Environments
- [**Wave Terminal**](https://waveterm.dev): An open-source, ai-native, terminal built for seamless workflows.
- [**eva**](https://github.com/info24/eva): Eva is a web application for SSH remote login, developed in Go.
- [**OpenSFTP**](https://opensftp.com): Super beautiful SSH and SFTP integrated workspace client.
- [**balena**](https://www.balena.io/): Balena is a full-stack solution for developing, deploying, updating, and troubleshooting IoT Edge devices. We use xterm.js to manage & debug devices on [balenaCloud](https://www.balena.io/cloud).
- [**Filet Cloud**](https://github.com/fuglaro/filet-cloud): The lean and powerful personal cloud ⛅.
- [**pyTermTk**](https://github.com/ceccopierangiolieugenio/pyTermTk): Python Terminal Toolkit - a Spiced Up Cross Compatible TUI Library 🌶️, use xterm.js for the [HTML5 exporter](https://ceccopierangiolieugenio.github.io/pyTermTk/sandbox/sandbox.html).
- [**ecmaOS**](https://ecmaos.sh): A kernel and suite of applications tying modern web technologies into a browser-based operating system.
- [**LabEx**](https://labex.io): Interactive learning platform with hands-on labs and xterm.js-based online terminals, focused on learn-by-doing approach.
- [**EmuDevz**](https://afska.github.io/emudevz): A free coding game where players learn how to build an emulator from scratch.
- [**SSH Connection Manager**](https://github.com/Amtrend/ssh-manager): Modern web-based SSH terminal and SFTP manager with AES-256 encryption, PWA support, and session management.
- [**WooTTY**](https://github.com/icoretech/wootty): Flawless browser terminal for real operators.
- [**Orca**](https://github.com/stablyai/orca): Agentic development environment for 100x builders.
- [**4bit**](https://ciembor.github.io/4bit): Terminal Color Scheme Designer.
- [**Cate**](https://github.com/0-AI-UG/cate): Open source desktop IDE on an infinite zoomable canvas. Editor, browser, and terminal panels float in spatial space; the terminals are xterm.js with the WebGL addon, backed by node-pty.
- [**OmnyShell PWA**](https://github.com/OmnyGrid/omnyshell_web): Web client for [**OmnyShell**](https://github.com/OmnyGrid/omnyshell), featuring remote terminals, a built-in IDE, AI-assisted workflows, and detachable/resumable sessions for seamless browser-based access to nodes.
- [**kmux**](https://github.com/kkd927/kmux): A keyboard-centric terminal workspace for running parallel AI coding agents side by side on macOS and Linux.
- [**LynxHub**](https://github.com/KindaBrazy/LynxHub): Cross-platform, extensible terminal and browser for AI management.
- [**Codeman**](https://github.com/Ark0N/Codeman): Self-hosted mission control for AI coding agents, streaming live tmux sessions to browser and phone.
- [**JSVIsion**](https://github.com/blendsdk/jsvision): A Turbo Vision-Style TUI SDK for TypeScript. We use xterm.js in our docs to show live JSVision examples running in a web terminal.
- [**BitFun**](https://github.com/GCWing/BitFun): Open-source desktop AI agent whose integrated terminals and read-only terminal output views use xterm.js with the Fit, Web Links, and WebGL addons.
- [And much more...](https://github.com/xtermjs/xterm.js/network/dependents?package_id=UGFja2FnZS0xNjYzMjc4OQ%3D%3D)
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only.
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Please add any new contributions to the end of the list.
## License Agreement
If you contribute code to this project, you implicitly allow your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work.
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables a canvas-based renderer using a 2d context to draw. This addon requires xterm.js v5+.
The purpose of this addon is to be used as a fallback for the [webgl addon](https://www.npmjs.com/package/@xterm/addon-webgl) when better performance is desired over the default DOM renderer, but WebGL2 isn't supported or performant for some reason.
### Install
```bash
npm install --save @xterm/addon-canvas
```
### Usage
```ts
import{Terminal}from'@xterm/xterm';
import{CanvasAddon}from'@xterm/addon-canvas';
constterminal=newTerminal();
terminal.open(element);
terminal.loadAddon(newCanvasAddon());
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-canvas/typings/addon-canvas.d.ts) for more advanced usage.
### See also
- [@xterm/addon-webgl](https://www.npmjs.com/package/@xterm/addon-webgl) A renderer for xterm.js that uses WebGL
sixelSizeLimit: 33554432, // size limit of a single sixel sequence
storageLimit: 128, // FIFO storage limit in MB
showPlaceholder: true, // whether to show a placeholder for evicted images
iipSupport: true, // enable iTerm IIP support
iipSizeLimit: 20000000 // size limit of a single IIP sequence
iipSizeLimit: 33554432, // size limit of a single IIP sequence
kittySupport: true, // enable Kitty graphics support
kittySizeLimit: 33554432 // size limit of a single Kitty sequence
}
// initialization
@@ -41,9 +44,6 @@ terminal.loadAddon(imageAddon);
### General Notes
- *IMPORTANT:* The worker approach as done in previous releases got removed.
The addon contructor no longer expects a worker path as first argument.
- By default the addon will activate these `windowOptions` reports on the terminal:
- getWinSizePixels (CSI 14 t)
- getCellSizePixels (CSI 16 t)
@@ -71,7 +71,7 @@ terminal.loadAddon(imageAddon);
- **Cursor Positioning**
If scrolling is set, the cursor will be placed at the first image column of the last image row (VT340 mode).
Other cursor positioning modes as used by xterm or mintty are not supported.
Other cursor positioning modes as used by xterm or mintty are not supported for sixel.
- **SIXEL Palette Handling**
By default the addon limits the palette size to 256 registers (as demanded by the DEC specification).
@@ -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.
- Image pixel size is restricted by `pixelLimit` (pre- and post resizing).
- Size parameter is restricted by `iipSizeLimit`.
- Cursor positioning behaves the same as for sixel (see above).
- Playload size is restricted by `iipSizeLimit`.
- size parameter is not mandatory anymore.
- Cursor gets positioned at the next cell of the last image line.
- ReportCellSize sequence is supported.
- Hi-res display support is currently limited (image gets blurred).
- **Note on IIP image format support**
For all image formats except QOI the addon only does the dimension extraction and leaves
the image decoding to the underlying browser engine. Note that some formats are implemented
and enabled with different constraints by browser vendors, especially newer formats
(e.g. under Firefox you might need to change the setting _image.avif.compliance_strictness_ to 0
to get broader AVIF support).
- **Kitty Graphics Support (TGP)**
Set by default, change it with `{kittySupport: true}`.
Note that the kitty graphics support is still WIP.
### Storage and Drawing Settings
@@ -125,6 +139,7 @@ The addon exposes two properties to interact with the storage limits at runtime:
By default the addon will show a placeholder pattern for evicted images that are still part
of the terminal (e.g. in the scrollback). The pattern can be deactivated by toggling `showPlaceholder`.
### Image Data Retrieval
The addon provides the following API endpoints to retrieve raw image data as canvas:
@@ -138,11 +153,12 @@ The addon provides the following API endpoints to retrieve raw image data as can
The buffer position is the 0-based absolute index (including scrollback at top).
Note that the canvas gets created and data copied over for every call, thus it is not suitable for performance critical actions.
### 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).
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 bookkeeping 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.
- `sixelSizeLimit`
A constructor setting. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken SIXEL sequences at an earlier phase, thus may stop the decoder from entering its memory intensive task for potentially invalid data.
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.
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
@@ -209,23 +225,5 @@ _How can I adjust the memory usage?_
### Status
- Sixel support and image handling in xterm.js is considered beta quality.
- IIP support is in alpha stage. Please file a bug for any awkwardities.
### Changelog
- 0.5.0 integrate with xtermjs base repo (at v0.4.3)
- 0.4.3 defer canvas creation
- 0.4.2 fix image canvas resize
- 0.4.1 compat release for xterm.js 5.2.0
- 0.4.0 IIP support
- 0.3.1 compat release for xterm.js 5.1.0
- 0.3.0 important change: worker removed from addon
- 0.2.0 compat release for xterm.js 5.0.0
- 0.1.3 bugfix: avoid striping
- 0.1.2 bugfix: reset clear flag
- 0.1.1 bugfixes:
- clear sticky image tiles on render
- create folder from bootstrap.sh
- fix peer dependency in package.json
- Sixel and IIP support and image handling in xterm.js is considered beta quality.
- Kitty support is in alpha stage. Please file a bug for any awkwardities or missing features.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.