termimage

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 30, 2026 License: MIT Imports: 13 Imported by: 0

README

termimage

Render images in the terminal — Kitty, Sixel, or Unicode half-blocks — with a sandboxed decoder.

Go Version Go Reference GitHub release (latest by date) CI

termimage is a small Go library that displays images in a terminal. It auto-detects the best supported protocol (Kitty graphics, DEC Sixel, or Unicode half-blocks as fallback) and decodes images in a sandboxed subprocess (Landlock + seccomp on Linux) so untrusted bytes never touch the parent process.

Features

  • Auto-detected protocols — Kitty, Sixel, half-block fallback. Works on any modern terminal.
  • Multiple sources — local files, data: URIs (base64), and http(s):// URLs.
  • Sandboxed decoder — image bytes parsed in an isolated subprocess with Landlock + seccomp on Linux.
  • No CGO required for the consumer — pure-Go API surface; the C decoder is contained in the worker subprocess.
  • Terminal pixel detection — sizes output to the actual cell pixel dimensions when available.

Install

go get github.com/floatpane/termimage

Requires Go 1.26+.

Usage

package main

import (
    "os"

    "github.com/floatpane/termimage"
)

func main() {
    // Required: must be the first call in main() so sandbox workers can
    // re-exec into the decoder without running the rest of your program.
    termimage.MaybeRunWorker()

    err := termimage.Display(os.Stdout, "cat.png", termimage.Options{
        Protocol:  termimage.Auto,
        Sandboxed: true,
    })
    if err != nil {
        panic(err)
    }
}
Sources

The src argument accepts any of:

termimage.Display(os.Stdout, "/path/to/cat.png", opts)
termimage.Display(os.Stdout, "https://example.com/cat.png", opts)
termimage.Display(os.Stdout, "data:image/png;base64,iVBORw0KGgo...", opts)

Remote URLs and data URIs are fetched/decoded in the parent process, then handed to the sandboxed worker over stdin — the worker still runs with Landlock denying all filesystem access. Remote payloads are capped at 64 MiB.

Use DisplayContext to pass a context.Context for cancellation of HTTP fetches and decoding.

Options
Field Description
MaxWidth, MaxHeight Pixel bounds. 0 = detect from terminal.
Protocol Auto, Kitty, Sixel, or HalfBlock.
Sandboxed Decode in a Landlock + seccomp subprocess.
Protocol detection

detect.Best() inspects $TERM, $TERM_PROGRAM, and $KITTY_WINDOW_ID. No ANSI queries are sent, so it works without a TTY.

Terminal Protocol
kitty, Ghostty, WezTerm Kitty graphics
foot, mlterm, Contour, xterm-sixel Sixel
everything else half-block

Sandbox

On Linux, the worker subprocess applies:

  • Landlock — restricts filesystem access to the single image path.
  • seccomp-bpf — blocks syscalls outside a read/decode allowlist.

On other platforms, the worker runs without OS-level restrictions but is still process-isolated.

Documentation

Full API reference: pkg.go.dev/github.com/floatpane/termimage

Contributing

PRs welcome. See CONTRIBUTING.md.

Security

Report vulnerabilities privately via SECURITY.md.

License

MIT. See LICENSE.

Documentation

Overview

Package termimage renders images in a terminal using Kitty graphics, Sixel, or Unicode half-block characters as fallback. Image decoding runs inside a sandboxed subprocess (Landlock + seccomp on Linux) so untrusted bytes never touch the parent process.

The consuming binary must call MaybeRunWorker() at the very top of main():

func main() {
    termimage.MaybeRunWorker()
    // ... rest of your app
}

HalfBlock and text layout

