@devicechain/widgets 0.24.0 → 0.25.0-rc.2

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 (67) hide show
  1. package/README.md +11 -1
  2. package/dist/absolute-range-picker.d.ts +22 -0
  3. package/dist/binding-policy.d.ts +1 -0
  4. package/dist/builtin-descriptors.d.ts +69 -0
  5. package/dist/builtin-registry.d.ts +68 -0
  6. package/dist/chunks/absolute-range-picker-E57G2TPX.js +139 -0
  7. package/dist/chunks/absolute-range-picker-E57G2TPX.js.map +7 -0
  8. package/dist/chunks/{aggregate-feed-4FSBQYJL.js → aggregate-feed-XX36GNY6.js} +2 -2
  9. package/dist/chunks/{aggregate-widgets-2ZQBW3YF.js → aggregate-widgets-72HMPMBU.js} +99 -57
  10. package/dist/chunks/aggregate-widgets-72HMPMBU.js.map +7 -0
  11. package/dist/chunks/{chunk-USRYKW2J.js → chunk-7BUCJQFP.js} +10 -2
  12. package/dist/chunks/chunk-7BUCJQFP.js.map +7 -0
  13. package/dist/chunks/chunk-AF4M5OXQ.js +23 -0
  14. package/dist/chunks/chunk-AF4M5OXQ.js.map +7 -0
  15. package/dist/chunks/{chunk-XQQQNTBX.js → chunk-EQOWBDS7.js} +1 -1
  16. package/dist/chunks/{chunk-XQQQNTBX.js.map → chunk-EQOWBDS7.js.map} +1 -1
  17. package/dist/chunks/chunk-JVC6JYUR.js +48 -0
  18. package/dist/chunks/chunk-JVC6JYUR.js.map +7 -0
  19. package/dist/chunks/{chunk-JFH4QMLA.js → chunk-NYHNQ5VR.js} +5 -1
  20. package/dist/chunks/{chunk-JFH4QMLA.js.map → chunk-NYHNQ5VR.js.map} +2 -2
  21. package/dist/chunks/chunk-VTGKDO44.js +87 -0
  22. package/dist/chunks/chunk-VTGKDO44.js.map +7 -0
  23. package/dist/chunks/chunk-XJ7UF2CP.js +90 -0
  24. package/dist/chunks/chunk-XJ7UF2CP.js.map +7 -0
  25. package/dist/chunks/{compare-feed-AY3CVKUZ.js → compare-feed-5OLZHFRL.js} +48 -23
  26. package/dist/chunks/compare-feed-5OLZHFRL.js.map +7 -0
  27. package/dist/chunks/{event-markers-feed-GEGTIHXT.js → event-markers-feed-B3MVFYUT.js} +4 -4
  28. package/dist/chunks/export-rows-OEWIBIUY.js +11 -0
  29. package/dist/chunks/{fleet-feed-2SKDR6ZQ.js → fleet-feed-D4T3LC25.js} +10 -10
  30. package/dist/chunks/history-widgets-NSNXDKEC.js +336 -0
  31. package/dist/chunks/history-widgets-NSNXDKEC.js.map +7 -0
  32. package/dist/chunks/{level-widgets-MJB7TOLU.js → level-widgets-HE6EDSWC.js} +13 -9
  33. package/dist/chunks/level-widgets-HE6EDSWC.js.map +7 -0
  34. package/dist/chunks/no-reading-B4MPXAS2.js +22 -0
  35. package/dist/chunks/no-reading-B4MPXAS2.js.map +7 -0
  36. package/dist/chunks/{presence-widgets-2LIUKN4Y.js → presence-widgets-M5VGG3OO.js} +12 -8
  37. package/dist/chunks/presence-widgets-M5VGG3OO.js.map +7 -0
  38. package/dist/chunks/table-CEVB3JZS.js +8 -0
  39. package/dist/chunks/table-CEVB3JZS.js.map +7 -0
  40. package/dist/compare-model.d.ts +1 -1
  41. package/dist/documented-descriptors.d.ts +206 -0
  42. package/dist/export-rows.d.ts +24 -0
  43. package/dist/history-feed.d.ts +10 -0
  44. package/dist/history-model.d.ts +45 -0
  45. package/dist/history-strings.d.ts +20 -0
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +544 -189
  48. package/dist/index.js.map +4 -4
  49. package/dist/level-reading.d.ts +1 -0
  50. package/dist/no-reading-core.d.ts +25 -0
  51. package/dist/no-reading.d.ts +10 -0
  52. package/dist/time-range-bar.d.ts +26 -2
  53. package/dist/widgets/history-lazy.d.ts +70 -0
  54. package/dist/widgets/history-table.d.ts +7 -0
  55. package/dist/widgets/history-widgets.d.ts +2 -0
  56. package/dist/widgets/no-reading-lazy.d.ts +7 -0
  57. package/package.json +8 -8
  58. package/dist/chunks/aggregate-widgets-2ZQBW3YF.js.map +0 -7
  59. package/dist/chunks/chunk-USRYKW2J.js.map +0 -7
  60. package/dist/chunks/compare-feed-AY3CVKUZ.js.map +0 -7
  61. package/dist/chunks/level-widgets-MJB7TOLU.js.map +0 -7
  62. package/dist/chunks/presence-widgets-2LIUKN4Y.js.map +0 -7
  63. package/dist/chunks/table-6EVV7QZH.js +0 -7
  64. /package/dist/chunks/{aggregate-feed-4FSBQYJL.js.map → aggregate-feed-XX36GNY6.js.map} +0 -0
  65. /package/dist/chunks/{event-markers-feed-GEGTIHXT.js.map → event-markers-feed-B3MVFYUT.js.map} +0 -0
  66. /package/dist/chunks/{table-6EVV7QZH.js.map → export-rows-OEWIBIUY.js.map} +0 -0
  67. /package/dist/chunks/{fleet-feed-2SKDR6ZQ.js.map → fleet-feed-D4T3LC25.js.map} +0 -0
