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-axis is True

  • Legend (bottom strip) — colour-coded series labels, when show-legend is 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

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 Mu

Replace 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.

Selkie v0.10.0

High-level TUI framework built on Notcurses

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

Notcurses::Native:ver<0.4.0+>:auth<zef:apogee>

Test Dependencies

Provides

  • Selkie
  • Selkie::App
  • Selkie::App::Internal::Dispatch
  • Selkie::App::Internal::ErrorLog
  • Selkie::App::Internal::FocusTree
  • Selkie::App::Internal::HitTest
  • Selkie::App::Internal::IdleBudget
  • Selkie::App::Internal::OverlayTree
  • Selkie::App::Internal::RenderLoop
  • Selkie::App::Internal::ScreenModalLifecycle
  • Selkie::App::Internal::Terminal
  • Selkie::App::Internal::TerminalSequences
  • Selkie::Container
  • Selkie::EffectiveBounds
  • Selkie::Event
  • Selkie::Layout::Allocate
  • Selkie::Layout::HBox
  • Selkie::Layout::Split
  • Selkie::Layout::VBox
  • Selkie::Plot::Palette
  • Selkie::Plot::Scaler
  • Selkie::Plot::Ticks
  • Selkie::ScreenManager
  • Selkie::Sizing
  • Selkie::Store
  • Selkie::Style
  • Selkie::Test::Focus
  • Selkie::Test::Keys
  • Selkie::Test::Snapshot
  • Selkie::Test::Snapshot::Harness
  • Selkie::Test::Store
  • Selkie::Test::Supply
  • Selkie::Test::Tree
  • Selkie::Theme
  • Selkie::Trace
  • Selkie::Tree
  • Selkie::Widget
  • Selkie::Widget::Axis
  • Selkie::Widget::BarChart
  • Selkie::Widget::Border
  • Selkie::Widget::Button
  • Selkie::Widget::CardList
  • Selkie::Widget::Checkbox
  • Selkie::Widget::CommandPalette
  • Selkie::Widget::ConfirmModal
  • Selkie::Widget::FileBrowser
  • Selkie::Widget::FocusableByDefault
  • Selkie::Widget::Heatmap
  • Selkie::Widget::HelpOverlay
  • Selkie::Widget::Histogram
  • Selkie::Widget::Image
  • Selkie::Widget::Legend
  • Selkie::Widget::LineChart
  • Selkie::Widget::ListView
  • Selkie::Widget::Modal
  • Selkie::Widget::MultiLineInput
  • Selkie::Widget::PasswordStrength
  • Selkie::Widget::Plot
  • Selkie::Widget::ProgressBar
  • Selkie::Widget::RadioGroup
  • Selkie::Widget::RichText
  • Selkie::Widget::RichText::Span
  • Selkie::Widget::ScatterPlot
  • Selkie::Widget::ScrollView
  • Selkie::Widget::Select
  • Selkie::Widget::Sparkline
  • Selkie::Widget::Spinner
  • Selkie::Widget::TabBar
  • Selkie::Widget::Table
  • Selkie::Widget::Text
  • Selkie::Widget::TextInput
  • Selkie::Widget::TextInput::HighlightSpan
  • Selkie::Widget::TextStream
  • Selkie::Widget::Toast
  • Selkie::Widget::ViewportedCardList

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.