@camstack/ui-library 1.2.305 → 1.2.306

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{MotionZonesSettings-C3Tj12yc.js → MotionZonesSettings-DR1g9ja3.js} +1 -1
  2. package/dist/{PrivacyMaskSettings-C9wfHkiB.js → PrivacyMaskSettings-CtetT6Jl.js} +1 -1
  3. package/dist/{SceneMonitorEditor-30H29vN4.js → SceneMonitorEditor-BQhVO2F2.js} +1 -1
  4. package/dist/composites/cap-settings/EventHeatmap.d.ts +8 -2
  5. package/dist/composites/charts/BarSeries.d.ts +49 -0
  6. package/dist/composites/charts/Sparkline.d.ts +26 -0
  7. package/dist/composites/charts/StateTimeline.d.ts +47 -0
  8. package/dist/composites/charts/TimeSeriesChart.d.ts +48 -0
  9. package/dist/composites/charts/bar-series-model.d.ts +25 -0
  10. package/dist/composites/charts/chart-data.d.ts +87 -0
  11. package/dist/composites/charts/chart-frame.d.ts +53 -0
  12. package/dist/composites/charts/chart-palette.d.ts +46 -0
  13. package/dist/composites/charts/chart-scale.d.ts +29 -0
  14. package/dist/composites/charts/chart-uplot.d.ts +44 -0
  15. package/dist/composites/charts/chart-window.d.ts +19 -0
  16. package/dist/composites/charts/index.d.ts +16 -0
  17. package/dist/composites/charts/time-series-model.d.ts +47 -0
  18. package/dist/composites/charts/uplot-host.d.ts +34 -0
  19. package/dist/composites/device-history/DeviceHistoryView.d.ts +26 -0
  20. package/dist/composites/device-history/HistoryChart.d.ts +31 -0
  21. package/dist/composites/device-history/HistoryWindowBar.d.ts +16 -0
  22. package/dist/composites/device-history/history-model.d.ts +121 -0
  23. package/dist/composites/device-history/history-reads.d.ts +43 -0
  24. package/dist/composites/device-history/history-selection.d.ts +55 -0
  25. package/dist/composites/device-history/history-window.d.ts +31 -0
  26. package/dist/composites/device-history/index.d.ts +14 -0
  27. package/dist/composites/device-history/use-device-history.d.ts +37 -0
  28. package/dist/composites/device-history/use-history-panels.d.ts +10 -0
  29. package/dist/composites/index.d.ts +2 -0
  30. package/dist/{icons-B1AFXUaZ.cjs → icons-BTux33dH.cjs} +2 -35
  31. package/dist/{icons-BT8c2hxp.js → icons-D178phML.js} +1 -34
  32. package/dist/index.cjs +3172 -45
  33. package/dist/index.js +3094 -52
  34. package/dist/uPlot.esm-BbxBUnav.cjs +3767 -0
  35. package/dist/uPlot.esm-C_8LiAPm.js +3767 -0
  36. package/package.json +2 -1
  37. package/src/tailwind/camstack-theme.css +104 -2
@@ -1,4 +1,4 @@
1
- import { TEXT_HINT, UiTrans, WidgetPanel, isAbsentProvider, useDeviceProxy, usePlayerOverlayLayer, useSystem, useUiText, yn as Grid2x2 } from "./index.js";
1
+ import { TEXT_HINT, UiTrans, WidgetPanel, bn as Grid2x2, isAbsentProvider, useDeviceProxy, usePlayerOverlayLayer, useSystem, useUiText } from "./index.js";
2
2
  import { MaskShapeCanvas } from "./MaskShapeCanvas-C9cJlxvG.js";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,5 +1,5 @@
1
1
  import { n as Hexagon, t as MicOff } from "./mic-off-C2VTYr91.js";
2
- import { H as Square, Nn as EyeOff, TEXT_HINT, UiTrans, Ut as Mic, WidgetPanel, isAbsentProvider, k as Trash2, useDeviceProxy, usePlayerOverlayLayer, useSystem, useUiText } from "./index.js";
2
+ import { H as Square, Pn as EyeOff, TEXT_HINT, UiTrans, Ut as Mic, WidgetPanel, isAbsentProvider, k as Trash2, useDeviceProxy, usePlayerOverlayLayer, useSystem, useUiText } from "./index.js";
3
3
  import { MaskShapeCanvas } from "./MaskShapeCanvas-C9cJlxvG.js";
4
4
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,5 +1,5 @@
1
1
  import { t as Settings2 } from "./settings-2-jT1iBqHD.js";
2
- import { SCENE_CAPTURE_VARIANTS, TEXT_HINT, UiTrans, badgeFor, br as Camera, coverageLine, dn as ImageOff, ft as RotateCcw, hubPath, isAbsentProvider, k as Trash2, roiTooLarge, statusLine, useDeviceProxy, useDeviceSnapshotImage, useDeviceStateSlice, useSystem, useUiText, variantLabel } from "./index.js";
2
+ import { SCENE_CAPTURE_VARIANTS, Sr as Camera, TEXT_HINT, UiTrans, badgeFor, coverageLine, dn as ImageOff, ft as RotateCcw, hubPath, isAbsentProvider, k as Trash2, roiTooLarge, statusLine, useDeviceProxy, useDeviceSnapshotImage, useDeviceStateSlice, useSystem, useUiText, variantLabel } from "./index.js";
3
3
  import { MaskShapeCanvas } from "./MaskShapeCanvas-C9cJlxvG.js";
