addon-image: Fix Kitty placement lifecycle - #6098
Open
mitchdenny wants to merge 1 commit into
Open
Conversation
Track Kitty image data separately from named and anonymous placements, implement protocol-correct placement deletion and replacement, and reuse decoded sources across placements. Add regression coverage for placement identity, cleanup, buffer scope, quotas, and async races, plus visual placement demos. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b97ee6e0-c828-45cd-a388-a4bdd396b4d6
Author
|
I spiced up the demo app to show how useful the ability retain and re-place images can be: Screen.Recording.2026-08-11.at.10.51.46.pm.movThe reason I started working on this is I was trying to hook up my .NET terminal emulator/UI stack project to xterm.js' KGP support and found a few glitches in my rendering which turned out to be incomplete implementation on the xterm.js side. Here is an example of xterm.js in action in a demo web-UX that hosts my TUI framework. Screen.Recording.2026-08-11.at.8.29.22.pm.mov |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #5707 and #5722. Partially addresses the
a/Aviewport scope andlower/uppercase lifetime requirements in #5710; the remaining delete selectors
from that issue are out of scope.
Why
Kitty image data and placements have different lifetimes. One transmitted image
can have multiple displayed placements, and a non-zero placement ID identifies
one placement by
(imageId, placementId).The current addon maps a Kitty image ID to one
ImageStorageentry. Multipleplacements therefore overwrite shared bookkeeping, same-ID replacement can
leave old cells visible, and targeted deletion can remove shared image data.
It also decodes the transmitted source again for each placement.
Behavior
a=p,i=N,p=P(N,P)a=p,i=Norp=0d=i,i=N,p=P(N,P)and preserves image datad=I,i=N,p=P(N,P)and frees data only if unreferencedd=i/I,i=Nd=a/Ai=NExplicit replacement and deletion clear their cell metadata so stale
placeholders cannot remain.
Implementation
placement records.
placement rather than terminal scrollback size.
entries.
and later deletion semantics.
cap of 256 transmitted images.
stale bitmaps are closed before they can reach storage.
HAS_EXTENDEDas authoritative in new visibility/index scans, matchingthe guidance in image addon: overwriting with text does not remove tiles anymore #5860. This PR does not change image addon: overwriting with text does not remove tiles anymore #5860's existing renderer/text
overwrite behavior.
transmits one transparent teardrop sprite, animates a dense randomized field
of named placements at independent pixel velocities using sub-cell offsets,
targeted-deletes and reuses them, adds anonymous
p=0placements, clearsvisible placements with
d=a, proves payload reuse once more, and ends withuppercase cleanup.
transparent 200x200 PNG frames once, then moves/replaces the active placement
and switches frames through lowercase hide/show operations before freeing all
stored frame data.
Performance
decoded source remains cached.
d=a/Ascans only the active viewport.rasters.
Scope and compatibility
as required by the Kitty protocol.
c/C,n/N,p/P,q/Q,r/R,x/X,y/Y,z/Z, orf/Fselectors tracked by Kitty graphics: Implement remaining delete selectors and case-sensitive layer distinction #5710.placements cannot yet stack and reveal an older image when a newer one is
removed; image-on-image composition requires a broader renderer/buffer design.
(Kitty graphics: Implement animation support (a=f, a=a, a=c) #5713) remain out of scope.
Validation
npm run buildnpm run esbuildnpm run esbuild-demo-clientnpm run esbuild-demo-servernpm run lint-changesnpm run test-unit -- addons/addon-image/out-esbuild/kitty/KittyGraphicsTypes.test.jsnpm run test-integration -- --suite=addon-image --project=Chromiumnpm run test-integration -- --suite=addon-imagegit diff --checkThe demo client/server bundles were built, and the Playwright suite exercised
the addon through its automatically started demo server. This was automated
integration validation, not a manual walkthrough of the interactive xterm.js
demo UI. The new placement-rain button was also exercised headlessly through its
full animation with no page errors. The wireframe-cube button completed its
preload, 15-second animation, and cleanup without page errors or viewport
scrolling.
The integration suite adds 31 regression cases covering named siblings,
same-placement replacement, anonymous placements, targeted and viewport-scoped
deletion, scrollback and alternate-buffer references, shared decode lifetime,
crop fragments, retransmission, quota pressure, stale metadata, ICH/DCH shifts,
resize/reflow, alternate-only index rebuilds, and deterministic async
reset/retransmit/delete/dispose races.
Downstream validation in Hex1b used the packed CommonJS addon with placeholders
enabled:
Hex1b was used as an independent black-box conformance oracle; the implementation
and regression tests are written directly for xterm.js.