Skip to content

Kitty graphics protocol load and display doesn't replace existing image with provided ID #6711

Closed

Description

@pluiedev

Discussed in #6680

Originally posted by chase March 12, 2025
Currently, from what I can tell, Ghostty will ignore the ID and allocate a new image ID instead of setting the image with that ID.

This part of the protocol is easy to miss because it's in a side note for the querying support section:

Sometimes, using an id is not appropriate, for example, if you do not want to replace a previously sent image with the same id, or if you are sending a dummy image and do not want it stored by the terminal emulator. In that case, you can use the query action, set a=q. Then the terminal emulator will try to load the image and respond with either OK or an error, as above, but it will not replace an existing image with the same id, nor will it store the image.

Or simply put, the logical opposite is: for transmission and display, it will replace an existing image with the same ID and store it.

This should make simple animation possible.

Activity

  1. added theissue type on Mar 14, 2025
  2. chase commented on Mar 15, 2025

    @chase

    After looking at the code for a bit, it seems like this behavior is already intended to be implemented and should be working.

    // If the image has no ID, we assign one
    if (loading.image.id == 0) {
    loading.image.id = storage.next_image_id;
    storage.next_image_id +%= 1;
    // If the image also has no number then its auto-ID is "implicit".
    // See the doc comment on the Image.implicit_id field for more detail.
    if (loading.image.number == 0) loading.image.implicit_id = true;
    }

    I'll try to figure out a minimal case to reproduce the issue. I'm guessing it has to do with not tracking evictions, but nothing certain yet.

  3. chase commented on Mar 16, 2025

    @chase

    Simply replacing 2 image IDs seems fine and works as expected.

    It does seem related to images being evicted, so maybe the case is that image.id == 0 after eviction?

  4. jun9898 commented on Aug 10, 2026

    @jun9898

    I ran into this issue while implementing a GIF-animation fallback for a terminal Slack client. Since Ghostty doesn't handle kitty animation frames yet (#5255), I stream GIF frames by retransmitting image data under a fixed image id — which made me care a lot about the exact replacement semantics, so I traced them through the source (current main, also checked v1.3.1).

    TL;DR: explicit-id replacement works as specified. I couldn't reproduce the behavior described in the OP at the source level, which also matches @chase's own follow-up ("Simply replacing 2 image IDs seems fine and works as expected").

    Details:

    One thing that can look like "replacement not working" from the client side: every a=T without an explicit p= creates a new internal placement (graphics_storage.zig L341-351 — intentionally, per the comment, and consistent with the protocol where each display command adds a placement). So a client that "replaces" an image by repeatedly sending a=T,i=<same-id> accumulates placements. The fix on the client side is to use a=t (transmit-only) for the data swap and keep the original placement — that works nicely against Ghostty today (it's what my GIF streaming ended up doing).

    Given the OP's follow-up and the above, this issue can probably be closed — unless the eviction edge case (@chase's last comment) is still worth tracking separately. Hope this helps with the triage effort under #8272.

  5. added this to the 1.4.0 milestone on Aug 20, 2026
  6. mitchellh commented on Aug 20, 2026

    @mitchellh
    Contributor

    This is fixed on main (to be 1.4) now, via multiple issues: #13173, then #13723, and #13893

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    vtControl sequence related

    Type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions