@camstack/ui-library 1.2.382 → 1.2.384
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.
- package/CHANGELOG.md +12 -0
- package/dist/composites/DeviceRunsPanel.d.ts +23 -0
- package/dist/composites/charts/TimeSeriesChart.d.ts +5 -1
- package/dist/composites/charts/chart-bands.d.ts +22 -0
- package/dist/composites/charts/chart-palette.d.ts +1 -1
- package/dist/composites/charts/chart-tooltip-model.d.ts +2 -0
- package/dist/composites/charts/chart-uplot.d.ts +3 -0
- package/dist/composites/charts/index.d.ts +1 -1
- package/dist/composites/device-export-panel.d.ts +17 -1
- package/dist/composites/device-history/HistoryChart.d.ts +5 -2
- package/dist/composites/device-history/history-runs.d.ts +29 -0
- package/dist/composites/device-history/index.d.ts +2 -0
- package/dist/composites/device-history/use-device-runs.d.ts +11 -0
- package/dist/composites/index.d.ts +3 -2
- package/dist/index.cjs +346 -83
- package/dist/index.js +340 -84
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24107,6 +24107,22 @@ function formatCursorTime(t, spanMs) {
|
|
|
24107
24107
|
return `${d.toLocaleDateString(void 0, DATE_DM)} ${d.toLocaleTimeString(void 0, TIME_HM)}`;
|
|
24108
24108
|
}
|
|
24109
24109
|
//#endregion
|
|
24110
|
+
//#region src/composites/charts/chart-bands.ts
|
|
24111
|
+
var TONE_TOKEN = {
|
|
24112
|
+
neutral: "foreground-subtle",
|
|
24113
|
+
info: "info",
|
|
24114
|
+
success: "success",
|
|
24115
|
+
warning: "warning",
|
|
24116
|
+
danger: "danger"
|
|
24117
|
+
};
|
|
24118
|
+
function bandToken(tone) {
|
|
24119
|
+
return TONE_TOKEN[tone];
|
|
24120
|
+
}
|
|
24121
|
+
/** The bands that contain `x` (edges included), in input order. */
|
|
24122
|
+
function bandsAt(bands, x) {
|
|
24123
|
+
return bands.filter((b) => x >= b.from && x <= b.to);
|
|
24124
|
+
}
|
|
24125
|
+
//#endregion
|
|
24110
24126
|
//#region src/composites/charts/chart-scale.ts
|
|
24111
24127
|
var LINEAR_MAX_MANTISSAS = [
|
|
24112
24128
|
1,
|
|
@@ -24338,6 +24354,23 @@ function gapShadeHook(spans) {
|
|
|
24338
24354
|
ctx.restore();
|
|
24339
24355
|
};
|
|
24340
24356
|
}
|
|
24357
|
+
/** Paint the bands behind the lines, each in its tone. */
|
|
24358
|
+
function bandShadeHook(bands) {
|
|
24359
|
+
return (u) => {
|
|
24360
|
+
const list = bands();
|
|
24361
|
+
if (list.length === 0) return;
|
|
24362
|
+
const ctx = u.ctx;
|
|
24363
|
+
ctx.save();
|
|
24364
|
+
for (const b of list) {
|
|
24365
|
+
const x0 = u.valToPos(b.from, "x", true);
|
|
24366
|
+
const x1 = u.valToPos(b.to, "x", true);
|
|
24367
|
+
ctx.fillStyle = withAlpha(resolveChartColor(bandToken(b.tone)), BAND_SHADE_ALPHA);
|
|
24368
|
+
ctx.fillRect(x0, u.bbox.top, Math.max(1, x1 - x0), u.bbox.height);
|
|
24369
|
+
}
|
|
24370
|
+
ctx.restore();
|
|
24371
|
+
};
|
|
24372
|
+
}
|
|
24373
|
+
var BAND_SHADE_ALPHA = .16;
|
|
24341
24374
|
var BYTE_UNITS = new Set([
|
|
24342
24375
|
"B",
|
|
24343
24376
|
"bytes",
|
|
@@ -24692,7 +24725,7 @@ function lineTooltipBody(input) {
|
|
|
24692
24725
|
preference: input.preference
|
|
24693
24726
|
})
|
|
24694
24727
|
}));
|
|
24695
|
-
return asBody(input.timeLabel, lines);
|
|
24728
|
+
return asBody(input.timeLabel, [...lines, ...input.extra ?? []]);
|
|
24696
24729
|
}
|
|
24697
24730
|
/** The state in force at `t`, and how long that span lasts. */
|
|
24698
24731
|
function stateSpanAt(segments, t) {
|
|
@@ -42821,6 +42854,176 @@ function CopyButton({ value, label, srLabel, className, disabled }) {
|
|
|
42821
42854
|
});
|
|
42822
42855
|
}
|
|
42823
42856
|
//#endregion
|
|
42857
|
+
//#region src/composites/device-history/history-runs.ts
|
|
42858
|
+
/** The context kept on each side of a run when a row opens it. */
|
|
42859
|
+
var RUN_WINDOW_MARGIN_MS = 15 * 6e4;
|
|
42860
|
+
function runStatus(run) {
|
|
42861
|
+
return run.state === "open" ? "running" : run.outcome ?? "unknown";
|
|
42862
|
+
}
|
|
42863
|
+
function runEnd(run, now) {
|
|
42864
|
+
return run.state === "open" || run.endedAt === null ? now : run.endedAt;
|
|
42865
|
+
}
|
|
42866
|
+
/** Elapsed time of a run; an open one counts up to `now`. `null` when a closed run has no duration. */
|
|
42867
|
+
function runDurationMs(run, now) {
|
|
42868
|
+
if (run.state === "open") return Math.max(0, now - run.startedAt);
|
|
42869
|
+
if (run.durationMs !== null) return run.durationMs;
|
|
42870
|
+
return run.endedAt === null ? null : Math.max(0, run.endedAt - run.startedAt);
|
|
42871
|
+
}
|
|
42872
|
+
function formatKwh(kwh) {
|
|
42873
|
+
return kwh === null ? "—" : `${kwh.toFixed(2)} kWh`;
|
|
42874
|
+
}
|
|
42875
|
+
function formatWatts(w) {
|
|
42876
|
+
return w === null ? "—" : `${String(Math.round(w))} W`;
|
|
42877
|
+
}
|
|
42878
|
+
function formatRunDuration(ms) {
|
|
42879
|
+
return ms === null ? "—" : formatDurationMs(ms);
|
|
42880
|
+
}
|
|
42881
|
+
var STATUS_TONE = {
|
|
42882
|
+
running: "info",
|
|
42883
|
+
completed: "success",
|
|
42884
|
+
aborted: "warning",
|
|
42885
|
+
error: "danger",
|
|
42886
|
+
unknown: "neutral"
|
|
42887
|
+
};
|
|
42888
|
+
function runTone(status) {
|
|
42889
|
+
return STATUS_TONE[status];
|
|
42890
|
+
}
|
|
42891
|
+
/** The window that frames a run: the run plus {@link RUN_WINDOW_MARGIN_MS} each side. */
|
|
42892
|
+
function runWindow(run, now) {
|
|
42893
|
+
return {
|
|
42894
|
+
from: run.startedAt - RUN_WINDOW_MARGIN_MS,
|
|
42895
|
+
to: runEnd(run, now) + RUN_WINDOW_MARGIN_MS
|
|
42896
|
+
};
|
|
42897
|
+
}
|
|
42898
|
+
/** One band per run that overlaps `window`; the label states the duration and the energy. */
|
|
42899
|
+
function runBands(runs, now, window, t) {
|
|
42900
|
+
const title = t("uiLibrary.history.runs.band", "Run");
|
|
42901
|
+
return runs.filter((r) => runEnd(r, now) > window.from && r.startedAt < window.to).map((r) => {
|
|
42902
|
+
const status = runStatus(r);
|
|
42903
|
+
const parts = [formatRunDuration(runDurationMs(r, now)), formatKwh(r.detail.energyKwh)];
|
|
42904
|
+
return {
|
|
42905
|
+
id: r.runId,
|
|
42906
|
+
from: r.startedAt,
|
|
42907
|
+
to: runEnd(r, now),
|
|
42908
|
+
title,
|
|
42909
|
+
label: status === "running" ? t("uiLibrary.history.runs.bandRunning", "running · {{parts}}", { parts: parts.join(" · ") }) : parts.join(" · "),
|
|
42910
|
+
tone: runTone(status)
|
|
42911
|
+
};
|
|
42912
|
+
});
|
|
42913
|
+
}
|
|
42914
|
+
//#endregion
|
|
42915
|
+
//#region src/composites/DeviceRunsPanel.tsx
|
|
42916
|
+
/**
|
|
42917
|
+
* DeviceRunsPanel — the runs of an appliance, newest first, on the device's
|
|
42918
|
+
* History tab: start, end, duration, energy, peak and mean power, outcome.
|
|
42919
|
+
* A row opens the run's window (the run plus a margin) in the history view.
|
|
42920
|
+
*
|
|
42921
|
+
* Presentational: the caller reads the runs (`useDeviceRuns`) and decides
|
|
42922
|
+
* whether the device has any — the panel never answers "no runs" itself, an
|
|
42923
|
+
* unanswered query is not an empty one (D315). A measurement that failed is
|
|
42924
|
+
* `—`, never `0` (D393).
|
|
42925
|
+
*/
|
|
42926
|
+
function statusLabel(status, t) {
|
|
42927
|
+
switch (status) {
|
|
42928
|
+
case "running": return t("uiLibrary.history.runs.status.running", "Running");
|
|
42929
|
+
case "completed": return t("uiLibrary.history.runs.status.completed", "Completed");
|
|
42930
|
+
case "aborted": return t("uiLibrary.history.runs.status.aborted", "Aborted");
|
|
42931
|
+
case "error": return t("uiLibrary.history.runs.status.error", "Error");
|
|
42932
|
+
case "unknown": return t("uiLibrary.history.runs.status.unknown", "Unknown");
|
|
42933
|
+
}
|
|
42934
|
+
}
|
|
42935
|
+
var formatStamp = (ms) => new Date(ms).toLocaleString(void 0, {
|
|
42936
|
+
dateStyle: "short",
|
|
42937
|
+
timeStyle: "short"
|
|
42938
|
+
});
|
|
42939
|
+
function runColumns(now, t) {
|
|
42940
|
+
return [
|
|
42941
|
+
{
|
|
42942
|
+
key: "start",
|
|
42943
|
+
header: t("uiLibrary.history.runs.start", "Start"),
|
|
42944
|
+
render: (r) => formatStamp(r.startedAt),
|
|
42945
|
+
sortValue: (r) => r.startedAt
|
|
42946
|
+
},
|
|
42947
|
+
{
|
|
42948
|
+
key: "end",
|
|
42949
|
+
header: t("uiLibrary.history.runs.end", "End"),
|
|
42950
|
+
render: (r) => r.state === "open" || r.endedAt === null ? t("uiLibrary.history.runs.runningSince", "Running since {{time}}", { time: formatStamp(r.startedAt) }) : formatStamp(r.endedAt),
|
|
42951
|
+
sortValue: (r) => r.endedAt
|
|
42952
|
+
},
|
|
42953
|
+
{
|
|
42954
|
+
key: "duration",
|
|
42955
|
+
header: t("uiLibrary.history.runs.duration", "Duration"),
|
|
42956
|
+
align: "right",
|
|
42957
|
+
render: (r) => formatRunDuration(runDurationMs(r, now)),
|
|
42958
|
+
sortValue: (r) => runDurationMs(r, now)
|
|
42959
|
+
},
|
|
42960
|
+
{
|
|
42961
|
+
key: "energy",
|
|
42962
|
+
header: t("uiLibrary.history.runs.energy", "Energy"),
|
|
42963
|
+
align: "right",
|
|
42964
|
+
render: (r) => formatKwh(r.detail.energyKwh),
|
|
42965
|
+
sortValue: (r) => r.detail.energyKwh
|
|
42966
|
+
},
|
|
42967
|
+
{
|
|
42968
|
+
key: "peak",
|
|
42969
|
+
header: t("uiLibrary.history.runs.peak", "Peak"),
|
|
42970
|
+
align: "right",
|
|
42971
|
+
render: (r) => formatWatts(r.detail.peakW),
|
|
42972
|
+
sortValue: (r) => r.detail.peakW
|
|
42973
|
+
},
|
|
42974
|
+
{
|
|
42975
|
+
key: "avg",
|
|
42976
|
+
header: t("uiLibrary.history.runs.avg", "Average"),
|
|
42977
|
+
align: "right",
|
|
42978
|
+
render: (r) => formatWatts(r.detail.avgW),
|
|
42979
|
+
sortValue: (r) => r.detail.avgW
|
|
42980
|
+
},
|
|
42981
|
+
{
|
|
42982
|
+
key: "outcome",
|
|
42983
|
+
header: t("uiLibrary.history.runs.outcome", "Outcome"),
|
|
42984
|
+
render: (r) => {
|
|
42985
|
+
const status = runStatus(r);
|
|
42986
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
42987
|
+
variant: runTone(status),
|
|
42988
|
+
children: statusLabel(status, t)
|
|
42989
|
+
});
|
|
42990
|
+
},
|
|
42991
|
+
notSortableReason: t("uiLibrary.history.runs.outcomeNoSort", "Outcomes have no order")
|
|
42992
|
+
}
|
|
42993
|
+
];
|
|
42994
|
+
}
|
|
42995
|
+
function DeviceRunsPanel({ runs, now, onSelectWindow, className }) {
|
|
42996
|
+
const t = useUiText();
|
|
42997
|
+
const [expanded, setExpanded] = (0, react$1.useState)(true);
|
|
42998
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleCard, {
|
|
42999
|
+
className,
|
|
43000
|
+
expanded,
|
|
43001
|
+
onExpandedChange: setExpanded,
|
|
43002
|
+
header: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
43003
|
+
className: "flex items-center gap-2 min-w-0",
|
|
43004
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
43005
|
+
className: "text-xs font-semibold text-foreground truncate",
|
|
43006
|
+
children: t("uiLibrary.history.runs.title", "Runs")
|
|
43007
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
43008
|
+
className: "text-[10px] text-foreground-subtle shrink-0",
|
|
43009
|
+
children: t("uiLibrary.history.runs.count", {
|
|
43010
|
+
one: "{{count}} run",
|
|
43011
|
+
other: "{{count}} runs"
|
|
43012
|
+
}, { count: runs.length })
|
|
43013
|
+
})]
|
|
43014
|
+
}),
|
|
43015
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTable, {
|
|
43016
|
+
columns: runColumns(now, t),
|
|
43017
|
+
rows: runs,
|
|
43018
|
+
rowKey: (r) => r.runId,
|
|
43019
|
+
onRowClick: (r) => onSelectWindow(runWindow(r, now)),
|
|
43020
|
+
maxBodyHeight: TABLE_SCROLL_MAX_H,
|
|
43021
|
+
bordered: false,
|
|
43022
|
+
minWidthPx: 640
|
|
43023
|
+
})
|
|
43024
|
+
});
|
|
43025
|
+
}
|
|
43026
|
+
//#endregion
|
|
42824
43027
|
//#region src/composites/device-list/fuzzy-match.ts
|
|
42825
43028
|
/**
|
|
42826
43029
|
* fuzzy-match — case and separator-insensitive text search helper.
|
|
@@ -63510,10 +63713,10 @@ function InstanceSetup({ addonId, instance, named }) {
|
|
|
63510
63713
|
* the instances are unanswered or when none reports an error or a setup. Shares
|
|
63511
63714
|
* `getStatus`'s query key with the panel, so mounting both costs one request.
|
|
63512
63715
|
*/
|
|
63513
|
-
function DeviceExportSetup({ addonId }) {
|
|
63716
|
+
function DeviceExportSetup({ addonId, instanceId }) {
|
|
63514
63717
|
const state = useExportInstances(addonId);
|
|
63515
63718
|
if (state.kind !== "answered") return null;
|
|
63516
|
-
const enabled = state.instances.filter((i) => i.enabled);
|
|
63719
|
+
const enabled = state.instances.filter((i) => i.enabled && (instanceId === void 0 || i.instanceId === instanceId));
|
|
63517
63720
|
if (enabled.length === 0) return null;
|
|
63518
63721
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: enabled.map((instance) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstanceSetup, {
|
|
63519
63722
|
addonId,
|
|
@@ -63616,7 +63819,7 @@ function BulkSummary({ outcome, onDismiss }) {
|
|
|
63616
63819
|
]
|
|
63617
63820
|
});
|
|
63618
63821
|
}
|
|
63619
|
-
function DeviceExportInstancePanel({ addonId, instance, named, onOpenDevice, showSetup = true }) {
|
|
63822
|
+
function DeviceExportInstancePanel({ addonId, instance, named = false, onOpenDevice, showSetup = true }) {
|
|
63620
63823
|
const instanceId = instance.instanceId;
|
|
63621
63824
|
const t = useUiText();
|
|
63622
63825
|
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
@@ -71771,6 +71974,7 @@ function latestValues(model) {
|
|
|
71771
71974
|
* for the cross-device History page, where the reader chose the series.
|
|
71772
71975
|
*/
|
|
71773
71976
|
var DEFAULT_HEIGHT = 120;
|
|
71977
|
+
var NO_BANDS = [];
|
|
71774
71978
|
function seriesSpan(range, xs) {
|
|
71775
71979
|
if (range !== void 0) return range.to - range.from;
|
|
71776
71980
|
if (xs.length < 2) return 0;
|
|
@@ -71797,18 +72001,25 @@ function seriesTooltip(input) {
|
|
|
71797
72001
|
value: values[i] ?? null,
|
|
71798
72002
|
quantity: sample.quantity,
|
|
71799
72003
|
unit: sample.unit ?? null
|
|
72004
|
+
})),
|
|
72005
|
+
extra: bandsAt(input.bands, at).map((b) => ({
|
|
72006
|
+
id: `band:${b.id}`,
|
|
72007
|
+
label: b.title,
|
|
72008
|
+
value: b.label
|
|
71800
72009
|
}))
|
|
71801
72010
|
});
|
|
71802
72011
|
},
|
|
71803
72012
|
onDataX: input.onDataX
|
|
71804
72013
|
};
|
|
71805
72014
|
}
|
|
71806
|
-
function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacked = false, area = false, syncKey, formatValue, displayPreference, axisFor, emptyMessage, pending = false, actions, footer, windowToggle, className }) {
|
|
72015
|
+
function TimeSeriesChart({ title, series, bands = NO_BANDS, range, height = DEFAULT_HEIGHT, stacked = false, area = false, syncKey, formatValue, displayPreference, axisFor, emptyMessage, pending = false, actions, footer, windowToggle, className }) {
|
|
71807
72016
|
const t = useUiText();
|
|
71808
72017
|
const model = (0, react$1.useMemo)(() => buildTimeSeriesModel(series, stacked), [series, stacked]);
|
|
71809
72018
|
const [cursorIdx, setCursorIdx] = (0, react$1.useState)(null);
|
|
71810
72019
|
const modelRef = (0, react$1.useRef)(model);
|
|
71811
72020
|
modelRef.current = model;
|
|
72021
|
+
const bandsRef = (0, react$1.useRef)(bands);
|
|
72022
|
+
bandsRef.current = bands;
|
|
71812
72023
|
const rangeRef = (0, react$1.useRef)(range);
|
|
71813
72024
|
rangeRef.current = range;
|
|
71814
72025
|
const fmt = (0, react$1.useCallback)((v, unit) => (formatValue ?? formatChartValue)(v, unit), [formatValue]);
|
|
@@ -71937,7 +72148,7 @@ function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacke
|
|
|
71937
72148
|
bands,
|
|
71938
72149
|
cursor: chartCursor(syncKey),
|
|
71939
72150
|
hooks: {
|
|
71940
|
-
drawClear: [gapShadeHook(() => modelRef.current.gaps)],
|
|
72151
|
+
drawClear: [bandShadeHook(() => bandsRef.current), gapShadeHook(() => modelRef.current.gaps)],
|
|
71941
72152
|
setCursor: [(u) => setCursorIdx(u.cursor.idx ?? null)]
|
|
71942
72153
|
}
|
|
71943
72154
|
};
|
|
@@ -71949,6 +72160,7 @@ function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacke
|
|
|
71949
72160
|
const tooltip = (0, react$1.useMemo)(() => seriesTooltip({
|
|
71950
72161
|
model,
|
|
71951
72162
|
series,
|
|
72163
|
+
bands,
|
|
71952
72164
|
range,
|
|
71953
72165
|
preference: displayPreference,
|
|
71954
72166
|
onDataX: (dataX) => {
|
|
@@ -71957,6 +72169,7 @@ function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacke
|
|
|
71957
72169
|
}), [
|
|
71958
72170
|
model,
|
|
71959
72171
|
series,
|
|
72172
|
+
bands,
|
|
71960
72173
|
range,
|
|
71961
72174
|
displayPreference
|
|
71962
72175
|
]);
|
|
@@ -72744,7 +72957,7 @@ function booleanLabels(key, t) {
|
|
|
72744
72957
|
function failure(entry) {
|
|
72745
72958
|
return entry !== void 0 && !entry.ok ? entry.error : null;
|
|
72746
72959
|
}
|
|
72747
|
-
function HistoryChart({ spec, entryFor, reads, window, compare, syncKey, actions }) {
|
|
72960
|
+
function HistoryChart({ spec, entryFor, reads, window, compare, syncKey, actions, runs, now }) {
|
|
72748
72961
|
const t = useUiText();
|
|
72749
72962
|
const pending = reads === null;
|
|
72750
72963
|
if (spec.type === "states") {
|
|
@@ -72819,13 +73032,15 @@ function HistoryChart({ spec, entryFor, reads, window, compare, syncKey, actions
|
|
|
72819
73032
|
compare,
|
|
72820
73033
|
syncKey,
|
|
72821
73034
|
actions,
|
|
73035
|
+
runs: spec.series.some((s) => s.key === "cap:power-meter.power") ? runs : void 0,
|
|
73036
|
+
now,
|
|
72822
73037
|
labelOf: (s) => spec.series.length > 1 || s.scope !== "" ? seriesLabel(s) : spec.title
|
|
72823
73038
|
});
|
|
72824
73039
|
}
|
|
72825
73040
|
function seriesLabel(s) {
|
|
72826
73041
|
return s.scope === "" ? s.displayName : `${s.displayName} · ${s.scope}`;
|
|
72827
73042
|
}
|
|
72828
|
-
function LinesChart({ title, series, entryFor, reads, window, compare, syncKey, actions, labelOf, toolbar, colorOf }) {
|
|
73043
|
+
function LinesChart({ title, series, entryFor, reads, window, compare, syncKey, actions, labelOf, toolbar, colorOf, runs, now }) {
|
|
72829
73044
|
const t = useUiText();
|
|
72830
73045
|
const errors = [...new Set(series.map((s) => failure(entryFor(s.key, false))).filter((e) => e !== null))];
|
|
72831
73046
|
const defs = (0, react$1.useMemo)(() => {
|
|
@@ -72875,6 +73090,12 @@ function LinesChart({ title, series, entryFor, reads, window, compare, syncKey,
|
|
|
72875
73090
|
colorOf,
|
|
72876
73091
|
t
|
|
72877
73092
|
]);
|
|
73093
|
+
const bands = (0, react$1.useMemo)(() => runs === void 0 ? [] : runBands(runs, now ?? window.to, window, t), [
|
|
73094
|
+
runs,
|
|
73095
|
+
now,
|
|
73096
|
+
window,
|
|
73097
|
+
t
|
|
73098
|
+
]);
|
|
72878
73099
|
if (errors.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadFailed, {
|
|
72879
73100
|
title,
|
|
72880
73101
|
error: errors.join("; "),
|
|
@@ -72883,6 +73104,7 @@ function LinesChart({ title, series, entryFor, reads, window, compare, syncKey,
|
|
|
72883
73104
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimeSeriesChart, {
|
|
72884
73105
|
title,
|
|
72885
73106
|
series: defs,
|
|
73107
|
+
bands,
|
|
72886
73108
|
range: window,
|
|
72887
73109
|
syncKey,
|
|
72888
73110
|
pending: reads === null,
|
|
@@ -73218,6 +73440,24 @@ function HistoryWindowBar({ value, onChange, custom, onCustomChange, compare, on
|
|
|
73218
73440
|
});
|
|
73219
73441
|
}
|
|
73220
73442
|
//#endregion
|
|
73443
|
+
//#region src/composites/device-history/use-device-runs.ts
|
|
73444
|
+
/** How many runs the panel and the bands are given; the cap's page size. */
|
|
73445
|
+
var DEVICE_RUNS_PAGE = 50;
|
|
73446
|
+
var REFRESH_MS = 6e4;
|
|
73447
|
+
/** The newest runs, or `undefined` while unanswered, absent or empty. */
|
|
73448
|
+
function useDeviceRuns(deviceId) {
|
|
73449
|
+
const q = useDeviceRunsListRuns({
|
|
73450
|
+
deviceId,
|
|
73451
|
+
limit: 50
|
|
73452
|
+
}, {
|
|
73453
|
+
staleTime: REFRESH_MS,
|
|
73454
|
+
refetchInterval: REFRESH_MS,
|
|
73455
|
+
retry: false
|
|
73456
|
+
});
|
|
73457
|
+
if (q.status !== "success" || q.data.runs.length === 0) return void 0;
|
|
73458
|
+
return q.data.runs;
|
|
73459
|
+
}
|
|
73460
|
+
//#endregion
|
|
73221
73461
|
//#region src/composites/device-history/history-reads.ts
|
|
73222
73462
|
/**
|
|
73223
73463
|
* Batched history reads. `queryMany` answers up to 24 reads over one window
|
|
@@ -73502,7 +73742,9 @@ function DeviceHistoryView({ deviceId, fixedWindow, onNavigate }) {
|
|
|
73502
73742
|
const custom = fixedWindow ? null : decodeCustomWindow(customParam);
|
|
73503
73743
|
const compare = fixedWindow === void 0 && compareParam === "1";
|
|
73504
73744
|
const spanGuess = resolveHistoryWindow(windowId, Date.now(), custom);
|
|
73505
|
-
const
|
|
73745
|
+
const now = anchorNow(Date.now(), spanGuess.to - spanGuess.from);
|
|
73746
|
+
const range = resolveHistoryWindow(windowId, now, custom);
|
|
73747
|
+
const runs = useDeviceRuns(deviceId);
|
|
73506
73748
|
const { presence, list } = useDeviceSeriesList(deviceId);
|
|
73507
73749
|
const panels = useHistoryPanels();
|
|
73508
73750
|
const hidden = (0, react$1.useMemo)(() => new Set(panels.value.hidden[String(deviceId)] ?? []), [panels.value.hidden, deviceId]);
|
|
@@ -73570,81 +73812,95 @@ function DeviceHistoryView({ deviceId, fixedWindow, onNavigate }) {
|
|
|
73570
73812
|
description: t("uiLibrary.history.empty", "A value appears here once it changes, or at the next five-minute check.")
|
|
73571
73813
|
});
|
|
73572
73814
|
const syncKey = `device-history:${String(deviceId)}`;
|
|
73815
|
+
const showWindow = (w) => {
|
|
73816
|
+
setWindowParam("custom");
|
|
73817
|
+
setCustomParam(encodeCustomWindow(w));
|
|
73818
|
+
};
|
|
73573
73819
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
73574
73820
|
className: STACK_GAP,
|
|
73575
|
-
children: [
|
|
73576
|
-
|
|
73577
|
-
|
|
73578
|
-
|
|
73579
|
-
|
|
73580
|
-
|
|
73581
|
-
|
|
73582
|
-
|
|
73583
|
-
|
|
73584
|
-
|
|
73585
|
-
|
|
73586
|
-
|
|
73587
|
-
|
|
73588
|
-
|
|
73589
|
-
|
|
73590
|
-
|
|
73591
|
-
|
|
73592
|
-
|
|
73593
|
-
|
|
73594
|
-
|
|
73595
|
-
|
|
73596
|
-
|
|
73597
|
-
|
|
73598
|
-
|
|
73599
|
-
|
|
73600
|
-
|
|
73601
|
-
|
|
73602
|
-
|
|
73603
|
-
|
|
73604
|
-
|
|
73605
|
-
|
|
73606
|
-
|
|
73607
|
-
|
|
73608
|
-
|
|
73609
|
-
|
|
73610
|
-
|
|
73611
|
-
|
|
73612
|
-
|
|
73613
|
-
|
|
73614
|
-
|
|
73615
|
-
|
|
73616
|
-
|
|
73617
|
-
|
|
73618
|
-
|
|
73619
|
-
|
|
73620
|
-
|
|
73621
|
-
|
|
73622
|
-
|
|
73623
|
-
|
|
73624
|
-
|
|
73625
|
-
|
|
73626
|
-
|
|
73627
|
-
|
|
73628
|
-
|
|
73629
|
-
|
|
73630
|
-
|
|
73631
|
-
|
|
73632
|
-
|
|
73633
|
-
|
|
73634
|
-
|
|
73635
|
-
|
|
73636
|
-
|
|
73637
|
-
|
|
73638
|
-
|
|
73639
|
-
|
|
73640
|
-
|
|
73641
|
-
|
|
73642
|
-
|
|
73643
|
-
|
|
73644
|
-
|
|
73645
|
-
|
|
73646
|
-
|
|
73647
|
-
|
|
73821
|
+
children: [
|
|
73822
|
+
fixedWindow === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
73823
|
+
className: "flex flex-wrap items-center justify-between gap-2",
|
|
73824
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(HistoryWindowBar, {
|
|
73825
|
+
value: windowId,
|
|
73826
|
+
onChange: (id) => setWindowParam(id === "24h" ? null : id),
|
|
73827
|
+
custom,
|
|
73828
|
+
onCustomChange: (w) => setCustomParam(encodeCustomWindow(w)),
|
|
73829
|
+
compare,
|
|
73830
|
+
onCompareChange: (on) => setCompareParam(on ? "1" : null)
|
|
73831
|
+
}), hiddenCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Button, {
|
|
73832
|
+
variant: "ghost",
|
|
73833
|
+
size: "sm",
|
|
73834
|
+
onClick: showAllHidden,
|
|
73835
|
+
disabled: !panels.writable,
|
|
73836
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EyeOff, {
|
|
73837
|
+
className: "h-3.5 w-3.5",
|
|
73838
|
+
"aria-hidden": "true"
|
|
73839
|
+
}), t("uiLibrary.history.showHidden", {
|
|
73840
|
+
one: "Show {{count}} hidden chart",
|
|
73841
|
+
other: "Show {{count}} hidden charts"
|
|
73842
|
+
}, { count: hiddenCount })]
|
|
73843
|
+
})]
|
|
73844
|
+
}),
|
|
73845
|
+
fixedWindow === void 0 && runs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceRunsPanel, {
|
|
73846
|
+
runs,
|
|
73847
|
+
now,
|
|
73848
|
+
onSelectWindow: showWindow
|
|
73849
|
+
}),
|
|
73850
|
+
visible.map((section) => {
|
|
73851
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleCard, {
|
|
73852
|
+
expanded: !collapsed.has(section.id),
|
|
73853
|
+
onExpandedChange: (next) => setCollapsed((prev) => {
|
|
73854
|
+
const s = new Set(prev);
|
|
73855
|
+
if (next) s.delete(section.id);
|
|
73856
|
+
else s.add(section.id);
|
|
73857
|
+
return s;
|
|
73858
|
+
}),
|
|
73859
|
+
header: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
73860
|
+
className: "flex items-center gap-2 min-w-0",
|
|
73861
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
73862
|
+
className: "text-xs font-semibold text-foreground truncate",
|
|
73863
|
+
children: humanizeName(section.name)
|
|
73864
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
73865
|
+
className: "text-[10px] text-foreground-subtle shrink-0",
|
|
73866
|
+
children: t("uiLibrary.history.chartCount", {
|
|
73867
|
+
one: "{{count}} chart",
|
|
73868
|
+
other: "{{count}} charts"
|
|
73869
|
+
}, { count: section.charts.length })
|
|
73870
|
+
})]
|
|
73871
|
+
}),
|
|
73872
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
73873
|
+
className: "grid grid-cols-1 gap-4 2xl:grid-cols-2",
|
|
73874
|
+
children: section.charts.map((spec) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HistoryChart, {
|
|
73875
|
+
spec,
|
|
73876
|
+
entryFor,
|
|
73877
|
+
reads: readsData,
|
|
73878
|
+
window: range,
|
|
73879
|
+
compare,
|
|
73880
|
+
syncKey,
|
|
73881
|
+
runs,
|
|
73882
|
+
now,
|
|
73883
|
+
actions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KebabMenu, {
|
|
73884
|
+
title: t("uiLibrary.history.chartMenu", "Chart actions"),
|
|
73885
|
+
items: [{
|
|
73886
|
+
id: "open",
|
|
73887
|
+
label: t("uiLibrary.history.openInHistory", "Open in History"),
|
|
73888
|
+
icon: ChartLine,
|
|
73889
|
+
onClick: () => navigate(historyPageHref(refsOf(deviceId, spec), windowId))
|
|
73890
|
+
}, {
|
|
73891
|
+
id: "hide",
|
|
73892
|
+
label: t("uiLibrary.history.hide", "Hide"),
|
|
73893
|
+
icon: EyeOff,
|
|
73894
|
+
hidden: fixedWindow !== void 0,
|
|
73895
|
+
disabled: !panels.writable,
|
|
73896
|
+
onClick: () => setHidden(spec.id, true)
|
|
73897
|
+
}]
|
|
73898
|
+
})
|
|
73899
|
+
}, spec.id))
|
|
73900
|
+
})
|
|
73901
|
+
}, section.id);
|
|
73902
|
+
})
|
|
73903
|
+
]
|
|
73648
73904
|
});
|
|
73649
73905
|
}
|
|
73650
73906
|
function HistorySkeleton() {
|
|
@@ -73804,7 +74060,7 @@ function TimelineSlider({ markers, bands = [], selectedId = null, step = 5, aria
|
|
|
73804
74060
|
}
|
|
73805
74061
|
//#endregion
|
|
73806
74062
|
//#region src/ui-library-version.ts
|
|
73807
|
-
var uiLibraryVersion = "1.2.
|
|
74063
|
+
var uiLibraryVersion = "1.2.384";
|
|
73808
74064
|
//#endregion
|
|
73809
74065
|
//#region src/hooks/use-account-unit-preferences.ts
|
|
73810
74066
|
/**
|
|
@@ -74909,6 +75165,7 @@ exports.DEVICE_NAME_JOIN = DEVICE_NAME_JOIN;
|
|
|
74909
75165
|
exports.DEVICE_PICKER_GROUP_STORAGE_KEY = DEVICE_PICKER_GROUP_STORAGE_KEY;
|
|
74910
75166
|
exports.DEVICE_ROLE_META = DEVICE_ROLE_META;
|
|
74911
75167
|
exports.DEVICE_ROLE_NOUNS = DEVICE_ROLE_NOUNS;
|
|
75168
|
+
exports.DEVICE_RUNS_PAGE = DEVICE_RUNS_PAGE;
|
|
74912
75169
|
exports.DEVICE_TYPE_CONTROL = DEVICE_TYPE_CONTROL;
|
|
74913
75170
|
exports.DEVICE_TYPE_META = DEVICE_TYPE_META;
|
|
74914
75171
|
exports.DEVICE_TYPE_NOUNS = DEVICE_TYPE_NOUNS;
|
|
@@ -74925,6 +75182,7 @@ exports.DeviceActivityPanel = DeviceActivityPanel;
|
|
|
74925
75182
|
exports.DeviceBatchToolbar = DeviceBatchToolbar;
|
|
74926
75183
|
exports.DeviceCard = DeviceCard;
|
|
74927
75184
|
exports.DeviceContextProvider = DeviceContextProvider;
|
|
75185
|
+
exports.DeviceExportInstancePanel = DeviceExportInstancePanel;
|
|
74928
75186
|
exports.DeviceExportPanel = DeviceExportPanel;
|
|
74929
75187
|
exports.DeviceExportSetup = DeviceExportSetup;
|
|
74930
75188
|
exports.DeviceFilterBar = DeviceFilterBar;
|
|
@@ -74938,6 +75196,7 @@ exports.DeviceMultiSelectField = DeviceMultiSelectField;
|
|
|
74938
75196
|
exports.DeviceName = DeviceName;
|
|
74939
75197
|
exports.DevicePicker = DevicePicker;
|
|
74940
75198
|
exports.DeviceQuickDetailProvider = DeviceQuickDetailProvider;
|
|
75199
|
+
exports.DeviceRunsPanel = DeviceRunsPanel;
|
|
74941
75200
|
exports.DeviceSelectField = DeviceSelectField;
|
|
74942
75201
|
exports.DeviceSelectorPicker = DeviceSelectorPicker;
|
|
74943
75202
|
exports.DeviceStepMatrix = DeviceStepMatrix;
|
|
@@ -75187,6 +75446,7 @@ exports.RIGHT = RIGHT;
|
|
|
75187
75446
|
exports.ROI_AREA_WARN_FRACTION = ROI_AREA_WARN_FRACTION;
|
|
75188
75447
|
exports.ROLE_DESCRIPTOR = ROLE_DESCRIPTOR;
|
|
75189
75448
|
exports.ROOT_DETECTOR_STEP_ID = ROOT_DETECTOR_STEP_ID;
|
|
75449
|
+
exports.RUN_WINDOW_MARGIN_MS = RUN_WINDOW_MARGIN_MS;
|
|
75190
75450
|
exports.Radar = Radar;
|
|
75191
75451
|
exports.RadialGauge = RadialGauge;
|
|
75192
75452
|
exports.Radio = Radio;
|
|
@@ -75684,6 +75944,8 @@ exports.roiTooLarge = roiTooLarge;
|
|
|
75684
75944
|
exports.roiTooLarge$1 = roiTooLarge;
|
|
75685
75945
|
exports.rootSourceState = rootSourceState;
|
|
75686
75946
|
exports.routerBasename = routerBasename;
|
|
75947
|
+
exports.runBands = runBands;
|
|
75948
|
+
exports.runWindow = runWindow;
|
|
75687
75949
|
exports.runningDurationSince = runningDurationSince;
|
|
75688
75950
|
exports.runningStatusParts = runningStatusParts;
|
|
75689
75951
|
exports.sameDeviceName = sameDeviceName;
|
|
@@ -76179,6 +76441,7 @@ exports.useDeviceProviderTestCreationField = useDeviceProviderTestCreationField;
|
|
|
76179
76441
|
exports.useDeviceProxy = useDeviceProxy;
|
|
76180
76442
|
exports.useDeviceProxy$1 = useDeviceProxy;
|
|
76181
76443
|
exports.useDeviceQuickDetailOpen = useDeviceQuickDetailOpen;
|
|
76444
|
+
exports.useDeviceRuns = useDeviceRuns;
|
|
76182
76445
|
exports.useDeviceRunsGetCurrentRun = useDeviceRunsGetCurrentRun;
|
|
76183
76446
|
exports.useDeviceRunsGetRun = useDeviceRunsGetRun;
|
|
76184
76447
|
exports.useDeviceRunsListRuns = useDeviceRunsListRuns;
|