Skip to content

Instantly share code, notes, and snippets.

@KuRRe8
Last active March 13, 2026 03:05
Show Gist options
  • Select an option

  • Save KuRRe8/94778a65cdc8e0560ce5fb0ed16bd028 to your computer and use it in GitHub Desktop.

Select an option

Save KuRRe8/94778a65cdc8e0560ce5fb0ed16bd028 to your computer and use it in GitHub Desktop.
Terminal and Shell related.

Show image in modern Terminal

As we known, Terminal has the ability to show image file. The typical protocals are iTerm2, sixel, kitty, etc. But there are also terminals which could only handle ASCII art pics(gnome-terminal, Terminal.app on Mac and the native tty on Ubuntu Server). Here is a good reference for the platform-capable protocal.

Thanks to yazi, I've written a script to detect different terminals and invoke proper image function tool.

Code

show_company_logo() {
    case $- in
        *i*) ;;
        *) return 0 ;;
    esac

    [ -t 1 ] || return 0

    if [ -n "${COMPANY_LOGO_SHOWN:-}" ]; then
        return 0
    fi
    export COMPANY_LOGO_SHOWN=1

    local png="$HOME/.some.png"
    local sixel="$HOME/.some.sixel"
    local term="${TERM:-}"
    local term_program="${TERM_PROGRAM:-}"

    _logo_with_chafa() {
        command -v chafa >/dev/null 2>&1 || return 1
        [ -f "$png" ] || return 1
        chafa --size=75x50 "$png"
    }

    _logo_with_imgcat() {
        command -v imgcat >/dev/null 2>&1 || return 1
        [ -f "$png" ] || return 1
        imgcat --height 25 "$png"
    }

    _logo_with_sixel() {
        [ -f "$sixel" ] || return 1
        cat "$sixel"
    }

    _supports_sixel() {
        [ -f "$HOME/.ifsixel" ] || return 1
        command -v python3 >/dev/null 2>&1 || return 1
        python3 "$HOME/.ifsixel" >/dev/null 2>&1
    }

    # Local Linux console on the server
    if [ "$term" = "linux" ]; then
        _logo_with_chafa && return 0
        return 0
    fi

    # VS Code terminal over SSH
    if [ "$term_program" = "vscode" ]; then
        _logo_with_imgcat && return 0
        return 0
    fi

    # Sixel-capable SSH terminal(Windows Terminal + SSH included), detected by active probe
    if _supports_sixel; then
        _logo_with_sixel && return 0
        _logo_with_chafa && return 0
        return 0
    fi

    # Generic fallback
    _logo_with_chafa && return 0
    return 0
}

show_company_logo

Here is the py script to determine the ability of sixel, $HOME/.ifsixel

#!/usr/bin/env python3
import os
import sys
import time
import tty
import termios
import select

def read_response(fd, timeout=0.3):
    data = b""
    deadline = time.time() + timeout

    while time.time() < deadline:
        remaining = max(0, deadline - time.time())
        r, _, _ = select.select([fd], [], [], remaining)
        if not r:
            break

        chunk = os.read(fd, 4096)
        if not chunk:
            break

        data += chunk

        # Most DA replies end with 'c'
        if b"c" in chunk:
            break

    return data

def supports_sixel_from_da(text: str) -> bool:
    # Heuristic matcher for DA-style replies that may indicate sixel capability.
    # This is intentionally conservative and can be refined with real samples.
    if not text.startswith("\x1b[?") or not text.endswith("c"):
        return False

    # Example style: ESC [ ? ... c
    body = text[3:-1]
    parts = body.split(";")

    # Common heuristic used in terminal capability discussions:
    # presence of parameter 4 is often associated with sixel graphics support.
    return "4" in parts

def main():
    debug = "--debug" in sys.argv

    try:
        fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY)
    except OSError:
        return 1

    old = termios.tcgetattr(fd)

    try:
        tty.setcbreak(fd)

        # Flush pending input
        while True:
            r, _, _ = select.select([fd], [], [], 0)
            if not r:
                break
            chunk = os.read(fd, 4096)
            if not chunk:
                break

        # Primary device attributes
        os.write(fd, b"\x1b[c")
        data = read_response(fd, timeout=0.3)

    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old)
        os.close(fd)

    text = data.decode("ascii", errors="ignore")

    if debug:
        print(repr(data))
        print(text)

    return 0 if supports_sixel_from_da(text) else 1

if __name__ == "__main__":
    raise SystemExit(main())

Here is the hleper to output sixel sequence to a file

import os
import sys
import types
from io import BytesIO

from PIL import Image

# Provide a minimal termios stub for Windows so the sixel package can import.
if os.name == "nt" and "termios" not in sys.modules:
    termios_stub = types.ModuleType("termios")
    termios_stub.ECHO = 0
    termios_stub.ICANON = 0
    termios_stub.TCSANOW = 0
    termios_stub.TCSAFLUSH = 0
    termios_stub.tcgetattr = lambda fd: [0, 0, 0, 0, 0, 0]
    termios_stub.tcsetattr = lambda fd, when, settings: None
    sys.modules["termios"] = termios_stub

from sixel import SixelWriter

# Load an image using Pillow
img = Image.open(sys.argv[1])


# Resize for better terminal display (optional)
img.thumbnail((800, 600))

# Convert image to bytes
output = BytesIO()
img.save(output, format='PNG')
data = output.getvalue()

# Display using libsixel if available, otherwise fall back to pure Python implementation
libsixel_error = None
try:
    from libsixel import sixel_output

    try:
        sixel_output(data)
        sys.exit(0)
    except Exception as e:  # pragma: no cover - informative path
        libsixel_error = e
except Exception as e:  # pragma: no cover - informative path
    libsixel_error = e

try:
    buffer = BytesIO(data)
    buffer.seek(0)
    writer = SixelWriter()
    writer.draw(buffer, output=sys.stdout)
except Exception as e:
    print(f"Error displaying image: {e}")
    if libsixel_error:
        print(f"libsixel fallback error: {libsixel_error}")
    print("Note: Your terminal may not support Sixel graphics")

How to detect terminal

Some terminals set environments to identify itself.

[
    ("KITTY_WINDOW_ID", Kitty),
    ("KONSOLE_VERSION", Konsole),
    ("ITERM_SESSION_ID", Iterm2),
    ("WEZTERM_EXECUTABLE", WezTerm),
    ("GHOSTTY_RESOURCES_DIR", Ghostty),
    ("WT_Session", Microsoft),
    ("WARP_HONOR_PS1", Warp),
    ("VSCODE_INJECTION", VSCode),
    ("TABBY_CONFIG_DIRECTORY", Tabby),
]

But not all of these env automatically pass through SSH session, so we need to send escape sequence and check the response.

The VT sequence provide a Query State code, DA(device attribute). The shell or cli program should send ESC [ 0 c to terminal and the terminal will respond some invisible characters. On official website it says that it will emit \x1b[?1;0c, indicating "VT101 with No Options". But in my experiment, Windows Terminal + SSH will respond \x1b[?61;4;6;7;14;21;22;23;24;28;32;42;52c, this is why the .issixel script check if there is character 4 in the response. And in native Windows Terminal, the response is \x1b[?61;1;6;7;21;22;23;24;28;32;42;52c. For comparison, gnome-terminal gives the \x1b[?65;1;9c, same as gnome-terminal + SSH. It's quite simple on mac Terminal.app with or without SSH, \x1b[?1;2c.

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