Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
3049436
Support Kitty graphics protocol
anthonykim1 Jan 22, 2026
6a02d7c
Add some playwright tests that will first fail
anthonykim1 Jan 22, 2026
4477463
npm install to update pckage-lock.json
anthonykim1 Jan 22, 2026
bea1628
Edit ci.yml ?
anthonykim1 Jan 22, 2026
fc0dc3f
Dont mess with og formatting for ci.yml
anthonykim1 Jan 22, 2026
8f0e711
ci.yml
anthonykim1 Jan 22, 2026
ec5b421
Renderer + og formatting for ci.yml
anthonykim1 Jan 22, 2026
a507afb
Add rendering, tweak send-png
anthonykim1 Jan 22, 2026
27825f9
param in kittyGraphicsAddon.ts , ci.yml
anthonykim1 Jan 22, 2026
8b6f8f3
Rename
anthonykim1 Jan 23, 2026
849110d
add query to say OK responses
anthonykim1 Jan 23, 2026
bc1d87d
comments
anthonykim1 Jan 23, 2026
5e95ef6
more comments
anthonykim1 Jan 23, 2026
1442f84
Fix chunking for large data, allow pixels
anthonykim1 Jan 28, 2026
40342ec
remove send-png
anthonykim1 Jan 28, 2026
e2dabc7
Prefer ?? and move disposable location
anthonykim1 Jan 28, 2026
ae171ac
Dont forget about action o for compression
anthonykim1 Jan 28, 2026
17d4395
switch from triggerDataEvnet to this._terminal.input
anthonykim1 Jan 28, 2026
d58e859
Use Terminal.dimensions intead
anthonykim1 Jan 28, 2026
b0b0868
Conste num for keys, describe in jsdoc
anthonykim1 Jan 28, 2026
259cc67
Handle actions, compression first, then parseInt before Switch
anthonykim1 Jan 28, 2026
fd64137
Dont use magic number, use symbol
anthonykim1 Jan 28, 2026
15c71e2
Replace more magic numbers with readable symbols
anthonykim1 Jan 28, 2026
a1c1702
Assemble and use dataArray for ImageData, make hot-loop faster
anthonykim1 Jan 28, 2026
f89524e
Create KittyApchandler.ts
anthonykim1 Jan 28, 2026
8c9afdb
Outsource a bunch to KittyApcHandler. TODO: go over this
anthonykim1 Jan 28, 2026
3e8fc48
Small details
anthonykim1 Jan 28, 2026
5a79b08
stale comments
anthonykim1 Jan 28, 2026
d777119
comments on file system
anthonykim1 Jan 28, 2026
a1b69e5
TODO on wasm stuff
anthonykim1 Jan 28, 2026
492d950
Add TODOs from jerch's comments
anthonykim1 Jan 28, 2026
cb8159c
Start integrating KittyGraphics stuff into imageAddon as suggested
anthonykim1 Jan 29, 2026
bae314f
Try using the wasm base64 decoder
anthonykim1 Jan 29, 2026
e4710d3
Use sharding, decode with streaming
anthonykim1 Feb 1, 2026
c9dc994
TODO for file system stufff
anthonykim1 Feb 1, 2026
a7c4a16
flip inControlData thing like in C
anthonykim1 Feb 1, 2026
ffac8c0
Merge branch 'master' into anthonykim1/scaffoldKittyAddon
anthonykim1 Feb 1, 2026
429b288
Terminal eixst
anthonykim1 Feb 1, 2026
ae58176
Lint
anthonykim1 Feb 1, 2026
5f724c5
Reduce MAX_CONTROL_DATA_SIZE + EINVAL for i and I
anthonykim1 Feb 3, 2026
d44900f
Parse control data early as suggested
anthonykim1 Feb 3, 2026
1029e19
Pre-calculate + fix bug to make sure we dont reset per chunk
anthonykim1 Feb 3, 2026
c907edd
Separate out test, add failing//skip test for cursor placements
anthonykim1 Feb 3, 2026
d15b426
Readme edit for kittySupport
anthonykim1 Feb 3, 2026
e95ec8d
feed to decoder directly, try to clean things before getting upgraded…
anthonykim1 Feb 4, 2026
08f85cf
Use wasm-parts v0.3.0., remove manual sharding/leftover, update iip!
anthonykim1 Feb 5, 2026
a59d527
Mirror const enum since esbuild cant inline const enum
anthonykim1 Feb 6, 2026
596ab84
Make copy of bytes with Uint8Array since they can be reused by decoder
anthonykim1 Feb 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion addons/addon-image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ const customSettings: IImageAddonOptions = {
storageLimit: 128, // FIFO storage limit in MB
showPlaceholder: true, // whether to show a placeholder for evicted images
iipSupport: true, // enable iTerm IIP support
iipSizeLimit: 20000000 // size limit of a single IIP sequence
iipSizeLimit: 20000000, // size limit of a single IIP sequence
kittySupport: true, // enable Kitty graphics support
kittySizeLimit: 20000000 // size limit of a single Kitty sequence
}

