fix(pets): render half-blocks in the VS Code/Cursor terminal
detect_terminal_graphics() mapped TERM_PROGRAM=vscode to the iTerm2 inline image protocol, but the integrated terminal doesn't render inline images unless terminal.integrated.enableImages is on — so `hermes pets show` emitted image escapes xterm.js silently drops, leaving just the label. It also inherits ITERM_SESSION_ID/KITTY_WINDOW_ID when launched from those terminals, which false-positived the same way. Trust the authoritative TERM_PROGRAM=vscode and fall back to truecolor half-blocks (always renderable in its grid); users who enabled images can still pin display.pet.render_mode explicitly.
This commit is contained in:
parent
6681bef707
commit
db00cbfd56
@ -7,7 +7,7 @@ decode + capability-detection + protocol-encoding logic exists exactly once.
|
||||
Supported output modes, in fidelity order:
|
||||
|
||||
- ``kitty`` — the kitty graphics protocol (kitty, Ghostty, WezTerm).
|
||||
- ``iterm`` — iTerm2 inline images (iTerm2, WezTerm, VS Code terminal).
|
||||
- ``iterm`` — iTerm2 inline images (iTerm2, WezTerm).
|
||||
- ``sixel`` — DEC sixel (xterm -ti vt340, foot, mlterm, WezTerm, …).
|
||||
- ``unicode`` — 24-bit half-block downscale; works in any truecolor terminal.
|
||||
|
||||
@ -56,6 +56,17 @@ def detect_terminal_graphics() -> str:
|
||||
term = os.environ.get("TERM", "").lower()
|
||||
term_program = os.environ.get("TERM_PROGRAM", "").lower()
|
||||
|
||||
# The VS Code / Cursor integrated terminal sets TERM_PROGRAM=vscode
|
||||
# authoritatively but does NOT scrub the terminal env vars it inherits when
|
||||
# launched from another emulator (ITERM_SESSION_ID, KITTY_WINDOW_ID, …).
|
||||
# Trusting those leaks emits an image protocol the embedded xterm.js can't
|
||||
# display — you get a blank frame. Inline images there are opt-in
|
||||
# (terminal.integrated.enableImages), so default to half-blocks, which
|
||||
# always render in its truecolor grid. Users who enabled images can pin
|
||||
# display.pet.render_mode explicitly.
|
||||
if term_program == "vscode":
|
||||
return "unicode"
|
||||
|
||||
# kitty graphics protocol
|
||||
if os.environ.get("KITTY_WINDOW_ID") or "kitty" in term or "ghostty" in term:
|
||||
return "kitty"
|
||||
@ -69,8 +80,6 @@ def detect_terminal_graphics() -> str:
|
||||
# iTerm2 inline images
|
||||
if term_program == "iterm.app" or os.environ.get("ITERM_SESSION_ID"):
|
||||
return "iterm"
|
||||
if term_program == "vscode":
|
||||
return "iterm"
|
||||
|
||||
# sixel-capable terminals (env heuristics only)
|
||||
if term_program in {"mintty"} or "foot" in term or "mlterm" in term:
|
||||
|
||||
@ -177,3 +177,19 @@ def test_detect_terminal_graphics_env(monkeypatch):
|
||||
|
||||
monkeypatch.setenv("TERM", "xterm-256color")
|
||||
assert render.detect_terminal_graphics() == "unicode"
|
||||
|
||||
|
||||
def test_vscode_terminal_ignores_leaked_graphics_env(monkeypatch):
|
||||
# The VS Code / Cursor integrated terminal can't show inline images by
|
||||
# default, yet inherits ITERM_SESSION_ID/KITTY_WINDOW_ID when launched from
|
||||
# those terminals. TERM_PROGRAM=vscode must win → unicode, never a protocol
|
||||
# whose escapes the embedded terminal would silently drop.
|
||||
for key in ("KITTY_WINDOW_ID", "TERM_PROGRAM", "ITERM_SESSION_ID", "WEZTERM_PANE", "TERM"):
|
||||
monkeypatch.delenv(key, raising=False)
|
||||
monkeypatch.setenv("TERM_PROGRAM", "vscode")
|
||||
|
||||
assert render.detect_terminal_graphics() == "unicode"
|
||||
for leaked in ("ITERM_SESSION_ID", "KITTY_WINDOW_ID", "WEZTERM_PANE"):
|
||||
monkeypatch.setenv(leaked, "1")
|
||||
assert render.detect_terminal_graphics() == "unicode"
|
||||
monkeypatch.delenv(leaked)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user