From f89524e577241bfd6cbf6a4fcdb7dcae9041ed8e Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 27 Jan 2026 21:25:31 -0800 Subject: [PATCH] Create KittyApchandler.ts --- .../src/KittyApcHandler.ts | 133 +++++++++++++++++ .../src/KittyGraphicsAddon.test.ts | 3 +- .../src/KittyGraphicsAddon.ts | 139 ++---------------- 3 files changed, 145 insertions(+), 130 deletions(-) create mode 100644 addons/addon-kitty-graphics/src/KittyApcHandler.ts diff --git a/addons/addon-kitty-graphics/src/KittyApcHandler.ts b/addons/addon-kitty-graphics/src/KittyApcHandler.ts new file mode 100644 index 00000000..062b5880 --- /dev/null +++ b/addons/addon-kitty-graphics/src/KittyApcHandler.ts @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2025 The xterm.js authors. All rights reserved. + * @license MIT + * + * Kitty graphics protocol types, constants, and parsing utilities. + */ + +/** + * Kitty graphics protocol action types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. + */ +export const enum KittyAction { + TRANSMIT = 't', + TRANSMIT_DISPLAY = 'T', + QUERY = 'q', + PLACEMENT = 'p', + DELETE = 'd' +} + +/** + * Kitty graphics protocol format types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference + */ +export const enum KittyFormat { + RGB = 24, + RGBA = 32, + PNG = 100 +} + +/** + * Kitty graphics protocol compression types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. + */ +export const enum KittyCompression { + NONE = '', + ZLIB = 'z' +} + +/** + * Kitty graphics protocol control data keys. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference + */ +export const enum KittyKey { + // Action to perform (t=transmit, T=transmit+display, q=query, p=placement, d=delete) + ACTION = 'a', + // Image format (24=RGB, 32=RGBA, 100=PNG) + FORMAT = 'f', + // Image ID for referencing stored images + ID = 'i', + // Source image width in pixels + WIDTH = 's', + // Source image height in pixels + HEIGHT = 'v', + // The left edge (in pixels) of the image area to display + X_OFFSET = 'x', + // The top edge (in pixels) of the image area to display + Y_OFFSET = 'y', + // Number of terminal columns to display the image over + COLUMNS = 'c', + // Number of terminal rows to display the image over + ROWS = 'r', + // More data flag (1=more chunks coming, 0=final chunk) + MORE = 'm', + // Compression type (z=zlib). This is essential for chunking larger images. + COMPRESSION = 'o', + // Quiet mode (1=suppress OK responses, 2=suppress error responses) + QUIET = 'q' +} + +// Pixel format constants +export const BYTES_PER_PIXEL_RGB = 3; +export const BYTES_PER_PIXEL_RGBA = 4; +export const ALPHA_OPAQUE = 255; + +/** + * Parsed Kitty graphics command. + */ +export interface IKittyCommand { + action?: string; + format?: number; + id?: number; + width?: number; + height?: number; + x?: number; + y?: number; + columns?: number; + rows?: number; + more?: number; + quiet?: number; + compression?: string; + payload?: string; +} + +/** + * Parses Kitty graphics control data into a command object. + */ +export function parseKittyCommand(data: string): IKittyCommand { + const cmd: IKittyCommand = {}; + const parts = data.split(','); + + for (const part of parts) { + const eqIdx = part.indexOf('='); + if (eqIdx === -1) continue; + + const key = part.substring(0, eqIdx); + const value = part.substring(eqIdx + 1); + + // Handle string keys first + if (key === KittyKey.ACTION) { + cmd.action = value; + continue; + } + if (key === KittyKey.COMPRESSION) { + cmd.compression = value; + continue; + } + const numValue = parseInt(value); + switch (key) { + case KittyKey.FORMAT: cmd.format = numValue; break; + case KittyKey.ID: cmd.id = numValue; break; + case KittyKey.WIDTH: cmd.width = numValue; break; + case KittyKey.HEIGHT: cmd.height = numValue; break; + case KittyKey.X_OFFSET: cmd.x = numValue; break; + case KittyKey.Y_OFFSET: cmd.y = numValue; break; + case KittyKey.COLUMNS: cmd.columns = numValue; break; + case KittyKey.ROWS: cmd.rows = numValue; break; + case KittyKey.MORE: cmd.more = numValue; break; + case KittyKey.QUIET: cmd.quiet = numValue; break; + } + } + + return cmd; +} diff --git a/addons/addon-kitty-graphics/src/KittyGraphicsAddon.test.ts b/addons/addon-kitty-graphics/src/KittyGraphicsAddon.test.ts index 4066a45f..eb721426 100644 --- a/addons/addon-kitty-graphics/src/KittyGraphicsAddon.test.ts +++ b/addons/addon-kitty-graphics/src/KittyGraphicsAddon.test.ts @@ -4,7 +4,8 @@ */ import { assert } from 'chai'; import { Terminal } from 'browser/public/Terminal'; -import { KittyGraphicsAddon, parseKittyCommand } from './KittyGraphicsAddon'; +import { KittyGraphicsAddon } from './KittyGraphicsAddon'; +import { parseKittyCommand } from './KittyApcHandler'; /** * Write data to terminal and wait for completion. diff --git a/addons/addon-kitty-graphics/src/KittyGraphicsAddon.ts b/addons/addon-kitty-graphics/src/KittyGraphicsAddon.ts index a386163c..7b3679a8 100644 --- a/addons/addon-kitty-graphics/src/KittyGraphicsAddon.ts +++ b/addons/addon-kitty-graphics/src/KittyGraphicsAddon.ts @@ -7,135 +7,16 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { KittyGraphicsAddon as IKittyGraphicsApi, IKittyGraphicsOptions, IKittyImage } from '@xterm/addon-kitty-graphics'; import { KittyImageRenderer } from './KittyImageRenderer'; import type { ITerminalExt } from './Types'; - -/** - * Kitty graphics protocol action types. - * Possible actions from protocol. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. - */ -const enum KittyAction { - TRANSMIT = 't', - TRANSMIT_DISPLAY = 'T', - QUERY = 'q', - PLACEMENT = 'p', - DELETE = 'd' -} - -/** - * Kitty graphics protocol format types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference where the format for *-bit came from. - */ -const enum KittyFormat { - RGB = 24, - RGBA = 32, - PNG = 100 -} - -/** - * Kitty graphics protocol compression types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. - */ -const enum KittyCompression { - NONE = '', - ZLIB = 'z' -} - -/** - * Kitty graphics protocol control data keys. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference - */ -const enum KittyKey { - // Action to perform (t=transmit, T=transmit+display, q=query, p=placement, d=delete) - ACTION = 'a', - // Image format (24=RGB, 32=RGBA, 100=PNG) - FORMAT = 'f', - // Image ID for referencing stored images - ID = 'i', - // Source image width in pixels - WIDTH = 's', - // Source image height in pixels - HEIGHT = 'v', - // The left edge (in pixels) of the image area to display - X_OFFSET = 'x', - // The top edge (in pixels) of the image area to display - Y_OFFSET = 'y', - // Number of terminal columns to display the image over - COLUMNS = 'c', - // Number of terminal rows to display the image over - ROWS = 'r', - // More data flag (1=more chunks coming, 0=final chunk) - MORE = 'm', - // Compression type (z=zlib). This is essential for chunking larger images. - COMPRESSION = 'o', - // Quiet mode (1=suppress OK responses, 2=suppress error responses) - QUIET = 'q' -} - -// Pixel format constants -const BYTES_PER_PIXEL_RGB = 3; -const BYTES_PER_PIXEL_RGBA = 4; -const ALPHA_OPAQUE = 255; - -/** - * Parsed Kitty graphics command. - */ -export interface IKittyCommand { - action?: string; - format?: number; - id?: number; - width?: number; - height?: number; - x?: number; - y?: number; - columns?: number; - rows?: number; - more?: number; - quiet?: number; - compression?: string; - payload?: string; -} - -/** - * Parses Kitty graphics control data into a command object. - * Exported for testing. - */ -export function parseKittyCommand(data: string): IKittyCommand { - const cmd: IKittyCommand = {}; - const parts = data.split(','); - - for (const part of parts) { - const eqIdx = part.indexOf('='); - if (eqIdx === -1) continue; - - const key = part.substring(0, eqIdx); - const value = part.substring(eqIdx + 1); - - // Handle string keys first - if (key === KittyKey.ACTION) { - cmd.action = value; - continue; - } - if (key === KittyKey.COMPRESSION) { - cmd.compression = value; - continue; - } - const numValue = parseInt(value); - switch (key) { - case KittyKey.FORMAT: cmd.format = numValue; break; - case KittyKey.ID: cmd.id = numValue; break; - case KittyKey.WIDTH: cmd.width = numValue; break; - case KittyKey.HEIGHT: cmd.height = numValue; break; - case KittyKey.X_OFFSET: cmd.x = numValue; break; - case KittyKey.Y_OFFSET: cmd.y = numValue; break; - case KittyKey.COLUMNS: cmd.columns = numValue; break; - case KittyKey.ROWS: cmd.rows = numValue; break; - case KittyKey.MORE: cmd.more = numValue; break; - case KittyKey.QUIET: cmd.quiet = numValue; break; - } - } - - return cmd; -} +import { + KittyAction, + KittyFormat, + KittyCompression, + BYTES_PER_PIXEL_RGB, + BYTES_PER_PIXEL_RGBA, + ALPHA_OPAQUE, + parseKittyCommand, + type IKittyCommand +} from './KittyApcHandler'; /** * Pending chunked transmission state.