Home: one sparkline style with a left fade, SI values, a full-width activity table, roomier panels

The trend curve was two components: a rich one in the node panel and on edges,
and a bare line in the flow table. It is one `Sparkline` now, taking the colour
token, the height and whether the live dot and the readout show. The flow table
draws its rollup in the chart ramp with no dot — the rollups are polled, so the
right edge is the last completed slice rather than this instant — and keeps its
"nothing yet" state, as the panel keeps its three distinct silences.

All of them fade out to the left, through an SVG mask over the curve and its
area. The dot sits outside the mask: the newest reading is the one thing that
must stay solid.

`si` replaces `compact` and the ad-hoc "k" the uPlot axis carried. It prefixes
k/M/G and m/µ, but only outside 0.01–1000, where the plain number is already
the shortest thing to read and a written unit ("0.4 ms") stays honest. The
sparkline readout asks for four digits, so two neighbouring readings never
collapse into one string. Exact counts and anything the user acts on — a
payload, a form field, the edge inspector's value — are left unrounded.
`src/lib/utils.check.ts` asserts the rounding cases.

The activity table now spans its card: the flow name anchors the left, the four
numbers read down their own centre, and the trend takes the slack on the right.
Panel rhythm steps up one notch, gap-5 to gap-6 outside and gap-2 to gap-3
within a section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XC2jX6Hdj7pxGGKzBTrbqB
This commit is contained in:
2026-08-17 11:27:54 +02:00
co-authored by Claude Opus 5
parent 5483de13c0
commit 78c605bd37
10 changed files with 320 additions and 188 deletions
+7 -16
View File
@@ -4,7 +4,7 @@ import "uplot/dist/uPlot.min.css"
import type { HistoryPoint } from "@/client"
import { useTheme } from "@/components/theme-provider"
import { compact } from "@/lib/utils"
import { si } from "@/lib/utils"
/**
* How many lines one chart carries.
@@ -29,17 +29,6 @@ function token(name: string): string {
const seriesColor = (index: number) => token(`--chart-${(index % 5) + 1}`)
/**
* An axis tick, kept short.
*
* The gutter the ticks are drawn in has a fixed width, so a grouped "15,000"
* is clipped to something that reads as a different number entirely.
*/
const tick = (value: number) =>
Math.abs(value) >= 1000
? `${+(value / 1000).toPrecision(3)}k`
: compact(value)
/** The series joined onto one x axis, which is what uPlot draws. */
function table(plots: HistoryPoint[][]): uPlot.AlignedData {
return uPlot.join(
@@ -156,7 +145,10 @@ export function UplotChart({
{
...axis,
size: 46,
values: (_self: uPlot, ticks: number[]) => ticks.map(tick),
// The gutter has a fixed width, so a grouped "15,000" would be
// clipped to something that reads as a different number entirely.
values: (_self: uPlot, ticks: number[]) =>
ticks.map((value) => si(value)),
},
],
series: [
@@ -168,9 +160,8 @@ export function UplotChart({
// rebuilt chart.
stroke: () => seriesColor(index),
// The cursor readout is what decides how wide the legend gets, so
// it is rounded here and the unit named in the card's title.
value: (_self: uPlot, raw: number) =>
Number.isFinite(raw) ? compact(raw) : "--",
// it is shortened here and the unit named in the card's title.
value: (_self: uPlot, raw: number) => si(raw),
// Series arrive on their own clocks; a joined table is mostly
// holes, and a line with a hole per point is not a line.
spanGaps: true,