33 * @license MIT
44 */
55
6+ import { IDisposable } from '@xterm/xterm' ;
67import { IApcHandler , IImageAddonOptions , IResetHandler , ITerminalExt , ImageLayer } from '../Types' ;
78import { ImageRenderer } from '../ImageRenderer' ;
8- import { ImageStorage , CELL_SIZE_DEFAULT } from '../ImageStorage' ;
9+ import { CELL_SIZE_DEFAULT } from '../ImageStorage' ;
10+ import { KittyImageStorage } from './KittyImageStorage' ;
911import Base64Decoder , { type DecodeStatus } from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm' ;
1012import {
1113 KittyAction ,
@@ -36,7 +38,7 @@ const SEMICOLON = 0x3B;
3638/**
3739 * Kitty graphics protocol handler with streaming base64 decoding.
3840 */
39- export class KittyGraphicsHandler implements IApcHandler , IResetHandler {
41+ export class KittyGraphicsHandler implements IApcHandler , IResetHandler , IDisposable {
4042 private _aborted = false ;
4143 private _decodeError = false ;
4244
@@ -69,21 +71,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
6971 * When a chunk arrives with no i=, this key is used to find the pending upload.
7072 */
7173 private _lastPendingKey : number | undefined ;
72- private _nextImageId = 1 ;
73- /** Maps Kitty protocol image ID → ImageStorage internal ID for deletion/lookup. */
74- private _kittyIdToStorageId : Map < number , number > = new Map ( ) ;
75- // TODO: Eliminate double storage — raw image data lives here (as Blob) AND rendered
76- // ImageBitmaps live in ImageStorage. Currently we only use ImageStorage.addImage(bitmap)
77- // for tiling + cursor movement + marker-based eviction.
78- //
79-
80- // See: https://github.com/xtermjs/xterm.js/pull/5619#issuecomment-3853678815
81- private _images : Map < number , IKittyImageData > = new Map ( ) ;
8274
8375 constructor (
8476 private readonly _opts : IImageAddonOptions ,
8577 private readonly _renderer : ImageRenderer ,
86- private readonly _storage : ImageStorage ,
78+ private readonly _kittyStorage : KittyImageStorage ,
8779 private readonly _coreTerminal : ITerminalExt
8880 ) {
8981 // Convert decoded size limit -> max encoded bytes.
@@ -102,8 +94,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
10294 this . _activeDecoder . release ( ) ;
10395 this . _activeDecoder = null ;
10496 }
105- this . _images . clear ( ) ;
106- this . _kittyIdToStorageId . clear ( ) ;
97+ this . _kittyStorage . reset ( ) ;
98+ }
99+
100+ public dispose ( ) : void {
101+ this . reset ( ) ;
107102 }
108103
109104 public start ( ) : void {
@@ -397,16 +392,13 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
397392
398393 if ( decodeError || bytes . length === 0 ) return true ;
399394
400- const id = cmd . id ?? this . _nextImageId ++ ;
401- const image : IKittyImageData = {
402- id,
395+ this . _kittyStorage . storeImage ( cmd . id , {
403396 data : new Blob ( [ bytes as BlobPart ] ) ,
404397 width : cmd . width ?? 0 ,
405398 height : cmd . height ?? 0 ,
406399 format : ( cmd . format ?? KittyFormat . RGBA ) as 24 | 32 | 100 ,
407400 compression : cmd . compression ?? ''
408- } ;
409- this . _images . set ( id , image ) ;
401+ } ) ;
410402 return true ;
411403 }
412404
@@ -426,8 +418,8 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
426418 if ( this . _pendingTransmissions . has ( pendingKey ) ) return true ;
427419
428420 // Display the completed image
429- const id = cmd . id ?? this . _nextImageId - 1 ;
430- const image = this . _images . get ( id ) ;
421+ const id = cmd . id ?? this . _kittyStorage . lastImageId ;
422+ const image = this . _kittyStorage . getImage ( id ) ;
431423 if ( image ) {
432424 const result = this . _displayImage ( image , cmd ) ;
433425 if ( cmd . id !== undefined ) {
@@ -508,7 +500,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
508500 }
509501 this . _pendingTransmissions . clear ( ) ;
510502 this . _lastPendingKey = undefined ;
511- this . _deleteAll ( ) ;
503+ this . _kittyStorage . deleteAll ( ) ;
512504 break ;
513505 case 'i' :
514506 case 'I' :
@@ -522,7 +514,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
522514 this . _lastPendingKey = undefined ;
523515 }
524516 }
525- this . _deleteById ( cmd . id ) ;
517+ this . _kittyStorage . deleteById ( cmd . id ) ;
526518 }
527519 break ;
528520 default :
@@ -532,23 +524,6 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
532524 return true ;
533525 }
534526
535- private _deleteById ( id : number ) : void {
536- this . _images . delete ( id ) ;
537- const storageId = this . _kittyIdToStorageId . get ( id ) ;
538- if ( storageId !== undefined ) {
539- this . _storage . deleteImage ( storageId ) ;
540- this . _kittyIdToStorageId . delete ( id ) ;
541- }
542- }
543-
544- private _deleteAll ( ) : void {
545- this . _images . clear ( ) ;
546- for ( const storageId of this . _kittyIdToStorageId . values ( ) ) {
547- this . _storage . deleteImage ( storageId ) ;
548- }
549- this . _kittyIdToStorageId . clear ( ) ;
550- }
551-
552527 private _sendResponse ( id : number , message : string , quiet : number ) : void {
553528 const isOk = message === 'OK' ;
554529 if ( isOk && quiet === 1 ) return ;
@@ -602,14 +577,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
602577 const layer : ImageLayer = ( wantsBottom && this . _coreTerminal . options . allowTransparency ) ? 'bottom' : 'top' ;
603578
604579 const zIndex = cmd . zIndex ?? 0 ;
605- let storageId : number ;
606580 if ( w !== bitmap . width || h !== bitmap . height ) {
607581 const resized = await createImageBitmap ( bitmap , { resizeWidth : w , resizeHeight : h } ) ;
608- storageId = this . _storage . addImage ( resized , true , layer , zIndex ) ;
582+ this . _kittyStorage . addImage ( image . id , resized , true , layer , zIndex ) ;
609583 } else {
610- storageId = this . _storage . addImage ( bitmap , true , layer , zIndex ) ;
584+ this . _kittyStorage . addImage ( image . id , bitmap , true , layer , zIndex ) ;
611585 }
612- this . _kittyIdToStorageId . set ( image . id , storageId ) ;
613586
614587 // Kitty cursor movement
615588 // Per spec: cursor placed at first column after last image column,
@@ -723,7 +696,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
723696 }
724697
725698 public get images ( ) : ReadonlyMap < number , IKittyImageData > {
726- return this . _images ;
699+ return this . _kittyStorage . images ;
700+ }
701+
702+ public get _kittyIdToStorageId ( ) : ReadonlyMap < number , number > {
703+ return this . _kittyStorage . kittyIdToStorageId ;
727704 }
728705
729706 public get pendingTransmissions ( ) : ReadonlyMap < number , IPendingTransmission > {
0 commit comments