Merge remote-tracking branch 'upstream/master' into osc52

This commit is contained in:
Ayman Bagabas
2024-04-07 23:22:01 +03:00
65 changed files with 1099 additions and 475 deletions
+81
View File
@@ -29,6 +29,25 @@ describe('Color', () => {
assert.equal(channels.toCss(0xf0, 0xf0, 0xf0), '#f0f0f0');
assert.equal(channels.toCss(0xff, 0xff, 0xff), '#ffffff');
});
it('should convert an rgba array to css hex string', () => {
assert.equal(channels.toCss(0x00, 0x00, 0x00, 0x00), '#00000000');
assert.equal(channels.toCss(0x10, 0x10, 0x10, 0x10), '#10101010');
assert.equal(channels.toCss(0x20, 0x20, 0x20, 0x20), '#20202020');
assert.equal(channels.toCss(0x30, 0x30, 0x30, 0x30), '#30303030');
assert.equal(channels.toCss(0x40, 0x40, 0x40, 0x40), '#40404040');
assert.equal(channels.toCss(0x50, 0x50, 0x50, 0x50), '#50505050');
assert.equal(channels.toCss(0x60, 0x60, 0x60, 0x60), '#60606060');
assert.equal(channels.toCss(0x70, 0x70, 0x70, 0x70), '#70707070');
assert.equal(channels.toCss(0x80, 0x80, 0x80, 0x80), '#80808080');
assert.equal(channels.toCss(0x90, 0x90, 0x90, 0x90), '#90909090');
assert.equal(channels.toCss(0xa0, 0xa0, 0xa0, 0xa0), '#a0a0a0a0');
assert.equal(channels.toCss(0xb0, 0xb0, 0xb0, 0xb0), '#b0b0b0b0');
assert.equal(channels.toCss(0xc0, 0xc0, 0xc0, 0xc0), '#c0c0c0c0');
assert.equal(channels.toCss(0xd0, 0xd0, 0xd0, 0xd0), '#d0d0d0d0');
assert.equal(channels.toCss(0xe0, 0xe0, 0xe0, 0xe0), '#e0e0e0e0');
assert.equal(channels.toCss(0xf0, 0xf0, 0xf0, 0xf0), '#f0f0f0f0');
assert.equal(channels.toCss(0xff, 0xff, 0xff, 0xff), '#ffffffff');
});
});
describe('toRgba', () => {
@@ -71,6 +90,47 @@ describe('Color', () => {
assert.equal(channels.toRgba(0xff, 0xff, 0xff, 0xff), 0xffffffff);
});
});
describe('toColor', () => {
it('should convert an rgb array to an IColor', () => {
assert.deepStrictEqual(channels.toColor(0x00, 0x00, 0x00), { css: '#000000', rgba: 0x000000FF });
assert.deepStrictEqual(channels.toColor(0x10, 0x10, 0x10), { css: '#101010', rgba: 0x101010FF });
assert.deepStrictEqual(channels.toColor(0x20, 0x20, 0x20), { css: '#202020', rgba: 0x202020FF });
assert.deepStrictEqual(channels.toColor(0x30, 0x30, 0x30), { css: '#303030', rgba: 0x303030FF });
assert.deepStrictEqual(channels.toColor(0x40, 0x40, 0x40), { css: '#404040', rgba: 0x404040FF });
assert.deepStrictEqual(channels.toColor(0x50, 0x50, 0x50), { css: '#505050', rgba: 0x505050FF });
assert.deepStrictEqual(channels.toColor(0x60, 0x60, 0x60), { css: '#606060', rgba: 0x606060FF });
assert.deepStrictEqual(channels.toColor(0x70, 0x70, 0x70), { css: '#707070', rgba: 0x707070FF });
assert.deepStrictEqual(channels.toColor(0x80, 0x80, 0x80), { css: '#808080', rgba: 0x808080FF });
assert.deepStrictEqual(channels.toColor(0x90, 0x90, 0x90), { css: '#909090', rgba: 0x909090FF });
assert.deepStrictEqual(channels.toColor(0xa0, 0xa0, 0xa0), { css: '#a0a0a0', rgba: 0xa0a0a0FF });
assert.deepStrictEqual(channels.toColor(0xb0, 0xb0, 0xb0), { css: '#b0b0b0', rgba: 0xb0b0b0FF });
assert.deepStrictEqual(channels.toColor(0xc0, 0xc0, 0xc0), { css: '#c0c0c0', rgba: 0xc0c0c0FF });
assert.deepStrictEqual(channels.toColor(0xd0, 0xd0, 0xd0), { css: '#d0d0d0', rgba: 0xd0d0d0FF });
assert.deepStrictEqual(channels.toColor(0xe0, 0xe0, 0xe0), { css: '#e0e0e0', rgba: 0xe0e0e0FF });
assert.deepStrictEqual(channels.toColor(0xf0, 0xf0, 0xf0), { css: '#f0f0f0', rgba: 0xf0f0f0FF });
assert.deepStrictEqual(channels.toColor(0xff, 0xff, 0xff), { css: '#ffffff', rgba: 0xffffffFF });
});
it('should convert an rgba array to an IColor', () => {
assert.deepStrictEqual(channels.toColor(0x00, 0x00, 0x00, 0x00), { css: '#00000000', rgba: 0x00000000 });
assert.deepStrictEqual(channels.toColor(0x10, 0x10, 0x10, 0x10), { css: '#10101010', rgba: 0x10101010 });
assert.deepStrictEqual(channels.toColor(0x20, 0x20, 0x20, 0x20), { css: '#20202020', rgba: 0x20202020 });
assert.deepStrictEqual(channels.toColor(0x30, 0x30, 0x30, 0x30), { css: '#30303030', rgba: 0x30303030 });
assert.deepStrictEqual(channels.toColor(0x40, 0x40, 0x40, 0x40), { css: '#40404040', rgba: 0x40404040 });
assert.deepStrictEqual(channels.toColor(0x50, 0x50, 0x50, 0x50), { css: '#50505050', rgba: 0x50505050 });
assert.deepStrictEqual(channels.toColor(0x60, 0x60, 0x60, 0x60), { css: '#60606060', rgba: 0x60606060 });
assert.deepStrictEqual(channels.toColor(0x70, 0x70, 0x70, 0x70), { css: '#70707070', rgba: 0x70707070 });
assert.deepStrictEqual(channels.toColor(0x80, 0x80, 0x80, 0x80), { css: '#80808080', rgba: 0x80808080 });
assert.deepStrictEqual(channels.toColor(0x90, 0x90, 0x90, 0x90), { css: '#90909090', rgba: 0x90909090 });
assert.deepStrictEqual(channels.toColor(0xa0, 0xa0, 0xa0, 0xa0), { css: '#a0a0a0a0', rgba: 0xa0a0a0a0 });
assert.deepStrictEqual(channels.toColor(0xb0, 0xb0, 0xb0, 0xb0), { css: '#b0b0b0b0', rgba: 0xb0b0b0b0 });
assert.deepStrictEqual(channels.toColor(0xc0, 0xc0, 0xc0, 0xc0), { css: '#c0c0c0c0', rgba: 0xc0c0c0c0 });
assert.deepStrictEqual(channels.toColor(0xd0, 0xd0, 0xd0, 0xd0), { css: '#d0d0d0d0', rgba: 0xd0d0d0d0 });
assert.deepStrictEqual(channels.toColor(0xe0, 0xe0, 0xe0, 0xe0), { css: '#e0e0e0e0', rgba: 0xe0e0e0e0 });
assert.deepStrictEqual(channels.toColor(0xf0, 0xf0, 0xf0, 0xf0), { css: '#f0f0f0f0', rgba: 0xf0f0f0f0 });
assert.deepStrictEqual(channels.toColor(0xff, 0xff, 0xff, 0xff), { css: '#ffffffff', rgba: 0xffffffff });
});
});
});
describe('color', () => {
@@ -271,6 +331,27 @@ describe('Color', () => {
});
describe('rgba', () => {
describe('blend', () => {
it('should blend colors based on the alpha channel', () => {
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF00), 0x000000FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF10), 0x101010FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF20), 0x202020FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF30), 0x303030FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF40), 0x404040FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF50), 0x505050FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF60), 0x606060FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF70), 0x707070FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF80), 0x808080FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF90), 0x909090FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFA0), 0xA0A0A0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFB0), 0xB0B0B0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFC0), 0xC0C0C0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFD0), 0xD0D0D0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFE0), 0xE0E0E0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFF0), 0xF0F0F0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFFF), 0xFFFFFFFF);
});
});
describe('ensureContrastRatio', () => {
it('should return undefined if the color already meets the contrast ratio (black bg)', () => {
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 1), undefined);
+33 -14
View File
@@ -3,7 +3,6 @@
* @license MIT
*/
import { isNode } from 'common/Platform';
import { IColor, IColorRGB } from 'common/Types';
let $r = 0;
@@ -33,6 +32,13 @@ export namespace channels {
// >>> 0 forces an unsigned int
return (r << 24 | g << 16 | b << 8 | a) >>> 0;
}
export function toColor(r: number, g: number, b: number, a?: number): IColor {
return {
css: channels.toCss(r, g, b, a),
rgba: channels.toRgba(r, g, b, a)
};
}
}
/**
@@ -70,7 +76,7 @@ export namespace color {
if (!result) {
return undefined;
}
return rgba.toColor(
return channels.toColor(
(result >> 24 & 0xFF),
(result >> 16 & 0xFF),
(result >> 8 & 0xFF)
@@ -110,9 +116,10 @@ export namespace color {
* '#rrggbbaa').
*/
export namespace css {
// Attempt to set get the shared canvas context
let $ctx: CanvasRenderingContext2D | undefined;
let $litmusColor: CanvasGradient | undefined;
if (!isNode) {
try {
// This is guaranteed to run in the first window, so document should be correct
const canvas = document.createElement('canvas');
canvas.width = 1;
@@ -126,6 +133,9 @@ export namespace css {
$litmusColor = $ctx.createLinearGradient(0, 0, 1, 1);
}
}
catch {
// noop
}
/**
* Converts a css string to an IColor, this should handle all valid CSS color strings and will
@@ -142,14 +152,14 @@ export namespace css {
$r = parseInt(css.slice(1, 2).repeat(2), 16);
$g = parseInt(css.slice(2, 3).repeat(2), 16);
$b = parseInt(css.slice(3, 4).repeat(2), 16);
return rgba.toColor($r, $g, $b);
return channels.toColor($r, $g, $b);
}
case 5: { // #rgba
$r = parseInt(css.slice(1, 2).repeat(2), 16);
$g = parseInt(css.slice(2, 3).repeat(2), 16);
$b = parseInt(css.slice(3, 4).repeat(2), 16);
$a = parseInt(css.slice(4, 5).repeat(2), 16);
return rgba.toColor($r, $g, $b, $a);
return channels.toColor($r, $g, $b, $a);
}
case 7: // #rrggbb
return {
@@ -171,7 +181,7 @@ export namespace css {
$g = parseInt(rgbaMatch[2]);
$b = parseInt(rgbaMatch[3]);
$a = Math.round((rgbaMatch[5] === undefined ? 1 : parseFloat(rgbaMatch[5])) * 0xFF);
return rgba.toColor($r, $g, $b, $a);
return channels.toColor($r, $g, $b, $a);
}
// Validate the context is available for canvas-based color parsing
@@ -245,6 +255,23 @@ export namespace rgb {
* Helper functions where the source type is "rgba" (number: 0xrrggbbaa).
*/
export namespace rgba {
export function blend(bg: number, fg: number): number {
$a = (fg & 0xFF) / 0xFF;
if ($a === 1) {
return fg;
}
const fgR = (fg >> 24) & 0xFF;
const fgG = (fg >> 16) & 0xFF;
const fgB = (fg >> 8) & 0xFF;
const bgR = (bg >> 24) & 0xFF;
const bgG = (bg >> 16) & 0xFF;
const bgB = (bg >> 8) & 0xFF;
$r = bgR + Math.round((fgR - bgR) * $a);
$g = bgG + Math.round((fgG - bgG) * $a);
$b = bgB + Math.round((fgB - bgB) * $a);
return channels.toRgba($r, $g, $b);
}
/**
* Given a foreground color and a background color, either increase or reduce the luminance of the
* foreground color until the specified contrast ratio is met. If pure white or black is hit
@@ -325,17 +352,9 @@ export namespace rgba {
return (fgR << 24 | fgG << 16 | fgB << 8 | 0xFF) >>> 0;
}
// FIXME: Move this to channels NS?
export function toChannels(value: number): [number, number, number, number] {
return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF];
}
export function toColor(r: number, g: number, b: number, a?: number): IColor {
return {
css: channels.toCss(r, g, b, a),
rgba: channels.toRgba(r, g, b, a)
};
}
}
export function toPaddedHex(c: number): string {
+5
View File
@@ -120,6 +120,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this._oscLinkService = this._instantiationService.createInstance(OscLinkService);
this._instantiationService.setService(IOscLinkService, this._oscLinkService);
// Register input handler and handle/forward events
this._inputHandler = this.register(new InputHandler(this._bufferService, this._charsetService, this.coreService, this._logService, this.optionsService, this._oscLinkService, this.coreMouseService, this.unicodeService));
this.register(forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed));
@@ -167,6 +168,10 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this._writeBuffer.writeSync(data, maxSubsequentCalls);
}
public input(data: string, wasUserInput: boolean = true): void {
this.coreService.triggerDataEvent(data, wasUserInput);
}
public resize(x: number, y: number): void {
if (isNaN(x) || isNaN(y)) {
return;
+1 -1
View File
@@ -14,7 +14,7 @@ interface INavigator {
declare const navigator: INavigator;
declare const process: unknown;
export const isNode = (typeof process !== 'undefined') ? true : false;
export const isNode = (typeof process !== 'undefined' && 'title' in (process as any)) ? true : false;
const userAgent = (isNode) ? 'node' : navigator.userAgent;
const platform = (isNode) ? 'node' : navigator.platform;
+2 -2
View File
@@ -110,8 +110,8 @@ export interface ICharset {
export type CharData = [number, string, number, number];
export interface IColor {
css: string;
rgba: number; // 32-bit int with rgba in each byte
readonly css: string;
readonly rgba: number; // 32-bit int with rgba in each byte
}
export type IColorRGB = [number, number, number];
+9 -1
View File
@@ -4,7 +4,7 @@
*/
import { EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { isMac } from 'common/Platform';
import { CursorStyle, IDisposable } from 'common/Types';
import { FontWeight, IOptionsService, ITerminalOptions } from 'common/services/Services';
@@ -44,6 +44,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
allowTransparency: false,
tabStopWidth: 8,
theme: {},
rescaleOverlappingGlyphs: false,
rightClickSelectsWord: isMac,
windowOptions: {},
windowsMode: false,
@@ -87,6 +88,13 @@ export class OptionsService extends Disposable implements IOptionsService {
this.rawOptions = defaultOptions;
this.options = { ... defaultOptions };
this._setupOptions();
// Clear out options that could link outside xterm.js as they could easily cause an embedder
// memory leak
this.register(toDisposable(() => {
this.rawOptions.linkHandler = null;
this.rawOptions.documentOverride = null;
}));
}
// eslint-disable-next-line @typescript-eslint/naming-convention
+1
View File
@@ -235,6 +235,7 @@ export interface ITerminalOptions {
macOptionIsMeta?: boolean;
macOptionClickForcesSelection?: boolean;
minimumContrastRatio?: number;
rescaleOverlappingGlyphs?: boolean;
rightClickSelectsWord?: boolean;
rows?: number;
screenReaderMode?: boolean;