@@ -0,0 +1,87 @@
1
+ import {
2
+ bucketLabel,
3
+ roundTo,
4
+ stamp
5
+ } from "./chunk-XJ7UF2CP.js";
6
+ import {
7
+ cellFor,
8
+ deviceLabel,
9
+ sortMembers
10
+ } from "./chunk-62CHYCHI.js";
11
+
12
+ // src/export-rows.ts
13
+ import { spanLabel } from "@devicechain/dashboards";
14
+ import { viewerLocale } from "@devicechain/widget-sdk";
15
+ var WORDS = {
16
+ en: {
17
+ time: "Time (UTC)",
18
+ measurement: "Measurement",
19
+ series: "Series",
20
+ value: "Value",
21
+ basis: "Basis",
22
+ reading: "reading",
23
+ average: (bucket) => `avg per ${bucket}`,
24
+ device: "Device",
25
+ deviceToken: "Device token",
26
+ readingTime: "reading time (UTC)",
27
+ latest: "latest"
28
+ },
29
+ es: {
30
+ time: "Hora (UTC)",
31
+ measurement: "Medici\xF3n",
32
+ series: "Serie",
33
+ value: "Valor",
34
+ basis: "Base",
35
+ reading: "lectura",
36
+ average: (bucket) => `prom. por ${bucket}`,
37
+ device: "Dispositivo",
38
+ deviceToken: "Token del dispositivo",
39
+ readingTime: "hora de la lectura (UTC)",
40
+ latest: "\xFAltima"
41
+ }
42
+ };
43
+ function words() {
44
+ return viewerLocale()?.toLowerCase().startsWith("es") ? WORDS.es : WORDS.en;
45
+ }
46
+ function isoOrEmpty(value) {
47
+ if (!value) return "";
48
+ const at = Date.parse(value);
49
+ return Number.isNaN(at) ? "" : new Date(at).toISOString();
50
+ }
51
+ function samplesExport(i) {
52
+ const w = words();
53
+ const points = i.samples.filter((s) => s.value !== null && s.occurredTime && !Number.isNaN(Date.parse(s.occurredTime))).map((s) => ({ s, at: Date.parse(s.occurredTime) })).sort((a, b) => a.at - b.at || (a.s.name < b.s.name ? -1 : a.s.name > b.s.name ? 1 : 0));
54
+ const basis = (s) => s.bucketSeconds !== void 0 ? w.average(bucketLabel(s.bucketSeconds)) : w.reading;
55
+ const kinds = [...new Set(points.map(({ s }) => basis(s)))];
56
+ const first = points[0]?.s.occurredTime;
57
+ const last = points[points.length - 1]?.s.occurredTime;
58
+ const parts = [i.subject, i.spanSeconds !== void 0 ? spanLabel(i.spanSeconds) : void 0, first ? stamp(first) : void 0, last ? stamp(last) : void 0, ...kinds].filter(Boolean);
59
+ return {
60
+ name: parts.join("-"),
61
+ columns: [w.time, i.seriesIs === "device" ? w.series : w.measurement, `${w.value}${i.unit ? ` (${i.unit})` : ""}`, w.basis],
62
+ rows: points.map(({ s, at }) => [new Date(at).toISOString(), s.name, roundTo(s.value, i.precision), basis(s)])
63
+ };
64
+ }
65
+ function fleetExport(i) {
66
+ const w = words();
67
+ const rows = sortMembers(i.members, i.sort).map((m) => [
68
+ deviceLabel(m),
69
+ m.token,
70
+ ...i.columns.flatMap((c) => {
71
+ const cell = cellFor(m, c.name);
72
+ return cell.kind === "value" ? [roundTo(cell.value, c.precision), isoOrEmpty(cell.sample.occurredTime)] : [null, null];
73
+ })
74
+ ]);
75
+ const partial = i.members.length < i.total ? `first-${i.members.length}-of-${i.total}` : void 0;
76
+ return {
77
+ name: [i.subject, w.latest, partial, stamp((/* @__PURE__ */ new Date()).toISOString())].filter(Boolean).join("-"),
78
+ columns: [w.device, w.deviceToken, ...i.columns.flatMap((c) => [`${c.heading} (${w.latest})`, `${c.heading} ${w.readingTime}`])],
79
+ rows
80
+ };
81
+ }
82
+
83
+ export {
84
+ samplesExport,
85
+ fleetExport
86
+ };
87
+ //# sourceMappingURL=chunk-VTGKDO44.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/export-rows.ts"],
4
+ "sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The tables the chart and the fleet table hand their frame to export (the frame's \"Download CSV\"):\n// built from the state the widget is rendering, at the moment the viewer asks. The history table's own\n// is in history-model.ts. Loaded on demand (the chart reaches this through a dynamic import), so none\n// of it is part of a board's first download.\n//\n// \uD83D\uDD34 AN EXPORT IS WHAT THE WIDGET SHOWS. It holds the samples on screen, not a new question to the\n// store, and its file name says which span they cover and what they are: \"avg per 1m\" for a point that\n// is a stored bucket's average, \"readings\" for one that arrived live. The two are told apart by the\n// sample itself (`bucketSeconds`), never guessed.\n\nimport { spanLabel, type FleetMember, type MeasurementSample } from '@devicechain/dashboards';\nimport { viewerLocale, type WidgetExportCell, type WidgetExportTable } from '@devicechain/widget-sdk';\n\nimport { cellFor, deviceLabel, sortMembers, type SortKey } from './fleet-model';\nimport { bucketLabel, roundTo, stamp } from './history-model';\n\ninterface ExportWords {\n time: string;\n measurement: string;\n series: string;\n value: string;\n basis: string;\n reading: string;\n average: (bucket: string) => string;\n device: string;\n deviceToken: string;\n readingTime: string;\n latest: string;\n}\n\nconst WORDS: Record<'en' | 'es', ExportWords> = {\n en: {\n time: 'Time (UTC)',\n measurement: 'Measurement',\n series: 'Series',\n value: 'Value',\n basis: 'Basis',\n reading: 'reading',\n average: (bucket) => `avg per ${bucket}`,\n device: 'Device',\n deviceToken: 'Device token',\n readingTime: 'reading time (UTC)',\n latest: 'latest',\n },\n es: {\n time: 'Hora (UTC)',\n measurement: 'Medici\u00F3n',\n series: 'Serie',\n value: 'Valor',\n basis: 'Base',\n reading: 'lectura',\n average: (bucket) => `prom. por ${bucket}`,\n device: 'Dispositivo',\n deviceToken: 'Token del dispositivo',\n readingTime: 'hora de la lectura (UTC)',\n latest: '\u00FAltima',\n },\n};\n\nfunction words(): ExportWords {\n return viewerLocale()?.toLowerCase().startsWith('es') ? WORDS.es : WORDS.en;\n}\n\nfunction isoOrEmpty(value: string | null): string {\n if (!value) return '';\n const at = Date.parse(value);\n return Number.isNaN(at) ? '' : new Date(at).toISOString();\n}\n\nexport interface SamplesExportInputs {\n // What the file is called after: the widget's title, else its device.\n subject: string;\n samples: readonly MeasurementSample[];\n unit?: string;\n precision?: number;\n // The first heading of the series column: the measurement (one device) or the device (a compare).\n seriesIs: 'measurement' | 'device';\n // The span the widget draws, when it knows one (the board's range or its own pin).\n spanSeconds?: number;\n}\n\n// The samples a chart draws, long: one row per point, oldest first, `Time, Measurement|Series, Value\n// (unit), Basis`. Long rather than wide because a chart's measurements do not share instants.\nexport function samplesExport(i: SamplesExportInputs): WidgetExportTable {\n const w = words();\n const points = i.samples\n .filter((s) => s.value !== null && s.occurredTime && !Number.isNaN(Date.parse(s.occurredTime)))\n .map((s) => ({ s, at: Date.parse(s.occurredTime as string) }))\n .sort((a, b) => a.at - b.at || (a.s.name < b.s.name ? -1 : a.s.name > b.s.name ? 1 : 0));\n const basis = (s: MeasurementSample): string => (s.bucketSeconds !== undefined ? w.average(bucketLabel(s.bucketSeconds)) : w.reading);\n // The file says what it holds: the buckets' widths and whether live readings are in it.\n const kinds = [...new Set(points.map(({ s }) => basis(s)))];\n const first = points[0]?.s.occurredTime;\n const last = points[points.length - 1]?.s.occurredTime;\n const parts = [i.subject, i.spanSeconds !== undefined ? spanLabel(i.spanSeconds) : undefined, first ? stamp(first) : undefined, last ? stamp(last) : undefined, ...kinds].filter(Boolean);\n return {\n name: parts.join('-'),\n columns: [w.time, i.seriesIs === 'device' ? w.series : w.measurement, `${w.value}${i.unit ? ` (${i.unit})` : ''}`, w.basis],\n rows: points.map(({ s, at }): WidgetExportCell[] => [new Date(at).toISOString(), s.name, roundTo(s.value as number, i.precision), basis(s)]),\n };\n}\n\nexport interface FleetExportInputs {\n subject: string;\n members: readonly FleetMember[];\n total: number;\n // The measurement columns as the table shows them, in order, with their formats.\n columns: ReadonlyArray<{ name: string; heading: string; precision?: number }>;\n sort: SortKey;\n}\n\n// The fleet table, wide: a row per device in the order on screen (all of them, not the page), the\n// device's name and token, then each measurement's latest reading and the instant it was taken. A\n// cell with no reading is empty; the table says why on screen, and an empty cell is never a zero.\nexport function fleetExport(i: FleetExportInputs): WidgetExportTable {\n const w = words();\n const rows = sortMembers(i.members, i.sort).map((m): WidgetExportCell[] => [\n deviceLabel(m),\n m.token,\n ...i.columns.flatMap((c) => {\n const cell = cellFor(m, c.name);\n return cell.kind === 'value' ? [roundTo(cell.value, c.precision), isoOrEmpty(cell.sample.occurredTime)] : [null, null];\n }),\n ]);\n // A fleet past what the resolver read says so in the name, as the table does on screen.\n const partial = i.members.length < i.total ? `first-${i.members.length}-of-${i.total}` : undefined;\n return {\n name: [i.subject, w.latest, partial, stamp(new Date().toISOString())].filter(Boolean).join('-'),\n columns: [w.device, w.deviceToken, ...i.columns.flatMap((c) => [`${c.heading} (${w.latest})`, `${c.heading} ${w.readingTime}`])],\n rows,\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,90 @@
1
+ import {
2
+ seedWindow
3
+ } from "./chunk-62CHYCHI.js";
4
+
5
+ // src/history-model.ts
6
+ import { bucketSecondsFor, spanLabel } from "@devicechain/dashboards";
7
+ var DEFAULT_PAGE_SIZE = 10;
8
+ function historyWindow(spanSeconds, at) {
9
+ return seedWindow(spanSeconds, at);
10
+ }
11
+ function historyRefreshMs(spanSeconds) {
12
+ return bucketSecondsFor(spanSeconds) * 1e3;
13
+ }
14
+ function bucketLabel(bucketSeconds) {
15
+ return spanLabel(bucketSeconds);
16
+ }
17
+ function historyColumns(authored, datasource, observed) {
18
+ if (authored.length > 0) return [...authored];
19
+ const names = datasource.length > 0 ? datasource : [...new Set(observed)].sort();
20
+ return names.map((name) => ({ name }));
21
+ }
22
+ function historyRows(buckets, columns, oldestFirst) {
23
+ const shown = new Set(columns);
24
+ const byStart = /* @__PURE__ */ new Map();
25
+ for (const b of buckets) {
26
+ if (!shown.has(b.name) || b.avg == null || !Number.isFinite(b.avg)) continue;
27
+ let row = byStart.get(b.bucketStart);
28
+ if (!row) byStart.set(b.bucketStart, row = /* @__PURE__ */ new Map());
29
+ row.set(b.name, b.avg);
30
+ }
31
+ const rows = [];
32
+ for (const [bucketStart, averages] of byStart) {
33
+ const at = Date.parse(bucketStart);
34
+ if (Number.isNaN(at)) continue;
35
+ rows.push({ bucketStart, at, averages });
36
+ }
37
+ rows.sort((a, b) => oldestFirst ? a.at - b.at : b.at - a.at);
38
+ return rows;
39
+ }
40
+ function observedNames(buckets) {
41
+ return [...new Set(buckets.filter((b) => b.avg != null).map((b) => b.name))];
42
+ }
43
+ function columnHeading(c) {
44
+ return `${c.label ?? c.name}${c.unit ? ` (${c.unit})` : ""}`;
45
+ }
46
+ function roundTo(value, precision) {
47
+ if (precision === void 0) return value;
48
+ const places = Math.min(100, Math.max(0, Math.trunc(precision)));
49
+ return Number(value.toFixed(places));
50
+ }
51
+ function stamp(iso) {
52
+ const minute = Math.round(Date.parse(iso) / 6e4) * 6e4;
53
+ return new Date(minute).toISOString().replace(/[-:]/g, "").replace(/\d\d\.\d+Z$/, "Z");
54
+ }
55
+ function historyExport(i) {
56
+ const bucket = bucketLabel(i.window.intervalSeconds);
57
+ return {
58
+ name: `${i.subject}-avg-per-${bucket}-${stamp(i.window.startTime)}-${stamp(i.window.endTime)}`,
59
+ columns: [i.heading.time, ...i.columns.map((c) => `${columnHeading(c)} ${i.heading.average(bucket)}`)],
60
+ rows: i.rows.map((r) => [
61
+ new Date(r.at).toISOString(),
62
+ ...i.columns.map((c) => {
63
+ const v = r.averages.get(c.name);
64
+ return v === void 0 ? null : roundTo(v, c.precision ?? i.precision);
65
+ })
66
+ ])
67
+ };
68
+ }
69
+ function spansDays(rows, timeZone) {
70
+ if (rows.length < 2) return false;
71
+ const day = (at) => new Date(at).toLocaleDateString(void 0, timeZone ? { timeZone } : void 0);
72
+ const first = day(rows[0].at);
73
+ return rows.some((r) => day(r.at) !== first);
74
+ }
75
+
76
+ export {
77
+ DEFAULT_PAGE_SIZE,
78
+ historyWindow,
79
+ historyRefreshMs,
80
+ bucketLabel,
81
+ historyColumns,
82
+ historyRows,
83
+ observedNames,
84
+ columnHeading,
85
+ roundTo,
86
+ stamp,
87
+ historyExport,
88
+ spansDays
89
+ };
90
+ //# sourceMappingURL=chunk-XJ7UF2CP.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/history-model.ts"],
4
+ "sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The history table's state, decided apart from the drawing and from the hub: which window it reads,\n// how the store's buckets become rows, how the rows are ordered and paged, and the CSV it exports.\n// Pure, so each rule is a test.\n//\n// \uD83D\uDD34 EVERY NUMBER HERE IS A BUCKET AVERAGE. The store answers a history read with buckets (a count, a\n// minimum, a maximum and a sum of the readings in a time slot) and this table shows each bucket's\n// average. It is never a reading and never the device's state at that time, so everything that names\n// it (the footer, the column headings, the export's headings and file name) says \"avg per <bucket>\".\n\nimport { bucketSecondsFor, spanLabel, type HistoryWindow, type MeasurementBucket } from '@devicechain/dashboards';\nimport type { WidgetExportTable } from '@devicechain/widget-sdk';\n\nimport { clampPage, pageCount, seedWindow } from './fleet-model';\n\nexport { clampPage, pageCount };\n\nexport const DEFAULT_PAGE_SIZE = 10;\n\n// What the feed hands the table: the buckets it read for a window, or why it has none.\nexport type HistoryView =\n | { status: 'loading' | 'unbound' | 'forbidden' | 'unsupported' | 'failed' }\n | { status: 'ready'; buckets: readonly MeasurementBucket[]; window: HistoryWindow };\n\n// How a column is presented: its heading, its unit, and its precision. By measurement name.\nexport interface HistoryColumn {\n name: string;\n label?: string;\n unit?: string;\n precision?: number;\n}\n\n// One row: a bucket's start and the average of each measurement in it. A measurement with no readings\n// in the bucket has no entry (not zero, not the previous value).\nexport interface HistoryRow {\n // RFC 3339, as the store returned it.\n bucketStart: string;\n at: number;\n averages: ReadonlyMap<string, number>;\n}\n\n// The window a history table over `spanSeconds` reads at `at` (epoch ms): whole minutes, in the\n// buckets of the span's chart (bucketSecondsFor). Delegates to the fleet seed window, which every\n// board widget reading a span uses, so the table reads exactly the buckets a chart over the same span\n// draws.\nexport function historyWindow(spanSeconds: number, at: number): HistoryWindow {\n return seedWindow(spanSeconds, at);\n}\n\n// How often a live table reads again: one bucket of its span, never more often than once a minute. A\n// bucket cannot change between two reads inside it by more than the readings of that slice, and a\n// week's span re-read every few seconds would scan a week of summaries to move by a few seconds.\nexport function historyRefreshMs(spanSeconds: number): number {\n return bucketSecondsFor(spanSeconds) * 1000;\n}\n\n// The bucket width as the sentence's unit: `5m`, `1h`, `42m`. Always exact (spanLabel).\nexport function bucketLabel(bucketSeconds: number): string {\n return spanLabel(bucketSeconds);\n}\n\n// The columns to show: the author's list if it names any, else the datasource's measurements, else\n// whatever the buckets hold (sorted, so the order is stable between reads).\nexport function historyColumns(\n authored: readonly HistoryColumn[],\n datasource: readonly string[],\n observed: Iterable<string>,\n): HistoryColumn[] {\n if (authored.length > 0) return [...authored];\n const names = datasource.length > 0 ? datasource : [...new Set(observed)].sort();\n return names.map((name) => ({ name }));\n}\n\n// Pivots the store's buckets into rows, one per bucket start. A bucket without an average (it holds\n// no readings) contributes nothing, and a start with no average for any column is no row. Newest\n// first unless `oldestFirst`.\nexport function historyRows(buckets: readonly MeasurementBucket[], columns: readonly string[], oldestFirst: boolean): HistoryRow[] {\n const shown = new Set(columns);\n const byStart = new Map<string, Map<string, number>>();\n for (const b of buckets) {\n if (!shown.has(b.name) || b.avg == null || !Number.isFinite(b.avg)) continue;\n let row = byStart.get(b.bucketStart);\n if (!row) byStart.set(b.bucketStart, (row = new Map()));\n row.set(b.name, b.avg);\n }\n const rows: HistoryRow[] = [];\n for (const [bucketStart, averages] of byStart) {\n const at = Date.parse(bucketStart);\n if (Number.isNaN(at)) continue;\n rows.push({ bucketStart, at, averages });\n }\n rows.sort((a, b) => (oldestFirst ? a.at - b.at : b.at - a.at));\n return rows;\n}\n\n// Every measurement name the buckets hold (for a table whose columns come from what was read).\nexport function observedNames(buckets: readonly MeasurementBucket[]): string[] {\n return [...new Set(buckets.filter((b) => b.avg != null).map((b) => b.name))];\n}\n\n// The heading of a column: its label (else the measurement's name) and its unit.\nexport function columnHeading(c: HistoryColumn): string {\n return `${c.label ?? c.name}${c.unit ? ` (${c.unit})` : ''}`;\n}\n\n// A stored value at the column's precision, or at the board-wide one: what the cell prints, as a\n// number, so the export carries what the table shows. With neither set the value is exact.\nexport function roundTo(value: number, precision: number | undefined): number {\n if (precision === undefined) return value;\n const places = Math.min(100, Math.max(0, Math.trunc(precision)));\n return Number(value.toFixed(places));\n}\n\n// `2026-10-22T14:05:00.000Z` -> `20261022T1405Z`, the way a file name carries a time: UTC, to the\n// nearest minute (a window's end is the instant just before its boundary, and names the boundary).\nexport function stamp(iso: string): string {\n const minute = Math.round(Date.parse(iso) / 60_000) * 60_000;\n return new Date(minute).toISOString().replace(/[-:]/g, '').replace(/\\d\\d\\.\\d+Z$/, 'Z');\n}\n\nexport interface HistoryExportInputs {\n // The widget's title, else its device: what the file is called after.\n subject: string;\n columns: readonly HistoryColumn[];\n rows: readonly HistoryRow[];\n window: Pick<HistoryWindow, 'startTime' | 'endTime' | 'intervalSeconds'>;\n precision: number | undefined;\n heading: { time: string; average: (bucket: string) => string };\n}\n\n// The CSV of what the table holds: EVERY row (not the page on screen), in the order on screen, one\n// column per measurement. The time is the bucket's start as an ISO-8601 instant in UTC (`Z`), and each\n// heading carries the unit and says it is an average per bucket. The file name carries the subject,\n// the bucket and the window it covers, so a file found later says which span and which bucket it holds.\nexport function historyExport(i: HistoryExportInputs): WidgetExportTable {\n const bucket = bucketLabel(i.window.intervalSeconds);\n return {\n name: `${i.subject}-avg-per-${bucket}-${stamp(i.window.startTime)}-${stamp(i.window.endTime)}`,\n columns: [i.heading.time, ...i.columns.map((c) => `${columnHeading(c)} ${i.heading.average(bucket)}`)],\n rows: i.rows.map((r) => [\n new Date(r.at).toISOString(),\n ...i.columns.map((c) => {\n const v = r.averages.get(c.name);\n return v === undefined ? null : roundTo(v, c.precision ?? i.precision);\n }),\n ]),\n };\n}\n\n// A date is printed only when it tells the rows apart: a table whose buckets all fall on one day\n// (in the zone shown) prints times; one that crosses midnight prints dates and times.\nexport function spansDays(rows: readonly HistoryRow[], timeZone: string | undefined): boolean {\n if (rows.length < 2) return false;\n const day = (at: number) => new Date(at).toLocaleDateString(undefined, timeZone ? { timeZone } : undefined);\n const first = day(rows[0].at);\n return rows.some((r) => day(r.at) !== first);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,16 +1,24 @@
1
- import {
2
- measurementSpan
3
- } from "./chunk-JFH4QMLA.js";
4
- import {
5
- seedWindow
6
- } from "./chunk-62CHYCHI.js";
7
1
  import {
8
2
  fleetStrings
9
3
  } from "./chunk-QMGKB6J3.js";
