@xterm/xterm 6.1.0-beta.9 → 6.1.0-beta.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +27 -28
  2. package/css/xterm.css +5 -11
  3. package/lib/xterm.js +1 -1
  4. package/lib/xterm.js.map +1 -1
  5. package/lib/xterm.mjs +17 -17
  6. package/lib/xterm.mjs.map +4 -4
  7. package/package.json +26 -19
  8. package/src/browser/AccessibilityManager.ts +4 -1
  9. package/src/browser/CoreBrowserTerminal.ts +37 -6
  10. package/src/browser/OscLinkProvider.ts +1 -1
  11. package/src/browser/Types.ts +4 -1
  12. package/src/browser/Viewport.ts +16 -1
  13. package/src/browser/input/CompositionHelper.ts +10 -1
  14. package/src/browser/public/Terminal.ts +6 -5
  15. package/src/browser/renderer/dom/DomRenderer.ts +74 -3
  16. package/src/browser/renderer/dom/WidthCache.ts +54 -52
  17. package/src/browser/renderer/shared/Constants.ts +7 -0
  18. package/src/browser/renderer/shared/Types.ts +5 -0
  19. package/src/browser/services/MouseService.ts +2 -1
  20. package/src/browser/services/RenderService.ts +9 -5
  21. package/src/browser/services/Services.ts +1 -1
  22. package/src/common/Color.ts +8 -0
  23. package/src/common/CoreTerminal.ts +2 -1
  24. package/src/common/InputHandler.ts +52 -9
  25. package/src/common/Platform.ts +4 -1
  26. package/src/common/Types.ts +1 -1
  27. package/src/common/Version.ts +9 -0
  28. package/src/common/buffer/Buffer.ts +4 -0
  29. package/src/common/buffer/Types.ts +4 -0
  30. package/src/common/data/Charsets.ts +1 -1
  31. package/src/common/input/Keyboard.ts +7 -6
  32. package/src/common/services/CharsetService.ts +4 -0
  33. package/src/common/services/DecorationService.ts +17 -3
  34. package/src/common/services/OptionsService.ts +2 -2
  35. package/src/common/services/Services.ts +6 -1
  36. package/typings/xterm.d.ts +165 -34
@@ -3,6 +3,7 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
+ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
6
7
  import { IDisposable } from 'common/Types';
7
8
  import { FontWeight } from 'common/services/Services';
8
9
 
@@ -24,6 +25,10 @@ const enum FontVariant {
24
25
  BOLD_ITALIC = 3
25
26
  }
26
27
 
28
+ export interface IWidthCacheFontVariantCanvas {
29
+ setFont(fontFamily: string, fontSize: number, fontWeight: FontWeight, italic: boolean): void;
30
+ measure(c: string): number;
31
+ }
27
32
 
