mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into #4121
This commit is contained in:
+12
-2
@@ -87,7 +87,12 @@
|
||||
],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"warn",
|
||||
{ "selector": "default", "format": ["camelCase"] },
|
||||
{ "selector": "default", "format": ["camelCase"],
|
||||
"filter": {
|
||||
"regex": "^[a-z]",
|
||||
"match": true
|
||||
}
|
||||
},
|
||||
// variableLike
|
||||
{ "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
|
||||
{ "selector": "variable", "filter": "^I.+Service$", "format": ["PascalCase"], "prefix": ["I"] },
|
||||
@@ -96,7 +101,12 @@
|
||||
{ "selector": "memberLike", "modifiers": ["protected"], "format": ["camelCase"], "leadingUnderscore": "require" },
|
||||
{ "selector": "enumMember", "format": ["UPPER_CASE"] },
|
||||
// memberLike - Allow enum-like objects to use UPPER_CASE
|
||||
{ "selector": "property", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"] },
|
||||
{ "selector": "property", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"],
|
||||
"filter": {
|
||||
"regex": "^[a-z]",
|
||||
"match": true
|
||||
}
|
||||
},
|
||||
// restrict on* naming for events only
|
||||
{ "selector": "method", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"], "custom": {
|
||||
"regex": "^on[A-Z].+",
|
||||
|
||||
@@ -28,6 +28,10 @@ opening an issue, read these pointers.
|
||||
- Be polite. Issues with an indignant or belligerent tone tend to be moved to the
|
||||
bottom of the pile.
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -216,6 +216,9 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**Super Terminal**](https://github.com/bugwheels94/super-terminal): It is a http based terminal for developers who dont like repetition and save time.
|
||||
- [**graSSHopper**](https://grasshopper.coding.kiwi): A simple SSH client with file explorer, history and many more features.
|
||||
- [**DomTerm**](https://domterm.org/xtermjs.html): Tiles and tabs. Detachable sessions (like tmux). [Remote connections](https://domterm.org/Remoting-over-ssh.html) using a nice ssh wrapper with predictive echo. Qt, Electron, Tauri/Wry, or desktop browser front-ends. Choose between xterm.js engine (faster) or native DomTerm (more functionality and graphics) - or both.
|
||||
- [**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
|
||||
- [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.
|
||||
|
||||
@@ -368,6 +368,11 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
|
||||
protected _drawChars(cell: ICellData, x: number, y: number): void {
|
||||
const chars = cell.getChars();
|
||||
this._cellColorResolver.resolve(cell, x, this._bufferService.buffer.ydisp + y);
|
||||
|
||||
if (!this._charAtlas) {
|
||||
return;
|
||||
}
|
||||
|
||||
let glyph: IRasterizedGlyph;
|
||||
if (chars && chars.length > 1) {
|
||||
glyph = this._charAtlas.getRasterizedGlyphCombinedChar(chars, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
|
||||
|
||||
@@ -65,7 +65,7 @@ export default async function load(fontFamily: string, cacheSize: number): Promi
|
||||
}
|
||||
}
|
||||
// Latest proposal https://bugs.chromium.org/p/chromium/issues/detail?id=1312603
|
||||
else if (typeof process !== 'object' && 'queryLocalFonts' in window) {
|
||||
else if (typeof window !== 'undefined' && 'queryLocalFonts' in window) {
|
||||
const fonts: Record<string, IFontMetadata[]> = {};
|
||||
try {
|
||||
const fontsIterator = await (window as any).queryLocalFonts();
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('xterm-addon-serialize', () => {
|
||||
terminal = new Terminal({ cols: 10, rows: 2, allowProposedApi: true });
|
||||
terminal.loadAddon(serializeAddon);
|
||||
|
||||
(terminal as any)._core._themeService = new ThemeService(new OptionsService({}));
|
||||
(terminal as any)._core._themeService = new ThemeService((terminal as any)._core.optionsService);
|
||||
(terminal as any)._core._selectionService = new TestSelectionService((terminal as any)._core._bufferService);
|
||||
});
|
||||
|
||||
@@ -205,5 +205,25 @@ describe('xterm-addon-serialize', () => {
|
||||
});
|
||||
assert.equal((output.match(/color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
|
||||
});
|
||||
|
||||
it('cells with custom color styling', async () => {
|
||||
terminal.options.theme.black = '#ffa500';
|
||||
terminal.options.theme = { ... terminal.options.theme };
|
||||
|
||||
await writeP(terminal, ' ' + sgr('38;5;0') + 'terminal' + sgr('39') + ' ');
|
||||
|
||||
const output = serializeAddon.serializeAsHTML();
|
||||
assert.equal((output.match(/<span style='color: #ffa500;'>terminal<\/span>/g) || []).length, 1, output);
|
||||
});
|
||||
|
||||
it('cells with color styling - xterm headless', async () => {
|
||||
// a headless terminal doesn't have a themeservice
|
||||
(terminal as any)._core._themeService = undefined;
|
||||
|
||||
await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' ');
|
||||
|
||||
const output = serializeAddon.serializeAsHTML();
|
||||
assert.equal((output.match(/<span style='color: #00ff00;'>terminal<\/span>/g) || []).length, 1, output);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
import { Terminal, ITerminalAddon, IBuffer, IBufferCell, IBufferRange } from 'xterm';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { IAttributeData } from 'common/Types';
|
||||
import { IAttributeData, IColor } from 'common/Types';
|
||||
import { DEFAULT_ANSI_COLORS } from 'browser/services/ThemeService';
|
||||
|
||||
function constrain(value: number, low: number, high: number): number {
|
||||
return Math.max(low, Math.min(value, high));
|
||||
@@ -536,7 +537,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
|
||||
|
||||
private _htmlContent = '';
|
||||
|
||||
private _colors: IColorSet;
|
||||
private _ansiColors: Readonly<IColor[]>;
|
||||
|
||||
constructor(
|
||||
buffer: IBuffer,
|
||||
@@ -545,8 +546,13 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
|
||||
) {
|
||||
super(buffer);
|
||||
|
||||
// https://github.com/xtermjs/xterm.js/issues/3601
|
||||
this._colors = (_terminal as any)._core._themeService.colors;
|
||||
// For xterm headless: fallback to ansi colors
|
||||
if ((_terminal as any)._core._themeService) {
|
||||
this._ansiColors = (_terminal as any)._core._themeService.colors.ansi;
|
||||
}
|
||||
else {
|
||||
this._ansiColors = DEFAULT_ANSI_COLORS;
|
||||
}
|
||||
}
|
||||
|
||||
private _padStart(target: string, targetLength: number, padString: string): string {
|
||||
@@ -602,7 +608,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
|
||||
return rgb.map(x => this._padStart(x.toString(16), 2, '0')).join('');
|
||||
}
|
||||
if (isFg ? cell.isFgPalette() : cell.isBgPalette()) {
|
||||
return this._colors.ansi[color].css;
|
||||
return this._ansiColors[color].css;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,14 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
modules: ['./node_modules'],
|
||||
extensions: [ '.js' ],
|
||||
alias: {
|
||||
common: path.resolve('../../out/common'),
|
||||
browser: path.resolve('../../out/browser')
|
||||
}
|
||||
},
|
||||
output: {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
|
||||
+4
-4
@@ -62,12 +62,12 @@
|
||||
"@types/utf8": "^3.0.0",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/ws": "^8.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||
"@typescript-eslint/parser": "^5.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.00",
|
||||
"@typescript-eslint/parser": "^6.2.00",
|
||||
"chai": "^4.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"deep-equal": "^2.0.5",
|
||||
"eslint": "^8.1.0",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-jsdoc": "^39.3.6",
|
||||
"express": "^4.17.1",
|
||||
"express-ws": "^5.0.2",
|
||||
@@ -81,7 +81,7 @@
|
||||
"source-map-loader": "^3.0.0",
|
||||
"source-map-support": "^0.5.20",
|
||||
"ts-loader": "^9.3.1",
|
||||
"typescript": "4.7",
|
||||
"typescript": "^5.1.6",
|
||||
"utf8": "^3.0.0",
|
||||
"webpack": "^5.61.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
|
||||
@@ -35,7 +35,7 @@ import * as Strings from 'browser/LocalizableStrings';
|
||||
import { AccessibilityManager } from './AccessibilityManager';
|
||||
import { ITheme, IMarker, IDisposable, ILinkProvider, IDecorationOptions, IDecoration } from 'xterm';
|
||||
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
|
||||
import { KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IColorEvent, ColorIndex, ColorRequestType } from 'common/Types';
|
||||
import { KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IColorEvent, ColorIndex, ColorRequestType, SpecialColorIndex } from 'common/Types';
|
||||
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
@@ -203,15 +203,15 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
let acc: 'foreground' | 'background' | 'cursor' | 'ansi';
|
||||
let ident = '';
|
||||
switch (req.index) {
|
||||
case ColorIndex.FOREGROUND: // OSC 10 | 110
|
||||
case SpecialColorIndex.FOREGROUND: // OSC 10 | 110
|
||||
acc = 'foreground';
|
||||
ident = '10';
|
||||
break;
|
||||
case ColorIndex.BACKGROUND: // OSC 11 | 111
|
||||
case SpecialColorIndex.BACKGROUND: // OSC 11 | 111
|
||||
acc = 'background';
|
||||
ident = '11';
|
||||
break;
|
||||
case ColorIndex.CURSOR: // OSC 12 | 112
|
||||
case SpecialColorIndex.CURSOR: // OSC 12 | 112
|
||||
acc = 'cursor';
|
||||
ident = '12';
|
||||
break;
|
||||
|
||||
@@ -13,6 +13,7 @@ import { color } from 'common/Color';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
||||
import { createStyle, IStyleSheet } from './StyleSheet';
|
||||
|
||||
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
||||
const ROW_CONTAINER_CLASS = 'xterm-rows';
|
||||
@@ -32,8 +33,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
private _rowFactory: DomRendererRowFactory;
|
||||
private _terminalClass: number = nextTerminalId++;
|
||||
|
||||
private _themeStyleElement!: HTMLStyleElement;
|
||||
private _dimensionsStyleElement!: HTMLStyleElement;
|
||||
private _themeStyle!: IStyleSheet;
|
||||
private _dimensionsStyle!: IStyleSheet;
|
||||
private _rowContainer: HTMLElement;
|
||||
private _rowElements: HTMLElement[] = [];
|
||||
private _selectionContainer: HTMLElement;
|
||||
@@ -88,8 +89,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
// https://github.com/xtermjs/xterm.js/issues/2960
|
||||
this._rowContainer.remove();
|
||||
this._selectionContainer.remove();
|
||||
this._themeStyleElement.remove();
|
||||
this._dimensionsStyleElement.remove();
|
||||
this._themeStyle.dispose();
|
||||
this._dimensionsStyle.dispose();
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -116,9 +117,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
element.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
if (!this._dimensionsStyleElement) {
|
||||
this._dimensionsStyleElement = document.createElement('style');
|
||||
this._screenElement.appendChild(this._dimensionsStyleElement);
|
||||
if (!this._dimensionsStyle) {
|
||||
this._dimensionsStyle = createStyle(this._screenElement);
|
||||
}
|
||||
|
||||
const styles =
|
||||
@@ -129,7 +129,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
` width: ${this.dimensions.css.cell.width}px` +
|
||||
`}`;
|
||||
|
||||
this._dimensionsStyleElement.textContent = styles;
|
||||
this._dimensionsStyle.setCss(styles);
|
||||
|
||||
this._selectionContainer.style.height = this._viewportElement.style.height;
|
||||
this._screenElement.style.width = `${this.dimensions.css.canvas.width}px`;
|
||||
@@ -137,9 +137,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
private _injectCss(colors: ReadonlyColorSet): void {
|
||||
if (!this._themeStyleElement) {
|
||||
this._themeStyleElement = document.createElement('style');
|
||||
this._screenElement.appendChild(this._themeStyleElement);
|
||||
if (!this._themeStyle) {
|
||||
this._themeStyle = createStyle(this._screenElement);
|
||||
}
|
||||
|
||||
// Base CSS
|
||||
@@ -236,7 +235,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`${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;
|
||||
this._themeStyle.setCss(styles);
|
||||
}
|
||||
|
||||
public handleDevicePixelRatioChange(): void {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export interface IStyleSheet {
|
||||
dispose: () => void;
|
||||
setCss: (value: string) => void;
|
||||
}
|
||||
|
||||
const createCssStyleSheet = (doc: Document): IStyleSheet => {
|
||||
const sheet = new CSSStyleSheet();
|
||||
doc.adoptedStyleSheets.push(sheet);
|
||||
return {
|
||||
dispose() {
|
||||
const index = doc.adoptedStyleSheets.indexOf(sheet);
|
||||
doc.adoptedStyleSheets.splice(index, 1);
|
||||
},
|
||||
setCss(css) {
|
||||
sheet.replaceSync(css);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const createStyleElement = (parent: HTMLElement): IStyleSheet => {
|
||||
const doc = parent.ownerDocument;
|
||||
const element = doc.createElement('style');
|
||||
parent.append(element);
|
||||
return {
|
||||
dispose() {
|
||||
element.remove();
|
||||
},
|
||||
setCss(css) {
|
||||
element.textContent = css;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const createStyle = (parent: HTMLElement): IStyleSheet => {
|
||||
try {
|
||||
return createCssStyleSheet(parent.ownerDocument);
|
||||
} catch {
|
||||
return createStyleElement(parent);
|
||||
}
|
||||
};
|
||||
@@ -8,8 +8,7 @@ import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types';
|
||||
import { IColorSet, ReadonlyColorSet } from 'browser/Types';
|
||||
import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
|
||||
import { createDecorator } from 'common/services/ServiceRegistry';
|
||||
import { ColorIndex, IDisposable } from 'common/Types';
|
||||
import { ITheme } from 'common/services/Services';
|
||||
import { AllColorIndex, IDisposable } from 'common/Types';
|
||||
|
||||
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
|
||||
export interface ICharSizeService {
|
||||
@@ -130,7 +129,7 @@ export interface IThemeService {
|
||||
|
||||
readonly onChangeColors: IEvent<ReadonlyColorSet>;
|
||||
|
||||
restoreColor(slot?: ColorIndex): void;
|
||||
restoreColor(slot?: AllColorIndex): void;
|
||||
/**
|
||||
* Allows external modifying of colors in the theme, this is used instead of {@link colors} to
|
||||
* prevent accidental writes.
|
||||
|
||||
@@ -10,7 +10,7 @@ import { channels, color, css, NULL_COLOR } from 'common/Color';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { IOptionsService, ITheme } from 'common/services/Services';
|
||||
import { ColorIndex, IColor } from 'common/Types';
|
||||
import { ColorIndex, SpecialColorIndex, IColor, AllColorIndex } from 'common/Types';
|
||||
|
||||
interface IRestoreColorSet {
|
||||
foreground: IColor;
|
||||
@@ -176,12 +176,12 @@ export class ThemeService extends Disposable implements IThemeService {
|
||||
this._onChangeColors.fire(this.colors);
|
||||
}
|
||||
|
||||
public restoreColor(slot?: ColorIndex): void {
|
||||
public restoreColor(slot?: AllColorIndex): void {
|
||||
this._restoreColor(slot);
|
||||
this._onChangeColors.fire(this.colors);
|
||||
}
|
||||
|
||||
private _restoreColor(slot: ColorIndex | undefined): void {
|
||||
private _restoreColor(slot: AllColorIndex | undefined): void {
|
||||
// unset slot restores all ansi colors
|
||||
if (slot === undefined) {
|
||||
for (let i = 0; i < this._restoreColors.ansi.length; ++i) {
|
||||
@@ -190,13 +190,13 @@ export class ThemeService extends Disposable implements IThemeService {
|
||||
return;
|
||||
}
|
||||
switch (slot) {
|
||||
case ColorIndex.FOREGROUND:
|
||||
case SpecialColorIndex.FOREGROUND:
|
||||
this._colors.foreground = this._restoreColors.foreground;
|
||||
break;
|
||||
case ColorIndex.BACKGROUND:
|
||||
case SpecialColorIndex.BACKGROUND:
|
||||
this._colors.background = this._restoreColors.background;
|
||||
break;
|
||||
case ColorIndex.CURSOR:
|
||||
case SpecialColorIndex.CURSOR:
|
||||
this._colors.cursor = this._restoreColors.cursor;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { InputHandler } from 'common/InputHandler';
|
||||
import { IBufferLine, IAttributeData, IColorEvent, ColorIndex, ColorRequestType } from 'common/Types';
|
||||
import { IBufferLine, IAttributeData, IColorEvent, ColorIndex, ColorRequestType, SpecialColorIndex } from 'common/Types';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { Attributes, BgFlags, UnderlineStyle } from 'common/buffer/Constants';
|
||||
@@ -2002,87 +2002,87 @@ describe('InputHandler', () => {
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single foreground query --> color undefined
|
||||
await inputHandler.parseP('\x1b]10;?\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.FOREGROUND }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: SpecialColorIndex.FOREGROUND }]]);
|
||||
stack.length = 0;
|
||||
// OSC with multiple values maps to OSC 10 & OSC 11 & OSC 12
|
||||
await inputHandler.parseP('\x1b]10;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.FOREGROUND }], [{ type: ColorRequestType.REPORT, index: ColorIndex.BACKGROUND }], [{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: SpecialColorIndex.FOREGROUND }], [{ type: ColorRequestType.REPORT, index: SpecialColorIndex.BACKGROUND }], [{ type: ColorRequestType.REPORT, index: SpecialColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set foreground color events
|
||||
await inputHandler.parseP('\x1b]10;rgb:01/02/03\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.FOREGROUND, color: [1, 2, 3] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: SpecialColorIndex.FOREGROUND, color: [1, 2, 3] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]10;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.FOREGROUND, color: [170, 187, 204] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: SpecialColorIndex.FOREGROUND, color: [170, 187, 204] }]]);
|
||||
stack.length = 0;
|
||||
// set FG, BG and cursor color at once
|
||||
await inputHandler.parseP('\x1b]10;rgb:aa/bb/cc;#001122;rgb:12/34/56\x07');
|
||||
assert.deepEqual(stack, [
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.FOREGROUND, color: [170, 187, 204] }],
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
[{ type: ColorRequestType.SET, index: SpecialColorIndex.FOREGROUND, color: [170, 187, 204] }],
|
||||
[{ type: ColorRequestType.SET, index: SpecialColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ type: ColorRequestType.SET, index: SpecialColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
]);
|
||||
});
|
||||
it('110: restore FG color', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]110\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: ColorIndex.FOREGROUND }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: SpecialColorIndex.FOREGROUND }]]);
|
||||
});
|
||||
it('11: BG set & query events', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single background query --> color undefined
|
||||
await inputHandler.parseP('\x1b]11;?\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.BACKGROUND }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: SpecialColorIndex.BACKGROUND }]]);
|
||||
stack.length = 0;
|
||||
// OSC 11 with multiple values creates only BG and cursor event
|
||||
await inputHandler.parseP('\x1b]11;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.BACKGROUND }], [{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: SpecialColorIndex.BACKGROUND }], [{ type: ColorRequestType.REPORT, index: SpecialColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set background color events
|
||||
await inputHandler.parseP('\x1b]11;rgb:01/02/03\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [1, 2, 3] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: SpecialColorIndex.BACKGROUND, color: [1, 2, 3] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]11;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [170, 187, 204] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: SpecialColorIndex.BACKGROUND, color: [170, 187, 204] }]]);
|
||||
stack.length = 0;
|
||||
// set BG and cursor color at once
|
||||
await inputHandler.parseP('\x1b]11;#001122;rgb:12/34/56\x07');
|
||||
assert.deepEqual(stack, [
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
[{ type: ColorRequestType.SET, index: SpecialColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ type: ColorRequestType.SET, index: SpecialColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
]);
|
||||
});
|
||||
it('111: restore BG color', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]111\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: ColorIndex.BACKGROUND }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: SpecialColorIndex.BACKGROUND }]]);
|
||||
});
|
||||
it('12: cursor color set & query events', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single cursor query --> color undefined
|
||||
await inputHandler.parseP('\x1b]12;?\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: SpecialColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// OSC 12 with multiple values creates only cursor event
|
||||
await inputHandler.parseP('\x1b]12;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: SpecialColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set cursor color events
|
||||
await inputHandler.parseP('\x1b]12;rgb:01/02/03\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [1, 2, 3] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: SpecialColorIndex.CURSOR, color: [1, 2, 3] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]12;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [170, 187, 204] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: SpecialColorIndex.CURSOR, color: [170, 187, 204] }]]);
|
||||
});
|
||||
it('112: restore cursor color', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]112\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: SpecialColorIndex.CURSOR }]]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType } from 'common/Types';
|
||||
import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from 'common/Types';
|
||||
import { C0, C1 } from 'common/data/EscapeSequences';
|
||||
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
|
||||
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
|
||||
@@ -2915,7 +2915,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const spec = slots.shift() as string;
|
||||
if (/^\d+$/.exec(idx)) {
|
||||
const index = parseInt(idx);
|
||||
if (0 <= index && index < 256) {
|
||||
if (isValidColorIndex(index)) {
|
||||
if (spec === '?') {
|
||||
event.push({ type: ColorRequestType.REPORT, index });
|
||||
} else {
|
||||
@@ -2988,7 +2988,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
// special colors - OSC 10 | 11 | 12
|
||||
private _specialColors = [ColorIndex.FOREGROUND, ColorIndex.BACKGROUND, ColorIndex.CURSOR];
|
||||
private _specialColors = [SpecialColorIndex.FOREGROUND, SpecialColorIndex.BACKGROUND, SpecialColorIndex.CURSOR];
|
||||
|
||||
/**
|
||||
* Apply colors requests for special colors in OSC 10 | 11 | 12.
|
||||
@@ -3073,7 +3073,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
for (let i = 0; i < slots.length; ++i) {
|
||||
if (/^\d+$/.exec(slots[i])) {
|
||||
const index = parseInt(slots[i]);
|
||||
if (0 <= index && index < 256) {
|
||||
if (isValidColorIndex(index)) {
|
||||
event.push({ type: ColorRequestType.RESTORE, index });
|
||||
}
|
||||
}
|
||||
@@ -3090,7 +3090,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y OSC 110 "Restore default foreground color" "OSC 110 BEL" "Restore default foreground to themed color."
|
||||
*/
|
||||
public restoreFgColor(data: string): boolean {
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: ColorIndex.FOREGROUND }]);
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: SpecialColorIndex.FOREGROUND }]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3100,7 +3100,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y OSC 111 "Restore default background color" "OSC 111 BEL" "Restore default background to themed color."
|
||||
*/
|
||||
public restoreBgColor(data: string): boolean {
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: ColorIndex.BACKGROUND }]);
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: SpecialColorIndex.BACKGROUND }]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3110,7 +3110,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y OSC 112 "Restore default cursor color" "OSC 112 BEL" "Restore default cursor to themed color."
|
||||
*/
|
||||
public restoreCursorColor(data: string): boolean {
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: ColorIndex.CURSOR }]);
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: SpecialColorIndex.CURSOR }]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3429,3 +3429,7 @@ class DirtyRowTracker implements IDirtyRowTracker {
|
||||
this.markRangeDirty(0, this._bufferService.rows - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function isValidColorIndex(value: number): value is ColorIndex {
|
||||
return 0 <= value && value < 256;
|
||||
}
|
||||
|
||||
Vendored
+13
-4
@@ -413,23 +413,32 @@ export const enum ColorRequestType {
|
||||
SET = 1,
|
||||
RESTORE = 2
|
||||
}
|
||||
export const enum ColorIndex {
|
||||
|
||||
// IntRange from https://stackoverflow.com/a/39495173
|
||||
type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N
|
||||
? Acc[number]
|
||||
: Enumerate<N, [...Acc, Acc['length']]>;
|
||||
type IntRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
|
||||
|
||||
type ColorIndex = IntRange<0, 256>; // number from 0 to 255
|
||||
type AllColorIndex = ColorIndex | SpecialColorIndex;
|
||||
export const enum SpecialColorIndex {
|
||||
FOREGROUND = 256,
|
||||
BACKGROUND = 257,
|
||||
CURSOR = 258
|
||||
}
|
||||
export interface IColorReportRequest {
|
||||
type: ColorRequestType.REPORT;
|
||||
index: ColorIndex;
|
||||
index: AllColorIndex;
|
||||
}
|
||||
export interface IColorSetRequest {
|
||||
type: ColorRequestType.SET;
|
||||
index: ColorIndex;
|
||||
index: AllColorIndex;
|
||||
color: IColorRGB;
|
||||
}
|
||||
export interface IColorRestoreRequest {
|
||||
type: ColorRequestType.RESTORE;
|
||||
index?: ColorIndex;
|
||||
index?: AllColorIndex;
|
||||
}
|
||||
export type IColorEvent = (IColorReportRequest | IColorSetRequest | IColorRestoreRequest)[];
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ const LOG_PREFIX = 'xterm.js: ';
|
||||
export class LogService extends Disposable implements ILogService {
|
||||
public serviceBrand: any;
|
||||
|
||||
public logLevel: LogLevelEnum = LogLevelEnum.OFF;
|
||||
private _logLevel: LogLevelEnum = LogLevelEnum.OFF;
|
||||
public get logLevel(): LogLevelEnum { return this._logLevel; }
|
||||
|
||||
constructor(
|
||||
@IOptionsService private readonly _optionsService: IOptionsService
|
||||
@@ -44,7 +45,7 @@ export class LogService extends Disposable implements ILogService {
|
||||
}
|
||||
|
||||
private _updateLogLevel(): void {
|
||||
this.logLevel = optionsKeyToLogLevel[this._optionsService.rawOptions.logLevel];
|
||||
this._logLevel = optionsKeyToLogLevel[this._optionsService.rawOptions.logLevel];
|
||||
}
|
||||
|
||||
private _evalLazyOptionalParams(optionalParams: any[]): void {
|
||||
@@ -57,30 +58,30 @@ export class LogService extends Disposable implements ILogService {
|
||||
|
||||
private _log(type: LogType, message: string, optionalParams: any[]): void {
|
||||
this._evalLazyOptionalParams(optionalParams);
|
||||
type.call(console, LOG_PREFIX + message, ...optionalParams);
|
||||
type.call(console, (this._optionsService.options.logger ? '' : LOG_PREFIX) + message, ...optionalParams);
|
||||
}
|
||||
|
||||
public debug(message: string, ...optionalParams: any[]): void {
|
||||
if (this.logLevel <= LogLevelEnum.DEBUG) {
|
||||
this._log(console.log, message, optionalParams);
|
||||
if (this._logLevel <= LogLevelEnum.DEBUG) {
|
||||
this._log(this._optionsService.options.logger?.debug.bind(this._optionsService.options.logger) ?? console.log, message, optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
public info(message: string, ...optionalParams: any[]): void {
|
||||
if (this.logLevel <= LogLevelEnum.INFO) {
|
||||
this._log(console.info, message, optionalParams);
|
||||
if (this._logLevel <= LogLevelEnum.INFO) {
|
||||
this._log(this._optionsService.options.logger?.info.bind(this._optionsService.options.logger) ?? console.info, message, optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
public warn(message: string, ...optionalParams: any[]): void {
|
||||
if (this.logLevel <= LogLevelEnum.WARN) {
|
||||
this._log(console.warn, message, optionalParams);
|
||||
if (this._logLevel <= LogLevelEnum.WARN) {
|
||||
this._log(this._optionsService.options.logger?.warn.bind(this._optionsService.options.logger) ?? console.warn, message, optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
public error(message: string, ...optionalParams: any[]): void {
|
||||
if (this.logLevel <= LogLevelEnum.ERROR) {
|
||||
this._log(console.error, message, optionalParams);
|
||||
if (this._logLevel <= LogLevelEnum.ERROR) {
|
||||
this._log(this._optionsService.options.logger?.error.bind(this._optionsService.options.logger) ?? console.error, message, optionalParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
|
||||
letterSpacing: 0,
|
||||
linkHandler: null,
|
||||
logLevel: 'info',
|
||||
logger: null,
|
||||
scrollback: 1000,
|
||||
scrollOnUserInput: true,
|
||||
scrollSensitivity: 1,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IEvent, IEventEmitter } from 'common/EventEmitter';
|
||||
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
||||
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable, IColor, CursorStyle, IOscLinkData } from 'common/Types';
|
||||
import { createDecorator } from 'common/services/ServiceRegistry';
|
||||
import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty } from 'xterm';
|
||||
import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty, ILogger } from 'xterm';
|
||||
|
||||
export const IBufferService = createDecorator<IBufferService>('BufferService');
|
||||
export interface IBufferService {
|
||||
@@ -157,7 +157,7 @@ export const ILogService = createDecorator<ILogService>('LogService');
|
||||
export interface ILogService {
|
||||
serviceBrand: undefined;
|
||||
|
||||
logLevel: LogLevelEnum;
|
||||
readonly logLevel: LogLevelEnum;
|
||||
|
||||
debug(message: any, ...optionalParams: any[]): void;
|
||||
info(message: any, ...optionalParams: any[]): void;
|
||||
@@ -229,6 +229,7 @@ export interface ITerminalOptions {
|
||||
lineHeight?: number;
|
||||
linkHandler?: ILinkHandler | null;
|
||||
logLevel?: LogLevel;
|
||||
logger?: ILogger | null;
|
||||
macOptionIsMeta?: boolean;
|
||||
macOptionClickForcesSelection?: boolean;
|
||||
minimumContrastRatio?: number;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user