Repository navigation
Release Notes
Added
Dashed lines. add_line(..., dash=(on, off)) and add_step(..., dash=...) stroke a series in dashes — on pixels drawn, off skipped,
the pattern running on across vertices so a polyline dashes evenly — and
set_line_dash(trace, dash) changes it later (None for solid). Reference
lines such as a target or a benchmark read as references, not data.
Axis units. set_axis_unit(axis, unit) appends a unit to an axis's
tick labels ("y2", "M" for data in millions) and, on a y axis, to the
readout values of the series on it — so 2.2e7 reads as 22M in both
places.
The readout as a leaderboard. set_readout_order("descending" | "ascending") sorts the crosshair readout's rows by the value at the
crosshair, so hovering a chart whose series compete on one scale reads as a
ranking; "traces" (the default) keeps trace order. Only primary-axis rows
are ranked — a series on a right-hand axis measures something else and
trails them in trace order.
The readout split by axis. set_readout_split_axes(True) groups the
crosshair readout's rows by y axis — the primary axis's rows, then y2's,
then y3's — with a rule between the groups, so a reader sees which
readings share a scale and which are something else entirely. A plot with
one axis draws no rule either way.
The readout names its x coordinate. The crosshair readout's header
says the x title instead of "x" when one is set, and
set_readout_x_label(text) names it without drawing an axis title — for a
host that labels the axis outside the image.
An opaque canvas. set_chrome(canvas=(r, g, b)) (C: plotui_set_canvas)
paints the whole image in one colour instead of leaving the background
transparent. The default still floats the plot over the terminal's own
background; a host that paints a surface of its own around the plot sets the
canvas to the same colour, so the two match on terminals that composite
images over the window rather than over the cells (iTerm2).
The legend row under the pointer is lit. Plot.legend_hover /
set_legend_hover(px_w, px_h, px, py) (and legend_row_at) light the
legend row under a pixel with a faint band, so the reader sees which row a
click would toggle; the Textual widget sets it on every mouse move and
clears it on leave, repainting only when the lit row changes.
Legend rows a host declares. Plot.legend_entries (Python
set_legend_entries([(label, swatch, color, border, visible), …])) replaces
the one-row-per-named-trace legend with rows the host names — for plots
whose categories are not traces, such as one graph whose nodes fall into
stages. Each row carries a swatch that mirrors what it names: square (the
trace chip), line, disc — inside a ring of border when given, drawn
with the node rasteriser — ring or star; a row with visible=False
stays, drained. legend_corner / set_legend_corner("top-left") moves the
box to the top left, and legend_entry_hit says which row a click landed
on (the trace legend_hit stands down while host rows are shown). The
hover readout keeps off the box wherever it sits.
The hover readout keeps off the host's text. Plot.keep_out (Python
set_keep_out(rects) / keep_out()) lists boxes, as frame fractions, that
the 2D crosshair readout must not cover — text a host draws over the plot in
its own layer, which the renderer could not see. The Textual widget sets it
from its overlay spans automatically, so a legend or label drawn with
set_overlay is dodged the way the in-canvas legend already was.
Several plots on one screen. PlotWidget(image_slot=n) places a
widget's frames under its own pair of Kitty image ids (image_ids_for(n);
slot 0 is the default pair), so two widgets side by side no longer show one
picture in both places (placeholder mode) or delete each other's frames
(direct mode). PlotWidget.kitty_cleanup() names every slot taken in the
process; the new kitty_cleanup_own() is what one widget emits on unmount.
Plot.render_kitty_placeholder_cells grew an image_id argument. Direct
frames now use the image id as their placement id too (p=<image id>
instead of p=1): iTerm2 keys placements by p= alone, so two images
placed as p=1 replaced each other.
Labels and borders on 3D graph nodes, and a star. set_graph_labels
draws a string inside each mark — centred, at the frame's text scale, in an
ink that contrasts with the fill (or one colour for all) — steps the text
down to the largest scale the mark can hold, and skips any label that fits
at none, so a dense graph grows its numbers as the host zooms in. set_graph_borders strokes an outline ring around each node, a
second category next to the fill. "star" joins the node shapes, and
mark_scale(px_w) exposes the factor radii are drawn at so a host can size
marks to a pixel budget (nodes that never overlap at the current zoom).
Rust, Python.
legend_visible. A plot can name its traces without drawing the
in-canvas legend box, so a host that draws its own legend still gets series
names in the crosshair readout instead of series 3.
Draggable nodes. A drag that starts on a 2D graph node's box moves the
node, its edges following, and the rest of the graph stays still. On by
default in every frontend: Textual posts NodeMoved(index, x, y), Ratatui
returns PlotEvent::NodeMoved, Bubble Tea sends NodeMovedMsg, and the
primitive behind them, Plot::drag_node(px_w, px_h, index, dx_px, dy_px),
is on every binding.
Changed
The crosshair readout lines its values up. The rows of the hover
readout box put every value in one left-aligned column, so the eye scans
the values straight down the box instead of finding each number after a
label of a different length.
Graphs are laid out in text columns. A LayeredLayout now places nodes
in layout units — one unit is one text column — and takes the labels (or
explicit sizes) its boxes will be drawn with, plus node_sep/rank_sep, so
box widths are part of the layout and no two boxes touch. A graph frame draws
at that scale instead of stretching the layout to fill the pane: it picks the
largest text scale at which the graph fits, shrinks only until boxes would
touch, and past that overflows for the user to pan. Long edges that share an
endpoint are bundled into one trunk with the other ends joining it, rather
than one wire per edge stacked into a comb, and boxes line up under the boxes
they are wired to.
A plain drag pans a 2D plot. The input map's yaw/pitch controls have
nothing to turn on a flat picture, so they pan there; shift-drag is unchanged.
The C ABI's plotui_layered_layout_new grew labels, n_labels, node_sep
and rank_sep parameters; the Go, Python and JavaScript signatures gained
the same as optional arguments.
Direct-mode frames no longer flicker. The Textual widget's direct path
(iTerm2, WezTerm, Konsole) used to delete the previous image before
transmitting the next, which blanked the plot until the terminal had decoded
the new frame — a flash on every interactive repaint. Frames now alternate
between two image ids: the new frame is placed first and only then is the
other id's placement deleted (kitty_compat_swap, render_kitty(image_id=, retire_id=)). PlotWidget.kitty_cleanup() deletes both buffers; hosts that
emitted Plot.kitty_cleanup() when covering a plot should use it.
The crosshair repaints only when its picture changes. set_hover2d(px, px_w, px_h) snaps the cursor the way the renderer will and reports a change
only when the snapped sample differs, so a mouse move inside one sample's
basin costs nothing instead of a full re-rasterize, upload and decode.
Plot.hover2d_snap_px exposes the same query. The readout keeps three
significant digits below 1 (0.0156, not 0.02).
Fixed
A y title longer than the frame no longer panics. Rotated text that
would not fit along the axis is dropped on that frame size instead of
tripping a clamp; a taller frame draws it.
Install plotui 0.6.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/sebaheg/plotui/releases/download/v0.6.0/plotui-installer.sh | shInstall prebuilt binaries via Homebrew
brew install sebaheg/tap/plotuiDownload plotui 0.6.0
| File | Platform | Checksum |
|---|---|---|
| plotui-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| plotui-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| plotui-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| plotui-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| plotui-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| plotui-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |