Skip to content

Commit f4db3a6

Browse files
committed
Added docs for the image support
1 parent 42a2ec3 commit f4db3a6

1 file changed

Lines changed: 381 additions & 0 deletions

File tree

Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
---
2+
date: '2026-02-03T12:00:00+01:00'
3+
draft: false
4+
title: 'Terminal Images'
5+
weight: 12
6+
---
7+
8+
Æsh Readline provides support for displaying images directly in the terminal using multiple graphics protocols. This allows CLI applications to show logos, charts, screenshots, and other visual content.
9+
10+
## Overview
11+
12+
The terminal image API supports three major graphics protocols:
13+
14+
| Protocol | Terminals |
15+
|----------|-----------|
16+
| **Kitty Graphics** | Kitty, Ghostty, Konsole (partial) |
17+
| **iTerm2 Inline Images** | iTerm2, WezTerm, Mintty, VSCode Terminal, Tabby, Hyper |
18+
| **Sixel Graphics** | xterm, mlterm, foot, Windows Terminal, Contour |
19+
20+
The API automatically detects which protocol the terminal supports and generates the appropriate escape sequences.
21+
22+
## Quick Start
23+
24+
```java
25+
import org.aesh.terminal.image.TerminalImage;
26+
import org.aesh.terminal.image.TerminalImageBuilder;
27+
import org.aesh.terminal.tty.TerminalConnection;
28+
29+
import java.nio.file.Files;
30+
import java.nio.file.Path;
31+
32+
TerminalConnection conn = new TerminalConnection();
33+
34+
// Load image from file
35+
byte[] imageData = Files.readAllBytes(Path.of("logo.png"));
36+
37+
// Build image with auto-detected protocol
38+
TerminalImage image = TerminalImageBuilder.builder(conn.device())
39+
.data(imageData)
40+
.filename("logo.png")
41+
.widthCells(40) // Display width in terminal cells
42+
.build();
43+
44+
// Display the image
45+
if (image != null) {
46+
conn.write(image.encode());
47+
}
48+
49+
conn.close();
50+
```
51+
52+
## Checking Image Support
53+
54+
Before displaying images, check if the terminal supports graphics:
55+
56+
```java
57+
import org.aesh.terminal.image.ImageProtocol;
58+
59+
ImageProtocol protocol = conn.device().getImageProtocol();
60+
61+
switch (protocol) {
62+
case KITTY:
63+
System.out.println("Using Kitty graphics protocol");
64+
break;
65+
case ITERM2:
66+
System.out.println("Using iTerm2 inline images");
67+
break;
68+
case SIXEL:
69+
System.out.println("Using Sixel graphics");
70+
break;
71+
case NONE:
72+
System.out.println("No image support detected");
73+
break;
74+
}
75+
76+
// Simple check
77+
if (conn.device().supportsImages()) {
78+
// Display image
79+
}
80+
```
81+
82+
## TerminalImageBuilder
83+
84+
The `TerminalImageBuilder` class provides a fluent API for creating images:
85+
86+
### Creating a Builder
87+
88+
```java
89+
// Auto-detect protocol from terminal device
90+
TerminalImageBuilder builder = TerminalImageBuilder.builder(conn.device());
91+
92+
// Or specify a protocol explicitly
93+
TerminalImageBuilder builder = TerminalImageBuilder.builder(ImageProtocol.ITERM2);
94+
```
95+
96+
### Setting Image Data
97+
98+
```java
99+
// From file path
100+
builder.file(Path.of("image.png"));
101+
102+
// From byte array
103+
builder.data(imageBytes);
104+
105+
// Set filename (used by iTerm2 protocol)
106+
builder.filename("image.png");
107+
```
108+
109+
### Setting Dimensions
110+
111+
```java
112+
// Width/height in terminal cells
113+
builder.widthCells(40);
114+
builder.heightCells(20);
115+
116+
// Width/height in pixels
117+
builder.widthPixels(800);
118+
builder.heightPixels(600);
119+
120+
// Preserve aspect ratio (iTerm2 only)
121+
builder.preserveAspectRatio(true);
122+
```
123+
124+
### Building and Displaying
125+
126+
```java
127+
TerminalImage image = builder.build();
128+
129+
if (image != null) {
130+
String escapeSequence = image.encode();
131+
conn.write(escapeSequence);
132+
}
133+
```
134+
135+
## Protocol-Specific Features
136+
137+
### iTerm2 Protocol
138+
139+
The iTerm2 protocol is widely supported and offers several options:
140+
141+
```java
142+
import org.aesh.terminal.image.ITermImage;
143+
144+
ITermImage image = new ITermImage(imageData)
145+
.filename("chart.png")
146+
.widthCells(60)
147+
.heightCells(30)
148+
.preserveAspectRatio(true);
149+
150+
conn.write(image.encode());
151+
```
152+
153+
**Sizing options:**
154+
- `widthCells(int)` / `heightCells(int)` - Size in terminal cells
155+
- `widthPixels(int)` / `heightPixels(int)` - Size in pixels
156+
- `widthPercent(int)` / `heightPercent(int)` - Size as percentage of terminal
157+
158+
**Other options:**
159+
- `preserveAspectRatio(boolean)` - Maintain image proportions
160+
- `useStTerminator(boolean)` - Use ST instead of BEL terminator
161+
162+
### Kitty Graphics Protocol
163+
164+
The Kitty protocol provides high-quality graphics with advanced features:
165+
166+
```java
167+
import org.aesh.terminal.image.KittyImage;
168+
169+
KittyImage image = new KittyImage(imageData)
170+
.widthCells(50)
171+
.heightCells(25)
172+
.zIndex(1) // Layering support
173+
.suppressResponse(true);
174+
175+
conn.write(image.encode());
176+
```
177+
178+
**Features:**
179+
- Automatic PNG conversion (Kitty requires PNG format)
180+
- Chunked transmission for large images
181+
- Z-index layering support
182+
- Response suppression to avoid input clutter
183+
184+
**Options:**
185+
- `widthCells(int)` / `heightCells(int)` - Display dimensions
186+
- `sourceDimensions(int width, int height)` - For raw RGB/RGBA data
187+
- `zIndex(int)` - Layer ordering
188+
- `suppressResponse(boolean)` - Suppress terminal responses (default: true)
189+
190+
### Sixel Graphics
191+
192+
Sixel is a legacy but widely supported protocol:
193+
194+
```java
195+
import org.aesh.terminal.image.SixelImage;
196+
197+
SixelImage image = new SixelImage(imageData)
198+
.maxWidth(800)
199+
.maxHeight(600)
200+
.maxColors(256) // Color palette size (2-256)
201+
.useRle(true); // Run-length encoding compression
202+
203+
conn.write(image.encode());
204+
```
205+
206+
**Features:**
207+
- Automatic image scaling with aspect ratio preservation
208+
- Color quantization (reduces colors to fit palette)
209+
- RLE compression for smaller output
210+
- Works in many legacy terminals
211+
212+
**Options:**
213+
- `maxWidth(int)` / `maxHeight(int)` - Maximum dimensions in pixels
214+
- `maxColors(int)` - Palette size (2-256, affects quality vs size)
215+
- `useRle(boolean)` - Enable run-length encoding compression
216+
217+
## Image Format Support
218+
219+
The API supports multiple image formats:
220+
221+
| Format | Kitty | iTerm2 | Sixel |
222+
|--------|-------|--------|-------|
223+
| PNG | Yes (native) | Yes | Yes |
224+
| JPEG | Converted to PNG | Yes | Yes |
225+
| GIF | Converted to PNG | Yes | Yes |
226+
227+
For Kitty protocol, non-PNG images are automatically converted to PNG.
228+
229+
### Format Detection
230+
231+
```java
232+
import org.aesh.terminal.image.ImageUtils;
233+
234+
byte[] data = Files.readAllBytes(Path.of("image.jpg"));
235+
236+
if (ImageUtils.isPng(data)) {
237+
System.out.println("PNG format");
238+
} else if (ImageUtils.isJpeg(data)) {
239+
System.out.println("JPEG format");
240+
} else if (ImageUtils.isGif(data)) {
241+
System.out.println("GIF format");
242+
}
243+
244+
// Convert any format to PNG
245+
byte[] pngData = ImageUtils.toPng(data);
246+
```
247+
248+
## Complete Example
249+
250+
Here's a complete example that displays an image with fallback handling:
251+
252+
```java
253+
import org.aesh.terminal.image.*;
254+
import org.aesh.terminal.tty.TerminalConnection;
255+
import java.nio.file.Files;
256+
import java.nio.file.Path;
257+
258+
public class ImageDemo {
259+
public static void main(String[] args) throws Exception {
260+
TerminalConnection conn = new TerminalConnection();
261+
262+
// Check for image support
263+
ImageProtocol protocol = conn.device().getImageProtocol();
264+
265+
if (protocol == ImageProtocol.NONE) {
266+
conn.write("This terminal does not support images.\n");
267+
conn.write("Try running in: Kitty, iTerm2, WezTerm, or VSCode\n");
268+
conn.close();
269+
return;
270+
}
271+
272+
conn.write("Detected protocol: " + protocol + "\n\n");
273+
274+
// Load and display image
275+
Path imagePath = Path.of(args[0]);
276+
byte[] imageData = Files.readAllBytes(imagePath);
277+
278+
TerminalImage image = TerminalImageBuilder.builder(conn.device())
279+
.data(imageData)
280+
.filename(imagePath.getFileName().toString())
281+
.widthCells(60)
282+
.preserveAspectRatio(true)
283+
.build();
284+
285+
if (image != null) {
286+
conn.write(image.encode());
287+
conn.write("\n\nImage displayed successfully!\n");
288+
} else {
289+
conn.write("Failed to create image.\n");
290+
}
291+
292+
conn.close();
293+
}
294+
}
295+
```
296+
297+
## Protocol Detection
298+
299+
The API uses environment variables and terminal type to detect the graphics protocol:
300+
301+
### Environment Variables Checked
302+
303+
| Variable | Protocol |
304+
|----------|----------|
305+
| `KITTY_WINDOW_ID` | Kitty |
306+
| `GHOSTTY_RESOURCES_DIR` | Kitty |
307+
| `ITERM_SESSION_ID` | iTerm2 |
308+
| `WEZTERM_PANE` | iTerm2 |
309+
| `TERM_PROGRAM=vscode` | iTerm2 |
310+
311+
### TERM Variable Patterns
312+
313+
| TERM contains | Protocol |
314+
|---------------|----------|
315+
| `kitty` | Kitty |
316+
| `ghostty` | Kitty |
317+
| `konsole` | Kitty |
318+
| `iterm`, `wezterm`, `mintty` | iTerm2 |
319+
| `vscode`, `tabby`, `hyper` | iTerm2 |
320+
| `mlterm`, `foot`, `contour` | Sixel |
321+
322+
### Manual Protocol Override
323+
324+
If automatic detection doesn't work, specify the protocol explicitly:
325+
326+
```java
327+
TerminalImage image = TerminalImageBuilder.builder(ImageProtocol.ITERM2)
328+
.data(imageData)
329+
.widthCells(40)
330+
.build();
331+
```
332+
333+
## Supported Terminals
334+
335+
| Terminal | Protocol | Notes |
336+
|----------|----------|-------|
337+
| Kitty | Kitty | Full support |
338+
| Ghostty | Kitty | Full support |
339+
| iTerm2 | iTerm2 | Full support |
340+
| WezTerm | iTerm2 | Full support |
341+
| VSCode Terminal | iTerm2 | Full support |
342+
| Mintty | iTerm2 | Full support |
343+
| Tabby | iTerm2 | Full support |
344+
| Hyper | iTerm2 | Full support |
345+
| Konsole | Kitty | Partial support |
346+
| xterm | Sixel | Requires `-ti vt340` flag |
347+
| mlterm | Sixel | Full support |
348+
| foot | Sixel | Full support |
349+
| Windows Terminal | Sixel | Full support |
350+
| Contour | Sixel | Full support |
351+
| Alacritty | None | No image support |
352+
| Apple Terminal | None | No image support |
353+
| GNOME Terminal | None | No image support |
354+
355+
## Performance Considerations
356+
357+
1. **Image Size**: Large images generate large escape sequences. Consider resizing before display.
358+
359+
2. **Sixel Colors**: Reducing `maxColors` improves performance but reduces quality.
360+
361+
3. **PNG Conversion**: Kitty protocol requires PNG, so JPEG/GIF images are converted at runtime.
362+
363+
4. **Chunked Transmission**: Kitty protocol sends large images in 4KB chunks to avoid buffer issues.
364+
365+
## Troubleshooting
366+
367+
### Image Not Displayed
368+
369+
1. **Check terminal support**: Run the example to see detected protocol
370+
2. **Try explicit protocol**: Use `TerminalImageBuilder.builder(ImageProtocol.ITERM2)`
371+
3. **Check image format**: Ensure the image file is valid
372+
373+
### Garbled Output
374+
375+
1. **Terminal doesn't support the protocol**: Try a different terminal
376+
2. **Image too large**: Reduce dimensions with `widthCells()` or `maxWidth()`
377+
378+
### Colors Look Wrong (Sixel)
379+
380+
1. **Increase color palette**: Use `.maxColors(256)` for better quality
381+
2. **Try different terminal**: Some terminals have better Sixel support

0 commit comments

Comments
 (0)