28
33
  export class WidthCache implements IDisposable {
29
34
  // flat cache for regular variant up to CacheSettings.FLAT_SIZE
@@ -42,50 +47,24 @@ export class WidthCache implements IDisposable {
42
47
  private _fontSize = 0;
43
48
  private _weight: FontWeight = 'normal';
44
49
  private _weightBold: FontWeight = 'bold';
45
- private _container: HTMLDivElement;
46
- private _measureElements: HTMLSpanElement[] = [];
47
-
48
- constructor(_document: Document, _helperContainer: HTMLElement) {
49
- this._container = _document.createElement('div');
50
- this._container.classList.add('xterm-width-cache-measure-container');
51
- this._container.setAttribute('aria-hidden', 'true');
52
- // SP should stack in spans
53
- this._container.style.whiteSpace = 'pre';
54
- // avoid undercuts in non-monospace fonts from kerning
55
- this._container.style.fontKerning = 'none';
56
-
57
- const regular = _document.createElement('span');
58
- regular.classList.add('xterm-char-measure-element');
59
-
60
- const bold = _document.createElement('span');
61
- bold.classList.add('xterm-char-measure-element');
62
- bold.style.fontWeight = 'bold';
63
-
64
- const italic = _document.createElement('span');
65
- italic.classList.add('xterm-char-measure-element');
66
- italic.style.fontStyle = 'italic';
67
-
68
- const boldItalic = _document.createElement('span');
69
- boldItalic.classList.add('xterm-char-measure-element');
70
- boldItalic.style.fontWeight = 'bold';
71
- boldItalic.style.fontStyle = 'italic';
72
-
73
- // NOTE: must be in order of FontVariant
74
- this._measureElements = [regular, bold, italic, boldItalic];
75
- this._container.appendChild(regular);
76
- this._container.appendChild(bold);
77
- this._container.appendChild(italic);
78
- this._container.appendChild(boldItalic);
79
-
80
- _helperContainer.appendChild(this._container);
50
+ private _canvasElements: IWidthCacheFontVariantCanvas[] = [];
51
+
52
+ constructor(
53
+ canvasFactory: () => IWidthCacheFontVariantCanvas = () => new WidthCacheFontVariantCanvas()
54
+ ) {
55
+ this._canvasElements = [
56
+ canvasFactory(),
57
+ canvasFactory(),
58
+ canvasFactory(),
59
+ canvasFactory()
60
+ ];
81
61
 
82
62
  this.clear();
83
63
  }
84
64
 
85
65
  public dispose(): void {
86
- this._container.remove(); // remove elements from DOM
87
- this._measureElements.length = 0; // release element refs
88
- this._holey = undefined; // free cache memory via GC
66
+ this._canvasElements.length = 0;
67
+ this._holey = undefined; // free cache memory via GC
89
68
  }
90
69
 
91
70
  /**
@@ -104,10 +83,11 @@ export class WidthCache implements IDisposable {
104
83
  */
105
84
  public setFont(font: string, fontSize: number, weight: FontWeight, weightBold: FontWeight): void {
106
85
  // skip if nothing changed
107
- if (font === this._font
108
- && fontSize === this._fontSize
109
- && weight === this._weight
110
- && weightBold === this._weightBold
86
+ if (
87
+ font === this._font &&
88
+ fontSize === this._fontSize &&
89
+ weight === this._weight &&
90
+ weightBold === this._weightBold
111
91
  ) {
112
92
  return;
113
93
  }
@@ -117,12 +97,10 @@ export class WidthCache implements IDisposable {
117
97
  this._weight = weight;
118
98
  this._weightBold = weightBold;
119
99
 
120
- this._container.style.fontFamily = this._font;
121
- this._container.style.fontSize = `${this._fontSize}px`;
122
- this._measureElements[FontVariant.REGULAR].style.fontWeight = `${weight}`;
123
- this._measureElements[FontVariant.BOLD].style.fontWeight = `${weightBold}`;
124
- this._measureElements[FontVariant.ITALIC].style.fontWeight = `${weight}`;
125
- this._measureElements[FontVariant.BOLD_ITALIC].style.fontWeight = `${weightBold}`;
100
+ this._canvasElements[FontVariant.REGULAR].setFont(font, fontSize, weight, false);
101
+ this._canvasElements[FontVariant.BOLD].setFont(font, fontSize, weightBold, false);
102
+ this._canvasElements[FontVariant.ITALIC].setFont(font, fontSize, weight, true);
103
+ this._canvasElements[FontVariant.BOLD_ITALIC].setFont(font, fontSize, weightBold, true);
126
104
 
127
105
  this.clear();
128
106
  }
@@ -160,8 +138,32 @@ export class WidthCache implements IDisposable {
160
138
  }
161
139
 
162
140
  protected _measure(c: string, variant: FontVariant): number {
163
- const el = this._measureElements[variant];
164
- el.textContent = c.repeat(WidthCacheSettings.REPEAT);
165
- return el.offsetWidth / WidthCacheSettings.REPEAT;
141
+ return this._canvasElements[variant].measure(c);
142
+ }
143
+ }
144
+
145
+ class WidthCacheFontVariantCanvas implements IWidthCacheFontVariantCanvas {
146
+ private _canvas: OffscreenCanvas | HTMLCanvasElement;
147
+ private _ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;
148
+
149
+ constructor() {
150
+ if (typeof OffscreenCanvas !== 'undefined') {
151
+ this._canvas = new OffscreenCanvas(1, 1);
152
+ this._ctx = throwIfFalsy(this._canvas.getContext('2d'));
153
+ } else {
154
+ this._canvas = document.createElement('canvas');
155
+ this._canvas.width = 1;
156
+ this._canvas.height = 1;
157
+ this._ctx = throwIfFalsy(this._canvas.getContext('2d'));
158
+ }
159
+ }
160
+
161
+ public setFont(fontFamily: string, fontSize: number, fontWeight: FontWeight, italic: boolean): void {
162
+ const fontStyle = italic ? 'italic' : '';
163
+ this._ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`.trim();
164
+ }
165
+
166
+ public measure(c: string): number {
167
+ return this._ctx.measureText(c).width;
166
168
  }
167
169
  }
@@ -4,3 +4,10 @@
4
4
  */
5
5
 
6
6
  export const INVERTED_DEFAULT_COLOR = 257;
7
+
8
+ export const enum RendererConstants {
9
+ /**
10
+ * The idle time after which cursor blinking stops.
11
+ */
12
+ CURSOR_BLINK_IDLE_TIMEOUT = 5 * 60 * 1000
13
+ }
@@ -39,6 +39,11 @@ export interface IRenderDimensions {
39
39
  export interface IRequestRedrawEvent {
40
40
  start: number;
41
41
  end: number;
42
+ /**
43
+ * Whether the redraw should happen synchronously. This is used to avoid
44
+ * flicker when the canvas is resized.
45
+ */
46
+ sync?: boolean;
42
47
  }
43
48
 
44
49
  /**
@@ -3,6 +3,7 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
+ import { getWindow } from 'vs/base/browser/dom';
6
7
  import { ICharSizeService, IRenderService, IMouseService } from './Services';
7
8
  import { getCoords, getCoordsRelativeToElement } from 'browser/input/Mouse';
8
9
 
@@ -30,7 +31,7 @@ export class MouseService implements IMouseService {
30
31
  }
31
32
 
32
33
  public getMouseReportCoords(event: MouseEvent, element: HTMLElement): { col: number, row: number, x: number, y: number } | undefined {
33
- const coords = getCoordsRelativeToElement(window, event, element);
34
+ const coords = getCoordsRelativeToElement(getWindow(element), event, element);
34
35
  if (!this._charSizeService.hasValidSize) {
35
36
  return undefined;
36
37
  }
@@ -92,7 +92,6 @@ export class RenderService extends Disposable implements IRenderService {
92
92
 
93
93
  // Clear the renderer when the a change that could affect glyphs occurs
94
94
  this._register(this._optionsService.onMultipleOptionChange([
95
- 'customGlyphs',
96
95
  'drawBoldTextInBrightColors',
97
96
  'letterSpacing',
98
97
  'lineHeight',
@@ -112,7 +111,7 @@ export class RenderService extends Disposable implements IRenderService {
112
111
  this._register(this._optionsService.onMultipleOptionChange([
113
112
  'cursorBlink',
114
113
  'cursorStyle'
115
- ], () => this.refreshRows(bufferService.buffer.y, bufferService.buffer.y, true)));
114
+ ], () => this.refreshRows(bufferService.buffer.y, bufferService.buffer.y, undefined, true)));
116
115
 
117
116
  this._register(themeService.onChangeColors(() => this._fullRefresh()));
118
117
 
@@ -145,7 +144,7 @@ export class RenderService extends Disposable implements IRenderService {
145
144
  }
146
145
  }
147
146
 
148
- public refreshRows(start: number, end: number, isRedrawOnly: boolean = false): void {
147
+ public refreshRows(start: number, end: number, sync: boolean = false, isRedrawOnly: boolean = false): void {
149
148
  if (this._isPaused) {
150
149
  this._needsFullRefresh = true;
151
150
  return;
@@ -165,7 +164,12 @@ export class RenderService extends Disposable implements IRenderService {
165
164
  if (!isRedrawOnly) {
166
165
  this._isNextRenderRedrawOnly = false;
167
166
  }
168
- this._renderDebouncer.refresh(start, end, this._rowCount);
167
+
168
+ if (sync) {
169
+ this._renderRows(start, end);
170
+ } else {
171
+ this._renderDebouncer.refresh(start, end, this._rowCount);
172
+ }
169
173
  }
170
174
 
171
175
  private _renderRows(start: number, end: number): void {
@@ -235,7 +239,7 @@ export class RenderService extends Disposable implements IRenderService {
235
239
  this._renderer.value = renderer;
236
240
  // If the value was not set, the terminal is being disposed so ignore it
237
241
  if (this._renderer.value) {
238
- this._renderer.value.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
242
+ this._renderer.value.onRequestRedraw(e => this.refreshRows(e.start, e.end, e.sync, true));
239
243
 
240
244
  // Force a refresh
241
245
  this._needsSelectionRefresh = true;
@@ -77,7 +77,7 @@ export interface IRenderService extends IDisposable {
77
77
 
78
78
  addRefreshCallback(callback: FrameRequestCallback): number;
79
79
 
80
- refreshRows(start: number, end: number): void;
80
+ refreshRows(start: number, end: number, sync?: boolean): void;
81
81
  clearTextureAtlas(): void;
82
82
  resize(cols: number, rows: number): void;
83
83
  hasRenderer(): boolean;
@@ -184,6 +184,14 @@ export namespace css {
184
184
  return channels.toColor($r, $g, $b, $a);
185
185
  }
186
186
 
187
+ // Handle the "transparent" keyword
188
+ if (css === 'transparent') {
189
+ return {
190
+ css: 'transparent',
191
+ rgba: 0x00000000
192
+ };
193
+ }
194
+
187
195
  // Validate the context is available for canvas-based color parsing
188
196
  if (!$ctx || !$litmusColor) {
189
197
  throw new Error('css.toColor: Unsupported css format');
@@ -65,6 +65,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
65
65
  public readonly onData = this._onData.event;
66
66
  protected _onLineFeed = this._register(new Emitter<void>());
67
67
  public readonly onLineFeed = this._onLineFeed.event;
68
+ protected readonly _onRender = this._register(new Emitter<{ start: number, end: number }>());
69
+ public readonly onRender = this._onRender.event;
68
70
  private readonly _onResize = this._register(new Emitter<{ cols: number, rows: number }>());
69
71
  public readonly onResize = this._onResize.event;
70
72
  protected readonly _onWriteParsed = this._register(new Emitter<void>());
@@ -124,7 +126,6 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
124
126
  // Register input handler and handle/forward events
125
127
  this._inputHandler = this._register(new InputHandler(this._bufferService, this._charsetService, this.coreService, this._logService, this.optionsService, this._oscLinkService, this.coreMouseService, this.unicodeService));
126
128
  this._register(Event.forward(this._inputHandler.onLineFeed, this._onLineFeed));
127
- this._register(this._inputHandler);
128
129
 
129
130
  // Setup listeners
130
131
  this._register(Event.forward(this._bufferService.onResize, this._onResize));
@@ -22,6 +22,7 @@ import { DcsHandler } from 'common/parser/DcsParser';
22
22
  import { IBuffer } from 'common/buffer/Types';
23
23
  import { parseColor } from 'common/input/XParseColor';
24
24
  import { Emitter } from 'vs/base/common/event';
25
+ import { XTERM_VERSION } from 'common/Version';
25
26
 
26
27
  /**
27
28
  * Map collect to glevel. Used in `selectCharset`.
@@ -239,6 +240,7 @@ export class InputHandler extends Disposable implements IInputHandler {
239
240
  this._parser.registerCsiHandler({ final: 'T' }, params => this.scrollDown(params));
240
241
  this._parser.registerCsiHandler({ final: 'X' }, params => this.eraseChars(params));
241
242
  this._parser.registerCsiHandler({ final: 'Z' }, params => this.cursorBackwardTab(params));
243
+ this._parser.registerCsiHandler({ final: '^' }, params => this.scrollDown(params));
242
244
  this._parser.registerCsiHandler({ final: '`' }, params => this.charPosAbsolute(params));
243
245
  this._parser.registerCsiHandler({ final: 'a' }, params => this.hPositionRelative(params));
244
246
  this._parser.registerCsiHandler({ final: 'b' }, params => this.repeatPrecedingCharacter(params));
@@ -256,6 +258,7 @@ export class InputHandler extends Disposable implements IInputHandler {
256
258
  this._parser.registerCsiHandler({ final: 'n' }, params => this.deviceStatus(params));
257
259
  this._parser.registerCsiHandler({ prefix: '?', final: 'n' }, params => this.deviceStatusPrivate(params));
258
260
  this._parser.registerCsiHandler({ intermediates: '!', final: 'p' }, params => this.softReset(params));
261
+ this._parser.registerCsiHandler({ prefix: '>', final: 'q' }, params => this.sendXtVersion(params));
259
262
  this._parser.registerCsiHandler({ intermediates: ' ', final: 'q' }, params => this.setCursorStyle(params));
260
263
  this._parser.registerCsiHandler({ final: 'r' }, params => this.setScrollRegion(params));
261
264
  this._parser.registerCsiHandler({ final: 's' }, params => this.saveCursor(params));
@@ -531,6 +534,12 @@ export class InputHandler extends Disposable implements IInputHandler {
531
534
  for (let pos = start; pos < end; ++pos) {
532
535
  code = data[pos];
533
536
 
537
+ // Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat
538
+ // it as a zero-width hint to text layout engines and simply ignore it.
539
+ if (code === 0xAD) {
540
+ continue;
541
+ }
542
+
534
543
  // get charset replacement character
535
544
  // charset is only defined for ASCII, therefore we only
536
545
  // search for an replacement char if code < 127
@@ -1145,7 +1154,10 @@ export class InputHandler extends Disposable implements IInputHandler {
1145
1154
  * @param respectProtect Whether to respect the protection attribute (DECSCA).
1146
1155
  */
1147
1156
  private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false, respectProtect: boolean = false): void {
1148
- const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
1157
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y);
1158
+ if (!line) {
1159
+ return;
1160
+ }
1149
1161
  line.replaceCells(
1150
1162
  start,
1151
1163
  end,
@@ -1215,7 +1227,10 @@ export class InputHandler extends Disposable implements IInputHandler {
1215
1227
  this._eraseInBufferLine(j, 0, this._activeBuffer.x + 1, true, respectProtect);
1216
1228
  if (this._activeBuffer.x + 1 >= this._bufferService.cols) {
1217
1229
  // Deleted entire previous line. This next line can no longer be wrapped.
1218
- this._activeBuffer.lines.get(j + 1)!.isWrapped = false;
1230
+ const nextLine = this._activeBuffer.lines.get(j + 1);
1231
+ if (nextLine) {
1232
+ nextLine.isWrapped = false;
1233
+ }
1219
1234
  }
1220
1235
  while (j--) {
1221
1236
  this._resetBufferLine(j, respectProtect);
@@ -1721,6 +1736,22 @@ export class InputHandler extends Disposable implements IInputHandler {
1721
1736
  return true;
1722
1737
  }
1723
1738
 
1739
+ /**
1740
+ * CSI > Ps q
1741
+ * Ps = 0 => Report xterm name and version (XTVERSION).
1742
+ *
1743
+ * The response is a DCS sequence identifying the version: DCS > | text ST
1744
+ *
1745
+ * @vt: #Y CSI XTVERSION "Report Xterm Version" "CSI > q" "Report the terminal name and version."
1746
+ */
1747
+ public sendXtVersion(params: IParams): boolean {
1748
+ if (params.params[0] > 0) {
1749
+ return true;
1750
+ }
1751
+ this._coreService.triggerDataEvent(`${C0.ESC}P>|xterm.js(${XTERM_VERSION})${C0.ESC}\\`);
1752
+ return true;
1753
+ }
1754
+
1724
1755
  /**
1725
1756
  * Evaluate if the current terminal is the given argument.
1726
1757
  * @param term The terminal name to evaluate
@@ -1853,7 +1884,7 @@ export class InputHandler extends Disposable implements IInputHandler {
1853
1884
  * | 7 | Auto-wrap Mode (DECAWM). | #Y |
1854
1885
  * | 8 | Auto-repeat Keys (DECARM). Always on. | #N |
1855
1886
  * | 9 | X10 xterm mouse protocol. | #Y |
1856
- * | 12 | Start Blinking Cursor. | #Y |
1887
+ * | 12 | Start Blinking Cursor. | #P[Requires the allowSetCursorBlink quirk option enabled.] |
1857
1888
  * | 25 | Show Cursor (DECTCEM). | #Y |
1858
1889
  * | 45 | Reverse wrap-around. | #Y |
1859
1890
  * | 47 | Use Alternate Screen Buffer. | #Y |
@@ -1906,7 +1937,9 @@ export class InputHandler extends Disposable implements IInputHandler {
1906
1937
  this._coreService.decPrivateModes.wraparound = true;
1907
1938
  break;
1908
1939
  case 12:
1909
- this._optionsService.options.cursorBlink = true;
1940
+ if (this._optionsService.rawOptions.quirks?.allowSetCursorBlink) {
1941
+ this._optionsService.options.cursorBlink = true;
1942
+ }
1910
1943
  break;
1911
1944
  case 45:
1912
1945
  this._coreService.decPrivateModes.reverseWraparound = true;
@@ -2101,7 +2134,7 @@ export class InputHandler extends Disposable implements IInputHandler {
2101
2134
  * | 7 | No Wraparound Mode (DECAWM). | #Y |
2102
2135
  * | 8 | No Auto-repeat Keys (DECARM). | #N |
2103
2136
  * | 9 | Don't send Mouse X & Y on button press. | #Y |
2104
- * | 12 | Stop Blinking Cursor. | #Y |
2137
+ * | 12 | Stop Blinking Cursor. | #P[Requires the allowSetCursorBlink quirk option enabled.] |
2105
2138
  * | 25 | Hide Cursor (DECTCEM). | #Y |
2106
2139
  * | 45 | No reverse wrap-around. | #Y |
2107
2140
  * | 47 | Use Normal Screen Buffer. | #Y |
@@ -2147,7 +2180,9 @@ export class InputHandler extends Disposable implements IInputHandler {
2147
2180
  this._coreService.decPrivateModes.wraparound = false;
2148
2181
  break;
2149
2182
  case 12:
2150
- this._optionsService.options.cursorBlink = false;
2183
+ if (this._optionsService.rawOptions.quirks?.allowSetCursorBlink) {
2184
+ this._optionsService.options.cursorBlink = false;
2185
+ }
2151
2186
  break;
2152
2187
  case 45:
2153
2188
  this._coreService.decPrivateModes.reverseWraparound = false;
@@ -2901,6 +2936,10 @@ export class InputHandler extends Disposable implements IInputHandler {
2901
2936
  this._activeBuffer.savedCurAttrData.fg = this._curAttrData.fg;
2902
2937
  this._activeBuffer.savedCurAttrData.bg = this._curAttrData.bg;
2903
2938
  this._activeBuffer.savedCharset = this._charsetService.charset;
2939
+ this._activeBuffer.savedCharsets = this._charsetService.charsets.slice();
2940
+ this._activeBuffer.savedGlevel = this._charsetService.glevel;
2941
+ this._activeBuffer.savedOriginMode = this._coreService.decPrivateModes.origin;
2942
+ this._activeBuffer.savedWraparoundMode = this._coreService.decPrivateModes.wraparound;
2904
2943
  return true;
2905
2944
  }
2906
2945
 
@@ -2918,10 +2957,12 @@ export class InputHandler extends Disposable implements IInputHandler {
2918
2957
  this._activeBuffer.y = Math.max(this._activeBuffer.savedY - this._activeBuffer.ybase, 0);
2919
2958
  this._curAttrData.fg = this._activeBuffer.savedCurAttrData.fg;
2920
2959
  this._curAttrData.bg = this._activeBuffer.savedCurAttrData.bg;
2921
- this._charsetService.charset = (this as any)._savedCharset;
2922
- if (this._activeBuffer.savedCharset) {
2923
- this._charsetService.charset = this._activeBuffer.savedCharset;
2960
+ for (let i = 0; i < this._activeBuffer.savedCharsets.length; i++) {
2961
+ this._charsetService.setgCharset(i, this._activeBuffer.savedCharsets[i]);
2924
2962
  }
2963
+ this._charsetService.setgLevel(this._activeBuffer.savedGlevel);
2964
+ this._coreService.decPrivateModes.origin = this._activeBuffer.savedOriginMode;
2965
+ this._coreService.decPrivateModes.wraparound = this._activeBuffer.savedWraparoundMode;
2925
2966
  this._restrictCursor();
2926
2967
  return true;
2927
2968
  }
@@ -3320,6 +3361,8 @@ export class InputHandler extends Disposable implements IInputHandler {
3320
3361
  * ESC c
3321
3362
  * DEC mnemonic: RIS (https://vt100.net/docs/vt510-rm/RIS.html)
3322
3363
  * Reset to initial state.
3364
+ *
3365
+ * @vt: #Y ESC RIS "Full Reset" "ESC c" "Reset to initial state."
3323
3366
  */
3324
3367
  public fullReset(): boolean {
3325
3368
  this._parser.reset();
@@ -14,7 +14,10 @@ interface INavigator {
14
14
  declare const navigator: INavigator;
15
15
  declare const process: unknown;
16
16
 
17
- export const isNode = (typeof process !== 'undefined' && 'title' in (process as any)) ? true : false;
17
+ // navigator.userAgent is also checked here because bundling with the process module can cause
18
+ // issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is
19
+ // "Node.js/<version>".
20
+ export const isNode = (typeof process !== 'undefined' && 'title' in (process as any) && (typeof navigator === 'undefined' || navigator.userAgent.startsWith('Node.js/'))) ? true : false;
18
21
  const userAgent = (isNode) ? 'node' : navigator.userAgent;
19
22
  const platform = (isNode) ? 'node' : navigator.platform;
20
23
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { IDeleteEvent, IInsertEvent } from 'common/CircularList';
7
- import { Attributes, UnderlineStyle } from 'common/buffer/Constants'; // eslint-disable-line no-unused-vars
7
+ import { UnderlineStyle } from 'common/buffer/Constants';
8
8
  import { IBufferSet } from 'common/buffer/Types';
9
9
  import { IParams } from 'common/parser/Types';
10
10
  import { ICoreMouseService, ICoreService, IOptionsService, IUnicodeService } from 'common/services/Services';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Copyright (c) 2025 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ /**
7
+ * The xterm.js version. This is updated by the publish script from package.json.
8
+ */
9
+ export const XTERM_VERSION = '6.1.0-beta.90';
@@ -38,6 +38,10 @@ export class Buffer implements IBuffer {
38
38
  public savedX: number = 0;
39
39
  public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
40
40
  public savedCharset: ICharset | undefined = DEFAULT_CHARSET;
41
+ public savedCharsets: (ICharset | undefined)[] = [];
42
+ public savedGlevel: number = 0;
43
+ public savedOriginMode: boolean = false;
44
+ public savedWraparoundMode: boolean = true;
41
45
  public markers: Marker[] = [];
42
46
  private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
43
47
  private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
@@ -22,6 +22,10 @@ export interface IBuffer {
22
22
  savedY: number;
23
23
  savedX: number;
24
24
  savedCharset: ICharset | undefined;
25
+ savedCharsets: (ICharset | undefined)[];
26
+ savedGlevel: number;
27
+ savedOriginMode: boolean;
28
+ savedWraparoundMode: boolean;
25
29
  savedCurAttrData: IAttributeData;
26
30
  isCursorInViewport: boolean;
27
31
  markers: IMarker[];
@@ -246,7 +246,7 @@ CHARSETS['='] = {
246
246
  '\\': 'ç',
247
247
  ']': 'ê',
248
248
  '^': 'î',
249
- // eslint-disable-next-line @typescript-eslint/naming-convention
249
+
250
250
  '_': 'è',
251
251
  '`': 'ô',
252
252
  '{': 'ä',
@@ -315,6 +315,8 @@ export function evaluateKeyboardEvent(
315
315
  result.key = String.fromCharCode(ev.keyCode - 51 + 27);
316
316
  } else if (ev.keyCode === 56) {
317
317
  result.key = C0.DEL;
318
+ } else if (ev.key === '/') {
319
+ result.key = C0.US; // https://github.com/xtermjs/xterm.js/issues/5457
318
320
  } else if (ev.keyCode === 219) {
319
321
  result.key = C0.ESC;
320
322
  } else if (ev.keyCode === 220) {
@@ -358,12 +360,11 @@ export function evaluateKeyboardEvent(
358
360
  // Include only keys that that result in a _single_ character; don't include num lock,
359
361
  // volume up, etc.
360
362
  result.key = ev.key;
361
- } else if (ev.key && ev.ctrlKey) {
362
- if (ev.key === '_') { // ^_
363
- result.key = C0.US;
364
- }
365
- if (ev.key === '@') { // ^ + shift + 2 = ^ + @
366
- result.key = C0.NUL;
363
+ } else if (ev.key && ev.ctrlKey && ev.shiftKey) {
364
+ switch (ev.code) {
365
+ case 'Minus': result.key = C0.US; break; // ^_ (Ctrl+Shift+-_
366
+ case 'Digit2': result.key = C0.NUL; break; // ^@ (Ctrl+Shift+2)
367
+ case 'Digit6': result.key = C0.RS; break; // ^^ (Ctrl+Shift+6)
367
368
  }
368
369
  }
369
370
  break;
@@ -14,6 +14,10 @@ export class CharsetService implements ICharsetService {
14
14
 
15
15
  private _charsets: (ICharset | undefined)[] = [];
16
16
 
17
+ public get charsets(): (ICharset | undefined)[] {
18
+ return this._charsets;
19
+ }
20
+
17
21
  public reset(): void {
18
22
  this.charset = undefined;
19
23
  this._charsets = [];
@@ -14,6 +14,8 @@ import { Emitter } from 'vs/base/common/event';
14
14
  // Work variables to avoid garbage collection
15
15
  let $xmin = 0;
16
16
  let $xmax = 0;
17
+ let $ymin = 0;
18
+ let $ymax = 0;
17
19
 
18
20
  export class DecorationService extends Disposable implements IDecorationService {
19
21
  public serviceBrand: any;
@@ -70,7 +72,14 @@ export class DecorationService extends Disposable implements IDecorationService
70
72
  public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator<IInternalDecoration> {
71
73
  let xmin = 0;
72
74
  let xmax = 0;
73
- for (const d of this._decorations.getKeyIterator(line)) {
75
+ let ymin = 0;
76
+ let ymax = 0;
77
+ for (const d of this._decorations.values()) {
78
+ ymin = d.marker.line;
79
+ ymax = ymin + (d.options.height ?? 1);
80
+ if (line < ymin || line >= ymax) {
81
+ continue;
82
+ }
74
83
  xmin = d.options.x ?? 0;
75
84
  xmax = xmin + (d.options.width ?? 1);
76
85
  if (x >= xmin && x < xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) {
@@ -80,13 +89,18 @@ export class DecorationService extends Disposable implements IDecorationService
80
89
  }
81
90
 
82
91
  public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void {
83
- this._decorations.forEachByKey(line, d => {
92
+ for (const d of this._decorations.values()) {
93
+ $ymin = d.marker.line;
94
+ $ymax = $ymin + (d.options.height ?? 1);
95
+ if (line < $ymin || line >= $ymax) {
96
+ continue;
97
+ }
84
98
  $xmin = d.options.x ?? 0;
85
99
  $xmax = $xmin + (d.options.width ?? 1);
86
100
  if (x >= $xmin && x < $xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) {
87
101
  callback(d);
88
102
  }
89
- });
103
+ }
90
104
  }
91
105
  }
92
106
 
@@ -16,7 +16,6 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
16
16
  cursorStyle: 'block',
17
17
  cursorWidth: 1,
18
18
  cursorInactiveStyle: 'outline',
19
- customGlyphs: true,
20
19
  drawBoldTextInBrightColors: true,
21
20
  documentOverride: null,
22
21
  fastScrollSensitivity: 5,
@@ -54,7 +53,8 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
54
53
  convertEol: false,
55
54
  termName: 'xterm',
56
55
  cancelEvents: false,
57
- overviewRuler: {}
56
+ overviewRuler: {},
57
+ quirks: {}
58
58
  };
59
59
 
60
60
  const FONT_WEIGHT_OPTIONS: Extract<FontWeight, string>[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'];
@@ -116,6 +116,7 @@ export interface ICharsetService {
116
116
 
117
117
  charset: ICharset | undefined;
118
118
  readonly glevel: number;
119
+ readonly charsets: (ICharset | undefined)[];
119
120
 
120
121
  reset(): void;
121
122
 
@@ -230,7 +231,6 @@ export interface ITerminalOptions {
230
231
  cursorStyle?: CursorStyle;
231
232
  cursorWidth?: number;
232
233
  cursorInactiveStyle?: CursorInactiveStyle;
233
- customGlyphs?: boolean;
234
234
  disableStdin?: boolean;
235
235
  documentOverride?: any | null;
236
236
  drawBoldTextInBrightColors?: boolean;
@@ -263,6 +263,7 @@ export interface ITerminalOptions {
263
263
  windowOptions?: IWindowOptions;
264
264
  wordSeparator?: string;
265
265
  overviewRuler?: IOverviewRulerOptions;
266
+ quirks?: ITerminalQuirks;
266
267
  scrollOnEraseInDisplay?: boolean;
267
268
 
268
269
  [key: string]: any;
@@ -301,6 +302,10 @@ export interface ITheme {
301
302
  extendedAnsi?: string[];
302
303
  }
303
304
 
305
+ export interface ITerminalQuirks {
306
+ allowSetCursorBlink?: boolean;
307
+ }
308
+
304
309
  export const IOscLinkService = createDecorator<IOscLinkService>('OscLinkService');
305
310
  export interface IOscLinkService {
306
311
  serviceBrand: undefined;