Auto-Detection¶
dapple can detect the current terminal's graphics capabilities and select the best renderer automatically. This is useful for tools and scripts that should produce the highest-quality output the terminal supports, without requiring the user to specify a renderer.
Quick Start¶
from dapple import auto_renderer, render_image
# One-liner: load image, detect terminal, render
render_image("photo.jpg")
# Or: get the best renderer, use it with a Canvas
renderer = auto_renderer()
canvas.out(renderer)
detect_terminal()¶
Returns a TerminalInfo dataclass with the detected protocol and capabilities.
from dapple import detect_terminal
info = detect_terminal()
print(info.protocol) # Protocol.KITTY, Protocol.SIXEL, etc.
print(info.terminal_name) # "kitty", "xterm-256color", etc.
print(info.color_support) # True or False
print(info.is_pixel_renderer) # True for KITTY/SIXEL
TerminalInfo fields¶
| Field | Type | Description |
|---|---|---|
protocol |
Protocol |
Best detected graphics protocol |
terminal_name |
str\|None |
Terminal name from TERM_PROGRAM or TERM |
color_support |
bool |
Whether color output is supported |
is_pixel_renderer |
bool |
True if protocol is KITTY or SIXEL (property) |
Protocol Enum¶
The Protocol enum represents the five graphics capability tiers that dapple detects:
| Value | Description | Renderer |
|---|---|---|
Protocol.KITTY |
Kitty graphics protocol (PNG inline) | kitty |
Protocol.SIXEL |
DEC Sixel graphics | sixel |
Protocol.QUADRANTS |
Unicode quadrant blocks with color | quadrants |
Protocol.BRAILLE |
Unicode braille patterns | braille |
Protocol.ASCII |
Pure ASCII (universal fallback) | ascii |
from dapple import Protocol
info = detect_terminal()
if info.protocol == Protocol.KITTY:
print("True pixel output via Kitty protocol")
elif info.protocol == Protocol.SIXEL:
print("True pixel output via Sixel protocol")
else:
print(f"Character-based output: {info.protocol.value}")
auto_renderer()¶
Returns the best Renderer instance for the current terminal. This is the primary function most code should use.
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
prefer_color |
bool |
True |
Prefer color-capable renderers |
plain |
bool |
False |
Force ASCII output (for pipes and redirects) |
Selection logic¶
The selection follows this priority:
1. If plain=True --> ascii
2. If stdout is not a tty --> braille (or ascii if prefer_color=False)
3. If Kitty detected --> kitty
4. If Sixel detected --> sixel
5. If color supported --> quadrants (or braille if prefer_color=False)
6. Otherwise --> ascii
Forcing ASCII for pipes¶
When output is piped to another program or redirected to a file, pixel protocols and ANSI escape codes are usually unwanted. auto_renderer() detects this automatically by checking sys.stdout.isatty():
# Automatic: falls back to braille when piped
renderer = auto_renderer()
# Explicit: always use ASCII (no Unicode, no escape codes)
renderer = auto_renderer(plain=True)
Monochrome mode¶
# Prefer braille over quadrants even on color terminals
renderer = auto_renderer(prefer_color=False)
render_image(path)¶
A convenience function that handles the entire pipeline: load an image from a file, auto-detect the terminal, and render to stdout.
from dapple import render_image
# Simplest possible usage
render_image("photo.jpg")
# With resizing
render_image("photo.jpg", width=160)
render_image("photo.jpg", width=160, height=80)
# With explicit renderer (skips auto-detection)
from dapple import braille
render_image("photo.jpg", renderer=braille)
| Parameter | Type | Default | Description |
|---|---|---|---|
image_path |
str |
(required) | Path to image file |
width |
int\|None |
None |
Target width in pixels |
height |
int\|None |
None |
Target height in pixels |
renderer |
Renderer\|None |
None |
Renderer to use (None = auto-detect) |
Note:
render_image()requires pillow (pip install pillow) for image loading.
How Detection Works¶
Kitty protocol detection¶
dapple checks two environment variables:
| Variable | Set by |
|---|---|
KITTY_WINDOW_ID |
Kitty terminal |
GHOSTTY_RESOURCES_DIR |
Ghostty terminal |
If either is present, the terminal supports the Kitty graphics protocol.
Sixel protocol detection¶
dapple checks TERM and TERM_PROGRAM against a list of known Sixel-capable terminals:
- mlterm
- yaft
- foot
- contour
- wezterm
- mintty
- xterm (when
XTERM_VERSIONis set)
Color support detection¶
Color support is determined by:
NO_COLORenvironment variable: if set, color is disabled (following the no-color convention).TERMvalue: checked for substrings likecolor,256,direct,truecolor,kitty,xterm.COLORTERMenvironment variable: if set, indicates true color support.- Default: color is assumed to be supported.
Fallback¶
If no pixel protocol is detected, the default is Protocol.QUADRANTS (Unicode block elements with ANSI colors). This provides a good balance of resolution and compatibility for modern terminals.
When to Use Auto vs Explicit¶
Use auto_renderer() when:¶
- Building CLI tools that should work across different terminals.
- Writing scripts that may run in various environments (local, SSH, CI).
- You do not know what terminal the user has.
Use explicit renderers when:¶
- You know the target terminal (e.g., "this always runs in Kitty").
- You need deterministic output (e.g., tests, file generation).
- You want a specific aesthetic (e.g., braille art style regardless of terminal capability).
- You are rendering to a file, not a terminal.
from dapple import braille, quadrants, auto_renderer
# Auto: best for the current terminal
canvas.out(auto_renderer())
# Explicit: always braille, regardless of terminal
canvas.out(braille)
# Explicit: always quadrants with specific settings
canvas.out(quadrants(true_color=False))
Complete Example¶
from dapple import Canvas, auto_renderer, detect_terminal, Protocol
from dapple.adapters.pil import load_image
from dapple.preprocess import auto_contrast
# Check what we are working with
info = detect_terminal()
print(f"Terminal: {info.terminal_name}")
print(f"Protocol: {info.protocol.value}")
print(f"Color: {info.color_support}")
print(f"Pixel: {info.is_pixel_renderer}")
# Load and preprocess
canvas = load_image("photo.jpg", width=160)
# Apply preprocessing only for character renderers
if not info.is_pixel_renderer:
bitmap = auto_contrast(canvas.bitmap)
canvas = Canvas(bitmap, colors=canvas.colors)
# Render with the best available renderer
renderer = auto_renderer()
canvas.out(renderer)