Merge branch 'master' into #4121

This commit is contained in:
xie jialong 努力鸭
2023-06-07 19:23:01 +08:00
committed by GitHub
36 changed files with 680 additions and 253 deletions
+153 -2
View File
@@ -8,16 +8,32 @@ import { ITerminal, IRenderDebouncer } from 'browser/Types';
import { isMac } from 'common/Platform';
import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { IRenderService } from 'browser/services/Services';
import { addDisposableDomListener } from 'browser/Lifecycle';
const MAX_ROWS_TO_READ = 20;
const enum BoundaryPosition {
TOP,
BOTTOM
}
export class AccessibilityManager extends Disposable {
private _accessibilityContainer: HTMLElement;
private _rowContainer: HTMLElement;
private _rowElements: HTMLElement[];
private _liveRegion: HTMLElement;
private _liveRegionLineCount: number = 0;
private _liveRegionDebouncer: IRenderDebouncer;
private _screenDprMonitor: ScreenDprMonitor;
private _topBoundaryFocusListener: (e: FocusEvent) => void;
private _bottomBoundaryFocusListener: (e: FocusEvent) => void;
/**
* This queue has a character pushed to it for keys that are pressed, if the
* next character added to the terminal is equal to the key char then it is
@@ -32,12 +48,30 @@ export class AccessibilityManager extends Disposable {
private _charsToAnnounce: string = '';
constructor(
private readonly _terminal: ITerminal
private readonly _terminal: ITerminal,
@IRenderService private readonly _renderService: IRenderService
) {
super();
this._accessibilityContainer = document.createElement('div');
this._accessibilityContainer.classList.add('xterm-accessibility');
this._rowContainer = document.createElement('div');
this._rowContainer.setAttribute('role', 'list');
this._rowContainer.classList.add('xterm-accessibility-tree');
this._rowElements = [];
for (let i = 0; i < this._terminal.rows; i++) {
this._rowElements[i] = this._createAccessibilityTreeNode();
this._rowContainer.appendChild(this._rowElements[i]);
}
this._topBoundaryFocusListener = e => this._handleBoundaryFocus(e, BoundaryPosition.TOP);
this._bottomBoundaryFocusListener = e => this._handleBoundaryFocus(e, BoundaryPosition.BOTTOM);
this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
this._refreshRowsDimensions();
this._accessibilityContainer.appendChild(this._rowContainer);
this._liveRegion = document.createElement('div');
this._liveRegion.classList.add('live-region');
this._liveRegion.setAttribute('aria-live', 'assertive');
@@ -50,6 +84,7 @@ export class AccessibilityManager extends Disposable {
this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityContainer);
this.register(this._liveRegionDebouncer);
this.register(this._terminal.onResize(e => this._handleResize(e.rows)));
this.register(this._terminal.onRender(e => this._refreshRows(e.start, e.end)));
this.register(this._terminal.onScroll(() => this._refreshRows()));
// Line feed is an issue as the prompt won't be read out after a command is run
@@ -58,7 +93,20 @@ export class AccessibilityManager extends Disposable {
this.register(this._terminal.onA11yTab(spaceCount => this._handleTab(spaceCount)));
this.register(this._terminal.onKey(e => this._handleKey(e.key)));
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
this.register(toDisposable(() => this._accessibilityContainer.remove()));
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
this._screenDprMonitor = new ScreenDprMonitor(window);
this.register(this._screenDprMonitor);
this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
// This shouldn't be needed on modern browsers but is present in case the
// media query that drives the ScreenDprMonitor isn't supported
this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions()));
this._refreshRows();
this.register(toDisposable(() => {
this._accessibilityContainer.remove();
this._rowElements.length = 0;
}));
}
private _handleTab(spaceCount: number): void {
@@ -126,4 +174,107 @@ export class AccessibilityManager extends Disposable {
this._liveRegion.textContent += this._charsToAnnounce;
this._charsToAnnounce = '';
}
private _handleBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void {
const boundaryElement = e.target as HTMLElement;
const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2];
// Don't scroll if the buffer top has reached the end in that direction
const posInSet = boundaryElement.getAttribute('aria-posinset');
const lastRowPos = position === BoundaryPosition.TOP ? '1' : `${this._terminal.buffer.lines.length}`;
if (posInSet === lastRowPos) {
return;
}
// Don't scroll when the last focused item was not the second row (focus is going the other
// direction)
if (e.relatedTarget !== beforeBoundaryElement) {
return;
}
// Remove old boundary element from array
let topBoundaryElement: HTMLElement;
let bottomBoundaryElement: HTMLElement;
if (position === BoundaryPosition.TOP) {
topBoundaryElement = boundaryElement;
bottomBoundaryElement = this._rowElements.pop()!;
this._rowContainer.removeChild(bottomBoundaryElement);
} else {
topBoundaryElement = this._rowElements.shift()!;
bottomBoundaryElement = boundaryElement;
this._rowContainer.removeChild(topBoundaryElement);
}
// Remove listeners from old boundary elements
topBoundaryElement.removeEventListener('focus', this._topBoundaryFocusListener);
bottomBoundaryElement.removeEventListener('focus', this._bottomBoundaryFocusListener);
// Add new element to array/DOM
if (position === BoundaryPosition.TOP) {
const newElement = this._createAccessibilityTreeNode();
this._rowElements.unshift(newElement);
this._rowContainer.insertAdjacentElement('afterbegin', newElement);
} else {
const newElement = this._createAccessibilityTreeNode();
this._rowElements.push(newElement);
this._rowContainer.appendChild(newElement);
}
// Add listeners to new boundary elements
this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
// Scroll up
this._terminal.scrollLines(position === BoundaryPosition.TOP ? -1 : 1);
// Focus new boundary before element
this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2].focus();
// Prevent the standard behavior
e.preventDefault();
e.stopImmediatePropagation();
}
private _handleResize(rows: number): void {
// Remove bottom boundary listener
this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener);
// Grow rows as required
for (let i = this._rowContainer.children.length; i < this._terminal.rows; i++) {
this._rowElements[i] = this._createAccessibilityTreeNode();
this._rowContainer.appendChild(this._rowElements[i]);
}
// Shrink rows as required
while (this._rowElements.length > rows) {
this._rowContainer.removeChild(this._rowElements.pop()!);
}
// Add bottom boundary listener
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
this._refreshRowsDimensions();
}
private _createAccessibilityTreeNode(): HTMLElement {
const element = document.createElement('div');
element.setAttribute('role', 'listitem');
element.tabIndex = -1;
this._refreshRowDimensions(element);
return element;
}
private _refreshRowsDimensions(): void {
if (!this._renderService.dimensions.css.cell.height) {
return;
}
this._accessibilityContainer.style.width = `${this._renderService.dimensions.css.canvas.width}px`;
if (this._rowElements.length !== this._terminal.rows) {
this._handleResize(this._terminal.rows);
}
for (let i = 0; i < this._terminal.rows; i++) {
this._refreshRowDimensions(this._rowElements[i]);
}
}
private _refreshRowDimensions(element: HTMLElement): void {
element.style.height = `${this._renderService.dimensions.css.cell.height}px`;
}
}
@@ -72,6 +72,7 @@ export class BufferDecorationRenderer extends Disposable {
private _createElement(decoration: IInternalDecoration): HTMLElement {
const element = document.createElement('div');
element.classList.add('xterm-decoration');
element.classList.toggle('xterm-decoration-top-layer', decoration?.options?.layer === 'top');
element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`;
element.style.height = `${(decoration.options.height || 1) * this._renderService.dimensions.css.cell.height}px`;
element.style.top = `${(decoration.marker.line - this._bufferService.buffers.active.ydisp) * this._renderService.dimensions.css.cell.height}px`;
+43 -6
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { BOLD_CLASS, CURSOR_BLINK_CLASS, CURSOR_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory, ITALIC_CLASS } from 'browser/renderer/dom/DomRendererRowFactory';
import { BOLD_CLASS, CURSOR_BLINK_CLASS, CURSOR_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DIM_CLASS, DomRendererRowFactory, ITALIC_CLASS } from 'browser/renderer/dom/DomRendererRowFactory';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
@@ -53,7 +53,7 @@ export class DomRenderer extends Disposable implements IRenderer {
@IOptionsService private readonly _optionsService: IOptionsService,
@IBufferService private readonly _bufferService: IBufferService,
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
@IThemeService themeService: IThemeService
@IThemeService private readonly _themeService: IThemeService
) {
super();
this._rowContainer = document.createElement('div');
@@ -69,8 +69,8 @@ export class DomRenderer extends Disposable implements IRenderer {
this._updateDimensions();
this.register(this._optionsService.onOptionChange(() => this._handleOptionsChanged()));
this.register(themeService.onChangeColors(e => this._injectCss(e)));
this._injectCss(themeService.colors);
this.register(this._themeService.onChangeColors(e => this._injectCss(e)));
this._injectCss(this._themeService.colors);
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document);
@@ -149,6 +149,10 @@ export class DomRenderer extends Disposable implements IRenderer {
` font-family: ${this._optionsService.rawOptions.fontFamily};` +
` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
`}`;
styles +=
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .xterm-dim {` +
` color: ${color.multiplyOpacity(colors.foreground, 0.5).css};` +
`}`;
// Text styles
styles +=
`${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +
@@ -224,10 +228,12 @@ export class DomRenderer extends Disposable implements IRenderer {
for (const [i, c] of colors.ansi.entries()) {
styles +=
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i}.${DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` +
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
}
styles +=
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` +
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
this._themeStyleElement.textContent = styles;
@@ -340,6 +346,8 @@ export class DomRenderer extends Disposable implements IRenderer {
private _handleOptionsChanged(): void {
// Force a refresh
this._updateDimensions();
// Refresh CSS
this._injectCss(this._themeService.colors);
}
public clear(): void {
@@ -385,8 +393,37 @@ export class DomRenderer extends Disposable implements IRenderer {
}
private _setCellUnderline(x: number, x2: number, y: number, y2: number, cols: number, enabled: boolean): void {
x = this._cellToRowElements[y][x];
x2 = this._cellToRowElements[y2][x2];
/**
* NOTE: The linkifier may send out of viewport y-values if:
* - negative y-value: the link started at a higher line
* - y-value >= maxY: the link ends at a line below viewport
*
* For negative y-values we can simply adjust x = 0,
* as higher up link start means, that everything from
* (0,0) is a link under top-down-left-right char progression
*
* Additionally there might be a small chance of out-of-sync x|y-values
* from a race condition of render updates vs. link event handler execution:
* - (sync) resize: chances terminal buffer in sync, schedules render update async
* - (async) link handler race condition: new buffer metrics, but still on old render state
* - (async) render update: brings term metrics and render state back in sync
*/
if (y < 0) x = 0;
if (y2 < 0) x2 = 0;
// avoid out-of-sync y-values, simply clamp into valid area
const maxY = this._cellToRowElements.length - 1;
y = Math.max(Math.min(y, maxY), 0);
y2 = Math.max(Math.min(y2, maxY), 0);
const elemY = this._cellToRowElements[y];
const elemY2 = this._cellToRowElements[y2];
if (x >= elemY.length || x2 >= elemY2.length) {
// avoid out-of-sync x-values
// simply exit early, gets fixed by the next render update
return;
}
x = elemY[x];
x2 = elemY2[x2];
if (x === -1 || x2 === -1) {
return;
@@ -167,6 +167,16 @@ describe('DomRendererRowFactory', () => {
});
});
it('should add class for overline', () => {
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.OVERLINE;
lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_ELEM_MAPPING);
assert.equal(getFragmentHtml(fragment),
'<span class="xterm-overline">a</span>'
);
});
it('should add class for strikethrough', () => {
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.STRIKETHROUGH;
@@ -9,7 +9,6 @@ import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/
import { CellData } from 'common/buffer/CellData';
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { color, rgba } from 'common/Color';
import { IColorSet, ReadonlyColorSet } from 'browser/Types';
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
import { excludeFromContrastRatioDemands } from 'browser/renderer/shared/RendererUtils';
@@ -19,6 +18,7 @@ export const BOLD_CLASS = 'xterm-bold';
export const DIM_CLASS = 'xterm-dim';
export const ITALIC_CLASS = 'xterm-italic';
export const UNDERLINE_CLASS = 'xterm-underline';
export const OVERLINE_CLASS = 'xterm-overline';
export const STRIKETHROUGH_CLASS = 'xterm-strikethrough';
export const CURSOR_CLASS = 'xterm-cursor';
export const CURSOR_BLINK_CLASS = 'xterm-cursor-blink';
@@ -186,6 +186,13 @@ export class DomRendererRowFactory {
}
}
if (cell.isOverline()) {
charElement.classList.add(OVERLINE_CLASS);
if (charElement.textContent === ' ') {
charElement.textContent = '\xa0'; // = &nbsp;
}
}
if (cell.isStrikethrough()) {
charElement.classList.add(STRIKETHROUGH_CLASS);
}
+16 -9
View File
@@ -304,12 +304,6 @@ export class TextureAtlas implements ITextureAtlas {
break;
}
if (dim) {
// Blend here instead of using opacity because transparent colors mess with clipping the
// glyph's bounding box
result = color.blend(this._config.colors.background, color.multiplyOpacity(result, DIM_OPACITY));
}
return result;
}
@@ -429,12 +423,12 @@ export class TextureAtlas implements ITextureAtlas {
// Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used
// to draw the glyph to the canvas as well as to restrict the bounding box search to ensure
// giant ligatures (eg. =====>) don't impact overall performance.
const allowedWidth = this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2;
const allowedWidth = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._textureSize);
if (this._tmpCanvas.width < allowedWidth) {
this._tmpCanvas.width = allowedWidth;
}
// Include line height when drawing glyphs
const allowedHeight = this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 4;
const allowedHeight = Math.min(this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 4, this._textureSize);
if (this._tmpCanvas.height < allowedHeight) {
this._tmpCanvas.height = allowedHeight;
}
@@ -455,6 +449,7 @@ export class TextureAtlas implements ITextureAtlas {
const italic = !!this._workAttributeData.isItalic();
const underline = !!this._workAttributeData.isUnderline();
const strikethrough = !!this._workAttributeData.isStrikethrough();
const overline = !!this._workAttributeData.isOverline();
let fgColor = this._workAttributeData.getFgColor();
let fgColorMode = this._workAttributeData.getFgColorMode();
let bgColor = this._workAttributeData.getBgColor();
@@ -638,12 +633,24 @@ export class TextureAtlas implements ITextureAtlas {
}
}
// Overline
if (overline) {
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 15));
const yOffset = lineWidth % 2 === 1 ? 0.5 : 0;
this._tmpCtx.lineWidth = lineWidth;
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
this._tmpCtx.beginPath();
this._tmpCtx.moveTo(padding, padding + yOffset);
this._tmpCtx.lineTo(padding + this._config.deviceCharWidth * chWidth, padding + yOffset);
this._tmpCtx.stroke();
}
// Draw the character
if (!customGlyph) {
this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight);
}
// If this charcater is underscore and beyond the cell bounds, shift it up until it is visible
// If this character is underscore and beyond the cell bounds, shift it up until it is visible
// even on the bottom row, try for a maximum of 5 pixels.
if (chars === '_' && !this._config.allowTransparency) {
let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck);
+3 -3
View File
@@ -2222,7 +2222,7 @@ describe('InputHandler', () => {
});
it('ANSI 2 (keyboard action mode)', async () => {
await inputHandler.parseP('\x1b[2$p');
assert.deepEqual(reportStack.pop(), '\x1b[2;3$y'); // always set
assert.deepEqual(reportStack.pop(), '\x1b[2;4$y'); // always reset
});
it('ANSI 4 (insert mode)', async () => {
await inputHandler.parseP('\x1b[4$p');
@@ -2236,7 +2236,7 @@ describe('InputHandler', () => {
});
it('ANSI 12 (send/receive)', async () => {
await inputHandler.parseP('\x1b[12$p');
assert.deepEqual(reportStack.pop(), '\x1b[12;4$y'); // always reset
assert.deepEqual(reportStack.pop(), '\x1b[12;3$y'); // always set
});
it('ANSI 20 (newline mode)', async () => {
await inputHandler.parseP('\x1b[20$p');
@@ -2280,7 +2280,7 @@ describe('InputHandler', () => {
});
it('DEC privates perma modes', async () => {
// [mode number, state value]
const perma = [[3, 0], [8, 3], [1005, 4], [1015, 4], [1048, 1]];
const perma = [[3, 0], [8, 3], [67, 4], [1005, 4], [1015, 4], [1048, 1]];
for (const [mode, value] of perma) {
await inputHandler.parseP(`\x1b[?${mode}$p`);
assert.deepEqual(reportStack.pop(), `\x1b[?${mode};${value}$y`);
+11 -2
View File
@@ -2223,9 +2223,9 @@ export class InputHandler extends Disposable implements IInputHandler {
const p = params.params[0];
if (ansi) {
if (p === 2) return f(p, V.PERMANENTLY_SET);
if (p === 2) return f(p, V.PERMANENTLY_RESET);
if (p === 4) return f(p, b2v(cs.modes.insertMode));
if (p === 12) return f(p, V.PERMANENTLY_RESET);
if (p === 12) return f(p, V.PERMANENTLY_SET);
if (p === 20) return f(p, b2v(opts.convertEol));
return f(p, V.NOT_RECOGNIZED);
}
@@ -2240,6 +2240,7 @@ export class InputHandler extends Disposable implements IInputHandler {
if (p === 25) return f(p, b2v(!cs.isCursorHidden));
if (p === 45) return f(p, b2v(dm.reverseWraparound));
if (p === 66) return f(p, b2v(dm.applicationKeypad));
if (p === 67) return f(p, V.PERMANENTLY_RESET);
if (p === 1000) return f(p, b2v(mouseProtocol === 'VT200'));
if (p === 1002) return f(p, b2v(mouseProtocol === 'DRAG'));
if (p === 1003) return f(p, b2v(mouseProtocol === 'ANY'));
@@ -2425,6 +2426,8 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 47 | Background color: White. | #Y |
* | 48 | Background color: Extended color. | #P[Support for RGB and indexed colors, see below.] |
* | 49 | Background color: Default (original). | #Y |
* | 53 | Overlined. | #Y |
* | 55 | Not Overlined. | #Y |
* | 58 | Underline color: Extended color. | #P[Support for RGB and indexed colors, see below.] |
* | 90 - 97 | Bright foreground color (analogous to 30 - 37). | #Y |
* | 100 - 107 | Bright background color (analogous to 40 - 47). | #Y |
@@ -2551,6 +2554,12 @@ export class InputHandler extends Disposable implements IInputHandler {
} else if (p === 38 || p === 48 || p === 58) {
// fg color 256 and RGB
i += this._extractColor(params, i, attr);
} else if (p === 53) {
// overline
attr.bg |= BgFlags.OVERLINE;
} else if (p === 55) {
// not overline
attr.bg &= ~BgFlags.OVERLINE;
} else if (p === 59) {
attr.extended = attr.extended.clone();
attr.extended.underlineColor = -1;
+1
View File
@@ -165,6 +165,7 @@ export interface IAttributeData {
isDim(): number;
isStrikethrough(): number;
isProtected(): number;
isOverline(): number;
/**
* The color mode of the foreground color which determines how to decode {@link getFgColor},
+1
View File
@@ -47,6 +47,7 @@ export class AttributeData implements IAttributeData {
public isDim(): number { return this.bg & BgFlags.DIM; }
public isStrikethrough(): number { return this.fg & FgFlags.STRIKETHROUGH; }
public isProtected(): number { return this.bg & BgFlags.PROTECTED; }
public isOverline(): number { return this.bg & BgFlags.OVERLINE; }
// color modes
public getFgColorMode(): number { return this.fg & Attributes.CM_MASK; }
+2 -1
View File
@@ -128,7 +128,8 @@ export const enum BgFlags {
ITALIC = 0x4000000,
DIM = 0x8000000,
HAS_EXTENDED = 0x10000000,
PROTECTED = 0x20000000
PROTECTED = 0x20000000,
OVERLINE = 0x40000000
}
export const enum ExtFlags {
+1 -1
View File
@@ -22,7 +22,7 @@ describe('Headless API Tests', function (): void {
it('Proposed API check', async () => {
term = new Terminal({ allowProposedApi: false });
throws(() => term.markers, (error) => error.message === 'You must set the allowProposedApi option to true to use proposed API');
throws(() => term.markers, (error: any) => error.message === 'You must set the allowProposedApi option to true to use proposed API');
});
it('write', async () => {