fix(artwork): ignore TMUX/ZELLIJ/STY values that mean 'not set'

Some shells and wrappers export ZELLIJ=0 or TMUX=0 outside a multiplexer.
A plain truthiness check treated that as 'inside tmux/zellij' and
downgraded iTerm2/Kitty to the ANSI mosaic, so real artwork silently
disappeared. Only meaningful values now count.
This commit is contained in:
beefcarver committed 2026-10-30 18:43:16 -04:00
1 parent 69b46b9035
commit 6eb0c6b6fe
2 files changed
+27 -4

No files matched your search

+16 -4
View File
@@ -30,6 +30,18 @@ _OFF_VALUES = {"0", "off", "no", "none", "false"}
_PROTOCOL_VALUES = {"tgp", "iterm2"}
def _env_flag(env: Mapping[str, str], name: str) -> bool:
"""True when an environment variable is set to a meaningful value.
Multiplexers are inconsistent here (``TMUX`` is a path, ``STY`` a
session name) and some wrappers export ``ZELLIJ=0``/``TMUX=0`` when they
are *not* running, so a plain truthiness check would wrongly downgrade a
perfectly good iTerm2/Kitty to the ANSI mosaic.
"""
value = (env.get(name) or "").strip()
return bool(value) and value.lower() not in _OFF_VALUES
def detect_protocol(environ: Mapping[str, str] | None = None) -> str | None:
"""Return ``"tgp"``, ``"iterm2"`` or ``None`` for the current terminal."""
env = os.environ if environ is None else environ
@@ -57,7 +69,7 @@ def detect_protocol(environ: Mapping[str, str] | None = None) -> str | None:
# Kitty inside tmux may not inherit TERM=xterm-kitty/KITTY_*. Ask tmux
# what the outer terminal is; TGP is wrapped for tmux passthrough.
if env.get("TMUX") and shutil.which("tmux"):
if _env_flag(env, "TMUX") and shutil.which("tmux"):
try:
result = subprocess.run(
["tmux", "display-message", "-p", "#{client_termname}"],
@@ -76,9 +88,9 @@ def detect_protocol(environ: Mapping[str, str] | None = None) -> str | None:
# iTerm2/WezTerm inside tmux would need their own passthrough wrapper, so
# default to the mosaic there.
if (
env.get("TMUX")
or env.get("ZELLIJ")
or env.get("STY")
_env_flag(env, "TMUX")
or _env_flag(env, "ZELLIJ")
or _env_flag(env, "STY")
or term.startswith("screen")
or term.startswith("tmux")
or program in {"tmux", "screen"}
+11
View File
@@ -62,3 +62,14 @@ async def test_fetch_validates_and_caches() -> None:
fetched = await artwork.fetch(client, url)
assert fetched == data
assert artwork.load_cached(url) == data
def test_multiplexer_env_values_that_mean_no() -> None:
# Some wrappers export ZELLIJ=0 / TMUX=0 outside a multiplexer; that must
# not disable iTerm2 or Kitty graphics.
assert artwork.detect_protocol({"ZELLIJ": "0", "TERM_PROGRAM": "iTerm.app"}) == "iterm2"
assert artwork.detect_protocol({"ZELLIJ": "", "TERM_PROGRAM": "iTerm.app"}) == "iterm2"
assert artwork.detect_protocol({"TMUX": "0", "KITTY_WINDOW_ID": "1"}) == "tgp"
# ...while a real multiplexer still downgrades to the mosaic.
assert artwork.detect_protocol({"TMUX": "/tmp/tmux-501/default,1,0", "TERM_PROGRAM": "iTerm.app"}) is None
assert artwork.detect_protocol({"STY": "1234.pts-0.host", "TERM_PROGRAM": "iTerm.app"}) is None