detached 6af4e0f

Disable automatic inline images in Herdr

commit
6af4e0f2ece7193709355d730adcc84a5057de59
author
patriceckhart · · 2026-07-23 12:50 UTC
parent
c0a7154
signature
unsigned
refs
tag: v0.3.2
3 files · +16 -7
M README.md +3 -1
@@ -703,11 +703,13 @@ When a tool returns an image (for example `read` on a PNG), zot renders it inlin
703703
704704 | Value | Effect |
705705 |---|---|
706-| unset (default) | Auto-detect based on `TERM_PROGRAM`. |
706+| unset (default) | Auto-detect based on `TERM_PROGRAM`; use the text placeholder inside VS Code and Herdr. |
707707 | `iterm`, `iterm2` | Force the iTerm2 OSC 1337 protocol. |
708708 | `kitty` | Force the Kitty graphics protocol. |
709709 | `off`, `none` | Always use the text placeholder. |
710710
711+Herdr's Kitty graphics support is currently experimental, so zot does not enable inline images there automatically. After enabling `experimental.kitty_graphics` in Herdr, set `ZOT_INLINE_IMAGES=kitty` to opt in.
712+
711713 Frames containing images are full-repainted (no differential diff) to prevent stale image pixels from lingering through scroll. That costs one terminal flash per image-containing frame; set `ZOT_INLINE_IMAGES=off` if that bothers you.
712714
713715 ## Tool rendering
@@ -703,11 +703,13 @@ When a tool returns an image (for example `read` on a PNG), zot renders it inlin
703 703
704 | Value | Effect |704 | Value | Effect |
705 |---|---|705 |---|---|
706-| unset (default) | Auto-detect based on `TERM_PROGRAM`. |706+| unset (default) | Auto-detect based on `TERM_PROGRAM`; use the text placeholder inside VS Code and Herdr. |
707 | `iterm`, `iterm2` | Force the iTerm2 OSC 1337 protocol. |707 | `iterm`, `iterm2` | Force the iTerm2 OSC 1337 protocol. |
708 | `kitty` | Force the Kitty graphics protocol. |708 | `kitty` | Force the Kitty graphics protocol. |
709 | `off`, `none` | Always use the text placeholder. |709 | `off`, `none` | Always use the text placeholder. |
710 710
711+Herdr's Kitty graphics support is currently experimental, so zot does not enable inline images there automatically. After enabling `experimental.kitty_graphics` in Herdr, set `ZOT_INLINE_IMAGES=kitty` to opt in.
712+
711 Frames containing images are full-repainted (no differential diff) to prevent stale image pixels from lingering through scroll. That costs one terminal flash per image-containing frame; set `ZOT_INLINE_IMAGES=off` if that bothers you.713 Frames containing images are full-repainted (no differential diff) to prevent stale image pixels from lingering through scroll. That costs one terminal flash per image-containing frame; set `ZOT_INLINE_IMAGES=off` if that bothers you.
712 714
713 ## Tool rendering715 ## Tool rendering
M packages/tui/image.go +5 -5
@@ -55,11 +55,11 @@ func detectImageProtocolAuto() ImageProtocol {
5555 term := os.Getenv("TERM")
5656 kittyWindow := os.Getenv("KITTY_WINDOW_ID")
5757
58- // VS Code's integrated terminal exposes several protocol-ish env
59- // combinations depending on the underlying shell/pty, but its image
60- // layer is inconsistent enough that auto-enable is more annoying than
61- // useful. Users can still force a protocol via ZOT_INLINE_IMAGES.
62- if strings.EqualFold(termProgram, "vscode") {
58+ // VS Code's integrated terminal and Herdr panes can inherit protocol
59+ // indicators from their host terminal without reliably rendering the
60+ // corresponding image escapes. Default to the text fallback in these
61+ // intermediaries. Users can still force a protocol via ZOT_INLINE_IMAGES.
62+ if strings.EqualFold(termProgram, "vscode") || os.Getenv("HERDR_ENV") != "" {
6363 return ImageProtocolNone
6464 }
6565
@@ -55,11 +55,11 @@ func detectImageProtocolAuto() ImageProtocol {
55 term := os.Getenv("TERM")55 term := os.Getenv("TERM")
56 kittyWindow := os.Getenv("KITTY_WINDOW_ID")56 kittyWindow := os.Getenv("KITTY_WINDOW_ID")
57 57
58- // VS Code's integrated terminal exposes several protocol-ish env58+ // VS Code's integrated terminal and Herdr panes can inherit protocol
59- // combinations depending on the underlying shell/pty, but its image59+ // indicators from their host terminal without reliably rendering the
60- // layer is inconsistent enough that auto-enable is more annoying than60+ // corresponding image escapes. Default to the text fallback in these
61- // useful. Users can still force a protocol via ZOT_INLINE_IMAGES.61+ // intermediaries. Users can still force a protocol via ZOT_INLINE_IMAGES.
62- if strings.EqualFold(termProgram, "vscode") {62+ if strings.EqualFold(termProgram, "vscode") || os.Getenv("HERDR_ENV") != "" {
63 return ImageProtocolNone63 return ImageProtocolNone
64 }64 }
65 65
M packages/tui/image_test.go +8 -1
@@ -112,19 +112,26 @@ func TestRowsForInlineImageCellAspectOverride(t *testing.T) {
112112 }
113113 }
114114
115-func TestDetectImageProtocolPlaceholderAndVSCode(t *testing.T) {
115+func TestDetectImageProtocolPlaceholderAndIntermediaries(t *testing.T) {
116116 t.Setenv("ZOT_INLINE_IMAGES", "placeholder")
117117 if got := DetectImageProtocol(); got != ImageProtocolNone {
118118 t.Fatalf("placeholder protocol = %v, want none", got)
119119 }
120120
121121 t.Setenv("ZOT_INLINE_IMAGES", "")
122+ t.Setenv("HERDR_ENV", "")
122123 t.Setenv("TERM_PROGRAM", "vscode")
123124 t.Setenv("KITTY_WINDOW_ID", "1")
124125 if got := DetectImageProtocol(); got != ImageProtocolNone {
125126 t.Fatalf("vscode auto protocol = %v, want none", got)
126127 }
127128
129+ t.Setenv("TERM_PROGRAM", "ghostty")
130+ t.Setenv("HERDR_ENV", "1")
131+ if got := DetectImageProtocol(); got != ImageProtocolNone {
132+ t.Fatalf("Herdr auto protocol = %v, want none", got)
133+ }
134+
128135 t.Setenv("ZOT_INLINE_IMAGES", "kitty")
129136 if got := DetectImageProtocol(); got != ImageProtocolKitty {
130137 t.Fatalf("forced kitty protocol = %v, want kitty", got)
@@ -112,19 +112,26 @@ func TestRowsForInlineImageCellAspectOverride(t *testing.T) {
112 }112 }
113 }113 }
114 114
115-func TestDetectImageProtocolPlaceholderAndVSCode(t *testing.T) {115+func TestDetectImageProtocolPlaceholderAndIntermediaries(t *testing.T) {
116 t.Setenv("ZOT_INLINE_IMAGES", "placeholder")116 t.Setenv("ZOT_INLINE_IMAGES", "placeholder")
117 if got := DetectImageProtocol(); got != ImageProtocolNone {117 if got := DetectImageProtocol(); got != ImageProtocolNone {
118 t.Fatalf("placeholder protocol = %v, want none", got)118 t.Fatalf("placeholder protocol = %v, want none", got)
119 }119 }
120 120
121 t.Setenv("ZOT_INLINE_IMAGES", "")121 t.Setenv("ZOT_INLINE_IMAGES", "")
122+ t.Setenv("HERDR_ENV", "")
122 t.Setenv("TERM_PROGRAM", "vscode")123 t.Setenv("TERM_PROGRAM", "vscode")
123 t.Setenv("KITTY_WINDOW_ID", "1")124 t.Setenv("KITTY_WINDOW_ID", "1")
124 if got := DetectImageProtocol(); got != ImageProtocolNone {125 if got := DetectImageProtocol(); got != ImageProtocolNone {
125 t.Fatalf("vscode auto protocol = %v, want none", got)126 t.Fatalf("vscode auto protocol = %v, want none", got)
126 }127 }
127 128
129+ t.Setenv("TERM_PROGRAM", "ghostty")
130+ t.Setenv("HERDR_ENV", "1")
131+ if got := DetectImageProtocol(); got != ImageProtocolNone {
132+ t.Fatalf("Herdr auto protocol = %v, want none", got)
133+ }
134+
128 t.Setenv("ZOT_INLINE_IMAGES", "kitty")135 t.Setenv("ZOT_INLINE_IMAGES", "kitty")
129 if got := DetectImageProtocol(); got != ImageProtocolKitty {136 if got := DetectImageProtocol(); got != ImageProtocolKitty {
130 t.Fatalf("forced kitty protocol = %v, want kitty", got)137 t.Fatalf("forced kitty protocol = %v, want kitty", got)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close