4
+ import {
5
+ Explained
6
+ } from "./chunk-AF4M5OXQ.js";
7
+ import "./chunk-JVC6JYUR.js";
8
+ import {
9
+ samplesExport
10
+ } from "./chunk-VTGKDO44.js";
11
+ import {
12
+ measurementSpan
13
+ } from "./chunk-NYHNQ5VR.js";
10
14
  import "./chunk-BRH66T5F.js";
11
15
  import {
12
16
  LevelEmpty
13
- } from "./chunk-XQQQNTBX.js";
17
+ } from "./chunk-EQOWBDS7.js";
18
+ import "./chunk-XJ7UF2CP.js";
19
+ import {
20
+ seedWindow
21
+ } from "./chunk-62CHYCHI.js";
14
22
 
15
23
  // src/compare-feed.tsx
16
24
  import {
@@ -55,7 +63,7 @@ function memberLabels(members) {
55
63
  members.forEach((m, i) => labels.set(m.token, (count.get(base[i]) ?? 0) > 1 && base[i] !== m.token ? `${base[i]} (${m.token})` : base[i]));
56
64
  return labels;
57
65
  }
58
- function bucketSamples(token, measurement, buckets) {
66
+ function bucketSamples(token, measurement, buckets, bucketSeconds) {
59
67
  return buckets.filter((b) => b.name === measurement && b.avg != null).map((b) => ({
60
68
  id: `cmp-${token}-${b.name}-${b.bucketStart}`,
61
69
  deviceToken: token,
@@ -63,7 +71,9 @@ function bucketSamples(token, measurement, buckets) {
63
71
  occurredTime: b.bucketStart,
64
72
  name: b.name,
65
73
  value: b.avg,
66
- classifier: null
74
+ classifier: null,
75
+ // A stored point is a bucket's average, and says how wide the bucket was (the export tells it from a reading).
76
+ ...bucketSeconds !== void 0 ? { bucketSeconds } : {}
67
77
  })).sort((a, b) => Date.parse(a.occurredTime) - Date.parse(b.occurredTime));
68
78
  }
69
79
  function pushLive(buffer, sample) {
@@ -223,7 +233,7 @@ ${[...drawn.current].join("\n")}`);
223
233
  // is empty because this chart draws the history itself, not a "latest" cell.
224
234
  seed: async (token, read) => {
225
235
  const buckets = await read({ startTime: window.startTime, endTime: window.endTime, intervalSeconds: window.intervalSeconds, name: measurement });
226
- if (!cancelled) s2.history.set(token, { samples: bucketSamples(token, measurement, buckets), end });
236
+ if (!cancelled) s2.history.set(token, { samples: bucketSamples(token, measurement, buckets, window.intervalSeconds), end });
227
237
  return { latest: {}, observed: [] };
228
238
  }
229
239
  },
@@ -260,24 +270,39 @@ ${[...drawn.current].join("\n")}`);
260
270
  if (s.error) return /* @__PURE__ */ jsx(ErrorFrame, { widget, error: s.error });
261
271
  const title = optString(widget.options, "title");
262
272
  const strings = fleetStrings();
263
- const empty = (message) => /* @__PURE__ */ jsx(WidgetFrame, { title, children: /* @__PURE__ */ jsx(LevelEmpty, { message }) });
273
+ const empty = (message, kind) => /* @__PURE__ */ jsx(WidgetFrame, { title, children: kind ? /* @__PURE__ */ jsx(Explained, { kind, message }) : /* @__PURE__ */ jsx(LevelEmpty, { message }) });
264
274
  if (!measurement) return empty(strings.noMeasurement);
265
275
  if (!supported) return empty(strings.unsupported);
266
- if (s.forbidden) return empty(strings.forbidden);
267
- if (ds?.kind === "unbound") return empty(strings.unbound);
276
+ if (s.forbidden) return empty(strings.forbidden, "forbidden");
277
+ if (ds?.kind === "unbound") return empty(strings.unbound, "unbound");
268
278
  if (!capped) return empty(strings.loading);
269
279
  if (capped.shown.length === 0) return empty(strings.noMembers);
270
- return /* @__PURE__ */ jsx(WidgetFreshnessProvider, { freshness, children: /* @__PURE__ */ jsx(WidgetFrame, { title, flush: true, children: /* @__PURE__ */ jsx(
271
- CompareBody,
280
+ return /* @__PURE__ */ jsx(WidgetFreshnessProvider, { freshness, children: /* @__PURE__ */ jsx(
281
+ WidgetFrame,
272
282
  {
273
- widget,
274
- names: capped.shown.map((m) => labels.get(m.token) ?? m.token),
275
- samples: composed?.samples ?? [],
276
- bucketSeconds: seedWindow(spanSeconds, pausedAt ?? now()).intervalSeconds,
277
- capped,
278
- historyFailed: composed?.historyFailed ?? 0
283
+ title,
284
+ flush: true,
285
+ exportRows: () => samplesExport({
286
+ subject: title ?? measurement,
287
+ samples: composed?.samples ?? [],
288
+ unit: optString(widget.options, "unit"),
289
+ precision: optNumber(widget.options, "precision"),
290
+ seriesIs: "device",
291
+ spanSeconds
292
+ }),
293
+ children: /* @__PURE__ */ jsx(
294
+ CompareBody,
295
+ {
296
+ widget,
297
+ names: capped.shown.map((m) => labels.get(m.token) ?? m.token),
298
+ samples: composed?.samples ?? [],
299
+ bucketSeconds: seedWindow(spanSeconds, pausedAt ?? now()).intervalSeconds,
300
+ capped,
301
+ historyFailed: composed?.historyFailed ?? 0
302
+ }
303
+ )
279
304
  }
280
- ) }) });
305
+ ) });
281
306
  }
282
307
  function CompareBody({
283
308
  widget,
@@ -343,4 +368,4 @@ export {
343
368
  FLUSH_MS,
344
369
  CompareFeed as default
345
370
  };
346
- //# sourceMappingURL=compare-feed-AY3CVKUZ.js.map
371
+ //# sourceMappingURL=compare-feed-5OLZHFRL.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/compare-feed.tsx", "../../src/compare-model.ts", "../../src/compare-strings.ts"],
4
+ "sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The multi-device compare chart: ONE measurement, a line per member of a group, over the board's\n// span. Its own lazy chunk, loaded from the fleet feed's entry only when a time-series chart is set\n// to one line per device -- a board without one never downloads it.\n//\n// - ONE live subscription (hub.subscribeMembers), then one history read per DRAWN member through\n// hub.readMemberSeeds: bounded hub-wide (MEMBER_SEED_CONCURRENCY) and shared with any widget\n// asking the same question. There is no batched history read in the platform (the store\n// aggregates one device at a time), so the cost of the chart is the number of lines it draws --\n// which is why that number is capped (`maxSeries`) and the cap is said on the chart.\n// - A history read is the measurement alone (the store narrows by name), over the board's span (or\n// the chart's pinned one) ending at the last minute boundary, in buckets of bucketSecondsFor(span):\n// one point per bucket, the bucket's AVERAGE. The chart says so (the badge), and says that\n// threshold lines are references, never a reading of a bucket.\n// - Live readings extend each line past the history's end. While the board is paused the view is\n// cut at the freeze instant; the buffer keeps filling and the chart catches up on resume.\n// - A member stream that resumes after an interruption re-reads the histories, at once and again\n// just after the next minute boundary (as the fleet feed does).\n//\n// Rebuilding the state per sample would re-render the chart hundreds of times a second, so changes\n// are folded into a store and flushed at most once per FLUSH_MS.\n\nimport {\n DEFAULT_TIME_RANGE_SECONDS,\n spanLabel,\n type MemberRoster,\n type MeasurementSample,\n type WidgetDataSource,\n type WidgetDescriptor,\n type WidgetInstance,\n} from '@devicechain/dashboards';\nimport {\n css,\n fontVar,\n optNumber,\n optString,\n optThresholds,\n resolveStaleAfterSeconds,\n spaceVar,\n useWidgetScale,\n useWidgetTimeRange,\n WidgetFrame,\n WidgetFreshnessProvider,\n} from '@devicechain/widget-sdk';\nimport { buildLineOption, EChart, useChartTheme } from '@devicechain/widget-sdk/echarts';\nimport { useEffect, useMemo, useReducer, useRef, useState, type ComponentType } from 'react';\n\nimport { measurementSpan } from './binding-policy';\nimport { bucketSamples, capMembers, composeSeries, memberLabels, pushLive, seriesLimit, type CappedMembers, type MemberHistory } from './compare-model';\nimport { compareStrings } from './compare-strings';\nimport { seedWindow } from './fleet-model';\nimport { fleetStrings } from './fleet-strings';\nimport { samplesExport } from './export-rows';\nimport { LevelEmpty } from './level-readout';\nimport { Explained } from './no-reading';\n\nexport const FLUSH_MS = 250;\nconst MINUTE_MS = 60_000;\n// How long past a minute boundary the second re-read after an interruption waits (the fleet feed's).\nconst RESEED_SETTLE_MS = 2_000;\n// A reading ages out of the span with no event to say so.\nconst AGE_OUT_MS = 30_000;\n// Below this width or height (px) the notes under the chart are said in a line.\nconst COMPACT_WIDTH = 320;\nconst COMPACT_HEIGHT = 200;\n\ninterface Store {\n key: string | null;\n roster: MemberRoster | null;\n live: Map<string, MeasurementSample[]>;\n history: Map<string, MemberHistory | 'failed'>;\n forbidden: boolean;\n error: unknown | null;\n}\n\nconst emptyStore = (key: string | null): Store => ({ key, roster: null, live: new Map(), history: new Map(), forbidden: false, error: null });\n\nexport interface CompareFeedProps {\n hub: WidgetDataSource;\n widget: WidgetInstance;\n descriptor: WidgetDescriptor;\n ErrorFrame: ComponentType<{ widget: WidgetInstance; error: unknown }>;\n}\n\nexport default function CompareFeed({ hub, widget, descriptor, ErrorFrame }: CompareFeedProps) {\n const range = useWidgetTimeRange();\n const now = useMemo(() => (hub.now ? () => hub.now!() : Date.now), [hub]);\n const spanSeconds = measurementSpan(descriptor, widget, range?.spanSeconds) ?? range?.spanSeconds ?? DEFAULT_TIME_RANGE_SECONDS;\n const pausedAt = range?.paused ? range.pausedAt : undefined;\n const measurement = optString(widget.options, 'measurement') ?? widget.datasource?.measurements[0];\n const limit = seriesLimit(optNumber(widget.options, 'maxSeries'));\n // The datasource the live feed is asked for: the one measurement, so the hub delivers no other.\n const ds = widget.datasource && measurement ? { ...widget.datasource, measurements: [measurement] } : undefined;\n const dsKey = JSON.stringify(ds ?? null);\n const supported = hub.subscribeMembers !== undefined && hub.readMemberSeeds !== undefined;\n\n const store = useRef<Store>(emptyStore(dsKey));\n const [version, bump] = useReducer((n: number) => n + 1, 0);\n const [rosterKey, setRosterKey] = useState<string | null>(null);\n const generation = useRef(0);\n const flushTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const boundaryTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const [resumes, resumed] = useReducer((n: number) => n + 1, 0);\n // The tokens drawn, which the live feed filters by.\n const drawn = useRef<Set<string>>(new Set());\n\n const schedule = (): void => {\n if (flushTimer.current !== undefined) return;\n flushTimer.current = setTimeout(() => {\n flushTimer.current = undefined;\n bump();\n }, FLUSH_MS);\n };\n\n useEffect(() => {\n const s = emptyStore(dsKey);\n store.current = s;\n drawn.current = new Set();\n setRosterKey(null);\n bump();\n const gen = ++generation.current;\n if (!ds || !supported) return;\n const dispose = hub.subscribeMembers!(ds, {\n roster: (roster) => {\n s.roster = roster;\n drawn.current = new Set(capMembers(roster, limit).shown.map((m) => m.token));\n setRosterKey(`${gen}\\n${[...drawn.current].join('\\n')}`);\n schedule();\n },\n next: (sample) => {\n if (!drawn.current.has(sample.deviceToken)) return;\n let buffer = s.live.get(sample.deviceToken);\n if (!buffer) s.live.set(sample.deviceToken, (buffer = []));\n pushLive(buffer, sample);\n schedule();\n },\n forbidden: () => {\n s.forbidden = true;\n schedule();\n },\n error: (err) => {\n s.error = err;\n schedule();\n },\n reconnected: () => {\n resumed();\n if (boundaryTimer.current !== undefined) return;\n const at = now();\n boundaryTimer.current = setTimeout(() => {\n boundaryTimer.current = undefined;\n resumed();\n }, MINUTE_MS - (at % MINUTE_MS) + RESEED_SETTLE_MS);\n },\n });\n return () => {\n dispose();\n if (flushTimer.current !== undefined) clearTimeout(flushTimer.current);\n flushTimer.current = undefined;\n if (boundaryTimer.current !== undefined) clearTimeout(boundaryTimer.current);\n boundaryTimer.current = undefined;\n };\n // `ds` is read through `dsKey` (value identity); a new limit re-draws from the same roster below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hub, dsKey, supported, limit]);\n\n // One history read per drawn member, again when the span or the freeze instant changes.\n useEffect(() => {\n const s = store.current;\n if (!supported || !s.roster || rosterKey === null || !measurement) return;\n const tokens = capMembers(s.roster, limit).shown.map((m) => m.token);\n if (tokens.length === 0) return;\n const window = seedWindow(spanSeconds, pausedAt ?? now());\n const end = Date.parse(window.endTime);\n let cancelled = false;\n void hub.readMemberSeeds!(\n tokens,\n {\n // The plan reads the one measurement over the span and keeps its buckets; the seed it returns\n // is empty because this chart draws the history itself, not a \"latest\" cell.\n seed: async (token, read) => {\n const buckets = await read({ startTime: window.startTime, endTime: window.endTime, intervalSeconds: window.intervalSeconds, name: measurement });\n // A read this effect no longer wants (the span, the freeze instant or the roster moved on\n // while it was in flight) must not overwrite the newer one's history when it lands last.\n if (!cancelled) s.history.set(token, { samples: bucketSamples(token, measurement, buckets, window.intervalSeconds), end });\n return { latest: {}, observed: [] };\n },\n },\n (token, seed, failure) => {\n if (cancelled) return;\n if (failure === 'forbidden') s.forbidden = true;\n // A re-read that failed keeps the history it would have replaced: those points are still true.\n else if (!seed && !s.history.has(token)) s.history.set(token, 'failed');\n schedule();\n },\n () => cancelled,\n );\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hub, supported, dsKey, rosterKey, spanSeconds, pausedAt, limit, resumes]);\n\n // A reading ages out of the span with no event to say so.\n useEffect(() => {\n if (pausedAt !== undefined) return;\n const id = setInterval(bump, AGE_OUT_MS);\n return () => clearInterval(id);\n }, [pausedAt]);\n\n const view = useMemo(() => {\n const s = store.current.key === dsKey ? store.current : emptyStore(dsKey);\n const capped = s.roster ? capMembers(s.roster, limit) : undefined;\n const labels = capped ? memberLabels(capped.shown) : new Map<string, string>();\n const until = pausedAt ?? now();\n const composed =\n capped && measurement\n ? composeSeries({ shown: capped.shown, labels, measurement, history: s.history, live: s.live, since: until - spanSeconds * 1000, until })\n : undefined;\n return { s, capped, labels, composed };\n // `version` is the store's change counter; the store itself is mutable.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [version, dsKey, limit, spanSeconds, pausedAt, now, measurement]);\n\n const { s, capped, labels, composed } = view;\n const freshness = useMemo(\n () => (descriptor.freshness === true ? { at: composed?.newest, staleAfterSeconds: resolveStaleAfterSeconds(widget.options) } : undefined),\n [descriptor, widget.options, composed?.newest],\n );\n if (s.error) return <ErrorFrame widget={widget} error={s.error} />;\n\n const title = optString(widget.options, 'title');\n const strings = fleetStrings();\n const empty = (message: string, kind?: 'unbound' | 'forbidden') => (\n <WidgetFrame title={title}>\n {kind ? <Explained kind={kind} message={message} /> : <LevelEmpty message={message} />}\n </WidgetFrame>\n );\n if (!measurement) return empty(strings.noMeasurement);\n if (!supported) return empty(strings.unsupported);\n if (s.forbidden) return empty(strings.forbidden, 'forbidden');\n if (ds?.kind === 'unbound') return empty(strings.unbound, 'unbound');\n if (!capped) return empty(strings.loading);\n if (capped.shown.length === 0) return empty(strings.noMembers);\n\n return (\n <WidgetFreshnessProvider freshness={freshness}>\n <WidgetFrame\n title={title}\n flush\n exportRows={() =>\n samplesExport({\n subject: title ?? measurement,\n samples: composed?.samples ?? [],\n unit: optString(widget.options, 'unit'),\n precision: optNumber(widget.options, 'precision'),\n seriesIs: 'device',\n spanSeconds,\n })\n }\n >\n <CompareBody\n widget={widget}\n names={capped.shown.map((m) => labels.get(m.token) ?? m.token)}\n samples={composed?.samples ?? []}\n bucketSeconds={seedWindow(spanSeconds, pausedAt ?? now()).intervalSeconds}\n capped={capped}\n historyFailed={composed?.historyFailed ?? 0}\n />\n </WidgetFrame>\n </WidgetFreshnessProvider>\n );\n}\n\nfunction CompareBody({\n widget,\n names,\n samples,\n bucketSeconds,\n capped,\n historyFailed,\n}: {\n widget: WidgetInstance;\n names: string[];\n samples: MeasurementSample[];\n bucketSeconds: number;\n capped: CappedMembers;\n historyFailed: number;\n}) {\n const theme = useChartTheme();\n const { size, scale } = useWidgetScale();\n const strings = compareStrings();\n const unit = optString(widget.options, 'unit');\n const precision = optNumber(widget.options, 'precision');\n const thresholds = useMemo(() => optThresholds(widget.options, 'thresholds'), [widget.options]);\n const min = optNumber(widget.options, 'min');\n const max = optNumber(widget.options, 'max');\n const namesKey = names.join('\\n');\n const option = useMemo(\n () => buildLineOption(samples, theme, { measurements: names, unit, precision, thresholds, scale, min, max }, size),\n // `names` is read through `namesKey` (value identity): the array is rebuilt on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [samples, theme, namesKey, unit, precision, thresholds, scale, size, min, max],\n );\n const bucket = spanLabel(bucketSeconds);\n // The sentences, or (in a box too narrow for them: the widget's smallest span is 4 cells) the same\n // facts in a line, with the sentences in its title. Nothing is dropped, only shortened.\n const compact = (size.width > 0 && size.width < COMPACT_WIDTH) || (size.height > 0 && size.height < COMPACT_HEIGHT);\n const facts: Array<{ full: string; short: string }> = [];\n if (capped.capped) {\n const full = capped.pagedBySource ? strings.cappedPage(capped.shown.length, capped.read, capped.total) : strings.capped(capped.shown.length, capped.total);\n facts.push({ full, short: strings.cappedShort(capped.shown.length, capped.total) });\n }\n if (historyFailed > 0) facts.push({ full: strings.historyFailed(historyFailed), short: strings.historyFailedShort(historyFailed) });\n if (thresholds.length > 0) facts.push({ full: strings.thresholdNote, short: strings.thresholdShort });\n const badge = (\n <span\n data-compare-bucket={bucket}\n title={strings.bucketedTitle(bucket)}\n style={{\n padding: `0 ${spaceVar(1)}`,\n fontSize: fontVar('xs'),\n whiteSpace: 'nowrap',\n color: css('muted-foreground'),\n background: css('muted'),\n borderRadius: 4,\n }}\n >\n {strings.bucketed(bucket)}\n </span>\n );\n const noteStyle = { flexShrink: 0, padding: `${spaceVar(1)} ${spaceVar(2)}`, fontSize: fontVar('xs'), color: css('muted-foreground') } as const;\n return (\n <div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100%' }} data-compare-chart=\"\">\n <div style={{ position: 'relative', flex: 1, minHeight: 0 }}>\n <EChart option={option} />\n </div>\n {/* Under the chart, never over it: a corner of the plot is where the legend runs once it fills\n the width (five devices on a phone) and where a threshold's label sits, and the badge\n covered both. */}\n {compact ? (\n <div data-compare-notes={facts.length > 0 ? '' : undefined} data-compare-compact=\"\" title={facts.map((f) => f.full).join(' ')} style={noteStyle}>\n {badge}\n {facts.map((f) => ` \u00B7 ${f.short}`).join('')}\n </div>\n ) : (\n <div data-compare-notes={facts.length > 0 ? '' : undefined} title={facts.length > 0 ? facts.map((f) => f.full).join(' ') : undefined} style={noteStyle}>\n <div>{badge}</div>\n {facts.map((f) => (\n <div key={f.full}>{f.full}</div>\n ))}\n </div>\n )}\n </div>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The multi-device compare chart's state, decided apart from the drawing and from the hub: which\n// members get a line, what each line is called, and which readings make it up. Pure, so each rule\n// is a test.\n//\n// A line is one member's readings of ONE measurement over the span: its stored history (one point\n// per bucket, the bucket's AVERAGE) followed by the readings that arrived live after the history\n// was read (raw). Everything is keyed by device; nothing here looks a value up by name alone.\n\nimport type { MeasurementBucket, MeasurementSample, MemberRef } from '@devicechain/dashboards';\n\n// The lines drawn when the author sets no limit: the chart palette's width, so no two lines share\n// a colour. The most an author may ask for: a comparison past this is a table, not a chart.\nexport const DEFAULT_MAX_SERIES = 5;\nexport const MAX_SERIES_LIMIT = 12;\n\n// Live readings kept per member. A comparison is a few lines over a span; this bounds the memory of\n// a chatty device (a reading a second over a week) without touching the stored history, which is\n// already bucketed to at most a couple of hundred points.\nexport const LIVE_PER_MEMBER = 600;\n\n// The limit as the author set it, held to what the chart supports. Unset, or anything that is not a\n// whole number of at least one, is the default: a typo must not turn into a different chart.\nexport function seriesLimit(value: number | undefined): number {\n if (value === undefined || !Number.isInteger(value) || value < 1) return DEFAULT_MAX_SERIES;\n return Math.min(value, MAX_SERIES_LIMIT);\n}\n\nexport interface CappedMembers {\n shown: MemberRef[];\n // The size of the group, as the source reports it. Never less than the members read.\n total: number;\n // True when lines were left out, for either reason: the author's limit, or a group larger than\n // the page the source read (shown + not drawn < total).\n capped: boolean;\n // True when the source itself read less than the group: the total is then the group's size and\n // `read` the members it listed.\n pagedBySource: boolean;\n read: number;\n}\n\n// The first `limit` members of the roster, in the roster's order (a stable order, so a line keeps\n// its colour as readings arrive). Which members are left out is the roster's, not the readings':\n// choosing \"the busiest\" would reshuffle the chart as data moves.\nexport function capMembers(roster: { members: readonly MemberRef[]; total: number }, limit: number): CappedMembers {\n const read = roster.members.length;\n const total = Math.max(roster.total, read);\n const shown = roster.members.slice(0, limit);\n return { shown, total, capped: shown.length < total, pagedBySource: total > read, read };\n}\n\n// A name per member: its device name, else its token. Two members with the same name get their\n// token appended, so two lines never carry one legend entry.\nexport function memberLabels(members: readonly MemberRef[]): Map<string, string> {\n const base = members.map((m) => m.name?.trim() || m.token);\n const count = new Map<string, number>();\n for (const b of base) count.set(b, (count.get(b) ?? 0) + 1);\n const labels = new Map<string, string>();\n members.forEach((m, i) => labels.set(m.token, (count.get(base[i]) ?? 0) > 1 && base[i] !== m.token ? `${base[i]} (${m.token})` : base[i]));\n return labels;\n}\n\n// One member's stored history as samples: a point per bucket that has an average, stamped at the\n// bucket's start, oldest first. A bucket with no readings has no average and no point.\nexport function bucketSamples(token: string, measurement: string, buckets: readonly MeasurementBucket[], bucketSeconds?: number): MeasurementSample[] {\n return buckets\n .filter((b) => b.name === measurement && b.avg != null)\n .map((b) => ({\n id: `cmp-${token}-${b.name}-${b.bucketStart}`,\n deviceToken: token,\n eventType: 0,\n occurredTime: b.bucketStart,\n name: b.name,\n value: b.avg,\n classifier: null,\n // A stored point is a bucket's average, and says how wide the bucket was (the export tells it from a reading).\n ...(bucketSeconds !== undefined ? { bucketSeconds } : {}),\n }))\n .sort((a, b) => Date.parse(a.occurredTime as string) - Date.parse(b.occurredTime as string));\n}\n\n// A member's stored history once read: the points, and the instant the read ended (epoch ms), after\n// which live readings are the only source.\nexport interface MemberHistory {\n samples: MeasurementSample[];\n end: number;\n}\n\n// Appends a live reading to a member's buffer: newest wins on a repeat id, and the buffer keeps the\n// newest LIVE_PER_MEMBER.\nexport function pushLive(buffer: MeasurementSample[], sample: MeasurementSample): void {\n if (buffer.some((s) => s.id === sample.id)) return;\n buffer.push(sample);\n if (buffer.length > LIVE_PER_MEMBER) buffer.splice(0, buffer.length - LIVE_PER_MEMBER);\n}\n\nexport interface ComposeInputs {\n shown: readonly MemberRef[];\n labels: ReadonlyMap<string, string>;\n measurement: string;\n history: ReadonlyMap<string, MemberHistory | 'failed'>;\n live: ReadonlyMap<string, readonly MeasurementSample[]>;\n // The span, epoch ms: nothing before `since` or after `until` is drawn (the board's freeze\n // instant while paused).\n since: number;\n until: number;\n}\n\nexport interface Composed {\n // One sample per point, named by the member's LABEL so the line chart draws one line per member.\n samples: MeasurementSample[];\n // Members whose stored history could not be read: their lines hold live readings only.\n historyFailed: number;\n // The newest reading drawn, epoch ms, for the frame's age.\n newest: number | undefined;\n}\n\nexport function composeSeries(i: ComposeInputs): Composed {\n const samples: MeasurementSample[] = [];\n let historyFailed = 0;\n let newest: number | undefined;\n for (const m of i.shown) {\n const label = i.labels.get(m.token) ?? m.token;\n const h = i.history.get(m.token);\n if (h === 'failed') historyFailed += 1;\n const stored = h && h !== 'failed' ? h.samples : [];\n // Live readings count only after the stored history's read ended: before it, the history holds\n // them already (as an average), and drawing both would put two values on one instant.\n const after = h && h !== 'failed' ? h.end : Number.NEGATIVE_INFINITY;\n const live = (i.live.get(m.token) ?? []).filter((s) => s.name === i.measurement && s.value != null && s.occurredTime != null && Date.parse(s.occurredTime) > after);\n const points = [...stored, ...live]\n .filter((s) => {\n const t = Date.parse(s.occurredTime as string);\n return t >= i.since && t <= i.until;\n })\n .sort((a, b) => Date.parse(a.occurredTime as string) - Date.parse(b.occurredTime as string));\n for (const s of points) {\n samples.push({ ...s, name: label });\n const t = Date.parse(s.occurredTime as string);\n if (newest === undefined || t > newest) newest = t;\n }\n }\n return { samples, historyFailed, newest };\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The fixed words the multi-device compare chart renders itself. The widget packages carry no i18n\n// runtime, so this is the one small per-language table (modelled on fleet-strings.ts); the option\n// labels are translated in the console's catalogs, not here. Loaded with the compare feed (a lazy\n// chunk), never by a board without a compare chart.\n\nimport { viewerLocale } from '@devicechain/widget-sdk';\n\nexport interface CompareStrings {\n // The group is larger than the lines drawn: say how many, and which ones.\n capped: (shown: number, total: number) => string;\n // The group was larger than what the resolver read: the total is a floor, not the size.\n cappedPage: (shown: number, read: number, total: number) => string;\n // History is a per-bucket average; the live tail is the raw readings.\n bucketed: (bucket: string) => string;\n bucketedTitle: (bucket: string) => string;\n // A threshold line is a reference drawn at a value, never a verdict on an average.\n thresholdNote: string;\n // Some devices' history could not be read: their lines hold only what arrived live.\n historyFailed: (n: number) => string;\n // The same facts for a box too narrow for the sentences above; the full text stays in the title.\n cappedShort: (shown: number, total: number) => string;\n thresholdShort: string;\n historyFailedShort: (n: number) => string;\n}\n\nexport const COMPARE_STRINGS: Record<'en' | 'es', CompareStrings> = {\n en: {\n capped: (shown, total) => `Showing ${shown} of ${total} devices (the first ${shown}); raise Most lines, up to 12, to see more.`,\n cappedPage: (shown, read, total) =>\n `Showing ${shown} of ${total} devices: the group is larger than the ${read} devices read, and the first ${shown} are drawn.`,\n bucketed: (bucket) => `avg per ${bucket}`,\n bucketedTitle: (bucket) =>\n `Stored history is drawn as the average of each ${bucket} bucket, one point per bucket; readings that arrived live since are drawn as they came, unaveraged.`,\n thresholdNote: 'Thresholds are reference lines; a plotted history point is a bucket average, not a reading.',\n historyFailed: (n) => (n === 1 ? '1 device\u2019s history could not be read; its line starts from live readings.' : `${n} devices\u2019 history could not be read; their lines start from live readings.`),\n cappedShort: (shown, total) => `${shown} of ${total} devices`,\n thresholdShort: 'thresholds are references',\n historyFailedShort: (n) => `${n} unread`,\n },\n es: {\n capped: (shown, total) =>\n `Mostrando ${shown} de ${total} dispositivos (los primeros ${shown}); aumente M\u00E1ximo de l\u00EDneas, hasta 12, para ver m\u00E1s.`,\n cappedPage: (shown, read, total) =>\n `Mostrando ${shown} de ${total} dispositivos: el grupo es mayor que los ${read} dispositivos le\u00EDdos, y se dibujan los primeros ${shown}.`,\n bucketed: (bucket) => `prom. por ${bucket}`,\n bucketedTitle: (bucket) =>\n `El historial almacenado se dibuja como el promedio de cada intervalo de ${bucket}, un punto por intervalo; las lecturas llegadas en vivo desde entonces se dibujan tal cual, sin promediar.`,\n thresholdNote: 'Los umbrales son l\u00EDneas de referencia; un punto de historial es un promedio por intervalo, no una lectura.',\n historyFailed: (n) =>\n n === 1 ? 'No se pudo leer el historial de 1 dispositivo; su l\u00EDnea parte de lecturas en vivo.' : `No se pudo leer el historial de ${n} dispositivos; sus l\u00EDneas parten de lecturas en vivo.`,\n cappedShort: (shown, total) => `${shown} de ${total} dispositivos`,\n thresholdShort: 'umbrales son referencias',\n historyFailedShort: (n) => `${n} sin leer`,\n },\n};\n\nexport function compareStrings(locale: string | undefined = viewerLocale()): CompareStrings {\n return locale?.toLowerCase().split('-')[0] === 'es' ? COMPARE_STRINGS.es : COMPARE_STRINGS.en;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["s", "capped", "labels", "composed"]
7
+ }
@@ -1,9 +1,9 @@
1
- import {
2
- measurementSpan
3
- } from "./chunk-JFH4QMLA.js";
4
1
  import {
5
2
  useHeld
6
3
  } from "./chunk-XNEBAY4J.js";
4
+ import {
5
+ measurementSpan
6
+ } from "./chunk-NYHNQ5VR.js";
7
7
 
8
8
  // src/event-markers-feed.tsx
9
9
  import {
@@ -370,4 +370,4 @@ function EventList({
370
370
  export {
371
371
  EventMarkersFeed as default
372
372
  };
373
- //# sourceMappingURL=event-markers-feed-GEGTIHXT.js.map
373
+ //# sourceMappingURL=event-markers-feed-B3MVFYUT.js.map
@@ -0,0 +1,11 @@
1
+ import {
2
+ fleetExport,
3
+ samplesExport
4
+ } from "./chunk-VTGKDO44.js";
5
+ import "./chunk-XJ7UF2CP.js";
6
+ import "./chunk-62CHYCHI.js";
7
+ export {
8
+ fleetExport,
9
+ samplesExport
10
+ };
11
+ //# sourceMappingURL=export-rows-OEWIBIUY.js.map
@@ -2,21 +2,21 @@ import {
2
2
  ChunkLoadingFrame,
3
3
  lazyChunk
4
4
  } from "./chunk-BMFQOTYG.js";
5
+ import {
6
+ fleetStrings
7
+ } from "./chunk-QMGKB6J3.js";
5
8
  import {
6
9
  measurementSpan
7
- } from "./chunk-JFH4QMLA.js";
10
+ } from "./chunk-NYHNQ5VR.js";
11
+ import "./chunk-BRH66T5F.js";
12
+ import {
13
+ LevelEmpty
14
+ } from "./chunk-EQOWBDS7.js";
8
15
  import {
9
16
  buildFleet,
10
17
  newestMemberTime,
11
18
  seedWindow
12
19
  } from "./chunk-62CHYCHI.js";
13
- import {
14
- fleetStrings
15
- } from "./chunk-QMGKB6J3.js";
16
- import "./chunk-BRH66T5F.js";
17
- import {
18
- LevelEmpty
19
- } from "./chunk-XQQQNTBX.js";
20
20
 
21
21
  // src/fleet-feed.tsx
22
22
  import {
@@ -143,7 +143,7 @@ function shownMeasurements(widget) {
143
143
  function comparesMembers(descriptor) {
144
144
  return descriptor.options.maxSeries !== void 0;
145
145
  }
146
- var CompareFeed = lazyChunk(() => import("./compare-feed-AY3CVKUZ.js")).component("default", void 0, ChunkLoadingFrame);
146
+ var CompareFeed = lazyChunk(() => import("./compare-feed-5OLZHFRL.js")).component("default", void 0, ChunkLoadingFrame);
147
147
  function FleetEntry(props) {
148
148
  if (props.refuse) return /* @__PURE__ */ jsx(GroupRefusal, { widget: props.widget, descriptor: props.descriptor });
149
149
  if (comparesMembers(props.descriptor)) {
@@ -308,4 +308,4 @@ export {
308
308
  FleetEntry as default,
309
309
  shownMeasurements
310
310
  };
311
- //# sourceMappingURL=fleet-feed-2SKDR6ZQ.js.map
311
+ //# sourceMappingURL=fleet-feed-D4T3LC25.js.map