Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
714fda4575 | ||
|
|
d07813da37 |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@xterm/xterm",
|
"name": "@xterm/xterm",
|
||||||
"description": "Full xterm terminal, in your browser",
|
"description": "Full xterm terminal, in your browser",
|
||||||
"version": "6.0.0",
|
"version": "6.0.0-uniterm.1",
|
||||||
"main": "lib/xterm.js",
|
"main": "lib/xterm.js",
|
||||||
"module": "lib/xterm.mjs",
|
"module": "lib/xterm.mjs",
|
||||||
"style": "css/xterm.css",
|
"style": "css/xterm.css",
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { IRenderer } from 'browser/renderer/shared/Types';
|
|||||||
import { CharSizeService } from 'browser/services/CharSizeService';
|
import { CharSizeService } from 'browser/services/CharSizeService';
|
||||||
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
|
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
|
||||||
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
||||||
|
import { CellColorOverrideStore, ICellColorSpan } from 'browser/services/CellColorOverrideStore';
|
||||||
import { LinkProviderService } from 'browser/services/LinkProviderService';
|
import { LinkProviderService } from 'browser/services/LinkProviderService';
|
||||||
import { MouseService } from 'browser/services/MouseService';
|
import { MouseService } from 'browser/services/MouseService';
|
||||||
import { RenderService } from 'browser/services/RenderService';
|
import { RenderService } from 'browser/services/RenderService';
|
||||||
@@ -953,6 +954,29 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|||||||
return this._decorationService.registerDecoration(decorationOptions);
|
return this._decorationService.registerDecoration(decorationOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly cellColorOverrides: CellColorOverrideStore = new CellColorOverrideStore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recolor a contiguous cell range on one absolute buffer line. Pass null to
|
||||||
|
* clear the line. The affected viewport row is re-rendered lazily. Overrides
|
||||||
|
* are keyed by the active buffer's absolute line index; the caller must
|
||||||
|
* clear/re-set them when indices shift (scrollback trim, resize reflow) or
|
||||||
|
* when the alternate buffer changes.
|
||||||
|
*/
|
||||||
|
public setCellColorOverrides(lineY: number, spans: ICellColorSpan[] | null): void {
|
||||||
|
if (this.cellColorOverrides.set(lineY, spans)) {
|
||||||
|
const row = lineY - this._bufferService.buffer.ydisp;
|
||||||
|
if (row >= 0 && row < this._bufferService.rows) {
|
||||||
|
this._renderService?.refreshRows(row, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearCellColorOverrides(): void {
|
||||||
|
this.cellColorOverrides.clear();
|
||||||
|
this._renderService?.refreshRows(0, this._bufferService.rows - 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets whether the terminal has an active selection.
|
* Gets whether the terminal has an active selection.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from '@xterm/xterm';
|
import { CellColorOverrideStore } from 'browser/services/CellColorOverrideStore';
|
||||||
|
import { ICellColorSpan, IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from '@xterm/xterm';
|
||||||
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
||||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
||||||
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from 'browser/Types';
|
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from 'browser/Types';
|
||||||
@@ -30,6 +31,7 @@ export class TestTerminal extends CoreBrowserTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MockTerminal implements ITerminal {
|
export class MockTerminal implements ITerminal {
|
||||||
|
public readonly cellColorOverrides = new CellColorOverrideStore();
|
||||||
public onBlur!: Event<void>;
|
public onBlur!: Event<void>;
|
||||||
public onFocus!: Event<void>;
|
public onFocus!: Event<void>;
|
||||||
public onA11yChar!: Event<string>;
|
public onA11yChar!: Event<string>;
|
||||||
@@ -108,6 +110,12 @@ export class MockTerminal implements ITerminal {
|
|||||||
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
|
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
public setCellColorOverrides(lineY: number, spans: ICellColorSpan[] | null): void {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
public clearCellColorOverrides(): void {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
|
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { IBuffer } from 'common/buffer/Types';
|
|||||||
import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
|
import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
|
||||||
import { channels, css } from 'common/Color';
|
import { channels, css } from 'common/Color';
|
||||||
import type { Event } from 'vs/base/common/event';
|
import type { Event } from 'vs/base/common/event';
|
||||||
|
import { CellColorOverrideStore } from 'browser/services/CellColorOverrideStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A portion of the public API that are implemented identially internally and simply passed through.
|
* A portion of the public API that are implemented identially internally and simply passed through.
|
||||||
@@ -20,6 +21,7 @@ export interface ITerminal extends InternalPassthroughApis, ICoreTerminal {
|
|||||||
buffer: IBuffer;
|
buffer: IBuffer;
|
||||||
linkifier: ILinkifier2 | undefined;
|
linkifier: ILinkifier2 | undefined;
|
||||||
options: Required<ITerminalOptions>;
|
options: Required<ITerminalOptions>;
|
||||||
|
cellColorOverrides: CellColorOverrideStore;
|
||||||
|
|
||||||
onBlur: Event<void>;
|
onBlur: Event<void>;
|
||||||
onFocus: Event<void>;
|
onFocus: Event<void>;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { AddonManager } from 'common/public/AddonManager';
|
|||||||
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
||||||
import { ParserApi } from 'common/public/ParserApi';
|
import { ParserApi } from 'common/public/ParserApi';
|
||||||
import { UnicodeApi } from 'common/public/UnicodeApi';
|
import { UnicodeApi } from 'common/public/UnicodeApi';
|
||||||
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling, ICellColorSpan } from '@xterm/xterm';
|
||||||
import type { Event } from 'vs/base/common/event';
|
import type { Event } from 'vs/base/common/event';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,6 +177,13 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|||||||
this._verifyPositiveIntegers(decorationOptions.x ?? 0, decorationOptions.width ?? 0, decorationOptions.height ?? 0);
|
this._verifyPositiveIntegers(decorationOptions.x ?? 0, decorationOptions.width ?? 0, decorationOptions.height ?? 0);
|
||||||
return this._core.registerDecoration(decorationOptions);
|
return this._core.registerDecoration(decorationOptions);
|
||||||
}
|
}
|
||||||
|
public setCellColorOverrides(lineY: number, spans: ICellColorSpan[] | null): void {
|
||||||
|
this._verifyIntegers(lineY);
|
||||||
|
this._core.setCellColorOverrides(lineY, spans);
|
||||||
|
}
|
||||||
|
public clearCellColorOverrides(): void {
|
||||||
|
this._core.clearCellColorOverrides();
|
||||||
|
}
|
||||||
public hasSelection(): boolean {
|
public hasSelection(): boolean {
|
||||||
return this._core.hasSelection();
|
return this._core.hasSelection();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
this._register(this._themeService.onChangeColors(e => this._injectCss(e)));
|
this._register(this._themeService.onChangeColors(e => this._injectCss(e)));
|
||||||
this._injectCss(this._themeService.colors);
|
this._injectCss(this._themeService.colors);
|
||||||
|
|
||||||
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document);
|
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document, this._terminal.cellColorOverrides);
|
||||||
|
|
||||||
this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
||||||
this._screenElement.appendChild(this._rowContainer);
|
this._screenElement.appendChild(this._rowContainer);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import jsdom = require('jsdom');
|
import jsdom = require('jsdom');
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory';
|
import { DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory';
|
||||||
|
import { CellColorOverrideStore } from 'browser/services/CellColorOverrideStore';
|
||||||
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from 'common/buffer/Constants';
|
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from 'common/buffer/Constants';
|
||||||
import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||||
import { IBufferLine } from 'common/Types';
|
import { IBufferLine } from 'common/Types';
|
||||||
@@ -27,6 +28,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
dom = new jsdom.JSDOM('');
|
dom = new jsdom.JSDOM('');
|
||||||
rowFactory = new DomRendererRowFactory(
|
rowFactory = new DomRendererRowFactory(
|
||||||
dom.window.document,
|
dom.window.document,
|
||||||
|
new CellColorOverrideStore(),
|
||||||
new MockCharacterJoinerService(),
|
new MockCharacterJoinerService(),
|
||||||
new MockOptionsService({ drawBoldTextInBrightColors: true }),
|
new MockOptionsService({ drawBoldTextInBrightColors: true }),
|
||||||
new MockCoreBrowserService(),
|
new MockCoreBrowserService(),
|
||||||
@@ -77,6 +79,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
coreBrowserService.isFocused = false;
|
coreBrowserService.isFocused = false;
|
||||||
const rowFactory = new DomRendererRowFactory(
|
const rowFactory = new DomRendererRowFactory(
|
||||||
dom.window.document,
|
dom.window.document,
|
||||||
|
new CellColorOverrideStore(),
|
||||||
new MockCharacterJoinerService(),
|
new MockCharacterJoinerService(),
|
||||||
new MockOptionsService({ drawBoldTextInBrightColors: true }),
|
new MockOptionsService({ drawBoldTextInBrightColors: true }),
|
||||||
coreBrowserService,
|
coreBrowserService,
|
||||||
@@ -101,6 +104,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
coreService.isCursorInitialized = false;
|
coreService.isCursorInitialized = false;
|
||||||
const rowFactory = new DomRendererRowFactory(
|
const rowFactory = new DomRendererRowFactory(
|
||||||
dom.window.document,
|
dom.window.document,
|
||||||
|
new CellColorOverrideStore(),
|
||||||
new MockCharacterJoinerService(),
|
new MockCharacterJoinerService(),
|
||||||
new MockOptionsService(),
|
new MockOptionsService(),
|
||||||
new MockCoreBrowserService(),
|
new MockCoreBrowserService(),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { CellData } from 'common/buffer/CellData';
|
|||||||
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||||
import { channels, color } from 'common/Color';
|
import { channels, color } from 'common/Color';
|
||||||
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||||
|
import { CellColorOverrideStore } from 'browser/services/CellColorOverrideStore';
|
||||||
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
||||||
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
|
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from 'common/buffer/AttributeData';
|
||||||
@@ -44,6 +45,7 @@ export class DomRendererRowFactory {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _document: Document,
|
private readonly _document: Document,
|
||||||
|
private readonly _colorOverrides: CellColorOverrideStore,
|
||||||
@ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService,
|
@ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService,
|
||||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
||||||
@@ -95,6 +97,13 @@ export class DomRendererRowFactory {
|
|||||||
let skipJoinedCheckUntilX = 0;
|
let skipJoinedCheckUntilX = 0;
|
||||||
const classes: string[] = [];
|
const classes: string[] = [];
|
||||||
|
|
||||||
|
// Cell color overrides for this line (absolute row index). Looked up once
|
||||||
|
// per row, then consumed with a moving pointer since x only advances.
|
||||||
|
const colorOverrideSpans = this._colorOverrides.get(row);
|
||||||
|
let colorOverrideIdx = 0;
|
||||||
|
let colorOverride: IColor | undefined;
|
||||||
|
let oldColorOverride: IColor | undefined;
|
||||||
|
|
||||||
const hasHover = linkStart !== -1 && linkEnd !== -1;
|
const hasHover = linkStart !== -1 && linkEnd !== -1;
|
||||||
|
|
||||||
for (let x = 0; x < lineLength; x++) {
|
for (let x = 0; x < lineLength; x++) {
|
||||||
@@ -154,6 +163,21 @@ export class DomRendererRowFactory {
|
|||||||
const isCursorCell = isCursorRow && x === cursorX;
|
const isCursorCell = isCursorRow && x === cursorX;
|
||||||
const isLinkHover = hasHover && x >= linkStart && x <= linkEnd;
|
const isLinkHover = hasHover && x >= linkStart && x <= linkEnd;
|
||||||
|
|
||||||
|
// Resolve the color override covering this cell (if any). Spans are
|
||||||
|
// sorted by column and don't overlap, so a single moving pointer works.
|
||||||
|
if (colorOverrideSpans) {
|
||||||
|
while (
|
||||||
|
colorOverrideIdx < colorOverrideSpans.length &&
|
||||||
|
colorOverrideSpans[colorOverrideIdx].col + colorOverrideSpans[colorOverrideIdx].width <= x
|
||||||
|
) {
|
||||||
|
colorOverrideIdx++;
|
||||||
|
}
|
||||||
|
const span = colorOverrideSpans[colorOverrideIdx];
|
||||||
|
colorOverride = (span && x >= span.col) ? span.color : undefined;
|
||||||
|
} else {
|
||||||
|
colorOverride = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
let isDecorated = false;
|
let isDecorated = false;
|
||||||
this._decorationService.forEachDecorationAtCell(x, row, undefined, d => {
|
this._decorationService.forEachDecorationAtCell(x, row, undefined, d => {
|
||||||
isDecorated = true;
|
isDecorated = true;
|
||||||
@@ -194,6 +218,7 @@ export class DomRendererRowFactory {
|
|||||||
&& cell.extended.ext === oldExt
|
&& cell.extended.ext === oldExt
|
||||||
&& isLinkHover === oldLinkHover
|
&& isLinkHover === oldLinkHover
|
||||||
&& spacing === oldSpacing
|
&& spacing === oldSpacing
|
||||||
|
&& colorOverride === oldColorOverride
|
||||||
&& !isCursorCell
|
&& !isCursorCell
|
||||||
&& !isJoined
|
&& !isJoined
|
||||||
&& !isDecorated
|
&& !isDecorated
|
||||||
@@ -228,6 +253,7 @@ export class DomRendererRowFactory {
|
|||||||
oldLinkHover = isLinkHover;
|
oldLinkHover = isLinkHover;
|
||||||
oldSpacing = spacing;
|
oldSpacing = spacing;
|
||||||
oldIsInSelection = isInSelection;
|
oldIsInSelection = isInSelection;
|
||||||
|
oldColorOverride = colorOverride;
|
||||||
|
|
||||||
if (isJoined) {
|
if (isJoined) {
|
||||||
// The DOM renderer colors the background of the cursor but for ligatures all cells are
|
// The DOM renderer colors the background of the cursor but for ligatures all cells are
|
||||||
@@ -417,7 +443,14 @@ export class DomRendererRowFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Foreground
|
// Foreground
|
||||||
switch (fgColorMode) {
|
if (colorOverride) {
|
||||||
|
// Cell color override: recolor the text directly, winning over the
|
||||||
|
// cell/decoration colors but still running through the minimum
|
||||||
|
// contrast adjustment like every other foreground source.
|
||||||
|
if (!this._applyMinimumContrast(charElement, resolvedBg, colorOverride, cell, bgOverride, colorOverride)) {
|
||||||
|
this._addStyle(charElement, `color:${colorOverride.css}`);
|
||||||
|
}
|
||||||
|
} else switch (fgColorMode) {
|
||||||
case Attributes.CM_P16:
|
case Attributes.CM_P16:
|
||||||
case Attributes.CM_P256:
|
case Attributes.CM_P256:
|
||||||
if (cell.isBold() && fg < 8 && this._optionsService.rawOptions.drawBoldTextInBrightColors) {
|
if (cell.isBold() && fg < 8 && this._optionsService.rawOptions.drawBoldTextInBrightColors) {
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IColor } from 'common/Types';
|
||||||
|
import { css } from 'common/Color';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single contiguous cell range on one buffer line that should render its
|
||||||
|
* text in a specific color.
|
||||||
|
*/
|
||||||
|
export interface ICellColorSpan {
|
||||||
|
/** Zero-based column of the first cell. */
|
||||||
|
col: number;
|
||||||
|
/** Number of cells covered (wide chars count as 2). */
|
||||||
|
width: number;
|
||||||
|
/** CSS color string (#rrggbb / rgb(...) etc.), parsed on acceptance. */
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StoredSpan {
|
||||||
|
col: number;
|
||||||
|
width: number;
|
||||||
|
color: IColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sparse per-line cell color overrides consumed by the DOM renderer's row
|
||||||
|
* factory. Keyed by absolute buffer line index of the active buffer; the
|
||||||
|
* owner is responsible for invalidating keys when the buffer shifts
|
||||||
|
* (scrollback trim, resize reflow, alternate buffer switch).
|
||||||
|
*/
|
||||||
|
export class CellColorOverrideStore {
|
||||||
|
private _overrides = new Map<number, StoredSpan[]>();
|
||||||
|
|
||||||
|
/** Replace the overrides of one buffer line. Returns true when the stored
|
||||||
|
* state changed and the line needs a re-render. Invalid color strings and
|
||||||
|
* zero-width spans are dropped. */
|
||||||
|
public set(lineY: number, spans: ICellColorSpan[] | null): boolean {
|
||||||
|
if (!spans || spans.length === 0) {
|
||||||
|
return this._overrides.delete(lineY);
|
||||||
|
}
|
||||||
|
const stored: StoredSpan[] = [];
|
||||||
|
for (const span of spans) {
|
||||||
|
if (span.width <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
stored.push({ col: span.col, width: span.width, color: css.toColor(span.color) });
|
||||||
|
} catch {
|
||||||
|
// Unsupported color format — skip the span rather than fail the batch.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stored.length === 0) {
|
||||||
|
return this._overrides.delete(lineY);
|
||||||
|
}
|
||||||
|
const existing = this._overrides.get(lineY);
|
||||||
|
if (
|
||||||
|
existing &&
|
||||||
|
existing.length === stored.length &&
|
||||||
|
existing.every((e, i) =>
|
||||||
|
e.col === stored[i].col &&
|
||||||
|
e.width === stored[i].width &&
|
||||||
|
e.color.css === stored[i].color.css
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this._overrides.set(lineY, stored);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(lineY: number): StoredSpan[] | undefined {
|
||||||
|
return this._overrides.get(lineY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(): void {
|
||||||
|
this._overrides.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import type { IRenderService } from 'browser/services/Services';
|
|||||||
import type { ICoreTerminal, IDisposable, IMarker } from 'common/Types';
|
import type { ICoreTerminal, IDisposable, IMarker } from 'common/Types';
|
||||||
import * as playwright from '@playwright/test';
|
import * as playwright from '@playwright/test';
|
||||||
import { PageFunction } from 'playwright-core/types/structs';
|
import { PageFunction } from 'playwright-core/types/structs';
|
||||||
import { IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm';
|
import { ICellColorSpan, IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm';
|
||||||
|
|
||||||
export interface ITestContext {
|
export interface ITestContext {
|
||||||
browser: Browser;
|
browser: Browser;
|
||||||
@@ -279,6 +279,8 @@ export class TerminalProxy implements ITerminalProxyCustomMethods, PlaywrightApi
|
|||||||
public async resize(cols: number, rows: number): Promise<void> { return this._page.evaluate(([term, cols, rows]) => term.resize(cols, rows), [await this.getHandle(), cols, rows] as const); }
|
public async resize(cols: number, rows: number): Promise<void> { return this._page.evaluate(([term, cols, rows]) => term.resize(cols, rows), [await this.getHandle(), cols, rows] as const); }
|
||||||
public async registerMarker(y?: number | undefined): Promise<IMarker> { return this._page.evaluate(([term, y]) => term.registerMarker(y), [await this.getHandle(), y] as const); }
|
public async registerMarker(y?: number | undefined): Promise<IMarker> { return this._page.evaluate(([term, y]) => term.registerMarker(y), [await this.getHandle(), y] as const); }
|
||||||
public async registerDecoration(decorationOptions: IDecorationOptions): Promise<IDecoration | undefined> { return this._page.evaluate(([term, decorationOptions]) => term.registerDecoration(decorationOptions), [await this.getHandle(), decorationOptions] as const); }
|
public async registerDecoration(decorationOptions: IDecorationOptions): Promise<IDecoration | undefined> { return this._page.evaluate(([term, decorationOptions]) => term.registerDecoration(decorationOptions), [await this.getHandle(), decorationOptions] as const); }
|
||||||
|
public setCellColorOverrides(lineY: number, spans: ICellColorSpan[] | null): void { throw new Error('not supported via proxy'); }
|
||||||
|
public async clearCellColorOverrides(): Promise<void> { return this._page.evaluate((term) => term.clearCellColorOverrides(), await this.getHandle()); }
|
||||||
public async clearTextureAtlas(): Promise<void> { return this.evaluate(([term]) => term.clearTextureAtlas()); }
|
public async clearTextureAtlas(): Promise<void> { return this.evaluate(([term]) => term.clearTextureAtlas()); }
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
|
|||||||
Vendored
+29
@@ -612,6 +612,19 @@ declare module '@xterm/xterm' {
|
|||||||
overviewRulerOptions?: IDecorationOverviewRulerOptions;
|
overviewRulerOptions?: IDecorationOverviewRulerOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A contiguous cell range on one buffer line whose text should be rendered
|
||||||
|
* in a specific color, used with {@link Terminal.setCellColorOverrides}.
|
||||||
|
*/
|
||||||
|
export interface ICellColorSpan {
|
||||||
|
/** Zero-based column of the first cell. */
|
||||||
|
col: number;
|
||||||
|
/** Number of cells covered (wide characters count as 2 cells). */
|
||||||
|
width: number;
|
||||||
|
/** CSS color string (#rrggbb, rgb(...), etc.). */
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The set of localizable strings.
|
* The set of localizable strings.
|
||||||
*/
|
*/
|
||||||
@@ -1156,6 +1169,22 @@ declare module '@xterm/xterm' {
|
|||||||
*/
|
*/
|
||||||
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
|
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recolor a contiguous range of cells on one buffer line by drawing the
|
||||||
|
* covered text in the given color. Pass null for spans to clear the line.
|
||||||
|
* Line indexes are absolute within the active buffer; the caller must
|
||||||
|
* clear/re-set overrides when indexes shift (scrollback trim, resize
|
||||||
|
* reflow) or when the alternate buffer changes.
|
||||||
|
* @param lineY The absolute buffer line index (ydisp-independent).
|
||||||
|
* @param spans The cell ranges and colors to apply, or null to clear.
|
||||||
|
*/
|
||||||
|
setCellColorOverrides(lineY: number, spans: ICellColorSpan[] | null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all cell color overrides set by setCellColorOverrides.
|
||||||
|
*/
|
||||||
|
clearCellColorOverrides(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets whether the terminal has an active selection.
|
* Gets whether the terminal has an active selection.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user