Selkie--Widget--LineChart
NAME
Selkie::Widget::LineChart - Static multi-series line chart with axes and legend
SYNOPSIS
use Selkie::Widget::LineChart;
use Selkie::Sizing;
# Single series ā auto-derives Y range from the data
my $cpu = Selkie::Widget::LineChart.new(
series => [
{ label => 'cpu %', values => @cpu-history },
],
sizing => Sizing.flex,
);
# Multi-series with explicit colours
my $cmp = Selkie::Widget::LineChart.new(
series => [
{ label => 'p50', values => @p50, color => 0xE69F00 },
{ label => 'p99', values => @p99, color => 0xCC4444 },
],
fill-below => True,
sizing => Sizing.flex,
);DESCRIPTION
A static-data line chart, hand-rolled with braille (U+2800-U+28FF) sub-cell resolution. Each cell holds 2Ć4 braille dots; lines are rasterised at 2Ć horizontal Ć 4Ć vertical resolution relative to the plain cell grid.
For streaming data, prefer Selkie::Widget::Plot (uses the native ncuplot / ncdplot ring buffer; better at high sample rates). For one-row inline charts, use Selkie::Widget::Sparkline.
What it composes
Internally LineChart manages three regions:
Body ā the chart area, drawn with braille dots
Y axis (left edge) ā labels + tick marks, when
show-axisis TrueLegend (bottom strip) ā colour-coded series labels, when
show-legendis True and there's more than one series
Each region renders inline via direct ncplane calls; the widget doesn't compose child widgets. Disable axis/legend to reclaim the reserved cells and devote all cells to the body.
Multi-series colour collision
Each braille cell renders with a single foreground colour. When two series cross in the same 2Ć4 sub-cell window, the last-drawn series' colour wins ("z-order"). Series are drawn in order; in practice this means the last series in your list "covers" earlier ones at intersections.
This is a fundamental limit of single-foreground terminal cells. For series that overlap heavily, a faceted layout (one chart per series, stacked) gives clearer attribution.
Range
Y range auto-derives from min(0, min-data) to max-data. Pass explicit :y-min and :y-max to fix it. X is always slot indices 0 .. (max-series-length - 1); series of differing lengths are plotted against the full domain (longer series fill the X span, shorter series stop before the right edge).
Fill below
Pass :fill-below to fill the area between each series line and the chart's baseline (the lower edge for positive-only data). Fill uses the graph-fill theme slot when the series has no color override; with multiple series the fill stacks visually with z-order priority.
EXAMPLES
Single static series
my @samples = (^60).map: { sin($_ * 0.1) * 100 };
my $chart = Selkie::Widget::LineChart.new(
series => [{ label => 'sine', values => @samples }],
sizing => Sizing.flex,
);Multi-series comparison
my $chart = Selkie::Widget::LineChart.new(
series => [
{ label => 'reads', values => @read-rate, color => 0x4477AA },
{ label => 'writes', values => @write-rate, color => 0xEE6677 },
],
sizing => Sizing.flex,
);Fill-below for area emphasis
my $chart = Selkie::Widget::LineChart.new(
series => [{ label => 'load', values => @load-1m }],
fill-below => True,
y-min => 0,
y-max => 4,
sizing => Sizing.flex,
);Reactive ā values bound to a store path
my $chart = Selkie::Widget::LineChart.new(
store-path-fn => -> $store {
[
{ label => 'series',
values => $store.get-in('metrics', 'history') // [] },
]
},
sizing => Sizing.flex,
);SEE ALSO
Selkie::Widget::Plot ā streaming variant backed by native ncuplot
Selkie::Widget::Sparkline ā single-row inline chart
Selkie::Widget::ScatterPlot ā points without lines (also braille)
Selkie::Plot::Palette ā series colour palettes
has Positional @.series
List of series. Each entry is a hash with label (Str), values (Positional of Real), and optional color (UInt RGB).
has Callable &.store-path-fn
Optional reactive data function: sub ($store -- List)>. Called inside render() to derive series. Mutually exclusive with series. Useful when the data is computed from store state.
has Str $.palette
Series-color palette name. Used when individual series don't specify color.
has Bool $.show-axis
Whether to draw the Y axis. Disable to reclaim ~5 columns.
has Bool $.show-legend
Whether to draw the legend below the chart. Auto-disabled if there's only one series. Disable to reclaim 1 row.
has Bool $.fill-below
Whether to fill the area below each line down to the baseline.
has Str $.overlap
How to handle cells where multiple series have dots. Currently only z-order is supported (last-drawn series wins the colour).
has Real $.y-min
Optional explicit Y bounds. Auto-derived when unset.
has UInt $.tick-count
Approximate tick count for the Y axis.
has Str $.empty-message
Message rendered when there are no samples. The default is the expected startup state for monitoring dashboards. Set to the empty string to suppress.
method set-series
method set-series(
@new
) returns MuReplace the chart's series array. Each series' values are realised into an Array up-front because callers often pass Seqs from .map chains, and a TUI re-renders every frame ā an exhausted Seq would produce an empty line on subsequent renders. Throws when the chart was constructed in :store-path-fn mode.