// initialization
Expand Down
Binary file added addons/addon-image/fixture/kitty/black-1x1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added addons/addon-image/fixture/kitty/rgb-3x1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion addons/addon-image/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
},
"devDependencies": {
"sixel": "^0.16.0",
"xterm-wasm-parts": "^0.1.0"
"xterm-wasm-parts": "^0.3.0"
}
}
38 changes: 24 additions & 14 deletions addons/addon-image/src/IIPHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
import { IImageAddonOptions, IOscHandler, IResetHandler, ITerminalExt } from './Types';
import { ImageRenderer } from './ImageRenderer';
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm';
import Base64Decoder, { type DecodeStatus } from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm';
import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser';
import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics';

// limit hold memory in base64 decoder
// limit hold memory in base64 decoder (encoded bytes)
const KEEP_DATA = 4194304;
const INITIAL_DATA = 1048576;

// Local mirror of const enum (esbuild can't inline const enums from external packages)
const DECODER_OK: DecodeStatus.OK = 0;

// default IIP header values
const DEFAULT_HEADER: IHeaderFields = {
Expand All @@ -27,15 +31,19 @@ export class IIPHandler implements IOscHandler, IResetHandler {
private _aborted = false;
private _hp = new HeaderParser();
private _header: IHeaderFields = DEFAULT_HEADER;
private _dec = new Base64Decoder(KEEP_DATA);
private _dec: Base64Decoder;
private _metrics = UNSUPPORTED_TYPE;

constructor(
private readonly _opts: IImageAddonOptions,
private readonly _renderer: ImageRenderer,
private readonly _storage: ImageStorage,
private readonly _coreTerminal: ITerminalExt
) {}
) {
const maxEncodedBytes = Math.ceil(this._opts.iipSizeLimit * 4 / 3);
const initialBytes = Math.min(INITIAL_DATA, maxEncodedBytes);
this._dec = new Base64Decoder(KEEP_DATA, maxEncodedBytes, initialBytes);
}

public reset(): void {}

Expand All @@ -50,7 +58,7 @@ export class IIPHandler implements IOscHandler, IResetHandler {
if (this._aborted) return;

if (this._hp.state === HeaderState.END) {
if (this._dec.put(data, start, end)) {
if (this._dec.put(data.subarray(start, end)) !== DECODER_OK) {
this._dec.release();
this._aborted = true;
}
Expand All @@ -66,8 +74,8 @@ export class IIPHandler implements IOscHandler, IResetHandler {
this._aborted = true;
return;
}
this._dec.init(this._header.size);
if (this._dec.put(data, dataPos, end)) {
this._dec.init();
if (this._dec.put(data.subarray(dataPos, end)) !== DECODER_OK) {
this._dec.release();
this._aborted = true;
}
Expand All @@ -85,13 +93,15 @@ export class IIPHandler implements IOscHandler, IResetHandler {
let cond: number | boolean = true;
if (cond = success) {
if (cond = !this._dec.end()) {
this._metrics = imageType(this._dec.data8);
if (cond = this._metrics.mime !== 'unsupported') {
w = this._metrics.width;
h = this._metrics.height;
if (cond = w && h && w * h < this._opts.pixelLimit) {
[w, h] = this._resize(w, h).map(Math.floor);
cond = w && h && w * h < this._opts.pixelLimit;
if (cond = this._dec.data8.length === this._header.size) {
this._metrics = imageType(this._dec.data8);
if (cond = this._metrics.mime !== 'unsupported') {
w = this._metrics.width;
h = this._metrics.height;
if (cond = w && h && w * h < this._opts.pixelLimit) {
[w, h] = this._resize(w, h).map(Math.floor);
cond = w && h && w * h < this._opts.pixelLimit;
}
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion addons/addon-image/src/ImageAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { ImageAddon as IImageApi } from '@xterm/addon-image';
import { IIPHandler } from './IIPHandler';
import { ImageRenderer } from './ImageRenderer';
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
import { KittyGraphicsHandler } from './kitty/KittyGraphicsHandler';
import { SixelHandler } from './SixelHandler';
import { ITerminalExt, IImageAddonOptions, IResetHandler } from './Types';

Expand All @@ -22,7 +23,9 @@ const DEFAULT_OPTIONS: IImageAddonOptions = {
storageLimit: 128,
showPlaceholder: true,
iipSupport: true,
iipSizeLimit: 20000000
iipSizeLimit: 20000000,
kittySupport: true,
kittySizeLimit: 20000000
};

// max palette size supported by the sixel lib (compile time setting)
Expand Down Expand Up @@ -144,6 +147,15 @@ export class ImageAddon implements ITerminalAddon, IImageApi {
terminal._core._inputHandler._parser.registerOscHandler(1337, iipHandler)
);
}

// Kitty graphics handler
if (this._opts.kittySupport) {
const kittyHandler = new KittyGraphicsHandler(this._opts, this._renderer!, this._storage!, terminal);
this._handlers.set('kitty', kittyHandler);
this._disposeLater(
terminal._core._inputHandler._parser.registerApcHandler(0x47, kittyHandler)
);
}
}

// Note: storageLimit is skipped here to not intoduce a surprising side effect.
Expand Down
6 changes: 4 additions & 2 deletions addons/addon-image/src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IDisposable, IMarker, Terminal } from '@xterm/xterm';
// private imports from base repo we build against
import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants';
import type { AttributeData } from 'common/buffer/AttributeData';
import type { IParams, IDcsHandler, IOscHandler, IEscapeSequenceParser } from 'common/parser/Types';
import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from 'common/parser/Types';
import type { IBufferLine, IExtendedAttrs, IInputHandler } from 'common/Types';
import type { ITerminal, ReadonlyColorSet } from 'browser/Types';
import type { IRenderDimensions } from 'browser/renderer/shared/Types';
Expand All @@ -22,7 +22,7 @@ export const enum Cell {
}

// export some privates for local usage
export { AttributeData, IParams, IDcsHandler, IOscHandler, BgFlags, IRenderDimensions, IRenderService, Content, ExtFlags, Attributes, UnderlineStyle, ReadonlyColorSet };
export { AttributeData, IParams, IDcsHandler, IOscHandler, IApcHandler, BgFlags, IRenderDimensions, IRenderService, Content, ExtFlags, Attributes, UnderlineStyle, ReadonlyColorSet };

/**
* Plugin ctor options.
Expand All @@ -38,6 +38,8 @@ export interface IImageAddonOptions {
sixelSizeLimit: number;
iipSupport: boolean;
iipSizeLimit: number;
kittySupport: boolean;
kittySizeLimit: number;
}

export interface IResetHandler {
Expand Down
Loading
Loading