Chart / sparkline
In reviewLive · web
Time series: hover or tab a point, toggle a series in the legend
Cost · this week
total $189Request latency · this week
p50 / p95, per dayCategorical: same primitive, different data + value format
Tool calls · by family
124 totalTokens · by model
2.7M this weekAt-a-glance: sparkline stat tiles
This week
trendTokens
1.2M
Sessions
42
Progress / usage ring: drive it past each threshold
Context window
used healthy < 60 elevated 60 to 85 high ≥ 85
Series + grid + axes + ring bands are all var(--token). Cyan is the brand series, extra series use the categorical ramp, warm = status. The island is live.
// Web: React, consuming @dizyx/nockerl-tokens. Series colors are tokens: brand cyan// (var(--color-accent-primary)) for the primary, the categorical ramp for the rest.// Axes/grid bind to border + muted tokens; nothing is hardcoded. The selectable legend// and card chrome stay in the CONSUMER; the chart primitives own only the plot.import { NockerlLineChart, NockerlBarChart, NockerlSparkline, NockerlGauge } from '@dizyx/nockerl-react';
export function UsagePanel() { return ( <> {/* `area` is a TOP-LEVEL NockerlLineChart prop (fills under every series), not per-series. Each series is { key, label, color, data }. There is no `legend` prop: the consumer owns the legend/card chrome (compose NockerlChip toggles). */} <NockerlLineChart xLabels={['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']} series={[ { key: 'cloud', label: 'Cloud', color: 'var(--color-accent-primary)', data: cloud }, { key: 'local', label: 'Local', color: 'var(--color-core-categorical-violet400)', data: local }, ]} area />
{/* NockerlBarChart takes `bars` ({ label, value, color }), where the color is per-bar (a categorical tint or brand cyan). There is no `data` / `colorBy` prop. */} <NockerlBarChart bars={[ { label: 'Edit', value: 42, color: 'var(--color-accent-primary)' }, { label: 'Read', value: 31, color: 'var(--color-core-categorical-sky400)' }, { label: 'Bash', value: 24, color: 'var(--color-core-categorical-emerald400)' }, ]} />
<NockerlSparkline data={tokenTrend} color="var(--color-accent-primary)" showLast />
{/* The usage ring is the NockerlGauge primitive in its `ring` shape. It owns the cyan < .60 → amber < .85 → red band semantics internally. */} <NockerlGauge shape="ring" value={58} max={100} centerPrimary="58%" label="Context window 58% used" /> </> );}// Android Jetpack Compose (canonical). The shipped Sparkline draws a stroked// Path normalized to its own min/max with a "now" dot (chat/ui/ClusterSheet.kt);// the context line fills by ratio with cyan→amber→red bands (SessionChipsBar.kt).// Series colors come from NockerlColors (accent + categorical ramp).@Composablefun Sparkline(data: List<Float>, color: Color, modifier: Modifier = Modifier) { val min = data.min(); val max = data.max(); val range = (max - min).takeIf { it > 0f } ?: 1f Canvas(modifier) { val stepX = (size.width - 2f) / (data.size - 1) val path = Path().apply { data.forEachIndexed { i, v -> val x = 1f + i * stepX val y = size.height - 1f - ((v - min) / range) * (size.height - 2f) if (i == 0) moveTo(x, y) else lineTo(x, y) } } drawPath(path, color, style = Stroke(width = 1.5f, cap = StrokeCap.Round, join = StrokeJoin.Round)) val lastY = size.height - 1f - ((data.last() - min) / range) * (size.height - 2f) drawCircle(color, radius = 1.5f, center = Offset(1f + (data.size - 1) * stepX, lastY)) }}
// A usage ring uses the same threshold semantics via drawArc.@Composablefun UsageRing(ratio: Float, modifier: Modifier = Modifier) { val colors = LocalNockerlColors.current val color = when { ratio >= 0.85f -> colors.statusError ratio >= 0.60f -> colors.statusWarning else -> colors.accent } Canvas(modifier) { val sw = 10.dp.toPx() drawArc(colors.canvasEdge, -90f, 360f, false, style = Stroke(sw)) drawArc(color, -90f, 360f * ratio.coerceIn(0f, 1f), false, style = Stroke(sw, cap = StrokeCap.Round)) }}// macOS SwiftUI + Swift Charts (canonical). The shipped chart card lives in// NockerlVoice/UI/HomeSection.swift: a BarMark with a vertical cyan→accentDark// gradient, a LEADING y-axis (hairline grid + muted labels) and a strided x-axis.import Charts
struct CostChart: View { let cloud: [DayCount] var body: some View { Chart(cloud) { item in BarMark(x: .value("Day", item.date, unit: .day), y: .value("Cost", item.count)) .foregroundStyle( LinearGradient(colors: [NockerlTheme.accent, NockerlTheme.accentDark], startPoint: .top, endPoint: .bottom) ) .cornerRadius(2) } .frame(height: 210) .chartXAxis { AxisMarks(values: .stride(by: .day, count: 6)) { AxisValueLabel(format: .dateTime.month(.abbreviated).day()) .foregroundStyle(NockerlTheme.onSurfaceMuted) } } .chartYAxis { AxisMarks(position: .leading) { AxisGridLine().foregroundStyle(NockerlTheme.hairline) AxisValueLabel().foregroundStyle(NockerlTheme.onSurfaceMuted) } } }}Parameters
Section titled “Parameters”| Prop | Type | Default | Description |
|---|---|---|---|
series (LineChart) * | ChartSeries[] | Each: key, label, a token color (a var(--color-…) string for brand cyan or a categorical tint), and data: number[]. The caller passes the already-filtered active set. | |
xLabels (LineChart) * | string[] | Category labels along the x-axis. Length should match each series. | |
area (LineChart) | boolean | false | TOP-LEVEL prop: also fill under the line (the AREA variant), applied to every series. There is no per-series area and no legend prop: the consumer owns the selectable legend + card chrome. |
bars (BarChart) * | ChartBar[] | Each: label, value, and a token color (a var(--color-…) string, either a categorical tint per bar or brand cyan). There is no data / colorBy prop. | |
showLast (Sparkline) | boolean | true | Sparkline only: draw the filled dot at the latest point ("now"). data is number[]; color is a var(--color-…) string. |
shape / value (Gauge ring) | 'ring'; number | The usage ring is Gauge shape="ring" with value / max. It resolves the band internally: cyan < .60 → amber < .85 → red. | |
ariaLabel | string | Accessible name for the figure (LineChart / BarChart). A data summary is generated when omitted. |
| Parameter | Type | Default | Description |
|---|---|---|---|
data * | List<Float> | Sparkline values; normalized to their own min/max. Needs ≥ 2 points (else a faint empty track renders). | |
ratio * | Float | UsageRing / context line fill, 0..1. Threshold bands: cyan < 0.60 → amber < 0.85 → red. | |
color * | Color | Series stroke color. Pass LocalNockerlColors.current.accent or a categorical tint. | |
modifier | Modifier | Modifier | External modifier that carries the size (the Canvas fills it). |
Chart from Swift Charts is configured by content marks + axis modifiers rather
than discrete parameters; the data is the input.
| Style | Type | Default | Description |
|---|---|---|---|
BarMark / LineMark | ChartContent | The mark per datum. Filled with a vertical NockerlTheme.accent → accentDark gradient; .cornerRadius(2). | |
.chartYAxis | modifier | Leading AxisMarks: AxisGridLine on NockerlTheme.hairline, AxisValueLabel on onSurfaceMuted. | |
.chartXAxis | modifier | Strided AxisMarks with muted-foreground labels. | |
Equalizer | View | The 5-bar audio meter (RecordingHUD): log-scaled capsules in NockerlTheme.accent; the live counterpart to a static sparkline. |