HalfBlock renders using real terminal character cells (▀ U+2580). Unlike Kitty or Sixel, the output occupies rows × cols cells in the terminal scroll buffer. Cursor save/restore (\x1b[s / \x1b[u) does not undo cell content. TUIs that need pixel-layer rendering should use Kitty or Sixel and set AllowHalfBlock: false on Options to prevent fallback.

Index

Constants

View Source
const (
	Auto      Protocol = -1
	HalfBlock          = detect.HalfBlock
	Sixel              = detect.Sixel
	Kitty              = detect.Kitty
)

Variables

This section is empty.

Functions

func Clear added in v0.2.0

func Clear(w io.Writer, proto Protocol, rows int) error

Clear erases a previously rendered image.

For Kitty, rows is ignored — all visible image placements are deleted via the Kitty graphics protocol delete command. Call immediately after the image is no longer needed; no cursor positioning is required.

For Sixel and HalfBlock, the caller must position the cursor on the last row of the image before calling Clear. rows should be the value returned by DisplayWithSize. Clear moves the cursor up by rows lines then erases to end of screen (\x1b[{rows}A\x1b[J).

func Display

func Display(w io.Writer, src string, opts Options) error

Display decodes the image at src and writes terminal graphics to w. src may be a local file path, a data: URI (base64), or an http(s):// URL.

func DisplayContext added in v0.1.0

func DisplayContext(ctx context.Context, w io.Writer, src string, opts Options) error

DisplayContext is Display with caller-supplied context for cancellation of remote fetches and sandboxed decoding.

func DisplayContextWithSize added in v0.2.0

func DisplayContextWithSize(ctx context.Context, w io.Writer, src string, opts Options) (cols, rows int, err error)

DisplayContextWithSize is DisplayWithSize with caller-supplied context.

func DisplayWithSize added in v0.2.0

func DisplayWithSize(w io.Writer, src string, opts Options) (cols, rows int, err error)

DisplayWithSize renders the image and returns the terminal character-cell dimensions (cols, rows) it occupies. Use this instead of Display + Dims to avoid decoding the image twice.

For HalfBlock, cols = image pixel width, rows = ceil(image pixel height / 2). For Kitty and Sixel, cols and rows are derived from the cell pixel size reported by the terminal (TIOCGWINSZ), falling back to 8×16 px per cell.

func MaybeRunWorker

func MaybeRunWorker()

MaybeRunWorker must be called at the top of main(). If this process was spawned as a sandbox worker it applies OS restrictions, decodes the image, writes pixels to stdout, and exits. Otherwise it returns immediately.

Types

type Options

type Options struct {
	// MaxWidth / MaxHeight are the pixel bounds for image scaling.
	//
	// For HalfBlock, 1 pixel = 1 character column (width) or half a character
	// row (height), so MaxHeight=100 produces at most 50 character rows.
	// For Kitty and Sixel, pixels map to physical screen pixels.
	//
	// 0 = detect from terminal. Default caps at (terminal_cols, terminal_rows-2)
	// in character-cell units, leaving two rows of headroom for the shell prompt.
	// Aspect ratio is always preserved: Fit scales uniformly so neither dimension
	// exceeds the limit. When only one dimension is set, the other is detected.
	MaxWidth, MaxHeight int

	// Protocol selects the rendering protocol. Auto detects from $TERM etc.
	// See detect.Best for detection rules.
	// TUIs that must avoid HalfBlock (which occupies real text cells — see
	// package doc) should call detect.Best() first; if it returns HalfBlock,
	// set Protocol explicitly or return an error before calling Display.
	Protocol Protocol

	// Sandboxed runs the decoder in a subprocess with Landlock + seccomp.
	// Requires the consuming binary to call MaybeRunWorker() in main().
	Sandboxed bool
}

Options configures image display.

type Protocol

type Protocol = detect.Protocol

Protocol selects a terminal rendering protocol.

Directories

Path Synopsis
Package decode wraps stb_image via CGo for fast multi-format image decoding.
Package decode wraps stb_image via CGo for fast multi-format image decoding.
Package detect identifies terminal graphics capabilities.
Package detect identifies terminal graphics capabilities.
internal
resize
Package resize provides fast bilinear image scaling.
Package resize provides fast bilinear image scaling.
source
Package source resolves image sources: file paths, data URIs, and remote URLs.
Package source resolves image sources: file paths, data URIs, and remote URLs.
Package sandbox runs image decoding in an isolated subprocess.
Package sandbox runs image decoding in an isolated subprocess.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL