Compare commits

..
16 changed files with 103 additions and 316 deletions
-10
View File
@@ -1,10 +0,0 @@
This is a TypeScript-based repository with a Ruby client for certain API endpoints. It includes several sub projects called addons which are built separately. It contains a demo application which showcases the functionality of the project.
Please follow these guidelines when contributing:
## Development Flow
- Install dependencies: `npm install && npm run setup`
- Build and bundle demo: `npm run build && npm run esbuild`
- Run unit tests: `npm run test-unit`
- Run playwright-based integration tests: `npm run test-integration`
-36
View File
@@ -1,36 +0,0 @@
name: "Copilot Setup Steps"
on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
copilot-setup-steps:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20.x
cache: 'yarn'
- name: Install dependencies
run: yarn --frozen-lockfile
- name: Setup and run tsc
run: yarn setup
- name: Esbuild
run: yarn esbuild
-4
View File
@@ -14,10 +14,6 @@ declare module '@xterm/addon-web-links' {
/**
* Creates a new web links addon.
* @param handler The callback when the link is called.
*
* Note that this may not work when the terminal is hosted inside an iframe,
* in that case provide a custom handler in that case being mindful of
* possible security issues like reverse tabnapping.
* @param options Options for the link provider.
*/
constructor(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkProviderOptions);
+1 -10
View File
@@ -10,15 +10,6 @@ import { IColorSet, ReadonlyColorSet } from 'browser/Types';
import { NULL_COLOR } from 'common/Color';
export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required<ITerminalOptions>, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number): ICharAtlasConfig {
// Convert lineHeight to a numeric multiplier for the char atlas
// If it's a px value, calculate the equivalent multiplier
let lineHeightMultiplier: number = typeof options.lineHeight === 'number' ? options.lineHeight : 1;
if (typeof options.lineHeight === 'string' && options.lineHeight.endsWith('px')) {
const pxValue = parseFloat(options.lineHeight.slice(0, -2));
// Calculate the multiplier based on font size
lineHeightMultiplier = pxValue / options.fontSize;
}
// null out some fields that don't matter
const clonedColors: IColorSet = {
foreground: colors.foreground,
@@ -45,7 +36,7 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number
devicePixelRatio,
deviceMaxTextureSize,
letterSpacing: options.letterSpacing,
lineHeight: lineHeightMultiplier,
lineHeight: options.lineHeight,
deviceCellWidth: deviceCellWidth,
deviceCellHeight: deviceCellHeight,
deviceCharWidth: deviceCharWidth,
+5 -18
View File
@@ -27,19 +27,6 @@ import { addDisposableListener } from 'vs/base/browser/dom';
import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
/**
* Calculates the line height in pixels based on the lineHeight option and character height.
*/
function calculateLineHeightInPixels(lineHeight: number | string, charHeight: number): number {
if (typeof lineHeight === 'string' && lineHeight.endsWith('px')) {
const pxValue = parseFloat(lineHeight.slice(0, -2));
// Ensure the pixel value is at least as large as the character height
return Math.max(pxValue, charHeight);
}
// Numeric lineHeight acts as a multiplier
return Math.floor(charHeight * (lineHeight as number));
}
export class WebglRenderer extends Disposable implements IRenderer {
private _renderLayers: IRenderLayer[];
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = new MutableDisposable();
@@ -583,14 +570,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
// cell.
this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * this._devicePixelRatio);
// Calculate the device cell height, supporting both numeric multipliers and 'px' values
this.dimensions.device.cell.height = calculateLineHeightInPixels(this._optionsService.rawOptions.lineHeight, this.dimensions.device.char.height);
// Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be
// floored since lineHeight can never be lower then 1, this guarentees the device cell height
// will always be larger than device char height.
this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight);
// Calculate the y offset within a cell that glyph should draw at in order for it to be centered
// correctly within the cell.
const isDefaultLineHeight = (typeof this._optionsService.rawOptions.lineHeight === 'number' && this._optionsService.rawOptions.lineHeight === 1) ||
(typeof this._optionsService.rawOptions.lineHeight === 'string' && this.dimensions.device.cell.height === this.dimensions.device.char.height);
this.dimensions.device.char.top = isDefaultLineHeight ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2);
this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2);
// Calculate the device cell width, taking the letterSpacing into account.
this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing);
+1 -1
View File
@@ -95,7 +95,7 @@
"jsdom": "^18.0.1",
"mocha": "^10.1.0",
"mustache": "^4.2.0",
"node-pty": "1.1.0-beta19",
"node-pty": "^1.1.0-beta31",
"nyc": "^15.1.0",
"source-map-loader": "^3.0.0",
"source-map-support": "^0.5.20",
-78
View File
@@ -1,78 +0,0 @@
/**
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
/**
* Calculates the line height in pixels based on the lineHeight option and character height.
* This is the same function used in both DomRenderer and WebglRenderer.
*/
function calculateLineHeightInPixels(lineHeight: number | string, charHeight: number): number {
if (typeof lineHeight === 'string' && lineHeight.endsWith('px')) {
const pxValue = parseFloat(lineHeight.slice(0, -2));
// Ensure the pixel value is at least as large as the character height
return Math.max(pxValue, charHeight);
}
// Numeric lineHeight acts as a multiplier
return Math.floor(charHeight * (lineHeight as number));
}
describe('calculateLineHeightInPixels', () => {
describe('numeric lineHeight', () => {
it('should calculate correctly with multiplier 1', () => {
assert.equal(calculateLineHeightInPixels(1, 20), 20);
});
it('should calculate correctly with multiplier 1.2', () => {
assert.equal(calculateLineHeightInPixels(1.2, 20), 24);
});
it('should calculate correctly with multiplier 1.5', () => {
assert.equal(calculateLineHeightInPixels(1.5, 20), 30);
});
it('should floor the result', () => {
assert.equal(calculateLineHeightInPixels(1.3, 20), 26); // Math.floor(26)
});
});
describe('string lineHeight with px format', () => {
it('should use px value directly when larger than char height', () => {
assert.equal(calculateLineHeightInPixels('25px', 20), 25);
});
it('should use char height when px value is smaller', () => {
assert.equal(calculateLineHeightInPixels('15px', 20), 20);
});
it('should handle decimal px values', () => {
assert.equal(calculateLineHeightInPixels('23.5px', 20), 23.5);
});
it('should handle edge case where px equals char height', () => {
assert.equal(calculateLineHeightInPixels('20px', 20), 20);
});
it('should handle very large px values', () => {
assert.equal(calculateLineHeightInPixels('100px', 20), 100);
});
});
describe('edge cases', () => {
it('should handle zero char height with numeric multiplier', () => {
assert.equal(calculateLineHeightInPixels(1.5, 0), 0);
});
it('should handle zero char height with px value', () => {
assert.equal(calculateLineHeightInPixels('25px', 0), 25);
});
it('should handle 1px with various char heights', () => {
assert.equal(calculateLineHeightInPixels('1px', 5), 5);
assert.equal(calculateLineHeightInPixels('1px', 20), 20);
assert.equal(calculateLineHeightInPixels('1px', 0), 1);
});
});
});
+1 -14
View File
@@ -24,19 +24,6 @@ const BG_CLASS_PREFIX = 'xterm-bg-';
const FOCUS_CLASS = 'xterm-focus';
const SELECTION_CLASS = 'xterm-selection';
/**
* Calculates the line height in pixels based on the lineHeight option and character height.
*/
function calculateLineHeightInPixels(lineHeight: number | string, charHeight: number): number {
if (typeof lineHeight === 'string' && lineHeight.endsWith('px')) {
const pxValue = parseFloat(lineHeight.slice(0, -2));
// Ensure the pixel value is at least as large as the character height
return Math.max(pxValue, charHeight);
}
// Numeric lineHeight acts as a multiplier
return Math.floor(charHeight * (lineHeight as number));
}
let nextTerminalId = 1;
/**
@@ -129,7 +116,7 @@ export class DomRenderer extends Disposable implements IRenderer {
this.dimensions.device.char.width = this._charSizeService.width * dpr;
this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * dpr);
this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing);
this.dimensions.device.cell.height = calculateLineHeightInPixels(this._optionsService.rawOptions.lineHeight, this.dimensions.device.char.height);
this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight);
this.dimensions.device.char.left = 0;
this.dimensions.device.char.top = 0;
this.dimensions.device.canvas.width = this.dimensions.device.cell.width * this._bufferService.cols;
+74 -1
View File
@@ -320,7 +320,37 @@ export class Buffer implements IBuffer {
if (toRemove.length > 0) {
const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove);
reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout);
// For conpty, it has its own copy of the buffer _without scrollback_ internally. Its behavior
// when reflowing larger is to insert empty lines at the bottom of the buffer as when lines
// unwrap conpty's view cannot pull scrollback down, so it adds empty lines at the end.
let removedInViewport = 0;
const isWindowsMode = this._optionsService.rawOptions.windowsMode || this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined;
if (isWindowsMode) {
for (let i = (toRemove.length / 2) - 1; i >= 0; i--) {
if (toRemove[i * 2 + 0] > this.ybase + removedInViewport) {
removedInViewport += toRemove[i * 2 + 1];
}
}
}
this._reflowLargerAdjustViewport(newCols, newRows, newLayoutResult.countRemoved);
// Apply empty lines for any removed in viewport for conpty.
if (isWindowsMode) {
if (removedInViewport > 0) {
for (let i = 0; i < removedInViewport; i++) {
// Just add the new missing rows on Windows as conpty reprints the screen with it's
// view of the world. Once a line enters scrollback for conpty it remains there
this.lines.push(new BufferLine(newCols, this.getNullCell(DEFAULT_ATTR_DATA)));
}
if (this.ybase === this.ydisp) {
this.ydisp += removedInViewport;
}
this.ybase += removedInViewport;
this.y -= removedInViewport;
}
}
}
}
@@ -352,7 +382,7 @@ export class Buffer implements IBuffer {
const nullCell = this.getNullCell(DEFAULT_ATTR_DATA);
// Gather all BufferLines that need to be inserted into the Buffer here so that they can be
// batched up and only committed once
const toInsert = [];
const toInsert: { start: number, newLines: IBufferLine[] }[] = [];
let countToInsert = 0;
// Go backwards as many lines may be trimmed and this will avoid considering them
for (let y = this.lines.length - 1; y >= 0; y--) {
@@ -467,6 +497,20 @@ export class Buffer implements IBuffer {
this.savedY = Math.min(this.savedY + linesToAdd, this.ybase + newRows - 1);
}
// For conpty, it has its own copy of the buffer _without scrollback_ internally. Its behavior
// when reflowing smaller is to reflow all lines inside the viewport, and removing empty or
// whitespace only lines from the bottom, until non-whitespace is hit in order to prevent
// content from being pushed into the scrollback.
let addedInViewport = 0;
const isWindowsMode = this._optionsService.rawOptions.windowsMode || this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined;
if (isWindowsMode) {
for (let i = toInsert.length - 1; i >= 0; i--) {
if (toInsert[i].start > this.ybase + addedInViewport) {
addedInViewport += toInsert[i].newLines.length;
}
}
}
// Rearrange lines in the buffer if there are any insertions, this is done at the end rather
// than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many
// costly calls to CircularList.splice.
@@ -520,6 +564,35 @@ export class Buffer implements IBuffer {
this.lines.onTrimEmitter.fire(amountToTrim);
}
}
// Apply empty lines to remove calculated earlier for conpty.
if (isWindowsMode) {
if (addedInViewport > 0) {
let emptyLinesAtBottom = 0;
for (let i = this.lines.length - 1; i >= this.ybase + this.y; i--) {
const line = this.lines.get(i) as BufferLine;
if (line.isWrapped || line.getTrimmedLength() > 0) {
break;
}
emptyLinesAtBottom++;
}
const emptyLinesToRemove = Math.min(addedInViewport, emptyLinesAtBottom);
if (emptyLinesToRemove > 0) {
for (let i = 0; i < emptyLinesToRemove; i++) {
this.lines.pop();
}
if (this.ybase === this.ydisp) {
this.ydisp -= emptyLinesToRemove;
}
this.ybase -= emptyLinesToRemove;
this.y += emptyLinesToRemove;
this.lines.onDeleteEmitter.fire({
index: this.lines.length - emptyLinesToRemove,
amount: emptyLinesToRemove
});
}
}
}
}
/**
+11 -25
View File
@@ -123,19 +123,18 @@ describe('Keyboard', () => {
});
describe('On non-macOS platforms', () => {
// Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100
// http://unix.stackexchange.com/a/108106
// Alt + arrow keys should now emit the standard Alt escape sequences instead of Ctrl sequences
it('should return \\x1b[1;3D for alt+left', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;3D'); // CSI 1;3 D
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;3D'); // Standard Alt+Left
});
it('should return \\x1b[1;3C for alt+right', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;3C'); // CSI 1;3 C
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;3C'); // Standard Alt+Right
});
it('should return \\x1b[1;3A for alt+up', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: false }).key, '\x1b[1;3A'); // CSI 1;3 A
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: false }).key, '\x1b[1;3A'); // Standard Alt+Up
});
it('should return \\x1b[1;3B for alt+down', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: false }).key, '\x1b[1;3B'); // CSI 1;3 B
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: false }).key, '\x1b[1;3B'); // Standard Alt+Down
});
it('should return \\x1ba for alt+a', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba');
@@ -150,16 +149,16 @@ describe('Keyboard', () => {
describe('On macOS platforms', () => {
it('should return \\x1b[1;3D for alt+left', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: true }).key, '\x1b[1;3D'); // CSI 1;3 D
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: true }).key, '\x1b[1;3D'); // Standard Alt+Left
});
it('should return \\x1b[1;3C for alt+right', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1b[1;3C'); // CSI 1;3 C
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1b[1;3C'); // Standard Alt+Right
});
it('should return \\x1b[1;3A for alt+up', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: true }).key, '\x1b[1;3A'); // CSI 1;3 A
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: true }).key, '\x1b[1;3A'); // Standard Alt+Up
});
it('should return \\x1b[1;3B for alt+down', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: true }).key, '\x1b[1;3B'); // CSI 1;3 B
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: true }).key, '\x1b[1;3B'); // Standard Alt+Down
});
it('should return undefined for alt+a', () => {
assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined);
@@ -177,10 +176,10 @@ describe('Keyboard', () => {
});
it('should return \\x1b[1;3A for alt+up', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }).key, '\x1b[1;3A'); // CSI 1;3 A
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }).key, '\x1b[1;3A'); // Standard Alt+Up
});
it('should return \\x1b[1;3B for alt+down', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }).key, '\x1b[1;3B'); // CSI 1;3 B
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }).key, '\x1b[1;3B'); // Standard Alt+Down
});
it('should return the correct escape sequence for modified F1-F12 keys', () => {
assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 112 }).key, '\x1b[1;2P');
@@ -336,19 +335,6 @@ describe('Keyboard', () => {
assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 49, key: '!' }).key, '!');
});
// Characters using alt+shift sequences (letters)
it('should return proper sequences for alt+shift+letter combinations', () => {
// Test alt+shift combinations produce uppercase letters
assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 65 }).key, '\x1bA'); // alt+shift+a
assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 72 }).key, '\x1bH'); // alt+shift+h
assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 90 }).key, '\x1bZ'); // alt+shift+z
// Test alt without shift produces lowercase letters
assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 65 }).key, '\x1ba'); // alt+a
assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 72 }).key, '\x1bh'); // alt+h
assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 90 }).key, '\x1bz'); // alt+z
});
it('should return proper sequence for ctrl+@', () => {
assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, shiftKey: true, keyCode: 50, key: '@' }).key, '\x00');
});
@@ -1,86 +0,0 @@
/**
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { OptionsService } from 'common/services/OptionsService';
describe('OptionsService lineHeight', () => {
let optionsService: OptionsService;
beforeEach(() => {
optionsService = new OptionsService({});
});
describe('numeric lineHeight', () => {
it('should accept numeric values', () => {
optionsService.options.lineHeight = 1.5;
assert.equal(optionsService.rawOptions.lineHeight, 1.5);
});
it('should reject values less than 1', () => {
assert.throws(() => optionsService.options.lineHeight = 0.5);
});
it('should accept 1 as minimum value', () => {
optionsService.options.lineHeight = 1;
assert.equal(optionsService.rawOptions.lineHeight, 1);
});
});
describe('string lineHeight with px format', () => {
it('should accept px values', () => {
optionsService.options.lineHeight = '24px';
assert.equal(optionsService.rawOptions.lineHeight, '24px');
});
it('should accept decimal px values', () => {
optionsService.options.lineHeight = '23.5px';
assert.equal(optionsService.rawOptions.lineHeight, '23.5px');
});
it('should reject px values less than 1', () => {
assert.throws(() => optionsService.options.lineHeight = '0.5px');
});
it('should accept 1px as minimum value', () => {
optionsService.options.lineHeight = '1px';
assert.equal(optionsService.rawOptions.lineHeight, '1px');
});
it('should reject non-px string values', () => {
assert.throws(() => optionsService.options.lineHeight = '24' as any);
assert.throws(() => optionsService.options.lineHeight = '24em' as any);
assert.throws(() => optionsService.options.lineHeight = 'normal' as any);
});
it('should reject invalid px values', () => {
assert.throws(() => optionsService.options.lineHeight = 'invalidpx' as any);
assert.throws(() => optionsService.options.lineHeight = 'px' as any);
});
});
describe('invalid lineHeight types', () => {
it('should reject null', () => {
assert.throws(() => optionsService.options.lineHeight = null as any);
});
it('should reject undefined when explicitly set', () => {
assert.throws(() => optionsService.options.lineHeight = undefined as any);
});
it('should reject boolean values', () => {
assert.throws(() => optionsService.options.lineHeight = true as any);
assert.throws(() => optionsService.options.lineHeight = false as any);
});
it('should reject arrays', () => {
assert.throws(() => optionsService.options.lineHeight = [] as any);
});
it('should reject objects', () => {
assert.throws(() => optionsService.options.lineHeight = {} as any);
});
});
});
+1 -20
View File
@@ -172,31 +172,12 @@ export class OptionsService extends Disposable implements IOptionsService {
case 'cursorWidth':
value = Math.floor(value);
// Fall through for bounds check
case 'lineHeight':
case 'tabStopWidth':
if (value < 1) {
throw new Error(`${key} cannot be less than 1, value: ${value}`);
}
break;
case 'lineHeight':
if (typeof value === 'string') {
// Handle 'px' format
if (value.endsWith('px')) {
const pxValue = parseFloat(value.slice(0, -2));
if (isNaN(pxValue) || pxValue < 1) {
throw new Error(`${key} with 'px' format must be a valid number >= 1, value: ${value}`);
}
// Keep the string value as-is for downstream processing
} else {
throw new Error(`${key} string value must end with 'px', value: ${value}`);
}
} else if (typeof value === 'number') {
if (value < 1) {
throw new Error(`${key} cannot be less than 1, value: ${value}`);
}
} else {
throw new Error(`${key} must be a number or string with 'px' suffix, value: ${value}`);
}
break;
case 'minimumContrastRatio':
value = Math.max(1, Math.min(21, Math.round(value * 10) / 10));
break;
+1 -1
View File
@@ -231,7 +231,7 @@ export interface ITerminalOptions {
fontWeightBold?: FontWeight;
ignoreBracketedPasteMode?: boolean;
letterSpacing?: number;
lineHeight?: number | string;
lineHeight?: number;
linkHandler?: ILinkHandler | null;
logLevel?: LogLevel;
logger?: ILogger | null;
+2 -4
View File
@@ -94,11 +94,9 @@ declare module '@xterm/headless' {
letterSpacing?: number;
/**
* The line height used to render text. This can be specified as:
* - A number (e.g., 1.2) which acts as a multiplier of the font size
* - A string with 'px' suffix (e.g., '24px') which specifies an absolute pixel height
* The line height used to render text.
*/
lineHeight?: number | string;
lineHeight?: number;
/**
* What log level to use, this will log for all levels below and including
+2 -4
View File
@@ -152,11 +152,9 @@ declare module '@xterm/xterm' {
letterSpacing?: number;
/**
* The line height used to render text. This can be specified as:
* - A number (e.g., 1.2) which acts as a multiplier of the font size
* - A string with 'px' suffix (e.g., '24px') which specifies an absolute pixel height
* The line height used to render text.
*/
lineHeight?: number | string;
lineHeight?: number;
/**
* The handler for OSC 8 hyperlinks. Links will use the `confirm` browser
+4 -4
View File
@@ -3302,10 +3302,10 @@ node-preload@^0.2.1:
dependencies:
process-on-spawn "^1.0.0"
node-pty@1.1.0-beta19:
version "1.1.0-beta19"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta19.tgz#a74dc04429903c5ac49ee81a15a24590da67d4f3"
integrity sha512-/p4Zu56EYDdXjjaLWzrIlFyrBnND11LQGP0/L6GEVGURfCNkAlHc3Twg/2I4NPxghimHXgvDlwp7Z2GtvDIh8A==
node-pty@^1.1.0-beta31:
version "1.1.0-beta9"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta9.tgz#ed643cb3b398d031b4e31c216e8f3b0042435f1d"
integrity sha512-/Ue38pvXJdgRZ3+me1FgfglLd301GhJN0NStiotdt61tm43N5htUyR/IXOUzOKuNaFmCwIhy6nwb77Ky41LMbw==
dependencies:
node-addon-api "^7.1.0"