Skip to main content

dapple 0.6: Composition and Layout for Terminal Graphics

dapple 0.6 introduced a layout engine and used it to ship seven new CLI extras. That was fast growth – maybe too fast. By the time the dust settled, there were 14 tools, and some of them overlapped.

csvcat rendered CSV tables. datacat rendered JSON tables. Both produced the same output for the same conceptual task: “show me this structured data.” imgcat displayed one image. thumbcat displayed many images in a grid. Two entry points for one domain.

0.7 consolidates. Each tool owns a domain, not a file format. The result is 11 tools that cover more ground than 14 did.

What Changed

BeforeAfterWhat happened
datacatdatcatRenamed for brevity
csvcat(merged into datcat)CSV/TSV parsing absorbed
thumbcat(merged into imgcat)Grid mode via --cols
diffcat(removed)
storycat(removed)
htmlcatNew: HTML terminal viewer
vidcatvidcatNew --play flag

The principle: users think in domains. “Show me this data” shouldn’t require knowing whether the file is JSON or CSV. “Display these images” shouldn’t require a different tool depending on whether there’s one image or twelve.

datcat: One Tool for Structured Data

datcat handles JSON, JSONL, CSV, and TSV. Format detection is automatic – by extension or content sniffing.

datcat records.json              # JSON table
datcat events.jsonl --bar event  # JSONL bar chart
datcat weather.csv --sort temp_c # CSV sorted by column
datcat sales.tsv --cols Q1,Q2    # TSV column selection

The internal representation is list[dict] for all formats. CSV rows become dicts on parse. One representation means no branching in downstream functions – table formatting, chart extraction, and plotting all work the same regardless of input format.

CSV-specific flags (--delimiter, --no-header, --sort, --cols, --desc) coexist with the JSON flags (--query, --tree). The tool routes to the right code path based on detected format.

imgcat –grid

When imgcat receives multiple images, it switches to grid mode automatically. The layout uses dapple’s Frame and Grid primitives – the same ones compcat and dashcat use.

imgcat photo.jpg                     # single image, as before
imgcat photos/*.jpg --cols 3         # 3-column contact sheet
imgcat *.png --cols 4 --no-titles    # grid without filenames

Preprocessing flags (--contrast, --dither, --invert) apply to every image in the grid.

vidcat –play

The new --play flag renders video frames in-place using ANSI cursor movement. Instead of stacking frames vertically (which scrolls your terminal into oblivion), it overwrites the previous frame.

vidcat video.mp4 --play              # 10 fps default
vidcat video.mp4 --play --fps 24     # faster
vidcat animation.gif --play -w 60    # smaller

The mechanism: render the first frame, count its output lines, then for each subsequent frame write \033[{N}A\033[J (cursor up N, clear to end) before rendering. If the destination isn’t a TTY, it falls back to stacked output with a warning.

The --asciinema export also got fixes – correct header dimensions and a hold event at the end so the last frame doesn’t vanish.

htmlcat

New tool. Renders HTML in the terminal by converting to markdown via markdownify, then rendering through Rich using the same pipeline as mdcat.

htmlcat page.html                    # render to terminal
htmlcat page.html --raw              # dump intermediate markdown
htmlcat page.html --no-images        # skip inline images

This handles documentation, articles, READMEs – content-oriented HTML. Not designed for CSS-heavy web apps.

The image rendering classes (DappleMarkdown, DappleImageItem) were extracted from mdcat into a shared richrender.py module so both tools reuse the same code.

Text Charts

A smaller change: dapple.textchart is now a separate module from dapple.charts. The bitmap charts API (sparkline, line_plot, bar_chart – all returning Canvas objects) stays in dapple.charts. The text charts (text_bar_chart, text_sparkline – returning ANSI strings) live in dapple.textchart.

Two different paradigms, two modules. datcat uses text charts for --bar and --spark on CSV data, bitmap charts for --plot and --histogram.

The Tool Inventory

ToolDomain
imgcatImages (single + grid)
datcatStructured data (JSON/JSONL/CSV/TSV)
vidcatVideo (stacked + play + asciinema)
mdcatMarkdown
htmlcatHTML
pdfcatPDFs
funcatMath plots
ansicatANSI art
compcatRenderer comparison
plotcatFaceted data plots
dashcatYAML-driven dashboards

Eleven tools covering eleven domains. No overlaps.

dapple 0.7.0 is on PyPI. Install with pip install dapple[all-tools]. Docs at queelius.github.io/dapple.

Discussion