4
4
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,5 +1,5 @@
1
- import type { DensityBucket } from "./recording-timeline-model";
2
1
  import { type JSX } from "react";
2
+ import type { DensityBucket } from "./recording-timeline-model";
3
3
  interface EventHeatmapProps {
4
4
  readonly windowFrom: number;
5
5
  readonly windowTo: number;
@@ -7,5 +7,11 @@ interface EventHeatmapProps {
7
7
  readonly selectedIndex: number | null;
8
8
  readonly onSelectBucket: (index: number) => void;
9
9
  }
10
- export declare function EventHeatmap({ windowFrom, windowTo, buckets, selectedIndex, onSelectBucket }: EventHeatmapProps): JSX.Element | null;
10
+ /**
11
+ * Event-density strip under a recording timeline: one stacked bar per bucket
12
+ * (motion / object / audio), height normalised to the busiest bucket, on the
13
+ * timeline's own window. A click (or the bucket's button, for keyboard and
14
+ * screen readers) selects the bucket — it drives the drill-down strip.
15
+ */
16
+ export declare function EventHeatmap({ windowFrom, windowTo, buckets, selectedIndex, onSelectBucket }: EventHeatmapProps): JSX.Element;
11
17
  export {};
@@ -0,0 +1,49 @@
1
+ /**
2
+ * BarSeries — bucketed counts or deltas on wall time (uPlot), optionally
3
+ * stacked, with a window total (kWh per hour with the day's sum, detections
4
+ * per 5 minutes by class, event density under a recording timeline).
5
+ *
6
+ * Bars sit on their bucket's real span, so a missing bucket is a missing bar
7
+ * and never a zero bar. `compact` drops the frame and axes for a strip that
8
+ * lives inside another control; there a bucket can be selected (`onSelect`)
9
+ * and every bucket is also a real button for keyboard and screen readers.
10
+ */
11
+ import { type JSX, type ReactNode } from "react";
12
+ import { type ChartWindowToggleProps } from "./chart-window";
13
+ import { type BarBucket, type BarLayerDef } from "./bar-series-model";
14
+ import type { ChartRange } from "./TimeSeriesChart";
15
+ export type { BarBucket, BarLayerDef } from "./bar-series-model";
16
+ export interface BarSeriesProps<TId extends string = string> {
17
+ readonly title: string;
18
+ readonly layers: readonly BarLayerDef[];
19
+ readonly buckets: readonly BarBucket[];
20
+ /** The width of one bucket in ms (5 min, 1 h, 1 d). */
21
+ readonly bucketMs: number;
22
+ readonly range: ChartRange;
23
+ readonly unit?: string | null;
24
+ readonly formatValue?: (v: number, unit: string | null) => string;
25
+ /** Plot height in px. Default 100 (24 when `compact`). */
26
+ readonly height?: number;
27
+ /** Stack the layers (default true for more than one layer). */
28
+ readonly stacked?: boolean;
29
+ /** Say the window total under the chart ("Total 12.4 kWh"). */
30
+ readonly showTotal?: boolean;
31
+ readonly syncKey?: string;
32
+ readonly pending?: boolean;
33
+ readonly emptyMessage?: string;
34
+ readonly actions?: ReactNode;
35
+ readonly footer?: ReactNode;
36
+ readonly windowToggle?: ChartWindowToggleProps<TId>;
37
+ /** No frame, no axes: a strip inside another control. */
38
+ readonly compact?: boolean;
39
+ /** A non-zero bar is at least this fraction of the plot (compact strips). */
40
+ readonly minBarFraction?: number;
41
+ readonly selectedIndex?: number | null;
42
+ readonly onSelect?: (index: number) => void;
43
+ /** Text for a bucket (its button and native tooltip). */
44
+ readonly bucketLabel?: (bucket: BarBucket, index: number) => string;
45
+ /** `data-testid` on each bucket button. */
46
+ readonly bucketTestId?: string;
47
+ readonly className?: string;
48
+ }
49
+ export declare function BarSeries<TId extends string = string>({ title, layers, buckets, bucketMs, range, unit, formatValue, height, stacked, showTotal, syncKey, pending, emptyMessage, actions, footer, windowToggle, compact, minBarFraction, selectedIndex, onSelect, bucketLabel, bucketTestId, className }: BarSeriesProps<TId>): JSX.Element;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Sparkline — one series, no axes, no cursor: a trend beside a number in a
3
+ * row (a training job's mAP per epoch). The x is whatever the caller orders
4
+ * by (an epoch, a time); the value next to it names what the line is.
5
+ */
6
+ import { type JSX } from "react";
7
+ import { type ChartChromeToken, type ChartSeriesToken } from "./chart-palette";
8
+ export interface SparklinePoint {
9
+ readonly x: number;
10
+ readonly v: number | null;
11
+ }
12
+ export interface SparklineProps {
13
+ readonly points: readonly SparklinePoint[];
14
+ /** Accessible name ("mAP50 per epoch"). */
15
+ readonly ariaLabel: string;
16
+ /** Line colour token. Default `chart-series-a`. */
17
+ readonly token?: ChartSeriesToken | Extract<ChartChromeToken, "primary">;
18
+ /** Plot height in px. Default 32. */
19
+ readonly height?: number;
20
+ /** Width class of the box (default `w-40`). */
21
+ readonly widthClassName?: string;
22
+ /** Start the value axis at zero (default true). */
23
+ readonly zeroBased?: boolean;
24
+ readonly className?: string;
25
+ }
26
+ export declare function Sparkline({ points, ariaLabel, token, height, widthClassName, zeroBased, className }: SparklineProps): JSX.Element;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * StateTimeline — boolean and enum states as bands over wall time (uPlot).
3
+ *
4
+ * One row per state series. A state is a filled band in its categorical slot
5
+ * (the ordinal of the value in its `states` list, so a colour follows the
6
+ * state, never its rank); a boolean's `false` is a recessive fill, and a span
7
+ * nothing was recorded for is left EMPTY — unknown never looks like a state.
8
+ * The legend gives the time spent in each state over the window ("open
9
+ * 2h 14m"), and the cursor (synced through `syncKey`) names the state under it.
10
+ */
11
+ import { type JSX, type ReactNode } from "react";
12
+ import { type StateSegment } from "./chart-data";
13
+ import { type ChartSwatchToken } from "./chart-palette";
14
+ import type { ChartRange } from "./TimeSeriesChart";
15
+ export interface StateRowDef {
16
+ readonly id: string;
17
+ readonly label: string;
18
+ readonly segments: readonly StateSegment[];
19
+ /** Every state this row can take, in their stable order (an enum's value list). */
20
+ readonly states: readonly string[];
21
+ /** `boolean`: `states[1]` is the "on" state, drawn in `colorIndex`; `states[0]` is recessive. */
22
+ readonly kind: "boolean" | "enum";
23
+ /** Boolean: the slot of the "on" state. Enum: the slot offset of `states[0]`. Default 0. */
24
+ readonly colorIndex?: number;
25
+ /** Operator text for a state value ("open" for `true`). */
26
+ readonly stateLabel?: (state: string) => string;
27
+ /**
28
+ * Exact time per state when the segments are an approximation (a rollup
29
+ * bucket drawn in its dominant state); default: summed from the segments.
30
+ */
31
+ readonly totals?: ReadonlyMap<string, number>;
32
+ }
33
+ export interface StateTimelineProps {
34
+ readonly title: string;
35
+ readonly rows: readonly StateRowDef[];
36
+ readonly range: ChartRange;
37
+ readonly syncKey?: string;
38
+ readonly pending?: boolean;
39
+ readonly emptyMessage?: string;
40
+ readonly actions?: ReactNode;
41
+ readonly className?: string;
42
+ }
43
+ /** The colour role of a state on a row: a categorical slot, or the recessive "off". */
44
+ export declare function stateStyle(row: Pick<StateRowDef, "kind" | "states" | "colorIndex">, state: string): {
45
+ readonly token: ChartSwatchToken;
46
+ };
47
+ export declare function StateTimeline({ title, rows, range, syncKey, pending, emptyMessage, actions, className }: StateTimelineProps): JSX.Element;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * TimeSeriesChart — gauges and counters over wall time (uPlot).
3
+ *
4
+ * Lines, an optional min/max band, stacked areas, null gaps (a hole breaks
5
+ * the line AND is shaded), one value axis per unit (left, right, left …),
6
+ * a synced cursor across every chart sharing `syncKey`, an optional window
7
+ * toggle. Identity is never colour alone: one series is named by the title,
8
+ * two or more always carry a legend with direct values.
9
+ *
10
+ * A device page draws ONE unit per chart; overlaying units (several axes) is
11
+ * for the cross-device History page, where the reader chose the series.
12
+ */
13
+ import { type JSX, type ReactNode } from "react";
14
+ import { type ValueAxisSpec } from "./chart-uplot";
15
+ import { type ChartWindowToggleProps } from "./chart-window";
16
+ import { type TimeSeriesDef } from "./time-series-model";
17
+ export type { TimeSeriesDef } from "./time-series-model";
18
+ export interface ChartRange {
19
+ readonly from: number;
20
+ readonly to: number;
21
+ }
22
+ export interface TimeSeriesChartProps<TId extends string = string> {
23
+ readonly title: string;
24
+ readonly series: readonly TimeSeriesDef[];
25
+ /** The window the x axis spans; default: the data's own span. */
26
+ readonly range?: ChartRange;
27
+ /** Plot height in px. Default 120. */
28
+ readonly height?: number;
29
+ /** Stack the non-compare series as areas (parts of one total). */
30
+ readonly stacked?: boolean;
31
+ /** Fill a single line down to its baseline. */
32
+ readonly area?: boolean;
33
+ /** Charts with the same key move one cursor together. */
34
+ readonly syncKey?: string;
35
+ /** Value formatter per unit; default: bytes binary, `%` glued, unit after a space. */
36
+ readonly formatValue?: (v: number, unit: string | null) => string;
37
+ /** Axis layout per unit; default by unit (°C floats, `%` floors at 10, bytes binary). */
38
+ readonly axisFor?: (unit: string | null) => ValueAxisSpec;
39
+ /** Shown when no series has a known value. */
40
+ readonly emptyMessage?: string;
41
+ /** The query has not answered: a placeholder, never an empty chart. */
42
+ readonly pending?: boolean;
43
+ readonly actions?: ReactNode;
44
+ readonly footer?: ReactNode;
45
+ readonly windowToggle?: ChartWindowToggleProps<TId>;
46
+ readonly className?: string;
47
+ }
48
+ export declare function TimeSeriesChart<TId extends string = string>({ title, series, range, height, stacked, area, syncKey, formatValue, axisFor, emptyMessage, pending, actions, footer, windowToggle, className }: TimeSeriesChartProps<TId>): JSX.Element;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The pure half of `BarSeries`: buckets on a fixed grid, optionally stacked,
3
+ * with a total. A bucket the source did not answer is `null` (no bar, not a
4
+ * zero bar); a bucket it answered with nothing is `0`.
5
+ */
6
+ /** One bucket: its START and one value per stack layer (`null` = not measured). */
7
+ export interface BarBucket {
8
+ readonly t: number;
9
+ readonly values: readonly (number | null)[];
10
+ }
11
+ export interface BarLayerDef {
12
+ readonly id: string;
13
+ readonly label: string;
14
+ readonly colorIndex: number;
15
+ }
16
+ /** Cumulative tops per layer, bottom layer first: `tops[l][i] = values[0..l]` summed. */
17
+ export declare function stackBuckets(buckets: readonly BarBucket[], layers: number): readonly (readonly (number | null)[])[];
18
+ /** Total per layer and overall over the window (unknown buckets add nothing). */
19
+ export declare function bucketTotals(buckets: readonly BarBucket[], layers: number): {
20
+ readonly perLayer: readonly number[];
21
+ readonly total: number;
22
+ readonly known: boolean;
23
+ };
24
+ /** Index of the bucket that holds `t`, or `null`. */
25
+ export declare function bucketIndexAt(buckets: readonly BarBucket[], bucketMs: number, t: number): number | null;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Data shaping for the chart family — pure, no DOM, no uPlot.
3
+ *
4
+ * uPlot draws from ONE shared x array with one y array per series. Two kinds
5
+ * of "no value" must stay distinct when several series are joined onto it:
6
+ *
7
+ * - `undefined` — this series has no sample at an x another series has. The
8
+ * line is JOINED across it (alignment filler, not a claim).
9
+ * - `null` — nobody measured here: an explicit unknown sample, or a hole
10
+ * longer than the series' `gapMs`. The line BREAKS and is never drawn as 0.
11
+ */
12
+ /** One sample. `null` is unknown, never zero. */
13
+ export interface TimePoint {
14
+ readonly t: number;
15
+ readonly v: number | null;
16
+ /** Optional spread of a rolled-up bucket — drawn as a band. */
17
+ readonly min?: number | null;
18
+ readonly max?: number | null;
19
+ }
20
+ export interface AlignInput {
21
+ readonly points: readonly TimePoint[];
22
+ /** Longest interval between two samples that may be joined; omit for change-only (held) series. */
23
+ readonly gapMs?: number;
24
+ }
25
+ export type AlignedValue = number | null | undefined;
26
+ export interface AlignedSeries {
27
+ /** Ascending, unique, epoch ms. */
28
+ readonly xs: readonly number[];
29
+ readonly ys: readonly (readonly AlignedValue[])[];
30
+ readonly mins: readonly (readonly AlignedValue[])[];
31
+ readonly maxs: readonly (readonly AlignedValue[])[];
32
+ }
33
+ /** Join several series onto one x array, inserting a `null` inside every hole wider than its `gapMs`. */
34
+ export declare function alignSeries(inputs: readonly AlignInput[]): AlignedSeries;
35
+ /** A time range nobody measured, for shading. */
36
+ export interface GapSpan {
37
+ readonly from: number;
38
+ readonly to: number;
39
+ }
40
+ /**
41
+ * The holes every series shares (the intersection), so the chart shades only
42
+ * what NOBODY measured. Series without `gapMs` never contribute a hole and so
43
+ * make the intersection empty: a held value is not unmeasured.
44
+ */
45
+ export declare function sharedGapSpans(inputs: readonly AlignInput[]): readonly GapSpan[];
46
+ /**
47
+ * Cumulative sums for a stacked area: `out[i] = ys[0] + … + ys[i]`. A stack
48
+ * level is unknown (`null`) as soon as any layer under it is unknown, and an
49
+ * alignment filler stays a filler.
50
+ */
51
+ export declare function stackSeries(ys: readonly (readonly AlignedValue[])[]): AlignedValue[][];
52
+ /**
53
+ * The value a reader sees at index `idx`: the nearest sample at or before it
54
+ * that is not an alignment filler. A `null` there is answered as unknown.
55
+ */
56
+ export declare function valueAtOrBefore(xs: readonly number[], ys: readonly AlignedValue[], idx: number): number | null;
57
+ /** A state held from `from` until `to`. `state: null` is unknown (not recorded), never a state. */
58
+ export interface StateSegment {
59
+ readonly from: number;
60
+ readonly to: number;
61
+ readonly state: string | null;
62
+ }
63
+ /** One state change, as a history read answers it: the state from `t` on. */
64
+ export interface StateChange {
65
+ readonly t: number;
66
+ readonly state: string | null;
67
+ }
68
+ /**
69
+ * Turn change points into segments clipped to [from, to]. `before` is the
70
+ * state in force when the window opens (the last change before it); without
71
+ * it the window starts unknown until the first change.
72
+ */
73
+ export declare function segmentsFromChanges(changes: readonly StateChange[], from: number, to: number, before?: string | null): readonly StateSegment[];
74
+ /** Total milliseconds per state over the segments (unknown spans are not counted). */
75
+ export declare function timeInState(segments: readonly StateSegment[]): ReadonlyMap<string, number>;
76
+ /** The state in force at `t`, or `null` when nothing was recorded there. */
77
+ export declare function stateAt(segments: readonly StateSegment[], t: number): string | null;
78
+ /** `2h 14m`, `3d 4h`, `45s` — two units at most, for a legend. */
79
+ export declare function formatSpan(ms: number): string;
80
+ /**
81
+ * A wall-clock label at the precision the window's length justifies:
82
+ * seconds under ten minutes, `HH:MM` within two days, the date past that,
83
+ * month + year past ~4 months.
84
+ */
85
+ export declare function formatTimeLabel(t: number, spanMs: number): string;
86
+ /** A cursor readout time: always the full instant at the window's precision. */
87
+ export declare function formatCursorTime(t: number, spanMs: number): string;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The DOM half every chart of the family shares: the title, the cursor
3
+ * readout, the legend with direct values, the actions slot (a ⋯ menu) and the
4
+ * accessible summary. The plot itself is canvas; everything a reader or a
5
+ * screen reader needs to IDENTIFY a series is here, in text tokens — a series
6
+ * colour only ever sits on a swatch beside the text.
7
+ *
8
+ * One series: no legend box — the title names it, the value sits beside it.
9
+ * Two or more: a legend is always present and every entry carries its value.
10
+ */
11
+ import { type JSX, type ReactNode } from "react";
12
+ import { type ChartSwatchToken } from "./chart-palette";
13
+ export interface ChartLegendEntry {
14
+ readonly id: string;
15
+ readonly label: string;
16
+ /** The swatch colour: the series' token. */
17
+ readonly token: ChartSwatchToken;
18
+ /** The swatch is drawn as a dashed line (a compare series, or a ninth+ series). */
19
+ readonly dashed?: boolean;
20
+ /** The value at the cursor (or the latest); `undefined` hides it. */
21
+ readonly value?: string;
22
+ /** A secondary entry (a compare period): recessive text. */
23
+ readonly muted?: boolean;
24
+ }
25
+ export interface ChartFrameProps {
26
+ readonly title: string;
27
+ /** Controls at the right of the title (a ⋯ menu). */
28
+ readonly actions?: ReactNode;
29
+ /** The instant the readout shows, when the cursor is on the plot. */
30
+ readonly readoutTime?: string | null;
31
+ readonly legend: readonly ChartLegendEntry[];
32
+ /** One sentence a screen reader gets instead of the canvas. */
33
+ readonly summary: string;
34
+ /** Text under the legend (a total, what one bar covers). */
35
+ readonly footer?: ReactNode;
36
+ /** Rendered INSTEAD of the plot (empty or not-yet-answered). */
37
+ readonly placeholder?: ReactNode;
38
+ /** Interactive content tied to the plot (bucket buttons), kept OUTSIDE its `role="img"`. */
39
+ readonly controls?: ReactNode;
40
+ readonly className?: string;
41
+ readonly children?: ReactNode;
42
+ }
43
+ export declare function ChartFrame({ title, actions, readoutTime, legend, summary, footer, placeholder, controls, className, children }: ChartFrameProps): JSX.Element;
44
+ /** The swatch: a filled square, or a dashed bar for a dashed series. */
45
+ export declare function ChartSwatch({ token, dashed }: {
46
+ readonly token: ChartSwatchToken;
47
+ readonly dashed?: boolean;
48
+ }): JSX.Element;
49
+ /** The plot-sized box for an empty or pending chart: a dashed outline and one line of text. */
50
+ export declare function ChartPlaceholder({ message, height }: {
51
+ readonly message: string;
52
+ readonly height: "sm" | "md" | "lg";
53
+ }): JSX.Element;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The chart family's colours — theme tokens only.
3
+ *
4
+ * Categorical identity comes from the eight `--color-chart-series-*` tokens in
5
+ * `tailwind/camstack-theme.css`, in FIXED order (a blue, b magenta, c yellow,
6
+ * d aqua, e orange, f violet, g green, h red). A series keeps its slot for as
7
+ * long as it is on screen: the caller assigns the index, never the rank, so a
8
+ * filter that hides one series does not repaint the others.
9
+ *
10
+ * Past eight, a slot is reused with a dash pattern (composite encoding) — a
11
+ * generated ninth hue would be indistinguishable from one of the eight.
12
+ *
13
+ * Canvas cannot read `var(--…)`, so a token is resolved through
14
+ * `getComputedStyle` at DRAW time: a theme switch (`.dark` / `.light` on
15
+ * `<html>`) followed by a redraw picks the new value up with no re-creation.
16
+ */
17
+ /** The categorical slots, in their fixed order. */
18
+ export declare const CHART_SERIES_TOKENS: readonly ["chart-series-a", "chart-series-b", "chart-series-c", "chart-series-d", "chart-series-e", "chart-series-f", "chart-series-g", "chart-series-h"];
19
+ export type ChartSeriesToken = (typeof CHART_SERIES_TOKENS)[number];
20
+ export interface ChartSeriesStyle {
21
+ readonly token: ChartSeriesToken;
22
+ /** Empty for a solid line. */
23
+ readonly dash: readonly number[];
24
+ }
25
+ /** The style of the series at `index` (0-based, its stable identity slot). */
26
+ export declare function chartSeriesStyle(index: number): ChartSeriesStyle;
27
+ /** A token a DOM swatch can wear: a series slot, or the recessive "off" state. */
28
+ export type ChartSwatchToken = ChartSeriesToken | "border";
29
+ /** The utility that fills a swatch with `token`. */
30
+ export declare function swatchFillClass(token: ChartSwatchToken): string;
31
+ /** The utility that colours a dashed swatch's line with `token`. */
32
+ export declare function swatchLineClass(token: ChartSwatchToken): string;
33
+ /** Neutral roles the canvas draws with (axes, grid, gap shading). */
34
+ export type ChartChromeToken = "foreground-subtle" | "foreground-muted" | "border" | "border-subtle" | "surface" | "primary";
35
+ /**
36
+ * Resolve a theme token to the colour the canvas paints. Reads the computed
37
+ * value of `--color-<token>` on the chart's own root (so a scoped theme block
38
+ * wins); answers `transparent` when the document has no value (a test DOM).
39
+ */
40
+ export declare function resolveChartColor(token: ChartSeriesToken | ChartChromeToken, el?: Element): string;
41
+ /**
42
+ * `color` with `alpha` (0..1). A resolved token is a six-digit hex, which
43
+ * becomes the eight-digit form every canvas accepts; anything else falls back
44
+ * to `color-mix`.
45
+ */
46
+ export declare function withAlpha(color: string, alpha: number): string;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Value-axis arithmetic for the chart family — pure, unit-tested, because the
3
+ * ways a chart LIES are arithmetic (a truncated baseline, an axis that tops
4
+ * out on `4.7 GB`, a gap threshold that either shatters every line or bridges
5
+ * an outage). Moved from the admin-ui load charts, which proved these rules.
6
+ */
7
+ /** `bytes` rounds on powers of two (what `formatBytes` prints); `linear` on 1/2/5×10ⁿ. */
8
+ export type AxisScale = "linear" | "bytes";
9
+ /**
10
+ * Nice upper bound of a value axis that starts at zero. `floor` is returned
11
+ * VERBATIM when nothing exceeds it, so an idle series keeps the scale the
12
+ * caller chose instead of a 0–0.3 axis on which noise looks like load.
13
+ */
14
+ export declare function niceMax(values: readonly (number | null)[], floor: number, scale?: AxisScale): number;
15
+ /** Gridline values for an axis topping out at `max`, ascending, `max` last; zero is the baseline, not a tick. */
16
+ export declare function axisTicks(max: number, scale?: AxisScale): readonly number[];
17
+ /**
18
+ * Nice [min, max] for a series that need NOT start at zero (a temperature, a
19
+ * dBFS level): the data range padded by a tenth and rounded outward to a
20
+ * readable step. A flat series gets a ±1 window so it is not a degenerate axis.
21
+ */
22
+ export declare function niceRange(values: readonly (number | null)[]): readonly [number, number] | null;
23
+ /**
24
+ * How many sampling intervals of silence make a HOLE rather than a late tick:
25
+ * past any plausible delivery jitter, well inside the shortest outage worth seeing.
26
+ */
27
+ export declare const GAP_INTERVAL_FACTOR = 2.5;
28
+ /** The gap threshold for a series sampled every `intervalMs` — derived, never a literal. */
29
+ export declare function gapThresholdMs(intervalMs: number): number;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * uPlot option builders shared by the family: the wall-time x axis, a value
3
+ * axis per unit, recessive chrome in theme tokens, the synced cursor and the
4
+ * hole shading. Every colour is a FUNCTION so a theme switch is a redraw.
5
+ */
6
+ import type uPlot from "uplot";
7
+ import { type GapSpan } from "./chart-data";
8
+ import { type AxisScale } from "./chart-scale";
9
+ /** The x axis: wall-clock time under the plot, at the window's precision. */
10
+ export declare function timeAxis(spanMs: () => number, show?: boolean): uPlot.Axis;
11
+ /** How a value axis is laid out. */
12
+ export interface ValueAxisSpec {
13
+ /** `bytes` rounds the top on powers of two. Default `linear`. */
14
+ readonly scale?: AxisScale;
15
+ /** Start at zero (default) — false for a level that lives far from it (°C, dBFS). */
16
+ readonly zeroBased?: boolean;
17
+ /** The axis top never goes below this (an idle 0.4 % must not fill the plot). */
18
+ readonly floor?: number;
19
+ /** Fixed range, e.g. `[0, 100]` for a percentage. */
20
+ readonly fixed?: readonly [number, number];
21
+ }
22
+ /** A y scale's range from the values it carries, per the axis spec. */
23
+ export declare function valueRange(values: readonly (number | null | undefined)[], spec: ValueAxisSpec): readonly [number, number];
24
+ /** Tick positions for a value axis over `range`: readable steps, 3–6 intervals. */
25
+ export declare function valueSplits(range: readonly [number, number], spec: ValueAxisSpec): number[];
26
+ /** One value axis. `side` 3 = left, 1 = right. */
27
+ export declare function valueAxis(scaleKey: string, side: 1 | 3, format: (v: number) => string, showGrid: boolean): uPlot.Axis;
28
+ /** The cursor: a vertical crosshair, synced across every chart with the same key. */
29
+ export declare function chartCursor(syncKey: string | undefined): uPlot.Cursor;
30
+ /**
31
+ * Shade every hole nobody measured, UNDER the series, so an outage reads as
32
+ * missing ground and never as a quiet period.
33
+ */
34
+ export declare function gapShadeHook(spans: () => readonly GapSpan[]): (u: uPlot) => void;
35
+ /** Is this unit a byte count (binary axis and formatter)? */
36
+ export declare function isByteUnit(unit: string | null | undefined): boolean;
37
+ /** The default axis spec for a unit. */
38
+ export declare function defaultAxisSpec(unit: string | null | undefined): ValueAxisSpec;
39
+ /** Binary byte count: `512 B`, `3.4 MB`, `1.2 GB`. */
40
+ export declare function formatChartBytes(bytes: number): string;
41
+ /** A number at a precision its magnitude justifies. */
42
+ export declare function formatChartNumber(v: number): string;
43
+ /** The default value formatter: bytes binary, `%` glued, any other unit after a space. */
44
+ export declare function formatChartValue(v: number, unit: string | null | undefined): string;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The window toggle every chart of the family uses: a small segmented control
3
+ * of presets. Pure display — the parent owns `value` and `onChange`. A control
4
+ * with one option is not rendered.
5
+ */
6
+ import { type JSX } from "react";
7
+ export interface ChartWindowOption<TId extends string> {
8
+ readonly id: TId;
9
+ readonly label: string;
10
+ }
11
+ export interface ChartWindowToggleProps<TId extends string> {
12
+ readonly options: readonly ChartWindowOption<TId>[];
13
+ readonly value: TId;
14
+ readonly onChange: (id: TId) => void;
15
+ /** Names the group for assistive tech ("Time window"). */
16
+ readonly ariaLabel: string;
17
+ readonly className?: string;
18
+ }
19
+ export declare function ChartWindowToggle<TId extends string>({ options, value, onChange, ariaLabel, className }: ChartWindowToggleProps<TId>): JSX.Element | null;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The chart family (D899): one look for every time-based chart, built on
3
+ * uPlot, themed only through `--color-chart-series-*` and the neutral tokens.
4
+ */
5
+ export { TimeSeriesChart, type ChartRange, type TimeSeriesChartProps } from "./TimeSeriesChart";
6
+ export { StateTimeline, stateStyle, type StateRowDef, type StateTimelineProps } from "./StateTimeline";
7
+ export { BarSeries, type BarSeriesProps } from "./BarSeries";
8
+ export { Sparkline, type SparklinePoint, type SparklineProps } from "./Sparkline";
9
+ export { ChartFrame, ChartPlaceholder, ChartSwatch, type ChartFrameProps, type ChartLegendEntry } from "./chart-frame";
10
+ export { ChartWindowToggle, type ChartWindowOption, type ChartWindowToggleProps } from "./chart-window";
11
+ export { CHART_SERIES_TOKENS, chartSeriesStyle, swatchFillClass, swatchLineClass, type ChartSeriesStyle, type ChartSwatchToken, type ChartSeriesToken } from "./chart-palette";
12
+ export { GAP_INTERVAL_FACTOR, axisTicks, gapThresholdMs, niceMax, niceRange, type AxisScale } from "./chart-scale";
13
+ export { alignSeries, formatSpan, formatTimeLabel, segmentsFromChanges, sharedGapSpans, stackSeries, stateAt, timeInState, type GapSpan, type StateChange, type StateSegment, type TimePoint } from "./chart-data";
14
+ export { defaultAxisSpec, formatChartBytes, formatChartNumber, formatChartValue, isByteUnit, type ValueAxisSpec } from "./chart-uplot";
15
+ export { buildTimeSeriesModel, unitAxes, type SeriesInterpolation, type TimeSeriesDef } from "./time-series-model";
16
+ export { bucketTotals, stackBuckets, type BarBucket, type BarLayerDef } from "./bar-series-model";
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The pure half of `TimeSeriesChart`: which series go on which value axis,
3
+ * the aligned data uPlot draws, and what the readout says at an index.
4
+ */
5
+ import { type AlignedValue, type GapSpan, type TimePoint } from "./chart-data";
6
+ export type SeriesInterpolation = "linear" | "step";
7
+ export interface TimeSeriesDef {
8
+ /** Stable id — React key and the identity the colour follows. */
9
+ readonly id: string;
10
+ readonly label: string;
11
+ /** `null` is an unknown unit; series sharing a unit share an axis. */
12
+ readonly unit?: string | null;
13
+ readonly points: readonly TimePoint[];
14
+ /** Categorical slot (`chartSeriesStyle`); the caller keeps it stable across filters. */
15
+ readonly colorIndex: number;
16
+ /** `step` holds each value until the next (a change-only series). Default `linear`. */
17
+ readonly interpolation?: SeriesInterpolation;
18
+ /** Longest gap that may be joined; omit for a change-only series (it is held, never a hole). */
19
+ readonly gapMs?: number;
20
+ /** Draw the points' `min`/`max` as a band behind the line. */
21
+ readonly band?: boolean;
22
+ /** A previous-period overlay: dashed, recessive, excluded from stacking and gap shading. */
23
+ readonly compare?: boolean;
24
+ }
25
+ /** The unit key a series lives on (`''` for no / unknown unit). */
26
+ export declare function unitKey(unit: string | null | undefined): string;
27
+ /** Units in order of first appearance — one value axis each. */
28
+ export declare function unitAxes(series: readonly TimeSeriesDef[]): readonly string[];
29
+ export interface TimeSeriesModel {
30
+ readonly xs: readonly number[];
31
+ /** RAW values per series (index-aligned with the input), for the readout. */
32
+ readonly raw: readonly (readonly AlignedValue[])[];
33
+ /** What is drawn per series: raw, or cumulative when stacked. */
34
+ readonly drawn: readonly (readonly AlignedValue[])[];
35
+ readonly mins: readonly (readonly AlignedValue[])[];
36
+ readonly maxs: readonly (readonly AlignedValue[])[];
37
+ readonly units: readonly string[];
38
+ readonly gaps: readonly GapSpan[];
39
+ /** No series has a single known value. */
40
+ readonly empty: boolean;
41
+ }
42
+ /** Align, stack (non-compare series only) and find the shared holes. */
43
+ export declare function buildTimeSeriesModel(series: readonly TimeSeriesDef[], stacked: boolean): TimeSeriesModel;
44
+ /** The value per series a reader sees at `idx` (`null` = cursor off → the latest). */
45
+ export declare function readoutAt(model: TimeSeriesModel, idx: number | null): readonly (number | null)[];
46
+ /** The last known value of each series (for the accessible summary). */
47
+ export declare function latestValues(model: TimeSeriesModel): readonly (number | null)[];
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The one place the chart family touches uPlot.
3
+ *
4
+ * - uPlot is imported LAZILY: its module reads `matchMedia` at load time, and
5
+ * a document without a 2D canvas (a test DOM, a print preview) has nothing
6
+ * for it to draw on. There the chart's DOM half — title, legend, readout and
7
+ * the accessible summary — still renders; only the plot is absent.
8
+ * - Width follows the container (`ResizeObserver`); the height is the caller's.
9
+ * - A theme switch (`.dark` / `.light` on `<html>`) only REDRAWS: every colour
10
+ * is a function that resolves its token at draw time (`chart-palette.ts`).
11
+ * - `optionsKey` decides when the plot is rebuilt (series set, axes); new
12
+ * `data` under the same key is a cheap `setData`.
13
+ */
14
+ import { type JSX } from "react";
15
+ import type uPlot from "uplot";
16
+ export type UPlotCtor = typeof uPlot;
17
+ /** Options without the size: the host owns width (the container) and height (the prop). */
18
+ export type ChartPlotOptions = Omit<uPlot.Options, "width" | "height">;
19
+ /** A 2D canvas this document can draw on — false in a test DOM. */
20
+ export declare function canDrawCanvas(): boolean;
21
+ export interface UPlotHostProps {
22
+ /** Rebuild the plot when this changes (series set, axes, scales). */
23
+ readonly optionsKey: string;
24
+ /** Build the options for a width; called on (re)build only. `lib` carries uPlot's path builders. */
25
+ readonly buildOptions: (width: number, lib: UPlotCtor) => ChartPlotOptions;
26
+ readonly data: uPlot.AlignedData;
27
+ readonly height: number;
28
+ /** Called with the live instance after each (re)build, and `null` when it goes. */
29
+ readonly onInstance?: (u: uPlot | null) => void;
30
+ readonly className?: string;
31
+ /** Render a `<span class="block">` host, for a plot inside phrasing content. */
32
+ readonly inline?: boolean;
33
+ }
34
+ export declare function UPlotHost({ optionsKey, buildOptions, data, height, onInstance, className, inline }: UPlotHostProps): JSX.Element;