Merge branch 'master' into anthonykim1/scaffoldKittyAddon

This commit is contained in:
Anthony Kim
2026-02-01 13:11:40 -08:00
committed by GitHub
145 changed files with 3361 additions and 2362 deletions
+2 -2
View File
@@ -22,10 +22,10 @@ npm run build && npm run esbuild # Build all TypeScript and bundle
**Testing**:
- Unit tests: `npm run test-unit` (Mocha)
- Unit tests filtering to file: `npm run test-unit -- **/fileName.ts
- Per-addon unit tests: `npm run test-unit addons/addon-image/out-esbuild/*.test.js`
- Per-addon unit tests: `npm run test-unit -- addons/addon-image/out-esbuild/*.test.js`
- Integration tests: `npm run test-integration` (Playwright across Chrome/Firefox/WebKit)
- Integration tests by file: `npm run test-integration -- test/playwright/InputHandler.test.ts`. Never use grep to filter tests, it doesn't work
- Integration tests by addon: `npm run test-integration --suite=addon-search`. Suites always follow the format `addon-<something>`
- Integration tests by addon: `npm run test-integration -- --suite=addon-search`. Suites always follow the format `addon-<something>`
- Lint changes: `npm run lint-changes` to lint only changed files, `npm run lint-changes-fix` to fix them
## Addon Development Pattern
@@ -0,0 +1,18 @@
---
applyTo: '**/*.benchmark.ts'
---
# Benchmark run instructions
- Full suite: `npm run benchmark`
- Single benchmark file:
- Tree: `npm run benchmark -- -t out-test/benchmark/Event.benchmark.js`
- Run file: `npm run benchmark -- -s "out-test/benchmark/Event.benchmark.js" out-test/benchmark/Event.benchmark.js`
- Single context/case:
- Use `-t` to get the path, then:
- `npm run benchmark -- -s "<path>" out-test/benchmark/Event.benchmark.js`
When writing instructions, use `RuntimeCase` to measure pure runtime in ms, use `ThroughputRuntimeCase` when measuring throughput in MB/s.
Notes:
- Benchmarks run from built JS in `out-test/benchmark/*.benchmark.js`.
- Keep `NODE_PATH=./out` (handled by the npm script).
+1 -1
View File
@@ -12,7 +12,7 @@ interface IAttachOptions {
bidirectional?: boolean;
}
export class AttachAddon implements ITerminalAddon , IAttachApi {
export class AttachAddon implements ITerminalAddon, IAttachApi {
private _socket: WebSocket;
private _bidirectional: boolean;
private _disposables: IDisposable[] = [];
@@ -5,10 +5,9 @@ const config: PlaywrightTestConfig = {
timeout: 10000,
projects: [
{
name: 'ChromeStable',
name: 'Chromium',
use: {
browserName: 'chromium',
channel: 'chrome'
browserName: 'chromium'
}
},
{
@@ -5,10 +5,9 @@ const config: PlaywrightTestConfig = {
timeout: 10000,
projects: [
{
name: 'ChromeStable',
name: 'Chromium',
use: {
browserName: 'chromium',
channel: 'chrome'
browserName: 'chromium'
}
},
{
+1 -1
View File
@@ -33,7 +33,7 @@ function _getComputedStyle(el: HTMLElement): CSSStyleDeclaration {
return getWindow(el).getComputedStyle(el, null);
}
export class FitAddon implements ITerminalAddon , IFitApi {
export class FitAddon implements ITerminalAddon, IFitApi {
private _terminal: Terminal | undefined;
public activate(terminal: Terminal): void {
+2 -3
View File
@@ -5,10 +5,9 @@ const config: PlaywrightTestConfig = {
timeout: 10000,
projects: [
{
name: 'ChromeStable',
name: 'Chromium',
use: {
browserName: 'chromium',
channel: 'chrome'
browserName: 'chromium'
}
},
{
+2 -5
View File
@@ -9,10 +9,6 @@ import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm';
import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser';
import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics';
// eslint-disable-next-line
declare const Buffer: any;
// limit hold memory in base64 decoder
const KEEP_DATA = 4194304;
@@ -105,7 +101,8 @@ export class IIPHandler implements IOscHandler, IResetHandler {
return true;
}
const blob = new Blob([new Uint8Array(this._dec.data8)], { type: this._metrics.mime });
// HACK: The types on Blob are too restrictive, this is a Uint8Array so the browser accepts it
const blob = new Blob([this._dec.data8 as Uint8Array<ArrayBuffer>], { type: this._metrics.mime });
this._dec.release();
if (!window.createImageBitmap) {
+3 -2
View File
@@ -8,6 +8,7 @@ declare const Buffer: any;
export interface IHeaderFields {
[key: string]: number | string | Uint32Array | null | undefined;
// base-64 encoded filename. Defaults to "Unnamed file".
name: string;
// File size in bytes. The file transfer will be canceled if this size is exceeded.
@@ -81,7 +82,7 @@ function toName(data: Uint32Array): string {
return new TextDecoder().decode(b);
}
const DECODERS: {[key: string]: (v: Uint32Array) => any} = {
const DECODERS: {[key: string]: (v: Uint32Array) => number | string} = {
inline: toInt,
size: toInt,
name: toName,
@@ -100,7 +101,7 @@ export class HeaderParser {
private _buffer = new Uint32Array(MAX_FIELDCHARS);
private _position = 0;
private _key = '';
public fields: {[key: string]: any} = {};
public fields: {[key: string]: number | string | Uint32Array | null | undefined} = {};
public reset(): void {
this._buffer.fill(0);
+2 -2
View File
@@ -51,7 +51,7 @@ const enum GaStatus {
}
export class ImageAddon implements ITerminalAddon , IImageApi {
export class ImageAddon implements ITerminalAddon, IImageApi {
private _opts: IImageAddonOptions;
private _defaultOpts: IImageAddonOptions;
private _storage: ImageStorage | undefined;
@@ -93,7 +93,7 @@ export class ImageAddon implements ITerminalAddon , IImageApi {
// windowOptions.getCellSizePixels = true;
// windowOptions.getWinSizeChars = true;
// terminal.setOption('windowOptions', windowOptions);
const windowOps = terminal.options.windowOptions || {};
const windowOps = terminal.options.windowOptions ?? {};
windowOps.getWinSizePixels = true;
windowOps.getCellSizePixels = true;
windowOps.getWinSizeChars = true;
+2 -2
View File
@@ -38,7 +38,7 @@ export class ImageRenderer extends Disposable implements IDisposable {
* Only the DOM output canvas should be on the terminal's document,
* which gets explicitly checked in `insertLayerToDom`.
*/
const canvas = (localDocument || document).createElement('canvas');
const canvas = (localDocument ?? document).createElement('canvas');
canvas.width = width | 0;
canvas.height = height | 0;
return canvas;
@@ -242,7 +242,7 @@ export class ImageRenderer extends Disposable implements IDisposable {
}
if (!this._placeholder) return;
this._ctx.drawImage(
this._placeholderBitmap || this._placeholder!,
this._placeholderBitmap ?? this._placeholder!,
col * width,
(row * height) % 2 ? 0 : 1, // needs %2 offset correction
width * count,
+9 -7
View File
@@ -132,8 +132,10 @@ export class ImageStorage implements IDisposable {
) {
try {
this.setLimit(this._opts.storageLimit);
} catch (e: any) {
console.error(e.message);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(e.message);
}
console.warn(`storageLimit is set to ${this.getLimit()} MB`);
}
this._viewportMetrics = {
@@ -381,7 +383,7 @@ export class ImageStorage implements IDisposable {
if (!line) return;
for (let col = 0; col < cols; ++col) {
if (line.getBg(col) & BgFlags.HAS_EXTENDED) {
let e: IExtendedAttrsImage = line._extendedAttrs[col] || EMPTY_ATTRS;
let e: IExtendedAttrsImage = line._extendedAttrs[col] ?? EMPTY_ATTRS;
const imageId = e.imageId;
if (imageId === undefined || imageId === -1) {
continue;
@@ -400,7 +402,7 @@ export class ImageStorage implements IDisposable {
while (
++col < cols
&& (line.getBg(col) & BgFlags.HAS_EXTENDED)
&& (e = line._extendedAttrs[col] || EMPTY_ATTRS)
&& (e = line._extendedAttrs[col] ?? EMPTY_ATTRS)
&& (e.imageId === imageId)
&& (e.tileId === startTile + count)
) {
@@ -442,7 +444,7 @@ export class ImageStorage implements IDisposable {
for (let row = 0; row < rows; ++row) {
const line = buffer.lines.get(row) as IBufferLineExt;
if (line.getBg(oldCol) & BgFlags.HAS_EXTENDED) {
const e: IExtendedAttrsImage = line._extendedAttrs[oldCol] || EMPTY_ATTRS;
const e: IExtendedAttrsImage = line._extendedAttrs[oldCol] ?? EMPTY_ATTRS;
const imageId = e.imageId;
if (imageId === undefined || imageId === -1) {
continue;
@@ -487,7 +489,7 @@ export class ImageStorage implements IDisposable {
const buffer = this._terminal._core.buffer;
const line = buffer.lines.get(y) as IBufferLineExt;
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
const e: IExtendedAttrsImage = line._extendedAttrs[x] || EMPTY_ATTRS;
const e: IExtendedAttrsImage = line._extendedAttrs[x] ?? EMPTY_ATTRS;
if (e.imageId && e.imageId !== -1) {
const orig = this._images.get(e.imageId)?.orig;
if (window.ImageBitmap && orig instanceof ImageBitmap) {
@@ -507,7 +509,7 @@ export class ImageStorage implements IDisposable {
const buffer = this._terminal._core.buffer;
const line = buffer.lines.get(y) as IBufferLineExt;
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
const e: IExtendedAttrsImage = line._extendedAttrs[x] || EMPTY_ATTRS;
const e: IExtendedAttrsImage = line._extendedAttrs[x] ?? EMPTY_ATTRS;
if (e.imageId && e.imageId !== -1 && e.tileId !== -1) {
const spec = this._images.get(e.imageId);
if (spec) {
+2 -3
View File
@@ -5,10 +5,9 @@ const config: PlaywrightTestConfig = {
timeout: 10000,
projects: [
{
name: 'ChromeStable',
name: 'Chromium',
use: {
browserName: 'chromium',
channel: 'chrome'
browserName: 'chromium'
}
},
{
+2 -2
View File
@@ -13,7 +13,7 @@ export interface ITerminalAddon {
dispose(): void;
}
export class LigaturesAddon implements ITerminalAddon , ILigaturesApi {
export class LigaturesAddon implements ITerminalAddon, ILigaturesApi {
private readonly _fallbackLigatures: string[];
private readonly _fontFeatureSettings?: string;
@@ -22,7 +22,7 @@ export class LigaturesAddon implements ITerminalAddon , ILigaturesApi {
constructor(options?: Partial<ILigatureOptions>) {
// Source: calt set from https://github.com/be5invis/Iosevka?tab=readme-ov-file#ligations
this._fallbackLigatures = (options?.fallbackLigatures || [
this._fallbackLigatures = (options?.fallbackLigatures ?? [
'<--', '<---', '<<-', '<-', '->', '->>', '-->', '--->',
'<==', '<===', '<<=', '<=', '=>', '=>>', '==>', '===>', '>=', '>>=',
'<->', '<-->', '<--->', '<---->', '<=>', '<==>', '<===>', '<====>', '::', ':::',
+11 -9
View File
@@ -42,11 +42,11 @@ export default async function load(fontFamily: string, cacheSize: number): Promi
if (status && status.state !== 'granted') {
throw new Error('Permission to access local fonts not granted.');
}
} catch (err: any) {
} catch (err: unknown) {
// A `TypeError` indicates the 'local-fonts'
// permission is not yet implemented, so
// only `throw` if this is _not_ the problem.
if (err.name !== 'TypeError') {
if (err instanceof Error && err.name !== 'TypeError') {
throw err;
}
}
@@ -60,8 +60,10 @@ export default async function load(fontFamily: string, cacheSize: number): Promi
fonts[metadata.family].push(metadata);
}
fontsPromise = Promise.resolve(fonts);
} catch (err: any) {
console.error(err.name, err.message);
} catch (err: unknown) {
if (err instanceof Error) {
console.error(err.name, err.message);
}
}
}
// Latest proposal https://bugs.chromium.org/p/chromium/issues/detail?id=1312603
@@ -76,13 +78,13 @@ export default async function load(fontFamily: string, cacheSize: number): Promi
fonts[metadata.family].push(metadata);
}
fontsPromise = Promise.resolve(fonts);
} catch (err: any) {
console.error(err.name, err.message);
} catch (err: unknown) {
if (err instanceof Error) {
console.error(err.name, err.message);
}
}
}
if (!fontsPromise) {
fontsPromise = Promise.resolve({});
}
fontsPromise ??= Promise.resolve({});
}
const fonts = await fontsPromise;
@@ -52,12 +52,10 @@ export function processLookaheadPosition(
}
processedEntries.add(currentEntry.entry);
if (!currentEntry.entry.forward) {
currentEntry.entry.forward = {
individual: {},
range: []
};
}
currentEntry.entry.forward ??= {
individual: {},
range: []
};
// All glyphs at this position share ONE entry - lookahead just needs to match,
// all paths lead to the same result
@@ -97,12 +95,10 @@ export function processBacktrackPosition(
}
processedEntries.add(currentEntry.entry);
if (!currentEntry.entry.reverse) {
currentEntry.entry.reverse = {
individual: {},
range: []
};
}
currentEntry.entry.reverse ??= {
individual: {},
range: []
};
// All glyphs at this position share ONE entry - backtrack just needs to match,
// all paths lead to the same result
@@ -54,9 +54,6 @@ export function getIndividualSubstitutionGlyph(table: SubstitutionTable, glyphId
return (glyphId + table.deltaGlyphId) % (2 ** 16);
// https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2
case 2:
// eslint-disable-next-line eqeqeq
return table.substitute[coverageIndex] != null
? table.substitute[coverageIndex]
: null;
return table.substitute[coverageIndex] ?? null;
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = []
let currentFontName: string | undefined = undefined;
let font: Font | undefined = undefined;
let loadingState: LoadingState = LoadingState.UNLOADED;
let loadError: any | undefined = undefined;
let loadError: unknown = undefined;
return term.registerCharacterJoiner((text: string): [number, number][] => {
// If the font hasn't been loaded yet, load it and return an empty result
@@ -5,10 +5,9 @@ const config: PlaywrightTestConfig = {
timeout: 10000,
projects: [
{
name: 'ChromeStable',
name: 'Chromium',
use: {
browserName: 'chromium',
channel: 'chrome'
browserName: 'chromium'
}
},
{

Some files were not shown because too many files have changed in this diff Show More