-
Notifications
You must be signed in to change notification settings - Fork 908
img_display: Add support for iTerm2's v3.5.0+ image protocol #3080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
iTerm2 v3.5.0 introduced a change which imposes a hard limit of 1 MiB on the length of escape sequences it will parse. Escape sequences longer than this will force iTerm2 to bail on parsing, which results in the file content spilling into the terminal as visible base64 characters. To retain support for large files, it introduced a new 'multipart' protocol variant intended to transfer a file as a series of escape sequences rather than a single big one. This change updates `ITerm2ImageDisplayer` to use this newer variant. Also added is a new setting, `iterm2_multipart_file_chunk_size`, which determines the chunk size. In many (hopefully most) cases, the default added to rc.conf should suffice. I even tried to avoid adding it, but reference implementations and documentation suggest there are a handful of situations where a user may want to tweak this value to make image display work, and I don't believe these situations can be reliably detected automatically. "Old" versions of tmux get a specific mention, and anyone using iTerm versions older than 3.5.0 will want to force the old protocol, which can be done with a value of 0.
85b6f0a
to
6c86b17
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code LGTM, some sort of queue where we can slice chunk_size
characters off the top every time would make things a bit clearer but I don't know of one in the Python stdlib.
I'm tagging this in the hope someone else steps forward to confirm things work but otherwise I'll merge it in a week or two. Ping me if that doesn't happen.
To be honest, I was grabbing the
Now I'm also thinking that perhaps the Anyway, I can get this all down in another commit in a moment and then we can see how badly I'm overthinking this 😅 |
This setting now determines the maximum length of the escape sequences sent - that is, the value *includes* the opening and closing strings for each `FilePart` sequence. `ITerm2ImageDisplayer` then figures how many bytes are left over per chunk for file data. This maps more cleanly to the various constraints that make this option necessary in the first place - i.e. iTerm2 3.5+ maxes out at 1048576 bytes, so set exactly that value here. Where old tmux source shows a buffer size of 256, set 256. No need for notions of 'overhead'
This makes for a more idiomatic read-out of the chunk data. Like any other file, now we just read(n) until we get an empty string Adds a six-style StringIO helper in ranger/ext/stringio.py
Okay, I thought streaming the file by incremental reads would be straightforward, but there are enough nuanced edge cases that I think memory optimization should be a separate goal/enhancement. The difficulty stems from iTerm's requirement that the file size be sent in the header. On the face of it, that seems trivial - just Also, we'd probably want a context manager to make sure that final During all of this, I realized we can use Also, I went ahead and made the change to how |
Update
ITerm2ImageDisplayer
to support iTerm's 'multipart' file escape sequences. Adds a new settingiterm2_multipart_file_chunk_size
to control the size of these parts, which may be necessary in some situations.ISSUE TYPE
RUNTIME ENVIRONMENT
CHECKLIST
CONTRIBUTING
document has been read [REQUIRED]DESCRIPTION
iTerm2 quietly introduced a change to how it handles escape sequences, which has consequences for image previewing. Since v3.5.0, it has imposed a hard limit of 1MiB on all escape sequences.
To retain support for large files, it introduced a new 'multipart' protocol variant intended to transfer a file as a series of escape sequences rather than a single big one. This change updates
ITerm2ImageDisplayer
to use this newer variant.Also added is a new setting,
iterm2_multipart_file_chunk_size
, which controls the chunk size, or falls back to the old single-part variant when set to 0.MOTIVATION AND CONTEXT
I wasn't able to find any existing issues relating to this, but I've certainly been running afoul of it, and the fix is fairly straightforward, so instead of an issue here's a PR.
Since iTerm v3.5.0 (May 2024), images with a base64 representation larger than 1MiB fail to display properly when the
preview_images_method
isiterm
. The way this manifests is by the terminal window being filled with base64 text instead of the highlighted image. This change restores the ability to preview large images.I was reluctant to expose such a fine-grained detail like
iterm2_multipart_file_chunk_size
, but various sources suggest there are a few situations where a user may want to tweak this, and I don't believe these situations can be reliably detected automatically. This also feels acceptable because of the presence of other iTerm2-specific settings (namelyiterm2_font_{width,height}
."Old" versions of tmux are specifically called out in iTerm's documentation1, and iTerm's
imgcat
utility2 chooses a conservative chunk size to maximize compatibility. "Old" is not quantified, but discussion suggests it applies to a tmux limitation that was lifted in 20143. Other discussion suggests slow connections may be another situation where a user might want to lower this4.Also, users of iTerm 3.4.x and older will want to disable multipart encoding entirely, as such versions won't understand it. A
iterm2_multipart_file_chunk_size
of 0 will revert to the older "File" variant - the behavior ranger had before this PR.I've settled on a default of 1048320, which is the 1MiB hard limit less 256 bytes for escape sequence overhead. Another approach could be to copy
imgcat
's default of 200 bytes for greatest compatibility and include some guidance to increase it, but:TESTING
No automated testing - I couldn't find any existing tests relating to image display. I have manually tested that this works in iTerm 3.5.12 on macOS 14.6.1 with its stock version of Python at 3.9.6, both with and without tmux 3.4, running both locally and over ssh.
Functional changes are entirely contained within the
ITerm2ImageDisplayer
class inranger/ext/img_display.py
, with the exception ofranger/container/settings.py
- edited to add theiterm2_multipart_file_chunk_size
setting. Changes to docs and .conf files have been kept near other iTerm-related areas and try to follow their style.Footnotes
https://iterm2.com/documentation-images.html ↩
https://iterm2.com/utilities/imgcat ↩
https://github.com/tmux/tmux/issues/487 ↩
https://github.com/tmux/tmux/issues/1388 ↩