@devicechain/widgets 0.14.0-0

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 (42) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +159 -0
  3. package/dist/basemap-context.d.ts +27 -0
  4. package/dist/chart-options.d.ts +14 -0
  5. package/dist/chunks/natural-earth-data-VMKQ6LY3.js +8 -0
  6. package/dist/chunks/natural-earth-data-VMKQ6LY3.js.map +7 -0
  7. package/dist/connected-widget.d.ts +8 -0
  8. package/dist/dashboard-renderer.d.ts +17 -0
  9. package/dist/definition-options.d.ts +9 -0
  10. package/dist/echart.d.ts +8 -0
  11. package/dist/flash.d.ts +13 -0
  12. package/dist/format.d.ts +3 -0
  13. package/dist/frame.d.ts +25 -0
  14. package/dist/hooks.d.ts +54 -0
  15. package/dist/index.d.ts +29 -0
  16. package/dist/index.js +2671 -0
  17. package/dist/index.js.map +7 -0
  18. package/dist/map-runtime-context.d.ts +91 -0
  19. package/dist/options.d.ts +312 -0
  20. package/dist/registry.d.ts +44 -0
  21. package/dist/theme.d.ts +9 -0
  22. package/dist/vite.d.ts +19 -0
  23. package/dist/vite.js +10 -0
  24. package/dist/vite.js.map +7 -0
  25. package/dist/widget.d.ts +16 -0
  26. package/dist/widgets/alarm-count.d.ts +3 -0
  27. package/dist/widgets/alarm-table.d.ts +3 -0
  28. package/dist/widgets/command-button.d.ts +3 -0
  29. package/dist/widgets/command-params.d.ts +7 -0
  30. package/dist/widgets/command-status.d.ts +3 -0
  31. package/dist/widgets/entity-selector.d.ts +2 -0
  32. package/dist/widgets/gauge.d.ts +2 -0
  33. package/dist/widgets/image.d.ts +2 -0
  34. package/dist/widgets/label.d.ts +2 -0
  35. package/dist/widgets/latest-card.d.ts +2 -0
  36. package/dist/widgets/map-geometry.d.ts +17 -0
  37. package/dist/widgets/map.d.ts +3 -0
  38. package/dist/widgets/natural-earth-data.d.ts +5 -0
  39. package/dist/widgets/severity.d.ts +4 -0
  40. package/dist/widgets/table.d.ts +2 -0
  41. package/dist/widgets/time-series-chart.d.ts +2 -0
  42. package/package.json +77 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/dashboard-renderer.tsx", "../src/frame.tsx", "../src/theme.ts", "../src/hooks.ts", "../src/widget.ts", "../src/widgets/severity.ts", "../src/widgets/alarm-count.tsx", "../src/widgets/alarm-table.tsx", "../src/flash.tsx", "../src/format.ts", "../src/widgets/command-button.tsx", "../src/widgets/command-status.ts", "../src/widgets/command-params.ts", "../src/widgets/entity-selector.tsx", "../src/widgets/gauge.tsx", "../src/chart-options.ts", "../src/echart.tsx", "../src/widgets/image.tsx", "../src/widgets/label.tsx", "../src/widgets/latest-card.tsx", "../src/widgets/map.tsx", "../src/basemap-context.tsx", "../src/map-runtime-context.tsx", "../src/widgets/map-geometry.ts", "../src/widgets/table.tsx", "../src/widgets/time-series-chart.tsx", "../src/registry.ts", "../src/connected-widget.tsx", "../src/options.ts", "../src/definition-options.ts"],
4
+ "sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// DashboardRenderer maps a parsed DashboardDefinition onto a real CSS Grid (ADR-039\n// amendment 2026-07-08): the canvas is `display:grid` with `repeat(columns, 1fr)`, so\n// the board FILLS whatever width its container gives it; each widget is placed by span\n// (`grid-column`/`grid-row`), layered by `z`, and nudged by an optional signed-pixel\n// `offset`. Container sizing (fill / fixed-width / fixed-height) is a mount knob the\n// host may override via the `sizing` prop. It is view-only \u2014 the canvas editor lives in\n// the console. This is the shared viewer the console and the reference /dash app mount.\n\nimport {\n activeBreakpoint,\n defaultHistoryWindow,\n fetchWidgetHistory,\n resolveWidgetBox,\n type Breakpoints,\n type CanvasBackground,\n type CanvasSizing,\n type DashboardDefinition,\n type SlotDefinition,\n type WidgetActions,\n type WidgetBox,\n type WidgetDataSource,\n type MeasurementSample,\n type SlotBinding,\n type WidgetInstance,\n} from '@devicechain/dashboards';\nimport { useEffect, useState, type CSSProperties } from 'react';\n\nimport { ConnectedWidget } from './connected-widget';\nimport {\n WidgetCandidatesProvider,\n WidgetSelectProvider,\n WidgetSubjectProvider,\n type WidgetCandidates,\n type WidgetSelect,\n} from './frame';\nimport { WIDGET_CHANNEL } from './registry';\n\nexport interface DashboardRendererProps {\n definition: DashboardDefinition;\n hub: WidgetDataSource;\n // The action seam (writes) for acting widgets (alarm ack/clear, command send). Omit\n // for a strictly read-only mount; the same instance as `hub` when the runtime provides\n // both (DashboardHub / SyntheticDataSource implement WidgetActions).\n actions?: WidgetActions;\n // Whether to backfill each widget from bucketedMeasurements (a real backend call).\n // Default true; set false for offline preview, where the data source (e.g.\n // SyntheticDataSource) supplies its own history and the backend must not be hit.\n seedHistory?: boolean;\n // The effective slot manifest, so a slot-based widget's history seed resolves to\n // the bound device (must match the bindings on `hub`). Omit for slot-free dashboards.\n bindings?: Record<string, SlotBinding>;\n // Host override for the definition's container sizing (the embed knob \u2014 a host can\n // force a dashboard authored `fill` into a fixed-px box, or vice versa). Omitted \u2192\n // the definition's own `canvas.sizing`.\n sizing?: CanvasSizing;\n // The selection callback (ADR-039 selection amendment): a widget (e.g. an alarm-table\n // originator drill) calls it to re-point a slot; the host accumulates it into the\n // selection overlay that produces `bindings`. Omit for a static dashboard \u2014 widgets\n // then render no drill affordance.\n select?: WidgetSelect;\n // The candidate provider (useSlotCandidates) an entity-selector widget reads to offer a\n // target slot's options. Omit alongside `select` for a static/edit/preview mount \u2014 the\n // selector then renders inert.\n candidates?: WidgetCandidates;\n}\n\nexport function DashboardRenderer({\n definition,\n hub,\n actions,\n seedHistory = true,\n bindings,\n sizing,\n select,\n candidates,\n}: DashboardRendererProps) {\n const breakpoint = useActiveBreakpoint(definition.canvas.breakpoints);\n const histories = useWidgetHistories(definition.widgets, seedHistory, bindings);\n\n const { grid, background: bg } = definition.canvas;\n const rowGap = typeof grid.gap === 'number' ? grid.gap : grid.gap.row;\n const colGap = typeof grid.gap === 'number' ? grid.gap : grid.gap.col;\n const effectiveSizing = sizing ?? definition.canvas.sizing;\n\n return (\n <div style={sizingStyle(effectiveSizing, bg)}>\n <WidgetSelectProvider select={select}>\n <WidgetCandidatesProvider candidates={candidates}>\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: `repeat(${grid.columns}, minmax(0, 1fr))`,\n gridAutoRows: `${grid.rowHeight}px`,\n columnGap: colGap,\n rowGap,\n width: '100%',\n }}\n >\n {definition.widgets.map((widget) => {\n const box = resolveWidgetBox(widget.layout, breakpoint);\n return (\n <div key={widget.id} style={gridItemStyle(box, grid.columns)}>\n <WidgetSubjectProvider label={widgetSubjectLabel(widget, definition.slots, bindings)}>\n <ConnectedWidget\n widget={widget}\n hub={hub}\n actions={actions}\n initialSamples={histories[widget.id]}\n />\n </WidgetSubjectProvider>\n </div>\n );\n })}\n </div>\n </WidgetCandidatesProvider>\n </WidgetSelectProvider>\n </div>\n );\n}\n\n// gridItemStyle maps a span box to a grid-item style. col/row are 0-based lines \u2192\n// CSS's 1-based `col+1 / span colSpan`. It CLAMPS the placement into the grid's\n// column count: a box whose `col`/`colSpan` overruns `columns` (a shrunk grid, a\n// hand-authored/overflowing stored box, a boundary-drag rounding edge) would\n// otherwise land in implicit `auto` tracks CSS appends past the `1fr` columns \u2014\n// unequal widths + horizontal overflow instead of a clamped placement. Rows are\n// unbounded (implicit rows are the intended vertical growth), so only columns clamp.\nexport function gridItemStyle(box: WidgetBox, columns: number): CSSProperties {\n const col = Math.min(Math.max(0, box.col), columns - 1);\n const colSpan = Math.min(Math.max(1, box.colSpan), columns - col);\n return {\n gridColumn: `${col + 1} / span ${colSpan}`,\n gridRow: `${box.row + 1} / span ${box.rowSpan}`,\n zIndex: box.z,\n transform: box.offset ? `translate(${box.offset.x}px, ${box.offset.y}px)` : undefined,\n // Let a grid item shrink below its content's intrinsic size so a wide chart/table\n // fits its track instead of overflowing it.\n minWidth: 0,\n minHeight: 0,\n };\n}\n\n// sizingStyle turns the container-sizing knob into the outer wrapper's box. `fill`\n// takes the mount container's full width+height (the grid fills it); `{width}` caps\n// the width (the fluid grid adjusts within it); `{height}` pins the height (rows\n// scroll). All scroll internally so the board owns its single scroll region. The\n// background (color AND image) paints on this wrapper so it covers the whole container\n// even when the grid's rows are shorter than the sizing box.\nexport function sizingStyle(sizing: CanvasSizing, bg: CanvasBackground | undefined): CSSProperties {\n const base: CSSProperties = {\n overflow: 'auto',\n backgroundColor: bg?.color ?? undefined,\n backgroundImage: bg?.imageUrl ? `url(${bg.imageUrl})` : undefined,\n backgroundSize: 'cover',\n };\n if (sizing === 'fill') return { ...base, width: '100%', height: '100%' };\n if ('width' in sizing) return { ...base, width: sizing.width, maxWidth: '100%', height: '100%' };\n return { ...base, width: '100%', height: sizing.height };\n}\n\n// ownGet reads a string-keyed map defensively: only an OWN property, so a hand-edited\n// definition naming a slot `__proto__`/`constructor` reads as absent rather than\n// returning a prototype object (the same defense the hub applies to slot lookups).\nfunction ownGet<T>(map: Record<string, T> | undefined, key: string): T | undefined {\n return map && Object.prototype.hasOwnProperty.call(map, key) ? map[key] : undefined;\n}\n\n// widgetSubjectLabel resolves the entity a widget shows \u2014 its datasource's bound device\n// or anchor \u2014 to a short label for the frame subtitle. A slot selector resolves through\n// the effective `bindings` (host manifest / selection overlay) first, then the slot's own\n// default, so the subtitle always names the CURRENTLY-resolved entity. Returns undefined\n// for a datasource-free widget (label/image), a tenant-wide alarm table, or a reserved\n// selector kind \u2014 the frame then shows no subtitle.\nexport function widgetSubjectLabel(\n widget: WidgetInstance,\n slots: Record<string, SlotDefinition> | undefined,\n bindings: Record<string, SlotBinding> | undefined,\n): string | undefined {\n const ds = widget.datasource;\n if (!ds) return undefined;\n let binding: SlotBinding | undefined;\n if (ds.kind === 'device') {\n binding = ds.deviceToken ? { kind: 'device', deviceToken: ds.deviceToken } : undefined;\n } else if (ds.kind === 'anchor') {\n binding = { kind: 'anchor', anchor: ds.anchor };\n } else if (ds.kind === 'slot') {\n const slot = ownGet(slots, ds.slot);\n // A SCOPED slot is authoritative in the resolved `bindings` only \u2014 the cascade omits\n // it when unbound, so falling back to its default would name an entity that isn't\n // being shown (a lying subtitle). A plain slot may still fall back to its default.\n binding = slot?.scope ? ownGet(bindings, ds.slot) : ownGet(bindings, ds.slot) ?? slot?.defaultBinding;\n }\n if (binding?.kind === 'device') return binding.deviceToken;\n if (binding?.kind === 'anchor') return binding.anchor.targetToken;\n return undefined;\n}\n\n// useActiveBreakpoint tracks the viewport width and returns the active breakpoint\n// name, recomputing on resize.\nfunction useActiveBreakpoint(breakpoints: Breakpoints): string {\n const [width, setWidth] = useState(() =>\n typeof window === 'undefined' ? 0 : window.innerWidth,\n );\n useEffect(() => {\n const onResize = () => setWidth(window.innerWidth);\n window.addEventListener('resize', onResize);\n return () => window.removeEventListener('resize', onResize);\n }, []);\n return activeBreakpoint(breakpoints, width);\n}\n\n// useWidgetHistories backfills each widget once from bucketedMeasurements and\n// returns a stable id\u2192samples map (a widget's array is set once, so it doesn't\n// churn the stream's seed). Fetched in parallel; failures yield an empty seed.\nfunction useWidgetHistories(\n widgets: WidgetInstance[],\n enabled: boolean,\n bindings: Record<string, SlotBinding> | undefined,\n): Record<string, MeasurementSample[]> {\n const [histories, setHistories] = useState<Record<string, MeasurementSample[]>>({});\n // Value-compare the manifest so an unchanged-but-new object reference doesn't\n // refetch history every render.\n const bindingsKey = bindings ? JSON.stringify(bindings) : null;\n\n useEffect(() => {\n // Preview (enabled=false) must not touch the backend; clear any prior seed so a\n // toggle from live\u2192preview doesn't leave stale real history under synthetic data.\n if (!enabled) {\n setHistories({});\n return;\n }\n let cancelled = false;\n const historyWindow = defaultHistoryWindow();\n Promise.all(\n widgets.map(\n // Only measurement widgets consume a history seed; a non-measurement widget\n // (alarm, command) reconciles from its own channel, so backfilling it would fire\n // a wasted (and all-measurements) bucketedMeasurements query it ignores.\n async (w) =>\n [\n w.id,\n WIDGET_CHANNEL[w.type] === 'measurement'\n ? await fetchWidgetHistory(w, historyWindow, bindings)\n : [],\n ] as const,\n ),\n ).then((entries) => {\n if (!cancelled) setHistories(Object.fromEntries(entries));\n });\n return () => {\n cancelled = true;\n };\n // bindings read via bindingsKey (value identity), not reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [widgets, enabled, bindingsKey]);\n\n return histories;\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The shared card frame every widget renders inside \u2014 a bordered, themed surface\n// that fills its dashboard-canvas slot, with an optional title bar. Styled with\n// inline CSS-variable references so it themes from the host without Tailwind.\n\nimport { createContext, useContext, type CSSProperties, type ReactNode } from 'react';\n\nimport type { SelectionCandidate, SelectionTarget } from '@devicechain/dashboards';\n\nimport { css } from './theme';\n\n// WidgetSubjectContext carries the resolved entity a widget shows (its datasource's\n// bound device/anchor), computed once by the renderer and provided per widget. The\n// frame reads it as a subtitle so every board answers \"which asset is this?\" \u2014 and,\n// because it reads the RESOLVED binding, it updates for free once selection re-points a\n// slot (ADR-039 selection-overlay amendment). Kept as ambient context, not a widget\n// prop, so the pure widget contract (widget, data) stays untouched and no widget needs\n// to know how a subject is resolved.\nconst WidgetSubjectContext = createContext<string | undefined>(undefined);\n\nexport function WidgetSubjectProvider({\n label,\n children,\n}: {\n label: string | undefined;\n children: ReactNode;\n}) {\n return <WidgetSubjectContext.Provider value={label}>{children}</WidgetSubjectContext.Provider>;\n}\n\n// WidgetSelect is a view-interaction callback (ADR-039 selection amendment): a widget\n// drives a slot selection (an alarm-table originator drill, a context-selector pick) by\n// naming its target slot + the entity to bind. Kept DISTINCT from WidgetActions (the\n// write seam) \u2014 selection changes only the view's binding overlay, never the backend.\n// Provided by the renderer from the host; absent (undefined) in edit mode and any host\n// that wires no selection, so a widget feature-detects it before offering a drill.\nexport type WidgetSelect = (target: SelectionTarget) => void;\n\nconst WidgetSelectContext = createContext<WidgetSelect | undefined>(undefined);\n\nexport function WidgetSelectProvider({\n select,\n children,\n}: {\n select: WidgetSelect | undefined;\n children: ReactNode;\n}) {\n return <WidgetSelectContext.Provider value={select}>{children}</WidgetSelectContext.Provider>;\n}\n\n// useWidgetSelect returns the ambient selection callback, or undefined where selection is\n// not wired (edit mode, a read-only embedder). A widget renders its drill affordance only\n// when this is defined.\nexport function useWidgetSelect(): WidgetSelect | undefined {\n return useContext(WidgetSelectContext);\n}\n\n// WidgetCandidates is the ambient companion to WidgetSelect (ADR-039 selection amendment): a\n// context/entity-selector widget asks it for the options a target slot may bind to. The host\n// computes candidates from the definition's scope + resolved bindings (useSlotCandidates), so\n// the widget stays presentational and the pure widget contract stays untouched. Distinct from\n// the hub's data channels \u2014 candidates are a fetch-on-open list, not a live stream. Undefined\n// where not wired (edit/preview, a host that supplies no selection) \u2192 the selector is inert.\nexport type WidgetCandidates = (slot: string) => Promise<SelectionCandidate[]>;\n\nconst WidgetCandidatesContext = createContext<WidgetCandidates | undefined>(undefined);\n\nexport function WidgetCandidatesProvider({\n candidates,\n children,\n}: {\n candidates: WidgetCandidates | undefined;\n children: ReactNode;\n}) {\n return <WidgetCandidatesContext.Provider value={candidates}>{children}</WidgetCandidatesContext.Provider>;\n}\n\n// useWidgetCandidates returns the ambient candidate provider, or undefined where selection is\n// not wired. A selector widget renders its picker only when both this and useWidgetSelect are\n// defined.\nexport function useWidgetCandidates(): WidgetCandidates | undefined {\n return useContext(WidgetCandidatesContext);\n}\n\nexport interface WidgetFrameProps {\n title?: string;\n // An explicit subject line; when omitted the frame falls back to the resolved-entity\n // subject from WidgetSubjectContext.\n subtitle?: string;\n children: ReactNode;\n // Extra style for the body region (e.g. removing padding for a chart canvas).\n bodyStyle?: CSSProperties;\n}\n\nexport function WidgetFrame({ title, subtitle, children, bodyStyle }: WidgetFrameProps) {\n const subject = useContext(WidgetSubjectContext);\n const sub = subtitle ?? subject;\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n width: '100%',\n height: '100%',\n boxSizing: 'border-box',\n background: css('card'),\n color: css('card-foreground'),\n border: `1px solid ${css('border')}`,\n borderRadius: 'var(--radius, 8px)',\n overflow: 'hidden',\n }}\n >\n {title || sub ? (\n <div\n style={{\n padding: '8px 12px',\n borderBottom: `1px solid ${css('border')}`,\n }}\n >\n {title ? (\n <div style={{ fontSize: 12, fontWeight: 600, color: css('muted-foreground') }}>{title}</div>\n ) : null}\n {sub ? (\n <div\n title={sub}\n style={{\n fontSize: 11,\n fontWeight: 400,\n color: css('muted-foreground'),\n opacity: 0.75,\n marginTop: title ? 2 : 0,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {sub}\n </div>\n ) : null}\n </div>\n ) : null}\n <div style={{ flex: 1, minHeight: 0, ...bodyStyle }}>{children}</div>\n </div>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// Theming without Tailwind (ADR-039: widgets are embeddable \u2014 no Tailwind leakage).\n//\n// Widgets style themselves with inline styles that reference the SAME CSS custom\n// properties the console (and any host) defines \u2014 `--card`, `--foreground`,\n// `--border`, `--chart-1..5`, etc. A host that sets those variables themes the\n// widgets for free (light/dark included); a host without Tailwind still works.\n//\n// DOM elements can use `hsl(var(--card))` directly. ECharts renders to a <canvas>,\n// which cannot resolve CSS variables \u2014 so for charts we read the variables off the\n// live computed style and hand ECharts concrete color strings (resolveChartTheme).\n\n// css returns an `hsl(var(--name))` reference for use in an inline style. The host\n// defines the variable as HSL channels (e.g. `--card: 0 0% 100%`), matching the\n// console's token set.\nexport function css(name: ThemeVar): string {\n return `hsl(var(--${name}))`;\n}\n\n// The subset of the console's design tokens widgets rely on. Kept small and\n// semantic so the contract a host must provide is explicit and stable.\nexport type ThemeVar =\n | 'background'\n | 'foreground'\n | 'card'\n | 'card-foreground'\n | 'muted'\n | 'muted-foreground'\n | 'primary'\n | 'border'\n | 'destructive';\n\n// Concrete colors for an ECharts canvas, resolved from the CSS variables in scope\n// at a given element (charts can't use CSS vars directly).\nexport interface ChartTheme {\n foreground: string;\n mutedForeground: string;\n border: string;\n series: string[];\n}\n\n// Fallbacks used when a variable is absent (e.g. jsdom, or a host that hasn't set\n// the token) so a chart still renders in sensible default colors.\nconst FALLBACK: ChartTheme = {\n foreground: 'hsl(240 10% 3.9%)',\n mutedForeground: 'hsl(240 3.8% 46.1%)',\n border: 'hsl(240 5.9% 90%)',\n series: [\n 'hsl(220 70% 50%)',\n 'hsl(160 60% 45%)',\n 'hsl(30 80% 55%)',\n 'hsl(280 65% 60%)',\n 'hsl(340 75% 55%)',\n ],\n};\n\nfunction readVar(style: CSSStyleDeclaration, name: string, fallback: string): string {\n const raw = style.getPropertyValue(`--${name}`).trim();\n return raw ? `hsl(${raw})` : fallback;\n}\n\n// resolveChartTheme reads the theme tokens in scope at `el` into concrete colors\n// for ECharts. Reading at the element (not the document root) means a widget\n// nested under a re-themed subtree picks up the local override.\nexport function resolveChartTheme(el: Element): ChartTheme {\n const style = getComputedStyle(el);\n return {\n foreground: readVar(style, 'foreground', FALLBACK.foreground),\n mutedForeground: readVar(style, 'muted-foreground', FALLBACK.mutedForeground),\n border: readVar(style, 'border', FALLBACK.border),\n series: FALLBACK.series.map((fallback, i) => readVar(style, `chart-${i + 1}`, fallback)),\n };\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// React bindings between the imperative DashboardHub / DOM theme and widget state.\n\nimport type {\n AlarmRow,\n AlarmSubscription,\n CommandRow,\n CommandSubscription,\n DashboardDefinition,\n DatasourceSelector,\n EntityCandidateLister,\n LocationSample,\n LocationSubscription,\n MemberResolver,\n MeasurementSample,\n SelectionCandidate,\n SlotBinding,\n WidgetDataSource,\n} from '@devicechain/dashboards';\nimport {\n bindingsWithoutScopedSlots,\n hasScopedSlots,\n resolveContextBindings,\n resolveSlotCandidates,\n} from '@devicechain/dashboards';\nimport { useEffect, useMemo, useRef, useState, useSyncExternalStore, type RefObject } from 'react';\n\nimport { useWidgetCandidates, type WidgetCandidates } from './frame';\nimport { resolveChartTheme, type ChartTheme } from './theme';\n\n// useResolvedBindings runs the scoped-slot cascade (ADR-039 selection amendment) for a\n// host: it takes the synchronous base manifest (effectiveBindings) plus the accumulated\n// selection overlay and returns the settled slot\u2192binding map the host feeds to the hub +\n// renderer. Selection state lives in the host (so it survives the hub rebuild a rebind\n// triggers); this hook just derives bindings from it.\n//\n// The common scope-FREE dashboard needs no async work \u2014 the synchronous overlay (base\n// with the selection laid over it) is already correct, and the hook returns it directly,\n// preserving today's behavior. Only a dashboard with scoped slots runs the async pass,\n// guarded by a monotonic generation so a slow members response can't overwrite a newer\n// selection; it keeps the last settled map until the new one arrives (one re-key per\n// walk, never a stale partial). A resolution error is fail-safe: keep the last map.\nexport function useResolvedBindings(\n definition: DashboardDefinition,\n base: Record<string, SlotBinding>,\n selection: Record<string, SlotBinding>,\n resolver: MemberResolver,\n): Record<string, SlotBinding> {\n const scoped = useMemo(() => hasScopedSlots(definition), [definition]);\n // Selection wins over base. Correct as-is for root + manual slots and every scope-free\n // dashboard; scoped `first` slots are refined by the async pass below.\n const overlaid = useMemo(() => ({ ...base, ...selection }), [base, selection]);\n // Seed the settled map with scoped slots STRIPPED, so a scoped dashboard's first paint\n // is unbound-not-stale (a scoped slot's default may be out of context) until the cascade\n // derives it. For a scope-free dashboard this equals `overlaid`.\n const [resolved, setResolved] = useState(() => bindingsWithoutScopedSlots(definition, overlaid));\n const genRef = useRef(0);\n\n useEffect(() => {\n // Scope-free: `overlaid` is authoritative. Keep `resolved` synced to it anyway so that\n // if the definition later gains a scope (an authoring edit), the flip to the `resolved`\n // path starts from the CURRENT bindings, not a stale mount-time snapshot.\n if (!scoped) {\n setResolved((prev) => (sameBindings(prev, overlaid) ? prev : overlaid));\n return;\n }\n const gen = ++genRef.current;\n let live = true;\n resolveContextBindings(definition, base, selection, resolver)\n .then((next) => {\n if (!live || gen !== genRef.current) return;\n // Only swap when the value actually changed, so an equal re-resolve doesn't churn\n // a new object reference (and rebuild the hub) for nothing.\n setResolved((prev) => (sameBindings(prev, next) ? prev : next));\n })\n .catch(() => {\n /* fail-safe: keep the last settled map */\n });\n return () => {\n live = false;\n };\n }, [scoped, definition, base, selection, resolver, overlaid]);\n\n return scoped ? resolved : overlaid;\n}\n\n// sameBindings is a value compare of two slot\u2192binding maps (stable-key JSON), so the\n// hook can suppress a no-op re-key.\nfunction sameBindings(a: Record<string, SlotBinding>, b: Record<string, SlotBinding>): boolean {\n const ak = Object.keys(a).sort();\n const bk = Object.keys(b).sort();\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i += 1) {\n if (ak[i] !== bk[i]) return false;\n if (JSON.stringify(a[ak[i]]) !== JSON.stringify(b[bk[i]])) return false;\n }\n return true;\n}\n\n// useSlotCandidates builds the ambient candidate provider a host passes to the renderer\n// (ADR-039 selection amendment): a function slot\u2192candidates that a context/entity-selector\n// widget calls. It closes over the CURRENT resolved bindings, so it is rebuilt whenever a\n// binding changes \u2014 a selector's option set (a scoped child's members) then follows the\n// parent for free, and a widget re-fetches because the provider identity changed. Returns\n// undefined when no lister is supplied (a host that wires no selection), so the selector\n// stays inert. Keyed by the bindings' VALUE so an equal-but-new bindings object doesn't\n// churn the provider (and every open selector's fetch) each render.\nexport function useSlotCandidates(\n definition: DashboardDefinition,\n bindings: Record<string, SlotBinding>,\n resolver: MemberResolver,\n lister: EntityCandidateLister | undefined,\n): WidgetCandidates | undefined {\n const bindingsKey = JSON.stringify(bindings);\n return useMemo(\n () => {\n if (!lister) return undefined;\n return (slot: string) => resolveSlotCandidates(definition, slot, bindings, resolver, lister);\n },\n // bindings read via bindingsKey (value identity), not reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [definition, bindingsKey, resolver, lister],\n );\n}\n\n// The state a selector widget renders from: the target slot's current candidate options,\n// plus load/error flags. `wired` reports whether an ambient candidate provider exists \u2014 a\n// selector feature-detects on it (with the select callback) to stay inert where selection\n// isn't wired (edit/preview, a host that supplies no lister) rather than showing a dead picker.\nexport interface CandidatesState {\n candidates: SelectionCandidate[];\n loading: boolean;\n error: unknown | null;\n wired: boolean;\n}\n\n// useCandidates resolves the options a selector widget offers for `slot` through the ambient\n// candidate provider. Re-fetches when the provider (i.e. the resolved bindings) or the slot\n// changes, guarded by a monotonic generation so a slow list can't overwrite a newer one\n// (the parent-switch-then-open race). Fail-safe: an error yields an empty option set.\nexport function useCandidates(slot: string | undefined): CandidatesState {\n const provider = useWidgetCandidates();\n const wired = !!provider;\n // Seed loading true when a fetch is imminent (provider + slot present) so the first paint\n // reads \"Loading\u2026\", not a spurious \"No options\" for the frame before the effect runs.\n const [state, setState] = useState<Omit<CandidatesState, 'wired'>>(() => ({\n candidates: [],\n loading: Boolean(provider && slot),\n error: null,\n }));\n const genRef = useRef(0);\n\n useEffect(() => {\n if (!provider || !slot) {\n setState({ candidates: [], loading: false, error: null });\n return;\n }\n const gen = ++genRef.current;\n let live = true;\n // Clear any prior error when a fresh (healthy) fetch starts, so a retry doesn't show\n // \"Loading\u2026\" and a stale error banner together.\n setState((prev) => ({ ...prev, loading: true, error: null }));\n provider(slot)\n .then((candidates) => {\n if (live && gen === genRef.current) setState({ candidates, loading: false, error: null });\n })\n .catch((error: unknown) => {\n if (live && gen === genRef.current) setState({ candidates: [], loading: false, error });\n });\n return () => {\n live = false;\n };\n }, [provider, slot]);\n\n return { ...state, wired };\n}\n\nexport interface MeasurementStreamState {\n // The most recent sample per measurement name (drives cards, gauges, tables).\n latest: Record<string, MeasurementSample>;\n // A rolling, chronological window of recent samples (drives the time chart).\n samples: MeasurementSample[];\n error: unknown | null;\n}\n\n// The state an alarm widget renders from: the current rows (newest first, capped),\n// the total match count (past the page \u2014 drives alarm-count), and load/error flags.\nexport interface AlarmStreamState {\n alarms: AlarmRow[];\n total: number;\n loading: boolean;\n error: unknown | null;\n}\n\n// The state a command widget renders from: the resolved target device (null = unbound,\n// so it can't issue), the recent commands (newest first, capped) with live status, the\n// total count, and load/error flags.\nexport interface CommandStreamState {\n deviceToken: string | null;\n commands: CommandRow[];\n total: number;\n loading: boolean;\n error: unknown | null;\n}\n\n// The state a map widget renders from: the located devices' positions, the device\n// tokens the selector resolved to (a never-located device has no position, so the two\n// lists differ), whether the viewer was REFUSED, and load/error flags.\n//\n// \uD83D\uDD34 `forbidden` is a field of its own and not an `error`. `location:read` is\n// deliberately outside the read-only viewer baseline, so being refused is the ordinary\n// case for an ordinary member \u2014 an error state would show an outage where the platform\n// is working exactly as designed, and an empty `locations` would blame the devices for\n// a fact about the caller.\nexport interface LocationStreamState {\n locations: LocationSample[];\n deviceTokens: string[];\n forbidden: boolean;\n loading: boolean;\n error: unknown | null;\n}\n\nexport interface MeasurementStreamOptions {\n // Max samples retained in the rolling window (oldest dropped past this).\n window?: number;\n // Historical samples to seed the window with before live data arrives (from a\n // bucketedMeasurements backfill). Assumed chronological and older than the live\n // tail; live samples for the same measurement name override the seed's latest.\n initialSamples?: MeasurementSample[];\n}\n\nconst EMPTY: MeasurementStreamState = { latest: {}, samples: [], error: null };\n\n// Build a name\u2192last-sample map from a chronological list (last occurrence wins).\nfunction latestOf(samples: MeasurementSample[]): Record<string, MeasurementSample> {\n const latest: Record<string, MeasurementSample> = {};\n for (const s of samples) latest[s.name] = s;\n return latest;\n}\n\n// useMeasurementStream subscribes a widget's datasource through the hub and keeps\n// the latest value per measurement plus a bounded rolling window. It re-subscribes\n// when the datasource changes (compared by value, so a re-rendered-but-equal\n// selector doesn't churn the subscription) and tears down on unmount. When\n// initialSamples is given, it is merged ahead of the live tail so a chart shows\n// history immediately (and can arrive after the live stream opens).\nexport function useMeasurementStream(\n hub: WidgetDataSource,\n datasource: DatasourceSelector | undefined,\n options: MeasurementStreamOptions = {},\n): MeasurementStreamState {\n const windowSize = options.window ?? 300;\n const initialSamples = options.initialSamples;\n const [live, setLive] = useState<MeasurementStreamState>(EMPTY);\n\n // Value-compare the selector so an unchanged-but-new object reference doesn't\n // resubscribe every render.\n const key = datasource ? JSON.stringify(datasource) : null;\n\n useEffect(() => {\n setLive(EMPTY); // reset the live buffer whenever the datasource changes (or clears)\n if (!datasource) return;\n\n return hub.subscribeWidget(datasource, {\n next: (sample) =>\n setLive((prev) => {\n const samples = prev.samples.concat(sample);\n if (samples.length > windowSize) samples.splice(0, samples.length - windowSize);\n return { latest: { ...prev.latest, [sample.name]: sample }, samples, error: null };\n }),\n error: (err) => setLive((prev) => ({ ...prev, error: err })),\n });\n // `datasource` is intentionally read via `key` (value identity), not reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hub, key, windowSize]);\n\n // Layer the history seed under the live tail. Kept separate from the live buffer\n // so late-arriving history (async backfill) recomputes without touching the\n // subscription, and switching datasource clears live while the new seed applies.\n return useMemo(() => {\n if (!initialSamples || initialSamples.length === 0) return live;\n const merged = initialSamples.concat(live.samples);\n const samples =\n merged.length > windowSize ? merged.slice(merged.length - windowSize) : merged;\n return {\n latest: { ...latestOf(initialSamples), ...live.latest },\n samples,\n error: live.error,\n };\n }, [initialSamples, live, windowSize]);\n}\n\nconst EMPTY_ALARMS: AlarmStreamState = { alarms: [], total: 0, loading: true, error: null };\n\n// useAlarmStream binds an alarm widget's scope+filters through the hub's alarm channel\n// and keeps the latest snapshot. It re-subscribes when the subscription changes\n// (value-compared, so a re-rendered-but-equal object doesn't churn) and tears down on\n// unmount. The hub delivers whole snapshots (query-then-reconcile), so this holds the\n// last one rather than accumulating \u2014 the query is the source of truth.\nexport function useAlarmStream(\n hub: WidgetDataSource,\n subscription: AlarmSubscription,\n): AlarmStreamState {\n const [state, setState] = useState<AlarmStreamState>(EMPTY_ALARMS);\n\n // Value-compare the subscription so an unchanged-but-new object reference doesn't\n // resubscribe every render.\n const key = JSON.stringify(subscription);\n\n useEffect(() => {\n setState(EMPTY_ALARMS); // reset to loading whenever the subscription changes\n return hub.subscribeAlarms(subscription, {\n next: (snapshot) =>\n setState({ alarms: snapshot.alarms, total: snapshot.total, loading: false, error: null }),\n error: (err) => setState((prev) => ({ ...prev, loading: false, error: err })),\n });\n // `subscription` is intentionally read via `key` (value identity), not reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hub, key]);\n\n return state;\n}\n\nconst EMPTY_COMMANDS: CommandStreamState = {\n deviceToken: null,\n commands: [],\n total: 0,\n loading: true,\n error: null,\n};\n\n// useCommandStream binds a command widget's scope through the hub's control channel and\n// keeps the latest command-history snapshot. It re-subscribes when the subscription\n// changes (value-compared) and tears down on unmount. The hub delivers whole snapshots\n// (poll-then-emit), so this holds the last one rather than accumulating.\nexport function useCommandStream(\n hub: WidgetDataSource,\n subscription: CommandSubscription,\n): CommandStreamState {\n const [state, setState] = useState<CommandStreamState>(EMPTY_COMMANDS);\n\n // Value-compare the subscription so an unchanged-but-new object reference doesn't\n // resubscribe every render.\n const key = JSON.stringify(subscription);\n\n useEffect(() => {\n setState(EMPTY_COMMANDS); // reset to loading whenever the subscription changes\n return hub.subscribeCommands(subscription, {\n next: (snapshot) =>\n setState({\n deviceToken: snapshot.deviceToken,\n commands: snapshot.commands,\n total: snapshot.total,\n loading: false,\n error: null,\n }),\n error: (err) => setState((prev) => ({ ...prev, loading: false, error: err })),\n });\n // `subscription` is intentionally read via `key` (value identity), not reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hub, key]);\n\n return state;\n}\n\nconst EMPTY_LOCATIONS: LocationStreamState = {\n locations: [],\n deviceTokens: [],\n forbidden: false,\n loading: true,\n error: null,\n};\n\n// useLocationStream binds a map widget's selector through the hub's location channel and\n// keeps the latest position snapshot. It re-subscribes when the subscription changes\n// (value-compared, so a re-rendered-but-equal object doesn't churn) and tears down on\n// unmount. The hub delivers whole snapshots (poll-then-emit), so this holds the last one\n// rather than accumulating.\n//\n// A `forbidden` snapshot CLEARS the positions as it sets the flag: an authority can be\n// revoked mid-session, and keeping the last-seen coordinates on screen under a\n// permission banner would leave a refused viewer looking at the very data they were\n// just refused.\nexport function useLocationStream(\n hub: WidgetDataSource,\n subscription: LocationSubscription,\n): LocationStreamState {\n const [state, setState] = useState<LocationStreamState>(EMPTY_LOCATIONS);\n\n // Value-compare the subscription so an unchanged-but-new object reference doesn't\n // resubscribe every render.\n const key = JSON.stringify(subscription);\n\n useEffect(() => {\n setState(EMPTY_LOCATIONS); // reset to loading whenever the subscription changes\n return hub.subscribeLocations(subscription, {\n next: (snapshot) =>\n setState(\n snapshot.kind === 'forbidden'\n ? { locations: [], deviceTokens: [], forbidden: true, loading: false, error: null }\n : {\n locations: snapshot.locations,\n deviceTokens: snapshot.deviceTokens,\n forbidden: false,\n loading: false,\n error: null,\n },\n ),\n error: (err) => setState((prev) => ({ ...prev, loading: false, error: err })),\n });\n // `subscription` is intentionally read via `key` (value identity), not reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hub, key]);\n\n return state;\n}\n\n// A widget's bound-entity availability: 'unknown' until the async check resolves\n// (render optimistically meanwhile), then 'available', or 'unavailable' when the bound\n// device no longer exists (a deleted device's stable token, ADR-044).\nexport type DatasourceAvailability = 'unknown' | 'available' | 'unavailable';\n\n// While a widget shows \"unavailable\", re-check on this cadence so a device deleted then\n// recreated with the same token (ADR-042 frees tokens on delete) recovers on a long-lived\n// viewer without a reload. Only runs while unavailable \u2014 a live device is checked once.\nconst AVAILABILITY_REVALIDATE_MS = 60_000;\n\n// useDatasourceAvailability resolves whether a widget's bound device still exists,\n// WITHOUT blocking the widget's data stream (optimistic + async): it starts 'unknown'\n// (the widget renders normally), then flips to 'available'/'unavailable' once the hub's\n// existence check resolves. Fails open (\u2192'available') so a check outage never falsely\n// blanks a live widget. A widget with no datasource is trivially available (no query).\n// Re-checks when the datasource changes (value-compared), and periodically while\n// unavailable so a recreated device recovers.\nexport function useDatasourceAvailability(\n hub: WidgetDataSource,\n datasource: DatasourceSelector | undefined,\n): DatasourceAvailability {\n const [state, setState] = useState<DatasourceAvailability>('unknown');\n const key = datasource ? JSON.stringify(datasource) : null;\n\n useEffect(() => {\n // No datasource (label/image, or tenant-wide) has nothing to validate \u2014 available\n // immediately, no query, no re-render churn.\n if (!datasource) {\n setState('available');\n return;\n }\n setState('unknown');\n let cancelled = false;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n const stopTimer = (): void => {\n if (timer) {\n clearInterval(timer);\n timer = undefined;\n }\n };\n\n const check = (): void => {\n hub\n .isDatasourceAvailable(datasource)\n .then((ok) => {\n if (cancelled) return;\n setState(ok ? 'available' : 'unavailable');\n if (ok) stopTimer(); // recovered (or was fine) \u2014 stop polling\n })\n .catch(() => {\n if (cancelled) return;\n setState('available'); // fail open\n stopTimer();\n });\n };\n\n check();\n // Poll only matters once unavailable; harmlessly runs until the first check clears it\n // for a live device (cleared in the resolver above, well before the first tick).\n timer = setInterval(check, AVAILABILITY_REVALIDATE_MS);\n\n return () => {\n cancelled = true;\n stopTimer();\n };\n // `datasource` is intentionally read via `key` (value identity), not reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hub, key]);\n\n return state;\n}\n\nexport interface ElementSize {\n width: number;\n height: number;\n}\n\n// useElementSize tracks an element's content-box size via ResizeObserver, so a\n// widget can scale its contents (chart fonts, gauge ticks, a big number) to the\n// slot it was resized to \u2014 ECharts' resize() re-lays-out the canvas but never\n// scales absolute font sizes, so a small widget crams without this. Returns\n// {0,0} until measured (and where ResizeObserver is unavailable, e.g. jsdom); the\n// consumers treat a zero size as \"use default sizing\".\nexport function useElementSize<T extends HTMLElement>(): [RefObject<T | null>, ElementSize] {\n const ref = useRef<T>(null);\n const [size, setSize] = useState<ElementSize>({ width: 0, height: 0 });\n\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect;\n if (!rect) return;\n setSize((prev) =>\n prev.width === rect.width && prev.height === rect.height\n ? prev\n : { width: rect.width, height: rect.height },\n );\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n return [ref, size];\n}\n\n// --- shared chart-theme store ------------------------------------------------\n//\n// Charts need concrete colors resolved from the CSS-variable theme. Rather than\n// have every chart widget mount its own MutationObserver on <html> and recompute\n// the same theme, one page-wide observer resolves it once per change and every\n// widget reads the shared, memoized value via useSyncExternalStore \u2014 so a busy\n// dashboard pays O(1), not O(number of charts), per theme toggle.\n\nlet sharedTheme: ChartTheme | null = null;\nconst themeListeners = new Set<() => void>();\nlet themeObserver: MutationObserver | null = null;\n\nfunction recomputeTheme(): void {\n sharedTheme = resolveChartTheme(document.documentElement);\n}\n\nfunction subscribeTheme(listener: () => void): () => void {\n if (!themeObserver) {\n recomputeTheme();\n themeObserver = new MutationObserver(() => {\n recomputeTheme();\n for (const l of themeListeners) l();\n });\n themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'style'],\n });\n }\n themeListeners.add(listener);\n return () => {\n themeListeners.delete(listener);\n if (themeListeners.size === 0 && themeObserver) {\n themeObserver.disconnect();\n themeObserver = null;\n sharedTheme = null;\n }\n };\n}\n\nfunction getThemeSnapshot(): ChartTheme {\n if (!sharedTheme) recomputeTheme();\n return sharedTheme as ChartTheme;\n}\n\n// useChartTheme returns the current chart colors and re-renders the caller when\n// the document theme changes (a light/dark class or inline-style swap on <html>).\nexport function useChartTheme(): ChartTheme {\n return useSyncExternalStore(subscribeTheme, getThemeSnapshot, getThemeSnapshot);\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The widget contract \u2014 the shape every built-in (and every future custom widget)\n// implements, plus small helpers for reading a widget's opaque `options` bag and\n// picking the measurement a single-value widget should show.\n\nimport type { MeasurementSample, WidgetActions, WidgetInstance } from '@devicechain/dashboards';\nimport type { ComponentType } from 'react';\n\nimport type { MeasurementStreamState } from './hooks';\n\n// A widget is a PURE React component of its definition instance and already-\n// resolved live data \u2014 it never touches the DashboardHub directly. This is the\n// public custom-widget interface: a widget is a function of (widget, data), so it\n// renders identically from a live stream, a recorded window, or a test fixture.\n// ConnectedWidget binds the hub to this contract and dispatches on the widget's data\n// CHANNEL (see WIDGET_CHANNEL): measurement widgets receive a MeasurementStreamState\n// (the default), alarm widgets an AlarmStreamState. The data type is a generic so each\n// widget declares exactly the shape it consumes.\nexport interface WidgetProps<D = MeasurementStreamState> {\n widget: WidgetInstance;\n data: D;\n // The action seam (writes), threaded in by the renderer. Undefined when the runtime\n // supplies none \u2014 an acting widget then renders read-only. Present for the live hub and\n // the synthetic preview source alike, so an action widget renders identically in both.\n actions?: WidgetActions;\n}\n\nexport type WidgetComponent<D = MeasurementStreamState> = ComponentType<WidgetProps<D>>;\n\n// pickSample chooses the sample a single-value widget (card, gauge) shows: the\n// named measurement, or the first available when no name is pinned.\nexport function pickSample(\n latest: Record<string, MeasurementSample>,\n name: string | undefined,\n): MeasurementSample | undefined {\n return name ? latest[name] : Object.values(latest)[0];\n}\n\n// --- options helpers ---------------------------------------------------------\n// A widget's `options` is opaque JSON (Record<string, unknown>) owned by the\n// widget; these read a key defensively, returning undefined on the wrong type\n// rather than throwing on a hand-edited definition.\n\ntype Options = Record<string, unknown> | undefined;\n\nexport function optString(options: Options, key: string): string | undefined {\n const value = options?.[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function optNumber(options: Options, key: string): number | undefined {\n const value = options?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n// A boolean flag option: true only when explicitly stored true (absent/other \u2192 false), so\n// an opt-in feature stays off by default and a hand-edited value can't accidentally enable it.\nexport function optBoolean(options: Options, key: string): boolean {\n return options?.[key] === true;\n}\n\n// primaryMeasurementName is the measurement a single-value widget (card, gauge)\n// displays: an explicit `options.measurement`, else the first measurement its\n// datasource selects, else undefined (fall back to whatever arrives first).\nexport function primaryMeasurementName(widget: WidgetInstance): string | undefined {\n const explicit = optString(widget.options, 'measurement');\n if (explicit) return explicit;\n const ds = widget.datasource;\n if (ds && ds.measurements.length > 0) return ds.measurements[0];\n return undefined;\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// Alarm-severity presentation shared by the alarm widgets (ADR-041 vocabulary:\n// CRITICAL | MAJOR | MINOR | WARNING | INDETERMINATE). Severity is SEMANTIC color \u2014\n// deliberately fixed, not derived from the dashboard accent \u2014 so a critical alarm\n// reads the same red on every tenant's theme. The warm-to-cool ramp mirrors the\n// console's alarm badges (red \u2192 orange \u2192 amber \u2192 sky \u2192 slate) so an operator reads the\n// same hue for the same severity on both surfaces; shades are tuned to hold contrast as\n// a foreground color on both light and dark widget backgrounds.\n\nconst SEVERITY_COLORS: Record<string, string> = {\n CRITICAL: '#dc2626', // red-600\n MAJOR: '#f97316', // orange-500\n MINOR: '#d97706', // amber-600 (readable as foreground; console uses amber-400 as a fill)\n WARNING: '#0ea5e9', // sky-500 \u2014 cool end of the ramp, matching the console\n INDETERMINATE: '#64748b', // slate-500\n};\n\nconst SEVERITY_ORDER: Record<string, number> = {\n CRITICAL: 0,\n MAJOR: 1,\n MINOR: 2,\n WARNING: 3,\n INDETERMINATE: 4,\n};\n\nconst UNKNOWN_COLOR = '#64748b';\n\n// severityColor maps a severity to its badge/stripe color, falling back to a muted\n// slate for an unrecognized value (a hand-edited or future severity).\nexport function severityColor(severity: string): string {\n return SEVERITY_COLORS[severity] ?? UNKNOWN_COLOR;\n}\n\n// severityRank orders severities most-severe-first; unknown severities sort last.\nexport function severityRank(severity: string): number {\n return SEVERITY_ORDER[severity] ?? 99;\n}\n\n// severityLabel renders a severity in Title Case ('CRITICAL' \u2192 'Critical').\nexport function severityLabel(severity: string): string {\n if (!severity) return '';\n return severity.charAt(0).toUpperCase() + severity.slice(1).toLowerCase();\n}\n\n// highestSeverity returns the most severe severity present in a list, or undefined for\n// an empty list \u2014 the accent an alarm-count uses to signal \"how bad is the worst one\".\nexport function highestSeverity(severities: string[]): string | undefined {\n let best: string | undefined;\n for (const s of severities) {\n if (best === undefined || severityRank(s) < severityRank(best)) best = s;\n }\n return best;\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// alarm-count \u2014 a KPI card of how many alarms match the widget's scope+filter\n// (ADR-041). The number is the server total (past the loaded page); its color is the\n// most severe among the CURRENTLY-LOADED alarms (the newest page), and the worst\n// severity is also spelled out as text/aria so the signal never reads on hue alone (a\n// color-blind or screen-reader viewer still gets it). At zero it stays muted.\n\nimport { WidgetFrame } from '../frame';\nimport type { AlarmStreamState } from '../hooks';\nimport { useElementSize } from '../hooks';\nimport { css } from '../theme';\nimport { optString, type WidgetProps } from '../widget';\nimport { highestSeverity, severityColor, severityLabel } from './severity';\n\nconst clamp = (v: number, lo: number, hi: number): number => Math.max(lo, Math.min(hi, v));\n\nexport function AlarmCount({ widget, data }: WidgetProps<AlarmStreamState>) {\n const [sizeRef, size] = useElementSize<HTMLDivElement>();\n const { total, alarms, loading } = data;\n\n const worst = highestSeverity(alarms.map((a) => a.severity));\n // At zero (or before any alarm is loaded) stay neutral; otherwise take the worst\n // loaded severity's color as the alert accent.\n const alerting = total > 0 && worst != null;\n const color = alerting ? severityColor(worst) : css('muted-foreground');\n\n const valueFont = size.height > 0 ? clamp(Math.round(size.height / 2.8), 20, 56) : 40;\n const display = loading && total === 0 ? '\u2014' : String(total);\n const unit = total === 1 ? 'alarm' : 'alarms';\n const ariaLabel = alerting ? `${total} ${unit}, worst severity ${severityLabel(worst)}` : `${total} ${unit}`;\n\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div\n ref={sizeRef}\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n height: '100%',\n padding: '12px 16px',\n textAlign: 'center',\n }}\n >\n <div aria-label={ariaLabel} style={{ fontSize: valueFont, fontWeight: 700, lineHeight: 1, color }}>\n {display}\n </div>\n <div\n style={{\n fontSize: 11,\n color: css('muted-foreground'),\n marginTop: 6,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n <span>{unit}</span>\n {alerting ? <span>{` \u00B7 worst ${severityLabel(worst)}`}</span> : null}\n </div>\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// alarm-table \u2014 the raised alarms in the widget's scope (ADR-041), newest first, with\n// a severity stripe per row. When the runtime supplies an action seam AND the viewer\n// holds alarm:write, each active row gets Acknowledge / Clear controls; otherwise the\n// table is read-only. Bound through the hub's alarm channel, so it renders identically\n// from live data or the synthetic preview source.\n\nimport type { AlarmRow, WidgetActions } from '@devicechain/dashboards';\nimport { useState, type CSSProperties } from 'react';\n\nimport type { AlarmStreamState } from '../hooks';\nimport { FlashValue } from '../flash';\nimport { formatDateTime } from '../format';\nimport { WidgetFrame, useWidgetSelect, type WidgetSelect } from '../frame';\nimport { css } from '../theme';\nimport { optBoolean, optNumber, optString, type WidgetProps } from '../widget';\nimport { severityColor, severityLabel } from './severity';\n\nconst cell: CSSProperties = { padding: '6px 10px', textAlign: 'left', whiteSpace: 'nowrap' };\nconst headCell: CSSProperties = {\n ...cell,\n position: 'sticky',\n top: 0,\n background: css('card'),\n fontSize: 11,\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.04em',\n color: css('muted-foreground'),\n borderBottom: `1px solid ${css('border')}`,\n};\n\n// statusLabel collapses the 4-state model (state \u00D7 acknowledged) into the one signal an\n// operator reads, matching the console's alarm badges: cleared wins regardless of the\n// acknowledged flag; an active alarm is \"Acknowledged\" once owned, else \"Active\".\nfunction statusLabel(alarm: AlarmRow): string {\n if (alarm.state === 'CLEARED') return 'Cleared';\n return alarm.acknowledged ? 'Acknowledged' : 'Active';\n}\n\nexport function AlarmTable({ widget, data, actions }: WidgetProps<AlarmStreamState>) {\n const { alarms, total, loading } = data;\n // Actions render only when the runtime supplies a seam AND the viewer may write.\n const canAct = actions?.can('alarm:write') ?? false;\n const columns = canAct ? 8 : 7;\n // Round the triggering value to a fixed precision when configured; unset keeps the raw\n // value (formatValue's default), matching the table widget. An alarm's raw lastValue is\n // otherwise a full-width float (e.g. 31.19073834583732).\n const precision = optNumber(widget.options, 'precision');\n // Opt-in directional flash on the triggering value, per row (keyed by alarm token).\n const flash = optBoolean(widget.options, 'flashOnChange');\n // Drill: when a `selectionTarget` slot is configured AND the renderer wired a selection\n // callback (view mode, a host that supports it), a device originator becomes a link that\n // re-points that slot \u2014 driving the per-device widgets to the clicked thermostat\n // (ADR-039 selection amendment). Absent either, the originator is plain text.\n const select = useWidgetSelect();\n const selectionTarget = optString(widget.options, 'selectionTarget');\n const drillTo = select && selectionTarget ? { select, slot: selectionTarget } : undefined;\n\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>\n <div style={{ flex: 1, minHeight: 0, overflow: 'auto' }}>\n <table\n style={{\n width: '100%',\n borderCollapse: 'collapse',\n fontSize: 13,\n color: css('foreground'),\n fontVariantNumeric: 'tabular-nums',\n }}\n >\n <thead>\n <tr>\n <th style={{ ...headCell, width: 4, padding: 0 }} aria-hidden=\"true\" />\n <th scope=\"col\" style={headCell}>\n Severity\n </th>\n <th scope=\"col\" style={headCell}>\n Status\n </th>\n <th scope=\"col\" style={headCell}>\n Originator\n </th>\n <th scope=\"col\" style={headCell}>\n Alarm\n </th>\n <th scope=\"col\" style={{ ...headCell, textAlign: 'right' }}>\n Value\n </th>\n <th scope=\"col\" style={headCell}>\n Raised\n </th>\n {canAct ? (\n <th scope=\"col\" style={{ ...headCell, textAlign: 'right' }}>\n Actions\n </th>\n ) : null}\n </tr>\n </thead>\n <tbody>\n {alarms.length === 0 ? (\n <tr>\n <td colSpan={columns} style={{ ...cell, textAlign: 'center', color: css('muted-foreground') }}>\n {loading ? 'Loading\u2026' : 'No alarms'}\n </td>\n </tr>\n ) : (\n alarms.map((alarm) => {\n const color = severityColor(alarm.severity);\n return (\n <tr key={alarm.token} style={{ borderBottom: `1px solid ${css('border')}` }}>\n <td aria-hidden=\"true\" style={{ padding: 0, width: 4, background: color }} />\n <td style={cell}>\n <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>\n <span\n aria-hidden=\"true\"\n style={{ width: 8, height: 8, borderRadius: '50%', background: color, flexShrink: 0 }}\n />\n {severityLabel(alarm.severity)}\n </span>\n </td>\n <td style={{ ...cell, color: css('muted-foreground') }}>{statusLabel(alarm)}</td>\n <td style={cell}>\n <OriginatorCell alarm={alarm} drillTo={drillTo} />\n </td>\n <td style={cell} title={alarm.message ?? undefined}>\n {alarm.alarmKey}\n </td>\n <td style={{ ...cell, textAlign: 'right' }}>\n <FlashValue\n value={alarm.lastValue}\n precision={precision}\n enabled={flash}\n identity={alarm.token}\n />\n </td>\n <td style={{ ...cell, color: css('muted-foreground') }}>\n {formatDateTime(alarm.raisedTime)}\n </td>\n {canAct ? (\n <td style={{ ...cell, textAlign: 'right' }}>\n {/* actions is defined whenever canAct is true. */}\n <AlarmRowActions alarm={alarm} actions={actions as WidgetActions} />\n </td>\n ) : null}\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n {total > alarms.length ? (\n <div\n style={{\n padding: '4px 10px',\n fontSize: 11,\n color: css('muted-foreground'),\n borderTop: `1px solid ${css('border')}`,\n }}\n >\n Showing {alarms.length} of {total}\n </div>\n ) : null}\n </div>\n </WidgetFrame>\n );\n}\n\n// OriginatorCell renders the alarm's originator. When drill is wired AND the originator\n// is a device (it carries a token), it is a link that re-points the target slot to that\n// device; otherwise it is plain text (the token, or the originator type when tokenless).\n// The drill target is a device binding \u2014 a building-scoped alarm table's originators are\n// already members of the context, so the drill stays in-context.\nfunction OriginatorCell({\n alarm,\n drillTo,\n}: {\n alarm: AlarmRow;\n drillTo: { select: WidgetSelect; slot: string } | undefined;\n}) {\n if (drillTo && alarm.originatorType === 'device' && alarm.originatorToken) {\n const token = alarm.originatorToken;\n return (\n <button\n type=\"button\"\n onClick={() => drillTo.select({ slot: drillTo.slot, binding: { kind: 'device', deviceToken: token } })}\n title={`Show ${token} in the linked widgets`}\n style={{\n padding: 0,\n border: 'none',\n background: 'none',\n font: 'inherit',\n color: css('primary'),\n cursor: 'pointer',\n textDecoration: 'underline',\n textUnderlineOffset: 2,\n }}\n >\n {token}\n </button>\n );\n }\n return <>{alarm.originatorToken ?? alarm.originatorType}</>;\n}\n\nconst actionButton: CSSProperties = {\n padding: '2px 8px',\n fontSize: 12,\n lineHeight: 1.4,\n borderRadius: 4,\n border: `1px solid ${css('border')}`,\n background: css('card'),\n color: css('foreground'),\n cursor: 'pointer',\n};\n\n// AlarmRowActions owns the pending/error state for one row's Acknowledge / Clear. Only\n// an ACTIVE alarm is actionable (a cleared \u2014 or any future non-active \u2014 state shows\n// \"\u2014\"); an acknowledged one still shows Clear. After a successful mutation the hub\n// reconciles the table, so the row updates itself.\nfunction AlarmRowActions({ alarm, actions }: { alarm: AlarmRow; actions: WidgetActions }) {\n const [pending, setPending] = useState<'ack' | 'clear' | null>(null);\n const [failed, setFailed] = useState(false);\n\n if (alarm.state !== 'ACTIVE') {\n return <span style={{ color: css('muted-foreground') }}>\u2014</span>;\n }\n\n const run = (kind: 'ack' | 'clear', op: () => Promise<void>) => {\n setPending(kind);\n setFailed(false);\n // Call op() synchronously (so an immediate dispatch is observable) but guard a\n // synchronous throw too, so a misbehaving seam can't leave the row stuck-disabled.\n let promise: Promise<void>;\n try {\n promise = op();\n } catch {\n setFailed(true);\n setPending(null);\n return;\n }\n promise.catch(() => setFailed(true)).finally(() => setPending(null));\n };\n\n return (\n <span style={{ display: 'inline-flex', gap: 6, justifyContent: 'flex-end', alignItems: 'center' }}>\n {failed ? (\n <span\n role=\"img\"\n aria-label=\"Action failed \u2014 retry\"\n title=\"Action failed \u2014 retry\"\n style={{ color: severityColor('CRITICAL'), fontSize: 12 }}\n >\n \u26A0\n </span>\n ) : null}\n {!alarm.acknowledged ? (\n <button\n type=\"button\"\n style={{ ...actionButton, opacity: pending ? 0.6 : 1 }}\n disabled={pending !== null}\n aria-busy={pending === 'ack'}\n aria-label={`Acknowledge alarm ${alarm.alarmKey}`}\n onClick={() => run('ack', () => actions.acknowledgeAlarm(alarm.token))}\n >\n {pending === 'ack' ? '\u2026' : 'Ack'}\n </button>\n ) : null}\n <button\n type=\"button\"\n style={{ ...actionButton, opacity: pending ? 0.6 : 1 }}\n disabled={pending !== null}\n aria-busy={pending === 'clear'}\n aria-label={`Clear alarm ${alarm.alarmKey}`}\n onClick={() => run('clear', () => actions.clearAlarm(alarm.token))}\n >\n {pending === 'clear' ? '\u2026' : 'Clear'}\n </button>\n </span>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// Directional change-flash \u2014 an opt-in cue (widget option `flashOnChange`) that briefly\n// tints a numeric value green when it rises and red when it falls, then fades back, so a\n// glance catches direction of change on a live board (cf. a trading \"last\" column). Purely\n// presentational: no hub/model change, driven off the value the widget already renders.\n//\n// Single-value widgets (latest-card) color the number; a multi-row widget (table,\n// alarm-table) renders each value through <FlashValue>, so React's per-key component\n// instances give every row its own independent flash with no map bookkeeping. The gauge\n// (an opaque ECharts canvas whose number can't be CSS-transitioned) tints its container\n// instead \u2014 see flashBackgroundStyle.\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\n\nimport { formatValue } from './format';\n\nexport type FlashDirection = 'up' | 'down' | null;\n\n// Semantic up/down colors, mid-saturation so they read on both light and dark host themes;\n// a host may override with the --flash-up / --flash-down custom properties. The background\n// variants are translucent so a tint sits over the widget's own surface without hiding it.\nconst UP = {\n fg: 'var(--flash-up, hsl(142 72% 40%))',\n bg: 'var(--flash-up-bg, hsl(142 72% 45% / 0.16))',\n};\nconst DOWN = {\n fg: 'var(--flash-down, hsl(0 72% 51%))',\n bg: 'var(--flash-down-bg, hsl(0 72% 55% / 0.16))',\n};\n\n// How long (ms) the solid tint holds before fading, and how long the fade back takes.\nconst HOLD_MS = 400;\nconst FADE_MS = 600;\n\n// useFlashOnChange watches a numeric value and returns the direction of its most recent\n// change ('up' | 'down') for a short window, then null. It stays null when the feature is\n// off, on the first value (no flash on mount), on a non-finite value, or when `identity`\n// changes \u2014 the last so a widget bound to an ANCHOR (whose one measurement slot interleaves\n// values from many member devices) adopts the new device's value instead of reading the\n// device-to-device jump as a real rise/fall. Pass the value's owning entity as `identity`\n// (e.g. `deviceToken:name`); leave it undefined for a single-device value.\n//\n// The held direction auto-clears after HOLD_MS; the consumer fades it out via flashTextStyle.\n// Every early-return path resets `direction` too: the effect only re-runs on a dep change,\n// and each such change has already cancelled the pending timer, so not clearing here would\n// strand a tint (e.g. value \u2192 null, or toggled off then on, mid-hold).\nexport function useFlashOnChange(\n value: number | null | undefined,\n enabled: boolean,\n identity?: string | number,\n): FlashDirection {\n const prev = useRef<number | null | undefined>(value);\n const prevIdentity = useRef(identity);\n const [direction, setDirection] = useState<FlashDirection>(null);\n\n useEffect(() => {\n const before = prev.current;\n const sameEntity = prevIdentity.current === identity;\n prev.current = value;\n prevIdentity.current = identity;\n if (\n !enabled ||\n !sameEntity ||\n typeof value !== 'number' ||\n typeof before !== 'number' ||\n !Number.isFinite(value) ||\n !Number.isFinite(before) ||\n value === before\n ) {\n setDirection(null); // a no-op when already null (React bails out); clears a stale tint otherwise\n return;\n }\n setDirection(value > before ? 'up' : 'down');\n const timer = setTimeout(() => setDirection(null), HOLD_MS);\n return () => clearTimeout(timer);\n }, [value, enabled, identity]);\n\n // Mask any lingering state when disabled, so toggling off is immediate.\n return enabled ? direction : null;\n}\n\n// flashTextStyle colors text for the current direction: snap to the tint (no transition)\n// while flashing, then fade `color` back to the element's own color when it clears. Merge\n// it AFTER an explicit base `color` so the tint overrides while set and the base shows\n// through (and animates) once cleared.\nexport function flashTextStyle(direction: FlashDirection, fadeMs: number = FADE_MS): CSSProperties {\n if (direction) return { color: direction === 'up' ? UP.fg : DOWN.fg, transition: 'none' };\n return { transition: `color ${fadeMs}ms ease-out` };\n}\n\n// flashBackgroundStyle is the same cue as a translucent background tint, for a widget whose\n// value can't be recolored directly (the gauge's canvas). Snap in, fade the background back.\nexport function flashBackgroundStyle(direction: FlashDirection, fadeMs: number = FADE_MS): CSSProperties {\n if (direction) {\n return { backgroundColor: direction === 'up' ? UP.bg : DOWN.bg, transition: 'none' };\n }\n return { transition: `background-color ${fadeMs}ms ease-out` };\n}\n\n// FlashValue renders a formatted numeric value that flashes on change \u2014 the reusable cell\n// for multi-row widgets. Each rendered instance owns its own flash state, so keying rows by\n// entity (React's list key) gives every row an independent cue for free.\nexport function FlashValue({\n value,\n precision,\n enabled,\n identity,\n baseColor = 'inherit',\n style,\n}: {\n value: number | null | undefined;\n precision?: number;\n enabled: boolean;\n // The value's owning entity (see useFlashOnChange): when it changes, adopt the new value\n // rather than flash. Distinguishes a real per-device change from an anchor's cross-device\n // interleave. Undefined for a single-device value.\n identity?: string | number;\n // The color the value fades back to (defaults to the inherited text color).\n baseColor?: string;\n style?: CSSProperties;\n}) {\n const direction = useFlashOnChange(value, enabled, identity);\n return (\n <span style={{ color: baseColor, ...style, ...flashTextStyle(direction) }}>\n {formatValue(value, precision)}\n </span>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// formatTimestamp renders an RFC3339 event time as a locale time string, leaving\n// an unparseable value untouched and an absent one blank.\nexport function formatTimestamp(iso: string | null): string {\n if (!iso) return '';\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? iso : date.toLocaleTimeString();\n}\n\n// formatDateTime renders an RFC3339 time as a locale date+time string. Unlike\n// formatTimestamp (time-only, right for a live measurement tick) this keeps the date,\n// because an alarm can have been raised days ago and a bare \"09:14:32\" would mislead.\nexport function formatDateTime(iso: string | null): string {\n if (!iso) return '';\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();\n}\n\n// formatValue renders a measurement value, applying a fixed precision when given\n// and showing an em dash for a missing value.\n//\n// The precision is clamped to toFixed's domain because it arrives from an opaque stored\n// definition: outside 0..100 toFixed throws a RangeError, which is an exception thrown\n// during render \u2014 it unmounts the widget's whole subtree rather than degrading, and every\n// other bad option value in this package degrades. The console's config panel can author\n// a negative one today, and a hand-edited definition always could.\nexport function formatValue(value: number | null | undefined, precision?: number): string {\n if (value == null) return '\u2014';\n if (precision == null) return String(value);\n const places = Math.min(100, Math.max(0, Math.trunc(precision)));\n return value.toFixed(places);\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// command-button \u2014 issue a device command from the dashboard and watch its delivery\n// lifecycle (command-delivery). The console bakes a command definition into the widget's\n// options at author time (its name + parameterSchema); this renders a typed form from\n// that schema, issues via the action seam (gated on command:write), and shows the recent\n// commands for the target device with live status (QUEUED \u2192 SENT \u2192\n// SUCCESSFUL / FAILED \u2026), so an operator can see whether a command was acted on rather\n// than only that it was sent. Bound through the hub's control channel, so it renders identically from live\n// data or the synthetic preview source.\n//\n// The baked schema is a SNAPSHOT taken when the author picked the command: a later edit\n// to the profile's command definition (a changed schema, or a deleted command) leaves\n// this widget's form stale until the author re-picks it. That's acceptable \u2014 dashboards\n// are opaque snapshots \u2014 and a since-removed command fails visibly at the delivery\n// boundary (the server validates device existence and JSON); it isn't silently wrong.\n\nimport type { CommandDispatch, CommandParameter } from '@devicechain/dashboards';\nimport { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';\n\nimport type { CommandStreamState } from '../hooks';\nimport { formatDateTime } from '../format';\nimport { WidgetFrame } from '../frame';\nimport { css } from '../theme';\nimport { optString, type WidgetProps } from '../widget';\nimport { commandStatusColor, commandStatusLabel } from './command-status';\nimport {\n buildPayload,\n defaultValues,\n isScalar,\n parseBool,\n parseParameterSchema,\n validateParams,\n} from './command-params';\n\nexport function CommandButton({ widget, data, actions }: WidgetProps<CommandStreamState>) {\n const commandName = optString(widget.options, 'commandName');\n const commandLabel = optString(widget.options, 'commandLabel') ?? commandName;\n const parameterSchema = optString(widget.options, 'parameterSchema') ?? null;\n\n const params = useMemo(() => parseParameterSchema(parameterSchema), [parameterSchema]);\n // A stable key over the configured command: reset the form when the author repoints\n // the widget at a different command (or edits its schema).\n const schemaKey = `${commandName ?? ''}::${parameterSchema ?? ''}`;\n\n const [values, setValues] = useState<Record<string, string>>(() => defaultValues(params));\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [pending, setPending] = useState(false);\n const [sendError, setSendError] = useState<string | null>(null);\n const [sentToken, setSentToken] = useState<string | null>(null);\n // Bumped whenever the configured command changes; an in-flight send captures the\n // current value and drops its result if it settles after a repoint (so command A's\n // dispatch token/error can't land under a freshly-configured command B).\n const sendGen = useRef(0);\n\n // Reset the form to the schema's defaults whenever the configured command changes.\n useEffect(() => {\n sendGen.current += 1;\n setValues(defaultValues(params));\n setErrors({});\n setSendError(null);\n setSentToken(null);\n // params is derived from schemaKey; keying on schemaKey avoids re-running on an\n // equal-but-new params array reference.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [schemaKey]);\n\n const canWrite = actions?.can('command:write') ?? false;\n const hasSeam = typeof actions?.sendCommand === 'function';\n const deviceToken = data.deviceToken;\n // The form is operable only when the viewer may write AND the runtime supplies the send\n // seam; a read-only mount shows the form disabled. Sending additionally needs a bound\n // target device and a configured command.\n const formDisabled = !canWrite || !hasSeam || pending;\n const canSend = !formDisabled && !!deviceToken && !!commandName;\n\n const setField = (name: string, value: string) => {\n setValues((prev) => ({ ...prev, [name]: value }));\n // Clear a field's error as the operator edits it.\n setErrors((prev) => (prev[name] ? { ...prev, [name]: '' } : prev));\n setSendError(null);\n };\n\n const send = () => {\n if (!deviceToken || !commandName || !actions?.sendCommand) return;\n const found = validateParams(params, values);\n const active = Object.fromEntries(Object.entries(found).filter(([, v]) => v));\n if (Object.keys(active).length > 0) {\n setErrors(active);\n return;\n }\n const payload = buildPayload(params, values);\n const gen = sendGen.current; // capture: drop the result if the command is repointed mid-flight\n setPending(true);\n setSendError(null);\n // Call synchronously so a dispatch is observable, but guard a synchronous throw so a\n // misbehaving seam can't leave the button stuck-disabled.\n let promise: Promise<CommandDispatch>;\n try {\n promise = actions.sendCommand(deviceToken, commandName, payload);\n } catch {\n setSendError(SEND_FAILED);\n setPending(false);\n return;\n }\n promise\n .then((dispatch) => {\n if (gen !== sendGen.current) return;\n // \uD83D\uDD34 A REFUSAL IS SHOWN, A FAILURE IS NOT. The server decided this command was\n // wrong and said why in client-safe terms (an unknown command for this device, a\n // parameter that violates the schema, the tenant's held-command ceiling) \u2014 that\n // reason is the whole point of the answer, and an operator can act on it. A\n // thrown error, below, is an availability failure: it says nothing about the\n // command, and its text is internal, so it gets one fixed message instead.\n if (dispatch.status === 'rejected') {\n setSendError(dispatch.reason);\n return;\n }\n setSentToken(dispatch.token);\n })\n .catch(() => {\n if (gen === sendGen.current) setSendError(SEND_FAILED);\n })\n .finally(() => {\n if (gen === sendGen.current) setPending(false);\n });\n };\n\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%', gap: 10 }}>\n {!commandName ? (\n <div style={{ ...hint, margin: 'auto' }}>No command configured.</div>\n ) : (\n <>\n {/* Typed parameter form. */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\n {params.map((param) =>\n isScalar(param) ? (\n <ParamField\n key={param.name}\n widgetId={widget.id}\n param={param}\n value={values[param.name] ?? ''}\n error={errors[param.name]}\n disabled={formDisabled}\n onChange={(v) => setField(param.name, v)}\n />\n ) : (\n // A structured (OBJECT) parameter isn't form-editable. A REQUIRED one\n // blocks sending (validateParams flags it); flag that in the note.\n <div key={param.name} style={{ ...hint, color: param.required ? commandStatusColor('FAILED') : css('muted-foreground') }}>\n {param.name}: {param.required ? 'required ' : ''}structured parameter \u2014 not supported here.\n </div>\n ),\n )}\n\n <button\n type=\"button\"\n style={{ ...sendButton, opacity: canSend ? 1 : 0.55, cursor: canSend ? 'pointer' : 'not-allowed' }}\n disabled={!canSend}\n aria-busy={pending}\n onClick={send}\n >\n {pending ? 'Sending\u2026' : `Send ${commandLabel}`}\n </button>\n\n {!canWrite ? (\n <div style={hint}>You don\u2019t have permission to issue commands.</div>\n ) : !hasSeam ? (\n <div style={hint}>Commands aren\u2019t available in this view.</div>\n ) : !deviceToken ? (\n <div style={hint}>No device is selected for this command.</div>\n ) : null}\n {sendError ? (\n <div role=\"alert\" style={{ ...hint, color: commandStatusColor('FAILED') }}>\n {sendError}\n </div>\n ) : null}\n </div>\n\n {/* Recent-command history with live delivery status. */}\n <CommandHistory commands={data.commands} sentToken={sentToken} loading={data.loading} error={data.error} />\n </>\n )}\n </div>\n </WidgetFrame>\n );\n}\n\n// \u2500\u2500 Parameter field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction ParamField({\n widgetId,\n param,\n value,\n error,\n disabled,\n onChange,\n}: {\n widgetId: string;\n param: CommandParameter;\n value: string;\n error?: string;\n disabled: boolean;\n onChange: (value: string) => void;\n}) {\n // Scope the input id to the widget so two command-buttons sharing a parameter name\n // don't produce duplicate DOM ids (which would misdirect a label click to the first).\n const labelId = `cmd-${widgetId}-${param.name}`;\n const unit = param.unit ? ` (${param.unit})` : '';\n return (\n <label htmlFor={labelId} style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>\n <span style={{ fontSize: 12, color: css('muted-foreground') }}>\n {param.name}\n {unit}\n {param.required ? <span style={{ color: commandStatusColor('FAILED') }}> *</span> : null}\n </span>\n <ParamInput id={labelId} param={param} value={value} disabled={disabled} onChange={onChange} />\n {error ? (\n <span style={{ fontSize: 11, color: commandStatusColor('FAILED') }}>{error}</span>\n ) : null}\n </label>\n );\n}\n\nfunction ParamInput({\n id,\n param,\n value,\n disabled,\n onChange,\n}: {\n id: string;\n param: CommandParameter;\n value: string;\n disabled: boolean;\n onChange: (value: string) => void;\n}) {\n // An enum (of any data type) \u2192 a select; a boolean \u2192 a checkbox; a number \u2192 a numeric\n // input with the declared bounds; everything else \u2192 text.\n if (param.enum && param.enum.length > 0) {\n return (\n <select id={id} style={inputStyle} value={value} disabled={disabled} onChange={(e) => onChange(e.target.value)}>\n {!param.required ? <option value=\"\">\u2014</option> : null}\n {param.enum.map((opt) => (\n <option key={opt} value={opt}>\n {opt}\n </option>\n ))}\n </select>\n );\n }\n if (param.dataType === 'BOOLEAN') {\n return (\n <input\n id={id}\n type=\"checkbox\"\n style={{ width: 16, height: 16 }}\n checked={parseBool(value)}\n disabled={disabled}\n onChange={(e) => onChange(e.target.checked ? 'true' : 'false')}\n />\n );\n }\n const numeric = param.dataType === 'INT' || param.dataType === 'DOUBLE';\n return (\n <input\n id={id}\n type={numeric ? 'number' : 'text'}\n style={inputStyle}\n value={value}\n disabled={disabled}\n min={param.minValue ?? undefined}\n max={param.maxValue ?? undefined}\n step={param.dataType === 'INT' ? 1 : 'any'}\n placeholder={param.default ?? undefined}\n onChange={(e) => onChange(e.target.value)}\n />\n );\n}\n\n// \u2500\u2500 History \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction CommandHistory({\n commands,\n sentToken,\n loading,\n error,\n}: {\n commands: CommandStreamState['commands'];\n sentToken: string | null;\n loading: boolean;\n error: unknown;\n}) {\n // An error is shown inline here (not by replacing the whole widget) so a transient poll\n // blip never tears down the still-usable send form. Last-good rows stay on screen when\n // present; only an empty-and-errored history shows the notice.\n const empty = commands.length === 0;\n return (\n <div\n style={{\n flex: 1,\n minHeight: 0,\n overflow: 'auto',\n borderTop: `1px solid ${css('border')}`,\n paddingTop: 6,\n }}\n >\n {empty ? (\n <div style={{ ...hint, textAlign: 'center', color: error ? commandStatusColor('FAILED') : css('muted-foreground') }}>\n {error ? 'Couldn\u2019t load command history.' : loading ? 'Loading\u2026' : 'No commands issued yet.'}\n </div>\n ) : (\n <ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 4 }}>\n {commands.map((command) => (\n <li\n key={command.token}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n fontSize: 12,\n // Highlight the command just issued from this widget.\n fontWeight: command.token === sentToken ? 600 : 400,\n }}\n >\n <StatusBadge status={command.status} />\n <span style={{ color: css('foreground'), whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>\n {command.name}\n </span>\n <span style={{ marginLeft: 'auto', color: css('muted-foreground'), whiteSpace: 'nowrap' }}>\n {formatDateTime(command.queuedTime)}\n </span>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n\nfunction StatusBadge({ status }: { status: string }) {\n const color = commandStatusColor(status);\n return (\n <span\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n flexShrink: 0,\n fontSize: 11,\n fontWeight: 600,\n color,\n }}\n >\n <span aria-hidden=\"true\" style={{ width: 7, height: 7, borderRadius: '50%', background: color }} />\n {commandStatusLabel(status)}\n </span>\n );\n}\n\n// \u2500\u2500 styles / helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst hint: CSSProperties = { fontSize: 11, color: css('muted-foreground') };\n\n// What a viewer is told when the platform could not decide the enqueue. It is fixed\n// prose on purpose: the thrown error's text describes in-cluster machinery, and a\n// dashboard is the surface most likely to be embedded outside the tenant's own console.\nconst SEND_FAILED = 'Couldn\u2019t issue the command. Try again.';\n\nconst inputStyle: CSSProperties = {\n width: '100%',\n boxSizing: 'border-box',\n padding: '4px 8px',\n fontSize: 13,\n borderRadius: 4,\n border: `1px solid ${css('border')}`,\n background: css('background'),\n color: css('foreground'),\n};\n\nconst sendButton: CSSProperties = {\n padding: '6px 12px',\n fontSize: 13,\n fontWeight: 600,\n borderRadius: 4,\n border: 'none',\n background: css('primary'),\n color: css('background'),\n marginTop: 2,\n};\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// Command delivery-status presentation for the command-button widget (command-delivery\n// lifecycle: QUEUED \u2192 SENT \u2192 SUCCESSFUL | FAILED, with HELD for a dispatch withheld from\n// a known-absent device, PARKED for one published to a device that turned out to be\n// unreachable, and TIMEOUT / EXPIRED / CANCELLED as the other terminals).\n// Status color is SEMANTIC (good/working/waiting/bad), deliberately fixed rather than\n// derived from the dashboard accent, so the same status reads the same on every theme \u2014\n// and it mirrors the console's DeviceCommandsPanel status variants (success /\n// destructive / outline / in-flight) so an operator reads the same signal on both\n// surfaces.\n\nimport { isTerminalCommandStatus } from '@devicechain/dashboards';\n\n// \uD83D\uDD34 EVERY STATUS NEEDS AN ENTRY, INCLUDING THE ONES THAT LOOK NEUTRAL. UNKNOWN_COLOR\n// below is the same slate as EXPIRED, so a status with no entry does not merely fall\n// back \u2014 it becomes INDISTINGUISHABLE from \"lapsed before it was sent\". HELD and PARKED\n// (the command is still coming) rendered in EXPIRED's slate tell an operator the opposite\n// of the truth, which is why they, and CANCELLED, are spelled out rather than left to the\n// fallback.\n//\n// The scheme is by MEANING, not by lifecycle position: sky = moving under its own steam,\n// amber = waiting on a device that isn't there. HELD and PARKED share the amber family\n// because they are the same answer to \"why hasn't it arrived?\" \u2014 the device is away \u2014 and\n// take different shades because they are different answers to \"what did the platform\n// already try?\": HELD never left the platform, PARKED was published and found nobody home.\nconst STATUS_COLORS: Record<string, string> = {\n SUCCESSFUL: '#16a34a', // green-600 \u2014 acknowledged by the device\n FAILED: '#dc2626', // red-600\n TIMEOUT: '#dc2626', // red-600 \u2014 never acknowledged in time\n EXPIRED: '#64748b', // slate-500 \u2014 lapsed before it was ever sent\n CANCELLED: '#7c3aed', // violet-600 \u2014 deliberately called off; a decision, not a failure\n QUEUED: '#0284c7', // sky-600 \u2014 in flight\n SENT: '#0284c7',\n HELD: '#d97706', // amber-600 \u2014 withheld: the device is absent, the command still stands\n PARKED: '#f59e0b', // amber-500 \u2014 published, nobody home; held for the device's return\n};\n\nconst UNKNOWN_COLOR = '#64748b';\n\n// commandStatusColor maps a status to its badge color, falling back to muted slate for\n// an unrecognized value (a future/hand-edited status).\nexport function commandStatusColor(status: string): string {\n return STATUS_COLORS[status] ?? UNKNOWN_COLOR;\n}\n\n// isTerminalStatus reports whether a command has reached a state it can't leave \u2014 the\n// widget stops treating it as in-flight.\n//\n// It is NOT the question \"can this still be cancelled?\": SENT is non-terminal and is not\n// cancellable, so a host gating a cancel control must ask @devicechain/dashboards'\n// isCancellableCommandStatus instead of negating this.\n//\n// It is an alias, not a second definition: the terminal set lives in @devicechain/dashboards\n// so the console, this package and the preview cannot disagree about it. The name is kept\n// because it is part of this package's published surface.\nexport function isTerminalStatus(status: string): boolean {\n return isTerminalCommandStatus(status);\n}\n\n// commandStatusLabel renders a status in Title Case ('SUCCESSFUL' \u2192 'Successful').\nexport function commandStatusLabel(status: string): string {\n if (!status) return '';\n return status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// Pure form logic for the command-button widget's typed parameter form (ADR-043). The\n// console bakes a command definition's `parameterSchema` (a JSON string) into the\n// widget's options; these helpers parse it, seed defaults, validate operator input, and\n// serialize the typed values back to the JSON payload command-delivery sends the device.\n// Kept separate from the widget so the coercion/validation is unit-tested in isolation.\n//\n// SCOPE: the form types top-level SCALAR parameters (DOUBLE / INT / BOOLEAN / STRING,\n// with enum / bounds / required / default). OBJECT (nested) parameters are not\n// form-editable \u2014 the form blocks sending when one is REQUIRED (it can't be satisfied\n// here) and otherwise omits it. The payload is a flat JSON object keyed by parameter name.\n\nimport type { CommandParameter } from '@devicechain/dashboards';\n\nconst DATA_TYPES = new Set(['DOUBLE', 'INT', 'BOOLEAN', 'STRING']);\n\n// parseParameterSchema turns the baked JSON string into descriptors, defensively:\n// a null/empty/malformed schema (or a non-array root) yields an empty list so a command\n// with no arguments (or a hand-broken schema) renders as a bare Send button, never throws.\n// Each descriptor is NORMALIZED (enum\u2192string[], default\u2192string, bounds\u2192finite number,\n// kind/dataType\u2192known value) so a hand-edited dashboard definition with wrong-typed fields\n// (e.g. `enum: \"on\"` or `default: 5`) can't crash the form at render/send time.\nexport function parseParameterSchema(schema: string | null | undefined): CommandParameter[] {\n if (!schema) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(schema);\n } catch {\n return [];\n }\n if (!Array.isArray(parsed)) return [];\n const out: CommandParameter[] = [];\n for (const raw of parsed) {\n const norm = normalizeParam(raw);\n if (norm) out.push(norm);\n }\n return out;\n}\n\n// normalizeParam validates one descriptor and coerces every field to its expected type,\n// dropping the descriptor entirely if it has no string `name`. Unknown/wrong-typed fields\n// are discarded rather than trusted, so downstream code never sees a non-array enum or a\n// numeric default.\nfunction normalizeParam(raw: unknown): CommandParameter | null {\n if (typeof raw !== 'object' || raw === null) return null;\n const r = raw as Record<string, unknown>;\n if (typeof r.name !== 'string') return null;\n\n const param: CommandParameter = { name: r.name };\n if (typeof r.description === 'string') param.description = r.description;\n if (r.kind === 'OBJECT') param.kind = 'OBJECT';\n else if (r.kind === 'SCALAR') param.kind = 'SCALAR';\n if (typeof r.dataType === 'string' && DATA_TYPES.has(r.dataType)) {\n param.dataType = r.dataType as CommandParameter['dataType'];\n }\n if (typeof r.unit === 'string') param.unit = r.unit;\n if (r.required === true) param.required = true;\n if (typeof r.default === 'string') param.default = r.default;\n if (typeof r.minValue === 'number' && Number.isFinite(r.minValue)) param.minValue = r.minValue;\n if (typeof r.maxValue === 'number' && Number.isFinite(r.maxValue)) param.maxValue = r.maxValue;\n if (Array.isArray(r.enum)) {\n const values = r.enum.filter((v): v is string => typeof v === 'string');\n if (values.length > 0) param.enum = values;\n }\n return param;\n}\n\n// isScalar reports whether a parameter is a typed single value the form renders an input\n// for. Kind absent means SCALAR (the Go default); only an explicit OBJECT is nested.\nexport function isScalar(param: CommandParameter): boolean {\n return param.kind !== 'OBJECT';\n}\n\n// parseBool interprets a schema/string boolean the way the server's strconv.ParseBool\n// does (accepting '1'/'t'/'true'/'yes'/'on', case-insensitively) so a definition default\n// of \"True\" doesn't silently render \u2014 and send \u2014 as false.\nexport function parseBool(raw: string): boolean {\n const s = raw.trim().toLowerCase();\n return s === 'true' || s === '1' || s === 't' || s === 'yes' || s === 'on';\n}\n\n// defaultValues seeds the form's value map from each scalar parameter. A BOOLEAN always\n// gets a concrete 'true'/'false' (default false) \u2014 a checkbox is never \"empty\", so the\n// value must reflect the box's state exactly. Other scalars seed from their declared\n// default (as a string \u2014 inputs are string-backed), else start empty.\nexport function defaultValues(params: CommandParameter[]): Record<string, string> {\n const values: Record<string, string> = {};\n for (const p of params) {\n if (!isScalar(p)) continue;\n if (p.dataType === 'BOOLEAN') values[p.name] = parseBool(p.default ?? '') ? 'true' : 'false';\n else if (p.default != null) values[p.name] = p.default;\n }\n return values;\n}\n\n// validateParams returns a per-parameter error map (empty = valid). It enforces required\n// (a missing value on a required scalar; a BOOLEAN always has a value so is never\n// \"required-missing\"), numeric validity/bounds for INT/DOUBLE, and blocks a REQUIRED\n// OBJECT parameter (it can't be supplied through the typed form).\nexport function validateParams(\n params: CommandParameter[],\n values: Record<string, string>,\n): Record<string, string> {\n const errors: Record<string, string> = {};\n for (const p of params) {\n if (!isScalar(p)) {\n // A required structured parameter can't be satisfied here \u2014 refuse to send an\n // incomplete command rather than silently omitting it.\n if (p.required) errors[p.name] = 'Structured parameter \u2014 not supported here';\n continue;\n }\n if (p.dataType === 'BOOLEAN') continue; // a checkbox always carries a valid value\n const raw = (values[p.name] ?? '').trim();\n if (raw === '') {\n if (p.required) errors[p.name] = 'Required';\n continue; // an empty optional value is fine \u2014 it's just omitted from the payload\n }\n if (p.dataType === 'INT' || p.dataType === 'DOUBLE') {\n const n = Number(raw);\n if (!Number.isFinite(n)) {\n errors[p.name] = 'Must be a number';\n } else if (p.dataType === 'INT' && !Number.isInteger(n)) {\n errors[p.name] = 'Must be a whole number';\n } else if (p.minValue != null && n < p.minValue) {\n errors[p.name] = `Must be \u2265 ${p.minValue}`;\n } else if (p.maxValue != null && n > p.maxValue) {\n errors[p.name] = `Must be \u2264 ${p.maxValue}`;\n }\n }\n }\n return errors;\n}\n\n// buildPayload serializes the typed values to the JSON payload string command-delivery\n// sends the device \u2014 coercing each scalar to its declared type (INT/DOUBLE \u2192 number,\n// BOOLEAN \u2192 boolean, STRING \u2192 string). A BOOLEAN is always included (its checkbox state\n// is meaningful); other empty optional values are omitted. OBJECT parameters are omitted\n// (validateParams blocks a required one first). Returns undefined when no parameter\n// contributes a value (a parameterless command sends no payload).\nexport function buildPayload(\n params: CommandParameter[],\n values: Record<string, string>,\n): string | undefined {\n const payload: Record<string, unknown> = {};\n for (const p of params) {\n if (!isScalar(p)) continue;\n const raw = (values[p.name] ?? '').trim();\n if (p.dataType === 'BOOLEAN') {\n payload[p.name] = parseBool(raw);\n continue;\n }\n if (raw === '') continue;\n payload[p.name] = coerce(p, raw);\n }\n return Object.keys(payload).length === 0 ? undefined : JSON.stringify(payload);\n}\n\nfunction coerce(param: CommandParameter, raw: string): unknown {\n switch (param.dataType) {\n case 'INT':\n case 'DOUBLE': {\n const n = Number(raw);\n return Number.isFinite(n) ? n : raw; // validation gates this; fall back to raw defensively\n }\n case 'BOOLEAN':\n return parseBool(raw);\n default:\n return raw; // STRING (and untyped)\n }\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// entity-selector \u2014 a flat picker that re-points a target slot's binding (ADR-039 selection\n// amendment). It is the authored companion to the alarm-originator drill: the viewer chooses\n// WHICH entity a slot binds to, and every widget bound to that slot (and its scoped children,\n// via the cascade) follows. Two shapes, both from `options.selectionTarget`:\n// \u2022 target is a SCOPED child slot \u2192 a member picker (which thermostat within the building).\n// \u2022 target is a ROOT slot \u2192 a context picker (which building the dashboard shows).\n// The candidate set comes from the ambient provider (useCandidates), which the host computes\n// from the target slot's scope; the pick fires the ambient select() callback. Inert (a muted\n// hint) wherever selection isn't wired \u2014 edit mode, preview, a read-only embedder.\n\nimport type { SlotBinding } from '@devicechain/dashboards';\nimport { useEffect, useState, type ChangeEvent, type CSSProperties } from 'react';\n\nimport { WidgetFrame, useWidgetSelect } from '../frame';\nimport { useCandidates } from '../hooks';\nimport { css } from '../theme';\nimport { optString, type WidgetProps } from '../widget';\n\n// A binding's stable option value \u2014 the device token or the anchor's target token. Used as\n// the <option> value (not the array index) so the control's value survives a re-fetch that\n// reorders/replaces candidates.\nfunction tokenOf(b: SlotBinding): string {\n return b.kind === 'device' ? b.deviceToken : b.anchor.targetToken;\n}\n\nconst control: CSSProperties = {\n width: '100%',\n fontSize: 14,\n padding: '8px 10px',\n borderRadius: 6,\n border: `1px solid ${css('border')}`,\n background: css('background'),\n color: css('foreground'),\n};\n\nconst hint: CSSProperties = {\n fontSize: 12,\n color: css('muted-foreground'),\n};\n\nexport function EntitySelector({ widget }: WidgetProps<null>) {\n const select = useWidgetSelect();\n const target = optString(widget.options, 'selectionTarget');\n const { candidates, loading, error, wired } = useCandidates(target);\n const title = optString(widget.options, 'title');\n\n // Optimistic pick: hold the just-chosen token so the control reflects the choice\n // immediately \u2014 through the (on a scoped dashboard, two-hop) async re-resolve \u2014 rather\n // than snapping back to the prior selection until the refetch settles. Cleared once the\n // fetched candidates reflect it, or it is no longer offered (the pick was dropped).\n const [pending, setPending] = useState<string | null>(null);\n const selected = candidates.find((c) => c.selected);\n const selectedToken = selected ? tokenOf(selected.binding) : '';\n useEffect(() => {\n if (pending !== null && (selectedToken === pending || !candidates.some((c) => tokenOf(c.binding) === pending))) {\n setPending(null);\n }\n }, [candidates, selectedToken, pending]);\n\n // Inert wherever selection isn't wired (no select callback OR no candidate provider) or no\n // target slot is authored \u2014 the picker would drive nothing, so show a muted explanation.\n const inert = !select || !target || !wired;\n const value = pending ?? selectedToken;\n\n const onChange = (e: ChangeEvent<HTMLSelectElement>) => {\n const token = e.target.value;\n if (!token) return; // the disabled placeholder ('') \u2014 nothing to select\n const candidate = candidates.find((c) => tokenOf(c.binding) === token);\n if (select && target && candidate) {\n setPending(token);\n select({ slot: target, binding: candidate.binding });\n }\n };\n\n return (\n <WidgetFrame title={title}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: '12px 16px', height: '100%' }}>\n {inert ? (\n <span style={hint}>\n {select && target ? 'Selection isn\u2019t available here.' : select ? 'No target slot configured.' : 'Selection is available on the live dashboard.'}\n </span>\n ) : (\n <>\n <select\n style={{ ...control, opacity: loading ? 0.6 : 1 }}\n value={value}\n disabled={candidates.length === 0}\n onChange={onChange}\n aria-label={title || `Select ${target}`}\n >\n {/* A blank option so an unbound slot (no member picked yet, or the prior pick\n is out of the new context) shows a prompt rather than defaulting to a row. */}\n <option value=\"\" disabled>\n {loading ? 'Loading\u2026' : candidates.length === 0 ? 'No options' : 'Select\u2026'}\n </option>\n {candidates.map((c) => (\n <option key={tokenOf(c.binding)} value={tokenOf(c.binding)}>\n {c.label}\n </option>\n ))}\n </select>\n {error ? <span style={hint}>Couldn\u2019t load options.</span> : null}\n </>\n )}\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// gauge \u2014 the current value of one measurement on an ECharts radial gauge.\n\nimport { useMemo } from 'react';\n\nimport { buildGaugeOption } from '../chart-options';\nimport { EChart } from '../echart';\nimport { flashBackgroundStyle, useFlashOnChange } from '../flash';\nimport { WidgetFrame } from '../frame';\nimport { useChartTheme, useElementSize } from '../hooks';\nimport {\n optBoolean,\n optNumber,\n optString,\n pickSample,\n primaryMeasurementName,\n type WidgetProps,\n} from '../widget';\n\nexport function Gauge({ widget, data }: WidgetProps) {\n const theme = useChartTheme();\n const [sizeRef, size] = useElementSize<HTMLDivElement>();\n const name = primaryMeasurementName(widget);\n const sample = pickSample(data.latest, name);\n\n const min = optNumber(widget.options, 'min');\n const max = optNumber(widget.options, 'max');\n const unit = optString(widget.options, 'unit');\n\n const option = useMemo(\n () => buildGaugeOption(sample?.value ?? null, theme, { min, max, unit }, size),\n [sample?.value, theme, min, max, unit, size.width, size.height],\n );\n\n // Opt-in directional flash. The value lives on an opaque canvas, so the cue is a\n // translucent tint on the container behind it rather than a recolored number. The identity\n // (device + measurement) suppresses a false flash on an anchor's cross-device interleave.\n const flashDirection = useFlashOnChange(\n sample?.value,\n optBoolean(widget.options, 'flashOnChange'),\n sample && `${sample.deviceToken}:${sample.name}`,\n );\n\n return (\n <WidgetFrame title={optString(widget.options, 'title') ?? name}>\n <div ref={sizeRef} style={{ width: '100%', height: '100%', ...flashBackgroundStyle(flashDirection) }}>\n <EChart option={option} />\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// Pure ECharts option builders. Kept free of React/DOM so the data\u2192chart mapping\n// is unit-testable without a canvas: a widget resolves a ChartTheme + samples and\n// hands the result straight to <EChart>.\n\nimport type { MeasurementSample } from '@devicechain/dashboards';\n\nimport type { EChartOption } from './echart';\nimport type { ElementSize } from './hooks';\nimport type { ChartTheme } from './theme';\n\n// distinctNames returns the measurement names present in `samples`, in first-seen\n// order \u2014 used when a chart's datasource doesn't pin an explicit series list.\nfunction distinctNames(samples: MeasurementSample[]): string[] {\n const seen: string[] = [];\n for (const s of samples) {\n if (!seen.includes(s.name)) seen.push(s.name);\n }\n return seen;\n}\n\nconst clamp = (v: number, lo: number, hi: number): number => Math.max(lo, Math.min(hi, v));\n\n// gauge/line fonts and tick counts scale with the smaller container dimension so a\n// widget stays legible when resized. A zero/absent size (before first measure, or\n// in a non-DOM test) falls back to a nominal medium size.\nfunction shortSide(size?: ElementSize): number {\n const s = Math.min(size?.width ?? 0, size?.height ?? 0);\n return s > 0 ? s : 200;\n}\n\nexport interface LineChartOptions {\n // Measurement names to plot, one series each. Empty \u2192 every name seen in the\n // window (first-seen order).\n measurements?: string[];\n}\n\n// buildLineOption maps a rolling window of samples to a multi-series time chart,\n// one line per measurement name, coloured from the theme's chart palette.\nexport function buildLineOption(\n samples: MeasurementSample[],\n theme: ChartTheme,\n options: LineChartOptions = {},\n size?: ElementSize,\n): EChartOption {\n const names = options.measurements?.length ? options.measurements : distinctNames(samples);\n const axisFont = clamp(Math.round(shortSide(size) / 22), 8, 12);\n\n const series = names.map((name, i) => {\n const color = theme.series[i % theme.series.length];\n return {\n name,\n type: 'line' as const,\n showSymbol: false,\n lineStyle: { color },\n itemStyle: { color },\n data: samples\n .filter((s) => s.name === name && s.value != null && s.occurredTime != null)\n .map((s) => [s.occurredTime as string, s.value as number]),\n };\n });\n\n return {\n animation: false,\n textStyle: { color: theme.foreground, fontSize: axisFont },\n grid: { left: 8, right: 12, top: names.length > 1 ? 28 : 12, bottom: 8, containLabel: true },\n tooltip: { trigger: 'axis' },\n legend:\n names.length > 1 ? { textStyle: { color: theme.mutedForeground, fontSize: axisFont }, top: 0 } : undefined,\n xAxis: {\n type: 'time',\n axisLine: { lineStyle: { color: theme.border } },\n axisLabel: { color: theme.mutedForeground, fontSize: axisFont, hideOverlap: true },\n },\n yAxis: {\n type: 'value',\n splitLine: { lineStyle: { color: theme.border } },\n axisLabel: { color: theme.mutedForeground, fontSize: axisFont },\n },\n series,\n };\n}\n\nexport interface GaugeChartOptions {\n min?: number;\n max?: number;\n unit?: string;\n}\n\n// buildGaugeOption maps a single latest value to a gauge. A null value (nothing\n// received yet) shows an em dash and rests the needle at the minimum. Ticks, fonts,\n// and the arc width scale with the widget size so it stays legible when resized\n// (ECharts' default gauge crams ~10 labels at fixed fonts on a small canvas).\nexport function buildGaugeOption(\n value: number | null,\n theme: ChartTheme,\n options: GaugeChartOptions = {},\n size?: ElementSize,\n): EChartOption {\n const min = options.min ?? 0;\n const max = options.max ?? 100;\n const accent = theme.series[0];\n const unitSuffix = options.unit ? ` ${options.unit}` : '';\n\n const s = shortSide(size);\n const arcWidth = clamp(Math.round(s / 26), 4, 12);\n const labelFont = clamp(Math.round(s / 20), 8, 13);\n const valueFont = clamp(Math.round(s / 7), 14, 40);\n\n return {\n animation: false,\n series: [\n {\n type: 'gauge',\n min,\n max,\n splitNumber: s < 160 ? 4 : 5, // fewer ticks than the default 10 \u2192 no cram\n radius: '95%',\n center: ['50%', '58%'],\n progress: { show: true, width: arcWidth, itemStyle: { color: accent } },\n pointer: { itemStyle: { color: accent }, length: '55%', width: clamp(Math.round(s / 50), 2, 6) },\n anchor: { show: false },\n axisLine: { lineStyle: { width: arcWidth, color: [[1, theme.border]] } },\n axisTick: { show: false },\n splitLine: { length: arcWidth, lineStyle: { color: theme.border, width: 1 } },\n axisLabel: { color: theme.mutedForeground, fontSize: labelFont, distance: arcWidth + 2 },\n detail: {\n valueAnimation: false,\n color: theme.foreground,\n fontSize: valueFont,\n offsetCenter: [0, '30%'],\n formatter: (v: number) => (value == null ? '\u2014' : `${v}${unitSuffix}`),\n },\n data: [{ value: value ?? min }],\n },\n ],\n };\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// A thin React wrapper over Apache ECharts (ADR-039 chart engine).\n//\n// It imports from echarts/core and registers ONLY the chart types + components the\n// widgets use (line + gauge, grid/tooltip, canvas renderer) \u2014 tree-shaking away the\n// rest of ECharts so the bundle carries only what a dashboard needs. The wrapper\n// owns the imperative lifecycle: init on mount, setOption on change, resize with the\n// container, dispose on unmount.\n\nimport { GaugeChart, LineChart } from 'echarts/charts';\nimport { GridComponent, TooltipComponent } from 'echarts/components';\nimport * as echarts from 'echarts/core';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport { useEffect, useRef } from 'react';\n\necharts.use([LineChart, GaugeChart, GridComponent, TooltipComponent, CanvasRenderer]);\n\n// A resolved ECharts option object (the shape echarts.setOption accepts). Widgets\n// build these with the pure builders in chart-options.ts.\nexport type EChartOption = echarts.EChartsCoreOption;\n\nexport interface EChartProps {\n option: EChartOption;\n className?: string;\n style?: React.CSSProperties;\n}\n\n// EChart fills its parent; the dashboard canvas sizes the parent, and a\n// ResizeObserver keeps the chart matched to it.\nexport function EChart({ option, className, style }: EChartProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const chartRef = useRef<echarts.ECharts | null>(null);\n\n // Init + teardown once for the element's lifetime.\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const chart = echarts.init(el);\n chartRef.current = chart;\n\n const observer = new ResizeObserver(() => chart.resize());\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n chart.dispose();\n chartRef.current = null;\n };\n }, []);\n\n // Re-apply the option whenever it changes. notMerge:true so a shrinking series\n // (rolling window) doesn't leave stale points behind.\n useEffect(() => {\n chartRef.current?.setOption(option, { notMerge: true });\n }, [option]);\n\n return (\n <div ref={containerRef} className={className} style={{ width: '100%', height: '100%', ...style }} />\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// image \u2014 a static image on the canvas (floor plans, logos, SCADA backdrops). No\n// datasource; the Phase-4 SCADA symbol family will layer live SVG on top of this.\n\nimport type { CSSProperties } from 'react';\n\nimport { WidgetFrame } from '../frame';\nimport { css } from '../theme';\nimport { optString, type WidgetProps } from '../widget';\n\nconst FITS = new Set(['contain', 'cover', 'fill']);\n\nexport function Image({ widget }: WidgetProps) {\n const url = optString(widget.options, 'url');\n const alt = optString(widget.options, 'alt') ?? '';\n const fitOption = optString(widget.options, 'fit');\n const objectFit = (fitOption && FITS.has(fitOption) ? fitOption : 'contain') as CSSProperties['objectFit'];\n\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n {url ? (\n <img src={url} alt={alt} style={{ width: '100%', height: '100%', objectFit, display: 'block' }} />\n ) : (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n color: css('muted-foreground'),\n fontSize: 13,\n }}\n >\n No image URL\n </div>\n )}\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// label \u2014 static text on the canvas (headings, captions, annotations). No\n// datasource. Renders plain text only (no raw HTML) so a tenant-authored\n// definition can't inject markup.\n\nimport type { CSSProperties } from 'react';\n\nimport { WidgetFrame } from '../frame';\nimport { css } from '../theme';\nimport { optNumber, optString, type WidgetProps } from '../widget';\n\nconst justify: Record<string, CSSProperties['justifyContent']> = {\n left: 'flex-start',\n center: 'center',\n right: 'flex-end',\n};\n\nexport function Label({ widget }: WidgetProps) {\n const text = optString(widget.options, 'text') ?? '';\n const align = optString(widget.options, 'align') ?? 'center';\n const fontSize = optNumber(widget.options, 'fontSize') ?? 16;\n\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: justify[align] ?? 'center',\n height: '100%',\n padding: 12,\n }}\n >\n <span\n style={{\n fontSize,\n color: css('foreground'),\n textAlign: align as CSSProperties['textAlign'],\n whiteSpace: 'pre-wrap',\n }}\n >\n {text}\n </span>\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// latest-card \u2014 the current value of one measurement, big and legible.\n\nimport { flashTextStyle, useFlashOnChange } from '../flash';\nimport { formatTimestamp, formatValue } from '../format';\nimport { WidgetFrame } from '../frame';\nimport { useElementSize } from '../hooks';\nimport { css } from '../theme';\nimport {\n optBoolean,\n optNumber,\n optString,\n pickSample,\n primaryMeasurementName,\n type WidgetProps,\n} from '../widget';\n\nconst clamp = (v: number, lo: number, hi: number): number => Math.max(lo, Math.min(hi, v));\n\nexport function LatestCard({ widget, data }: WidgetProps) {\n const [sizeRef, size] = useElementSize<HTMLDivElement>();\n const name = primaryMeasurementName(widget);\n const sample = pickSample(data.latest, name);\n\n const title = optString(widget.options, 'title') ?? name;\n const unit = optString(widget.options, 'unit');\n const precision = optNumber(widget.options, 'precision');\n const display = formatValue(sample?.value, precision);\n\n // Opt-in directional flash: tint the number green/red on a rise/fall, then fade back. The\n // identity (device + measurement) suppresses a false flash when an anchor's single slot\n // interleaves values across member devices, or the shown measurement switches.\n const flashDirection = useFlashOnChange(\n sample?.value,\n optBoolean(widget.options, 'flashOnChange'),\n sample && `${sample.deviceToken}:${sample.name}`,\n );\n\n // Scale the value to the card so it doesn't overflow a small slot (falls back to\n // a comfortable size before first measure / in a non-DOM test).\n const valueFont = size.height > 0 ? clamp(Math.round(size.height / 3.5), 16, 44) : 32;\n\n return (\n <WidgetFrame title={title}>\n <div\n ref={sizeRef}\n style={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n height: '100%',\n padding: '12px 16px',\n }}\n >\n <div\n style={{\n fontSize: valueFont,\n fontWeight: 600,\n lineHeight: 1.1,\n color: css('foreground'),\n ...flashTextStyle(flashDirection),\n }}\n >\n {display}\n {sample?.value != null && unit ? (\n <span style={{ fontSize: Math.round(valueFont / 2), color: css('muted-foreground'), marginLeft: 6 }}>\n {unit}\n </span>\n ) : null}\n </div>\n {sample?.occurredTime ? (\n <div style={{ fontSize: 11, color: css('muted-foreground'), marginTop: 4 }}>\n {formatTimestamp(sample.occurredTime)}\n </div>\n ) : null}\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// map \u2014 the bound devices' last-known positions, on the location channel.\n//\n// Three rules shape everything here, and each of them is a state some other design\n// would have collapsed into \"the map is empty\":\n//\n// \uD83D\uDD34 REFUSED IS NOT EMPTY. Position is gated on its own `location:read` authority,\n// deliberately absent from the read-only viewer baseline, so an ordinary member with\n// full telemetry access is routinely refused. \"No devices to show\" and \"you are not\n// allowed to see this\" are opposite facts and only one of them is actionable, so the\n// refusal gets its own copy and its own look \u2014 never a blank map.\n//\n// \uD83D\uDD34 NO TILE SOURCE IS NOT A BROKEN MAP. This package hardcodes no tile URL (see\n// rasterStyleFor) and inherits one from the tenant (ADR-079), whose instance-wide\n// default now ships a real provider \u2014 so in practice this branch is rare. It is not\n// dead, though: an operator can empty that default, an air-gapped install can have no\n// route to any provider at all, and a viewer can be looking at a board on either.\n//\n// That case renders a REAL MapLibre map against the BUNDLED public-domain world\n// (loadLandStyle) \u2014 continents and country outlines, compiled into this package.\n//\n// \uD83D\uDD34 Be precise about what survives of the old promise, because an earlier version of\n// this comment was not. It said the widget \"issues no request to any host\" and backed\n// that with \"loads no map library\", and the second half is now FALSE: this path loads\n// the renderer and the geometry, two chunks, from the app's OWN origin. What is still\n// true \u2014 and is the part that ever mattered \u2014 is that it contacts no THIRD-PARTY host,\n// because it has been given none it would be entitled to ask. What it no longer does\n// is show a void that reads as breakage.\n//\n// \uD83D\uDD34 ABSENT IS NOT ZERO. A marker describes only what its device reported; an\n// unreported heading is not due north. See describePosition.\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\nimport { renderableBasemap, resolveBasemap } from '@devicechain/client';\n\nimport { useTenantBasemap } from '../basemap-context';\nimport { useMapRuntime, type MapRuntime } from '../map-runtime-context';\nimport { WidgetFrame } from '../frame';\nimport type { LocationStreamState } from '../hooks';\nimport { css } from '../theme';\nimport { optString, type WidgetProps } from '../widget';\nimport {\n describePosition,\n loadLandStyle,\n placeable,\n projectToPanel,\n rasterStyleFor,\n type PlaceableLocation,\n} from './map-geometry';\n\n// The MapLibre pieces this widget drives. A TYPE-only import is erased at build time,\n// so naming them here creates no static dependency on the library \u2014 the value only ever\n// arrives through the dynamic import below.\nimport type { Map as MapLibreMap, Marker as MapLibreMarker } from 'maplibre-gl';\n\n// \uD83D\uDD34 THE LIBRARY IS LOADED LAZILY, BY CONSTRUCTION. MapLibre is ~200 KB gzipped, and\n// only a dashboard that actually contains a map widget may pay for it \u2014 a console user\n// who never opens a board with a map must not download a map renderer. The dynamic\n// import is what puts it in its own chunk; a top-level `import 'maplibre-gl'` anywhere\n// in this package would fold it into the main bundle for every viewer, and nothing\n// about the widget's behaviour would change to reveal it.\n//\n// MapLibre's STYLESHEET is no longer imported here at all \u2014 it is the host's, along with\n// the worker URL, for the reason set out below and in map-runtime-context. A host may hand\n// back a loader for it (`MapRuntime.loadStyles`) to keep it on this same lazy chunk, or\n// import it eagerly and pay 83 KB (10.7 KB gzipped) in its main stylesheet. That is the\n// host's call, and both are reasonable; what a LIBRARY cannot do is decide it for them in\n// one bundler's dialect.\n//\n// Loaded once and memoized: several map widgets on one board share the fetch.\nlet mapLibrePromise: Promise<typeof import('maplibre-gl')> | undefined;\n\n// \uD83D\uDD34 THE WORKER HAS TO BE ASKED FOR BY NAME, and getting this wrong produces a map\n// that builds, type-checks, passes every test and then renders nothing. MapLibre\n// otherwise derives the worker's URL at runtime from its own module URL, which is a\n// string no bundler can follow \u2014 nothing emits the file, the browser 404s it, and\n// `new Worker()` does not throw on a 404: the failure arrives later as an async error\n// event, so the map simply sits there having loaded no tiles.\n//\n// \uD83D\uDD34 THE URL COMES FROM THE HOST, and this package no longer computes it. Emitting a\n// worker entry is a bundler-specific act \u2014 Vite spells it `?worker&url`, webpack and\n// Rollup spell it differently \u2014 so a library that writes one dialect works under that\n// bundler and silently renders a blank map under every other. See map-runtime-context\n// for the full argument; the short version is that the specifier is externalized, so\n// no build anywhere reports the breakage.\n//\n// Memoized for the whole module, and FIRST CALLER WINS: several map widgets on one\n// board share the fetch, and `setWorkerUrl` is global state in MapLibre, so a second\n// caller with a different URL could not be honoured even if we tried. In practice\n// there is one provider at the top of the tree and therefore one URL.\nfunction loadMapLibre(runtime: MapRuntime): Promise<typeof import('maplibre-gl')> {\n if (!mapLibrePromise) {\n // The stylesheet rides the SAME dynamic boundary as the renderer, which is why a host\n // may pass a loader rather than importing it at module scope: it is 83 KB raw, 10.7 KB\n // gzipped (maplibre-gl 6.6.0), and a viewer who opens no map should download none of\n // it. Optional \u2014 a host that imports the stylesheet itself is doing the ordinary thing.\n //\n // \uD83D\uDD34 `Promise.resolve().then(...)` IS NOT CEREMONY. `loadStyles()` is host code, and a\n // host CAN throw synchronously \u2014 `loadStyles: () => require('\u2026css')` on a bundler with\n // no CSS loader is the realistic form. Called while building the argument array, that\n // throw escapes loadMapLibre entirely, propagates through the caller's own\n // `Promise.all` argument evaluation, and lands in React's commit phase where the\n // effect's `.catch` cannot see it: React 19 unmounts the tree, so one misconfigured\n // host loader takes down the whole dashboard instead of degrading to the plain panel.\n // Deferring the call puts it inside the promise chain, where the existing catch works.\n mapLibrePromise = Promise.all([\n import('maplibre-gl'),\n Promise.resolve().then(() => runtime.loadStyles?.()),\n ]).then(([mod]) => {\n mod.setWorkerUrl(runtime.workerUrl);\n return mod;\n });\n }\n return mapLibrePromise;\n}\n\n// The zoom a single-device map opens at \u2014 close enough to read a site, wide enough that\n// a lone marker is not floating in featureless tiles. A multi-device map ignores it and\n// fits the fleet's bounds instead.\nconst SINGLE_DEVICE_ZOOM = 14;\n\nexport function MapWidget({ widget, data }: WidgetProps<LocationStreamState>) {\n const title = optString(widget.options, 'title');\n const positions = placeable(data.locations);\n\n // The host's bundler-produced MapLibre runtime. Read unconditionally, with the other\n // hooks, because a hook cannot live behind one of the early returns below.\n const runtime = useMapRuntime();\n\n // Precedence is per-widget override \u2192 tenant \u2192 no basemap (ADR-079 \u00A79). The\n // per-widget options are kept, not replaced: they are how someone tries a provider\n // on one board before committing it tenant-wide.\n //\n // \uD83D\uDD34 resolveBasemap is NOT `override ?? tenant` per field. A widget that names its\n // own tile URL does not inherit the tenant's credit line, because that would credit\n // the tenant's provider for tiles it did not serve. The SDK owns that rule so the\n // fence editor and the /dash app cannot disagree with this widget about it.\n const basemap = renderableBasemap(\n resolveBasemap(\n {\n tileUrl: optString(widget.options, 'tileUrl'),\n attribution: optString(widget.options, 'attribution'),\n },\n useTenantBasemap(),\n ),\n );\n\n // The refusal is checked FIRST and unconditionally: it is a fact about the caller, so\n // it outranks anything about the data, and it must not be reachable only through some\n // \"no positions\" branch that a later edit could reorder past.\n if (data.forbidden) {\n return (\n <WidgetFrame title={title}>\n <MapNotice\n glyph=\"\uD83D\uDD12\"\n heading=\"Location not permitted\"\n detail=\"You do not have permission to view device locations.\"\n />\n </WidgetFrame>\n );\n }\n\n // \uD83D\uDD34 AND THE HOST'S OWN WIRING IS CHECKED SECOND, for the same reason the refusal is\n // checked first: it is a fact about the environment rather than about the data, so it\n // outranks every \"nothing to show\" branch below. Checking it later would hide it behind\n // \"No devices selected\" on exactly the board a developer is using to find it.\n //\n // There is no fallback to take. The only alternative to a host-supplied worker URL is\n // MapLibre's runtime derivation, which is the silent blank map this seam exists to\n // prevent \u2014 so an unwired host is told, in the widget, what it has to install.\n if (!runtime) {\n return (\n <WidgetFrame title={title}>\n <MapNotice\n glyph=\"\uD83E\uDDE9\"\n heading=\"Map runtime not configured\"\n detail=\"This host must install MapRuntimeProvider with its bundler's MapLibre worker URL.\"\n />\n </WidgetFrame>\n );\n }\n\n if (data.loading && positions.length === 0) {\n return (\n <WidgetFrame title={title}>\n <MapNotice heading=\"Loading\u2026\" />\n </WidgetFrame>\n );\n }\n\n if (positions.length === 0) {\n // Two different empty states, because they call for two different actions: bind the\n // widget, or wait for a device to report.\n return (\n <WidgetFrame title={title}>\n <MapNotice\n heading={data.deviceTokens.length === 0 ? 'No devices selected' : 'No positions reported'}\n detail={\n data.deviceTokens.length === 0\n ? 'Bind this widget to a device or anchor and select its location series.'\n : 'None of the selected devices has reported a position yet.'\n }\n />\n </WidgetFrame>\n );\n }\n\n return (\n <WidgetFrame title={title} bodyStyle={{ padding: 0 }}>\n <LiveMap basemap={basemap} positions={positions} runtime={runtime} />\n </WidgetFrame>\n );\n}\n\n// ---- The tile-less panel ----------------------------------------------------\n\n// PlainPanel draws the positions on a themed background with no basemap at all.\n//\n// \uD83D\uDD34 IT HAS EXACTLY ONE JOB LEFT, AND IT IS NOT THE UNCONFIGURED CASE. Since the\n// bundled world basemap landed, an unconfigured widget renders a REAL MapLibre map\n// against public-domain geometry \u2014 so this panel is reached only when the map could\n// not be BUILT at all. Three ways in, not one: the MapLibre chunk fails to load, the\n// bundled-geometry chunk fails to load, or the Map constructor throws (a browser or a\n// context with no usable WebGL). They share a catch because they share an answer.\n//\n// It survives because on that path it is strictly better than an error box. The\n// question a map widget answers is \"where is my fleet, relative to each other\",\n// and this still answers it when the renderer cannot load. That is also why\n// projectToPanel exists at all, and why deleting either would look like tidying up\n// dead code right until someone's dashboard went blank behind a proxy.\nfunction PlainPanel({ positions, note }: { positions: PlaceableLocation[]; note: string }) {\n const points = projectToPanel(positions);\n return (\n <div\n data-testid=\"map-plain-panel\"\n style={{\n position: 'relative',\n width: '100%',\n height: '100%',\n overflow: 'hidden',\n background: css('muted'),\n }}\n >\n {positions.map((position, i) => (\n <Marker\n key={position.deviceToken}\n position={position}\n style={{\n position: 'absolute',\n left: `${points[i].x * 100}%`,\n top: `${points[i].y * 100}%`,\n transform: 'translate(-50%, -50%)',\n }}\n />\n ))}\n <div\n data-testid=\"map-no-tiles-note\"\n style={{\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n padding: '4px 8px',\n fontSize: 10,\n lineHeight: 1.3,\n textAlign: 'center',\n color: css('muted-foreground'),\n background: css('card'),\n opacity: 0.9,\n }}\n >\n {note}\n </div>\n </div>\n );\n}\n\n// ---- The map ----------------------------------------------------------------\n\ntype MapStatus = 'loading' | 'ready' | 'unavailable';\n\n// LiveMap renders MapLibre for BOTH basemap states.\n//\n// The branch is over the STYLE, not over whether there is a map: a configured tile\n// source gets a raster style, and no tile source gets the bundled public-domain\n// world. Everything downstream of that \u2014 the projection, the markers, the fit, the\n// teardown \u2014 is the same code on both paths, which is the point. When the two were\n// different components, \"unconfigured\" was a second rendering path that could drift\n// away from the real one without any test noticing.\n// `runtime` is a required prop rather than a context read, so that LiveMap cannot be\n// constructed without one. The null case is handled once, in MapWidget, and this\n// component never has to reason about it.\nfunction LiveMap({\n basemap,\n positions,\n runtime,\n}: {\n basemap: { tileUrl: string; attribution: string | null } | null;\n positions: PlaceableLocation[];\n runtime: MapRuntime;\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const mapRef = useRef<MapLibreMap | null>(null);\n const markersRef = useRef<MapLibreMarker[]>([]);\n const [status, setStatus] = useState<MapStatus>('loading');\n\n const tileUrl = basemap?.tileUrl ?? null;\n const attribution = basemap?.attribution ?? null;\n\n // Init + teardown for this basemap. Re-runs when the source changes, which is the\n // only thing that invalidates the style.\n useEffect(() => {\n let cancelled = false;\n setStatus('loading');\n\n // \uD83D\uDD34 The bundled style is FETCHED, not built inline: its geometry sits behind a\n // dynamic import so a configured viewer never downloads a world map they will\n // not look at. Resolved alongside the renderer so the Map is still constructed\n // once, with its final style \u2014 nothing re-styles after mount.\n Promise.all([\n loadMapLibre(runtime),\n tileUrl ? rasterStyleFor(tileUrl, attribution ?? undefined) : loadLandStyle(),\n ])\n .then(([maplibre, style]) => {\n const el = containerRef.current;\n if (cancelled || !el) return;\n const map = new maplibre.Map({\n container: el,\n style: style as never,\n center: [0, 0],\n zoom: 1,\n });\n mapRef.current = map;\n setStatus('ready');\n })\n .catch(() => {\n // The renderer or the bundled geometry could not be fetched (offline viewer,\n // a CSP that blocks the chunk). Fall back to the plain panel rather than to\n // nothing: the positions are what the operator came for, and the basemap is\n // the part that failed.\n if (!cancelled) setStatus('unavailable');\n });\n\n return () => {\n cancelled = true;\n for (const marker of markersRef.current) marker.remove();\n markersRef.current = [];\n mapRef.current?.remove();\n mapRef.current = null;\n };\n // \uD83D\uDD34 Keyed on runtime.workerUrl, NOT on `runtime`. A host that builds the runtime\n // inline in JSX hands a new object every render; depending on the object would tear the\n // map down and rebuild it each time, which looks like flicker and loses view state. The\n // URL is the only part of it this effect's outcome depends on.\n //\n // `runtime.loadStyles` is used inside and is deliberately not a dependency: loadMapLibre\n // memoizes at module scope, first caller wins, so only the first runtime's loader is\n // ever invoked and a later one could not take effect however this array were written.\n // That is the documented contract, not an oversight \u2014 and no eslint config in this repo\n // lints packages/*, so a disable comment here would suppress nothing and merely look\n // like the question had been settled by a linter.\n }, [tileUrl, attribution, runtime.workerUrl]);\n\n // Sync the markers to the current positions. Rebuilt wholesale rather than diffed:\n // a poll delivers a whole snapshot, the marker count is a dashboard's worth of\n // devices, and a diff here would be state to get wrong for no measurable saving.\n useEffect(() => {\n const map = mapRef.current;\n if (status !== 'ready' || !map) return;\n\n let live = true;\n loadMapLibre(runtime).then((maplibre) => {\n if (!live || mapRef.current !== map) return;\n for (const marker of markersRef.current) marker.remove();\n markersRef.current = positions.map((position) => {\n const el = document.createElement('div');\n el.setAttribute('data-testid', 'map-marker');\n el.setAttribute('data-device', position.deviceToken);\n el.setAttribute('title', describePosition(position));\n el.setAttribute('aria-label', describePosition(position));\n Object.assign(el.style, MARKER_DOT_STYLE);\n // \uD83D\uDD34 setLngLat takes [LONGITUDE, LATITUDE] \u2014 x before y, the opposite order\n // from how a coordinate is spoken and written everywhere else on the platform.\n // Swapping them puts a device reporting Atlanta into the Indian Ocean, which\n // renders perfectly.\n return new maplibre.Marker({ element: el })\n .setLngLat([position.longitude, position.latitude])\n .addTo(map);\n });\n\n if (positions.length === 1) {\n map.jumpTo({ center: [positions[0].longitude, positions[0].latitude], zoom: SINGLE_DEVICE_ZOOM });\n } else if (positions.length > 1) {\n const bounds = new maplibre.LngLatBounds();\n for (const p of positions) bounds.extend([p.longitude, p.latitude]);\n map.fitBounds(bounds, { padding: 40, animate: false });\n }\n });\n\n return () => {\n live = false;\n };\n // Positions are compared by VALUE: a poll hands back a fresh array every tick, and\n // rebuilding every marker 4 times a minute for an unchanged fleet would make the\n // markers flicker.\n }, [status, positionsKey(positions)]);\n\n if (status === 'unavailable') {\n return (\n <PlainPanel\n positions={positions}\n note=\"Map renderer unavailable \u2014 positions are shown without a basemap.\"\n />\n );\n }\n\n return <div ref={containerRef} data-testid=\"map-canvas\" style={{ width: '100%', height: '100%' }} />;\n}\n\n// positionsKey is the value identity of a marker set: what is drawn, not which array\n// object carries it.\nfunction positionsKey(positions: PlaceableLocation[]): string {\n return positions.map((p) => `${p.deviceToken}:${p.latitude}:${p.longitude}:${p.heading ?? ''}`).join('|');\n}\n\n// ---- Shared bits ------------------------------------------------------------\n\nconst MARKER_DOT_STYLE = {\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n background: css('primary'),\n border: `2px solid ${css('card')}`,\n boxShadow: '0 0 0 1px rgba(0,0,0,0.25)',\n cursor: 'default',\n} as const;\n\n// Marker is the tile-less panel's dot. The tiled map builds an equivalent element\n// imperatively (MapLibre owns the DOM its markers live in), which is why the styling is\n// shared as an object rather than as a component.\nfunction Marker({\n position,\n style,\n}: {\n position: PlaceableLocation;\n style: CSSProperties;\n}) {\n const label = describePosition(position);\n return (\n <div\n data-testid=\"map-marker\"\n data-device={position.deviceToken}\n title={label}\n aria-label={label}\n style={{ ...MARKER_DOT_STYLE, ...style }}\n />\n );\n}\n\n// MapNotice is the widget's non-map state: permission, loading, empty. One component so\n// the three read as siblings \u2014 a viewer should be able to tell them apart by their words,\n// not by their layout.\nfunction MapNotice({ glyph, heading, detail }: { glyph?: string; heading: string; detail?: string }) {\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 4,\n width: '100%',\n height: '100%',\n padding: 12,\n boxSizing: 'border-box',\n textAlign: 'center',\n color: css('muted-foreground'),\n }}\n >\n {glyph ? (\n <div style={{ fontSize: 20, lineHeight: 1 }} aria-hidden=\"true\">\n {glyph}\n </div>\n ) : null}\n <div style={{ fontSize: 13, fontWeight: 600 }}>{heading}</div>\n {detail ? <div style={{ fontSize: 11, opacity: 0.8 }}>{detail}</div> : null}\n </div>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The tenant basemap, threaded to map widgets as ambient environment (ADR-079).\n//\n// \uD83D\uDD34 Why context rather than a new field on WidgetProps. A widget is contractually a\n// pure function of (widget, data): the props carry what the widget SHOWS. A tenant's\n// tile source is not data \u2014 it is environment, like the theme \u2014 and putting it in the\n// props would change the contract for all fifteen widgets to serve one of them.\n//\n// The default is null, deliberately. A host that installs no provider (widgetlab, the\n// synthetic preview, every existing test) behaves exactly as before: the map falls\n// back to its per-widget options, and to a plain panel when it has none. Adding the\n// provider is what turns a tenant's configured basemap on; nothing breaks without it.\n\nimport { createContext, useContext, type ReactNode } from 'react';\nimport type { Basemap } from '@devicechain/client';\n\nconst TenantBasemapContext = createContext<Basemap | null>(null);\n\n/**\n * Supplies the tenant's effective basemap to every map widget beneath it.\n *\n * Both hosts install this ONCE, high in the tree, rather than at each place they\n * render a widget: the console from its TenantProvider, the /dash viewer app from the\n * one place it mounts a DashboardRenderer.\n *\n * \uD83D\uDD34 That placement is the whole safety argument, because a host that forgets this\n * does NOT fail loudly \u2014 its maps simply render as they did before the tenant setting\n * existed, on that surface only. Wrapping individual render sites is the version that\n * silently half-works; there is no render site to forget from the top of the tree.\n */\nexport function TenantBasemapProvider({\n basemap,\n children,\n}: {\n basemap: Basemap | null;\n children: ReactNode;\n}) {\n return <TenantBasemapContext.Provider value={basemap}>{children}</TenantBasemapContext.Provider>;\n}\n\n/**\n * The tenant's effective basemap, or null when no host supplied one.\n *\n * Null means \"no tenant basemap in force\" \u2014 either the host installs no provider, or\n * neither the tenant nor the operator has configured one. Both cases resolve the same\n * way downstream (fall back to the per-widget override, then to a plain panel), so\n * callers do not need to distinguish them.\n */\nexport function useTenantBasemap(): Basemap | null {\n return useContext(TenantBasemapContext);\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The map runtime: the pieces of MapLibre that only the HOST'S BUNDLER can produce.\n//\n// \uD83D\uDD34 WHY THIS EXISTS AT ALL. MapLibre parses tiles and geometry off the main thread, and\n// left to itself it derives the worker's URL at runtime from its own module URL \u2014\n// `new URL('./maplibre-gl-worker.mjs', import.meta.url)`. That is a string no bundler can\n// follow, so nothing emits the file, the browser resolves it to a 404, and `new Worker()`\n// does not throw on one: the failure arrives later as an async error event. The map builds,\n// type-checks, passes every test, and renders nothing.\n//\n// The only thing that can emit that file is the application's own bundler, using its own\n// dialect \u2014 Vite spells it `?worker&url`, webpack and Rollup each spell it differently.\n// So the URL is something a HOST computes and a LIBRARY cannot.\n//\n// \uD83D\uDD34 THIS PACKAGE USED TO WRITE VITE'S DIALECT ITSELF, and that is the defect this seam\n// removes. A published library containing `import('\u2026?worker&url')` works under a consumer's\n// Vite and silently renders a blank map under anything else \u2014 and because the specifier is\n// externalized, no bundler ever tries to resolve it, so NOTHING IN ANY BUILD REPORTS IT.\n// A green pipeline over a broken artifact is strictly worse than a red one.\n//\n// \u26A0\uFE0F HOW THIS DIFFERS FROM TenantBasemapProvider, which sits beside it and looks alike.\n// The basemap provider is OPTIONAL BY DESIGN: a host that installs none behaves exactly as\n// it did before the tenant setting existed, and that is a coherent state. This one is\n// REQUIRED, and a host that omits it gets a loud notice rather than a map. The asymmetry is\n// deliberate and is the whole point: a missing basemap is a configuration a viewer can read\n// off the screen, while a missing worker URL is invisible \u2014 it is the blank map above.\n// Copying the basemap's optional-by-default shape here would relocate that defect into\n// every unwired host instead of removing it.\n\nimport { createContext, useContext, type ReactNode } from 'react';\n\n/**\n * The bundler-produced runtime a map widget needs from its host.\n *\n * `workerUrl` is the emitted, host-resolvable URL of MapLibre's worker entry\n * (`maplibre-gl/dist/maplibre-gl-worker.mjs`). It is handed to MapLibre's `setWorkerUrl`\n * before the first `Map` is constructed.\n *\n * `loadStyles` is OPTIONAL, and imports MapLibre's stylesheet\n * (`maplibre-gl/dist/maplibre-gl.css`). Omit it and import the stylesheet yourself, the way\n * every map library asks you to \u2014 that is the ordinary path and nothing here second-guesses\n * it. Supplying it buys one thing: the CSS rides the renderer's lazy chunk instead of your\n * main stylesheet, which is worth **83 KB raw / 10.7 KB gzipped** to a viewer who never\n * opens a board with a map on it.\n *\n * \uD83D\uDD34 The size is measured against maplibre-gl 6.6.0, the version this package peers on. An\n * earlier draft said 70 KB \"measured, not estimated\" \u2014 measured, but against a stale\n * `node_modules` carrying 6.2.0. Measuring the right thing in the wrong tree reads exactly\n * like measuring, which is why the version is named here.\n *\n * \uD83D\uDD34 It is a FUNCTION rather than a boolean or a URL because the host's bundler owns its\n * CSS pipeline: only the host can say \"load this, lazily, your way\".\n */\nexport type MapRuntime = {\n workerUrl: string;\n loadStyles?: () => Promise<unknown>;\n};\n\nconst MapRuntimeContext = createContext<MapRuntime | null>(null);\n\n/**\n * Supplies the host's MapLibre runtime to every map widget beneath it.\n *\n * Install it ONCE, high in the tree, next to TenantBasemapProvider \u2014 the console from its\n * TenantProvider, the /dash viewer from the one place it mounts a DashboardRenderer.\n *\n * \uD83D\uDD11 ON VITE, DO NOT WRITE ANY OF THIS \u2014 import the ready-made runtime:\n *\n * ```tsx\n * import { viteMapRuntime } from '@devicechain/widgets/vite';\n *\n * <MapRuntimeProvider runtime={viteMapRuntime}>{children}</MapRuntimeProvider>\n * ```\n *\n * On any other bundler you supply the URL yourself, and there is ONE REQUIREMENT that\n * decides whether it works: MapLibre loads the URL as a MODULE worker, so whatever it\n * serves must be a module with no unresolved imports left in it.\n * `maplibre-gl/dist/maplibre-gl-worker.mjs` does not satisfy that on its own \u2014 its first\n * line is `import \u2026 from './maplibre-gl-shared.mjs'`.\n *\n * \uD83D\uDD34 SO DO NOT POINT AT A VERBATIM COPY OF THE WORKER FILE, and in particular do not\n * reach for `new URL('maplibre-gl/dist/maplibre-gl-worker.mjs', import.meta.url)`. That\n * looks exactly right and is wrong: webpack treats the target as an ASSET and copies it,\n * the sibling is never emitted, and the worker dies on its own first line. Measured\n * against webpack 5, from a packed tarball: the worker URL answered 200, both maps drew\n * canvases, all markers appeared, tiles loaded, THE BUILD EXITED 0 AND THE CONSOLE WAS\n * EMPTY \u2014 and the bundled basemap rendered ocean with no land on it.\n *\n * Two recipes that were measured working, in `hack/consumer-proof`:\n *\n * ```js\n * // webpack \u2014 make the worker a second ENTRY, so its imports are bundled into it.\n * entry: { main: './src/index.tsx', 'maplibre-worker': 'maplibre-gl/dist/maplibre-gl-worker.mjs' }\n * // \u2026then: <MapRuntimeProvider runtime={{ workerUrl: '/maplibre-worker.js' }}>\n * ```\n *\n * ```js\n * // any bundler, or none \u2014 copy the worker AND ITS SIBLING, under their own names,\n * // into one served directory: maplibre-gl-worker.mjs + maplibre-gl-shared.mjs\n * // \u2026then: <MapRuntimeProvider runtime={{ workerUrl: '/vendor/maplibre-gl-worker.mjs' }}>\n * ```\n *\n * \uD83D\uDD34 Hold the runtime in a MODULE CONSTANT, as above, rather than building it inline in\n * JSX. An object literal in the element is a new identity on every render, and while the\n * widget deliberately keys its effects on `workerUrl` rather than on the object to survive\n * that, a host that also passes it anywhere keyed on identity would tear down and rebuild\n * the map on every parent render. There is nothing per-render in it to justify the risk.\n *\n * \uD83D\uDD34 `maplibre-gl` is a PEER dependency of this package precisely so that the copy the host\n * takes the worker from is the same copy this package renders with. A worker built from one\n * version driving a main thread from another is the blank map again, by a different route.\n */\nexport function MapRuntimeProvider({\n runtime,\n children,\n}: {\n runtime: MapRuntime;\n children: ReactNode;\n}) {\n return <MapRuntimeContext.Provider value={runtime}>{children}</MapRuntimeContext.Provider>;\n}\n\n/**\n * The host's map runtime, or null when no host supplied one.\n *\n * \uD83D\uDD34 Callers MUST treat null as a refusal to render a map, not as a default to paper over.\n * There is no sensible fallback: the only alternative to a host-supplied URL is MapLibre's\n * own runtime derivation, which is the broken behaviour this seam exists to prevent.\n */\nexport function useMapRuntime(): MapRuntime | null {\n return useContext(MapRuntimeContext);\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The map widget's pure parts: which samples are placeable, where they land on a\n// tile-less panel, and how a fix is described in words.\n//\n// Split out from the component for the usual reason the chart-option builders are \u2014\n// this is the arithmetic that decides whether a marker is in the right place, and it is\n// worth an awful lot more when it can be asserted directly than when it can only be\n// inferred from a rendered `style` string.\n\nimport type { LocationSample } from '@devicechain/dashboards';\n\n// A sample that actually has coordinates. A LocationSample's latitude/longitude are\n// nullable (the projection's columns are), and a sample missing either is not a\n// position \u2014 placing it would put a marker at (0, 0) in the Gulf of Guinea and label it\n// with a real device's name.\nexport interface PlaceableLocation extends LocationSample {\n latitude: number;\n longitude: number;\n}\n\n// placeable keeps the samples that can be drawn.\n//\n// `!= null`, NOT truthiness: latitude 0 is the equator and longitude 0 is Greenwich,\n// and both are real reported positions. A truthy filter would silently drop a device\n// sitting on either line.\nexport function placeable(samples: readonly LocationSample[]): PlaceableLocation[] {\n return samples.filter(\n (s): s is PlaceableLocation => s.latitude != null && s.longitude != null,\n );\n}\n\n// A point on the tile-less panel, as a FRACTION of its width/height, so the component\n// can express it as a percentage and stay resolution-independent.\nexport interface PanelPoint {\n x: number;\n y: number;\n}\n\n// Fraction of the panel kept clear at each edge, so a marker at the extreme of the\n// bounding box sits inside the panel rather than half-clipped by it.\nconst PANEL_PADDING = 0.1;\n\n// projectToPanel lays a set of positions out on a plain (tile-less) panel.\n//\n// An equirectangular fit of the positions' own bounding box: longitude \u2192 x, latitude \u2192\n// y, each stretched across the padded panel. Deliberately NOT a web-mercator\n// projection \u2014 without a basemap there is nothing to align to, so the only job is to\n// show the relative arrangement of a fleet truthfully, and at the extent of a site or a\n// city the difference between the two is invisible. (With a tile source the widget uses\n// MapLibre's real projection instead; this function is never asked to agree with one.)\n//\n// \uD83D\uDD34 THE AXES ARE NOT INTERCHANGEABLE, AND THE Y AXIS IS INVERTED. Longitude is\n// east-west (x); latitude is north-south (y); and screen y grows DOWNWARD while\n// latitude grows northward, so the maximum latitude maps to y = 0. Swap the two and\n// every marker still lands inside the panel, in a plausible-looking scatter \u2014 the\n// output stays well-formed, which is exactly why this is asserted directly rather than\n// eyeballed.\n//\n// A degenerate axis (one device, or several at the same latitude) has no span to\n// stretch across, so it centres on that axis rather than dividing by zero.\nexport function projectToPanel(positions: readonly PlaceableLocation[]): PanelPoint[] {\n if (positions.length === 0) return [];\n\n let minLat = Infinity;\n let maxLat = -Infinity;\n let minLon = Infinity;\n let maxLon = -Infinity;\n for (const p of positions) {\n if (p.latitude < minLat) minLat = p.latitude;\n if (p.latitude > maxLat) maxLat = p.latitude;\n if (p.longitude < minLon) minLon = p.longitude;\n if (p.longitude > maxLon) maxLon = p.longitude;\n }\n\n const lonSpan = maxLon - minLon;\n const latSpan = maxLat - minLat;\n const scale = 1 - 2 * PANEL_PADDING;\n\n return positions.map((p) => ({\n x: PANEL_PADDING + scale * (lonSpan === 0 ? 0.5 : (p.longitude - minLon) / lonSpan),\n // maxLat at the TOP: screen y grows downward, latitude grows northward.\n y: PANEL_PADDING + scale * (latSpan === 0 ? 0.5 : (maxLat - p.latitude) / latSpan),\n }));\n}\n\n// Coordinates are WGS84 decimal degrees platform-wide. Six decimal places is ~0.1 m at\n// the equator \u2014 finer than any consumer receiver \u2014 and fixed notation stops a small\n// value rendering in exponential form. Matches the device-detail position panel, so the\n// same fix reads identically in both places.\nexport function formatDegrees(value: number): string {\n return value.toFixed(6);\n}\n\n// describePosition renders one fix as a marker's hover/screen-reader text.\n//\n// \uD83D\uDD34 IT REPORTS ONLY WHAT THE DEVICE REPORTED. Elevation, accuracy, speed and heading\n// are each optional, and an absent one contributes NO SEGMENT at all \u2014 it is never\n// filled in with a zero. A marker reading \"0\u00B0\" claims the device reported due north; a\n// marker with no heading segment claims nothing, which is the truth. Every test is\n// `!= null`, so a genuine 0 (parked, sea level, due north) is still shown.\n//\n// Units are fixed platform-wide: metres, metres per second, degrees clockwise from true\n// north.\nexport function describePosition(sample: PlaceableLocation): string {\n const parts: string[] = [\n sample.deviceToken,\n `${formatDegrees(sample.latitude)}, ${formatDegrees(sample.longitude)}`,\n ];\n if (sample.elevation != null) parts.push(`${sample.elevation} m`);\n if (sample.accuracy != null) parts.push(`\u00B1${sample.accuracy} m`);\n if (sample.speed != null) parts.push(`${sample.speed} m/s`);\n if (sample.heading != null) parts.push(`${sample.heading}\u00B0`);\n return parts.join(' \u00B7 ');\n}\n\n// rasterStyleFor builds the MapLibre style document for a user-supplied raster tile\n// source.\n//\n// \uD83D\uDD34 THIS PACKAGE HARDCODES NO TILE SOURCE, AND THAT IS A DECISION RATHER THAN AN\n// OMISSION. The library is not the map \u2014 the TILES are, and they carry their own terms,\n// so a widget library is the wrong place to choose a provider on an operator's behalf.\n// The URL arrives as config, and with none set the widget falls back to the BUNDLED\n// world basemap below \u2014 public-domain geometry compiled into this package, which asks\n// no host for anything and so adopts nobody's terms. That is the distinction the rule\n// is actually about: not \"ship no map\", but \"make no request on an operator's behalf\n// to a host they never chose\".\n//\n// The PLATFORM does ship a default (ADR-079): an instance-wide setting a tenant can\n// override, which a board then inherits when its own options name nothing. That is a\n// named, visible, replaceable choice made once by an operator \u2014 a different thing from\n// this module quietly baking a host into every build of the library.\n//\n// `attribution` is passed to the source rather than hidden: a tile source's licence\n// commonly REQUIRES visible credit, so it is a property of the data, not decoration a\n// host may strip.\nexport function rasterStyleFor(tileUrl: string, attribution: string | undefined): unknown {\n return {\n version: 8,\n projection: PROJECTION,\n sources: {\n basemap: {\n type: 'raster',\n tiles: [tileUrl],\n tileSize: 256,\n ...(attribution ? { attribution } : {}),\n },\n },\n layers: [{ id: 'basemap', type: 'raster', source: 'basemap' }],\n };\n}\n\n// ---- The bundled world basemap ----------------------------------------------\n\n// \uD83D\uDD34 THE PROJECTION IS THE CORRECTNESS PROPERTY OF THIS WHOLE FILE, and it is\n// declared explicitly \u2014 on BOTH styles \u2014 rather than left to the default.\n//\n// The fence editor stores the coordinates a click produces, so what a style\n// changes must be what is DRAWN and never where a click LANDS. Mercator is\n// MapLibre's default, so writing it out changes no behaviour today. What it buys\n// is that the two styles now state the same thing in the same words, and a test\n// can compare them: an invariant nobody wrote down is an invariant a later edit\n// breaks without anything going red.\n//\n// This is not hypothetical in MapLibre 6. `projection` is a root style key whose\n// `type` is a ZOOM-INTERPOLATABLE expression, so a style is entitled to hand back\n// a globe past some zoom. A globe re-projects the pointer; every ring an author\n// then drew would be saved somewhere they never pointed at, on a map that looked\n// beautiful throughout.\nconst PROJECTION = { type: 'mercator' } as const;\n\n// The bundled basemap's credit line. Natural Earth requires NO attribution \u2014 its\n// licence says in as many words that crediting the authors is unnecessary \u2014 so\n// this is their own suggested short citation, shown for a reason of ours: a\n// viewer has to be able to tell a schematic bundled world apart from a configured\n// provider, or \"no tiles\" reads as \"broken\" all over again.\nexport const BUNDLED_BASEMAP_ATTRIBUTION = 'Made with Natural Earth';\n\n// The bundled style's palette. Literal colours, not theme tokens: a MapLibre style\n// document is JSON handed to a WebGL renderer, and it does not resolve the CSS\n// custom properties the rest of this package themes with. These sit close to the\n// dark surface the fence editor already drew, so the change reads as \"the void\n// grew continents\" rather than as a new skin.\nconst OCEAN = '#0b1220';\nconst LAND = '#1e293b';\nconst BOUNDARY = '#475569';\n\n// landStyleFrom builds the bundled world style from Natural Earth geometry.\n//\n// Kept separate from the loader, and pure, so the style document can be asserted\n// without pulling 150 KiB of coordinates into a test \u2014 and so the one thing worth\n// asserting about it (that it projects exactly as the tiled style does) is\n// checkable in isolation.\nexport function landStyleFrom(land: unknown, boundaries: unknown): unknown {\n return {\n version: 8,\n projection: PROJECTION,\n sources: {\n land: { type: 'geojson', data: land, attribution: BUNDLED_BASEMAP_ATTRIBUTION },\n boundaries: { type: 'geojson', data: boundaries },\n },\n layers: [\n { id: 'ocean', type: 'background', paint: { 'background-color': OCEAN } },\n { id: 'land', type: 'fill', source: 'land', paint: { 'fill-color': LAND } },\n {\n id: 'boundaries',\n type: 'line',\n source: 'boundaries',\n paint: { 'line-color': BOUNDARY, 'line-width': 0.5 },\n },\n ],\n };\n}\n\n// loadLandStyle fetches the bundled geometry and builds its style.\n//\n// \uD83D\uDD34 THE DYNAMIC IMPORT IS THE WHOLE POINT. natural-earth-data is ~150 KiB (~44\n// KiB over the wire) and it must never enter this package's entry chunk: with a\n// tile source configured \u2014 which, since the platform ships a default provider, is\n// the ordinary case \u2014 nobody should download a world map they will not see. A\n// static `import { NATURAL_EARTH }` at the top of this file would give every\n// viewer the payload and change nothing observable, which is how it would survive\n// review.\n//\n// Memoized on the promise, so several map widgets on one board parse it once.\n//\n// \uD83D\uDD34 That memoizes a REJECTION too: one transient chunk failure leaves every\n// unconfigured map on the page falling back until a reload. Deliberate, and the same\n// bargain loadMapLibre has always made \u2014 a retry-on-failure cache would let N widgets\n// re-request a chunk that is failing for a reason retrying will not fix. Worth knowing\n// that the blast radius grew when this became the DEFAULT unconfigured path rather\n// than a rarity.\nlet landStylePromise: Promise<unknown> | undefined;\n\nexport function loadLandStyle(): Promise<unknown> {\n if (!landStylePromise) {\n landStylePromise = import('./natural-earth-data').then((mod) =>\n landStyleFrom(mod.NATURAL_EARTH.land, mod.NATURAL_EARTH.boundaries),\n );\n }\n return landStylePromise;\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// table \u2014 the latest value of every measurement the datasource carries, one row\n// per measurement name.\n\nimport type { CSSProperties } from 'react';\n\nimport { FlashValue } from '../flash';\nimport { formatTimestamp } from '../format';\nimport { WidgetFrame } from '../frame';\nimport { css } from '../theme';\nimport { optBoolean, optNumber, optString, type WidgetProps } from '../widget';\n\nconst cell: CSSProperties = { padding: '6px 12px', textAlign: 'left' };\nconst headCell: CSSProperties = {\n ...cell,\n fontSize: 11,\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.04em',\n color: css('muted-foreground'),\n borderBottom: `1px solid ${css('border')}`,\n};\n\nexport function Table({ widget, data }: WidgetProps) {\n const rows = Object.values(data.latest).sort((a, b) => a.name.localeCompare(b.name));\n // Round to a fixed number of decimals when configured; unset keeps the raw value\n // (formatValue's default). A live sensor value is otherwise a wall of digits.\n const precision = optNumber(widget.options, 'precision');\n // Opt-in directional flash per row (keyed by measurement name, so each value's cue is\n // independent \u2014 see FlashValue).\n const flash = optBoolean(widget.options, 'flashOnChange');\n\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div style={{ height: '100%', overflow: 'auto' }}>\n <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13, color: css('foreground') }}>\n <thead>\n <tr>\n <th style={headCell}>Measurement</th>\n <th style={headCell}>Value</th>\n <th style={headCell}>Time</th>\n </tr>\n </thead>\n <tbody>\n {rows.length === 0 ? (\n <tr>\n <td colSpan={3} style={{ ...cell, textAlign: 'center', color: css('muted-foreground') }}>\n No data yet\n </td>\n </tr>\n ) : (\n rows.map((row) => (\n <tr key={row.name}>\n <td style={cell}>{row.name}</td>\n <td style={{ ...cell, fontVariantNumeric: 'tabular-nums' }}>\n <FlashValue\n value={row.value}\n precision={precision}\n enabled={flash}\n identity={`${row.deviceToken}:${row.name}`}\n />\n {/* classifier is deliberately NOT shown: it is the bound metric\n definition's internal id (ADR-016), not a user-facing label \u2014\n appending it rendered as spurious digits after the value. */}\n </td>\n <td style={{ ...cell, color: css('muted-foreground') }}>{formatTimestamp(row.occurredTime)}</td>\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// timeseries-chart \u2014 a live line chart of the rolling measurement window, one\n// series per measurement name. The window is seeded from bucketedMeasurements\n// history (via useMeasurementStream's initialSamples) and extended by the live tail.\n\nimport { useMemo } from 'react';\n\nimport { buildLineOption } from '../chart-options';\nimport { EChart } from '../echart';\nimport { WidgetFrame } from '../frame';\nimport { useChartTheme, useElementSize } from '../hooks';\nimport { optString, type WidgetProps } from '../widget';\n\nexport function TimeSeriesChart({ widget, data }: WidgetProps) {\n const theme = useChartTheme();\n const [sizeRef, size] = useElementSize<HTMLDivElement>();\n\n const measurements = widget.datasource?.measurements;\n const option = useMemo(\n () => buildLineOption(data.samples, theme, { measurements }, size),\n [data.samples, theme, measurements, size.width, size.height],\n );\n\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div ref={sizeRef} style={{ width: '100%', height: '100%' }}>\n <EChart option={option} />\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The widget registries + the channel classifier. A renderer (ConnectedWidget) looks a\n// widget up by `widget.type`, but FIRST reads WIDGET_CHANNEL to decide which data\n// channel \u2014 and therefore which registry + hook \u2014 binds it: measurement widgets stream\n// telemetry samples; alarm widgets consume the raised-alarm surface; location widgets\n// read the bound devices' positions. Custom widgets extend these same maps.\n\nimport type { WidgetType } from '@devicechain/dashboards';\n\nimport type { AlarmStreamState, CommandStreamState, LocationStreamState } from './hooks';\nimport type { WidgetComponent } from './widget';\nimport { AlarmCount } from './widgets/alarm-count';\nimport { AlarmTable } from './widgets/alarm-table';\nimport { CommandButton } from './widgets/command-button';\nimport { EntitySelector } from './widgets/entity-selector';\nimport { Gauge } from './widgets/gauge';\nimport { Image } from './widgets/image';\nimport { Label } from './widgets/label';\nimport { LatestCard } from './widgets/latest-card';\nimport { MapWidget } from './widgets/map';\nimport { Table } from './widgets/table';\nimport { TimeSeriesChart } from './widgets/time-series-chart';\n\n// A widget's data channel: which hub subscription + registry the renderer binds it\n// through. WIDGET_CHANNEL is the SINGLE source of truth \u2014 `satisfies` keeps it\n// exhaustive over WidgetType (a new type won't compile without a channel), and the\n// per-channel type subsets are DERIVED from it, so the channel map and the two\n// registries can't drift out of sync.\nexport type WidgetChannel = 'measurement' | 'alarm' | 'control' | 'selection' | 'location';\n\nexport const WIDGET_CHANNEL = {\n 'timeseries-chart': 'measurement',\n 'latest-card': 'measurement',\n gauge: 'measurement',\n table: 'measurement',\n label: 'measurement',\n image: 'measurement',\n 'alarm-table': 'alarm',\n 'alarm-count': 'alarm',\n 'command-button': 'control',\n 'entity-selector': 'selection',\n map: 'location',\n} as const satisfies Record<WidgetType, WidgetChannel>;\n\n// The widget types on each channel, derived from WIDGET_CHANNEL so the registries below\n// stay exhaustive without a hand-maintained parallel union. Each channel derives its\n// members the same way (a self-describing `extends '<channel>'` filter), so adding a\n// fourth channel never silently folds its types into another registry.\ntype WidgetTypeOn<C extends WidgetChannel> = {\n [K in WidgetType]: (typeof WIDGET_CHANNEL)[K] extends C ? K : never;\n}[WidgetType];\ntype MeasurementWidgetType = WidgetTypeOn<'measurement'>;\ntype AlarmWidgetType = WidgetTypeOn<'alarm'>;\ntype ControlWidgetType = WidgetTypeOn<'control'>;\ntype SelectionWidgetType = WidgetTypeOn<'selection'>;\ntype LocationWidgetType = WidgetTypeOn<'location'>;\n\nexport const WIDGET_REGISTRY: Record<MeasurementWidgetType, WidgetComponent> = {\n 'timeseries-chart': TimeSeriesChart,\n 'latest-card': LatestCard,\n gauge: Gauge,\n table: Table,\n label: Label,\n image: Image,\n};\n\nexport const ALARM_WIDGET_REGISTRY: Record<AlarmWidgetType, WidgetComponent<AlarmStreamState>> = {\n 'alarm-table': AlarmTable,\n 'alarm-count': AlarmCount,\n};\n\nexport const CONTROL_WIDGET_REGISTRY: Record<ControlWidgetType, WidgetComponent<CommandStreamState>> = {\n 'command-button': CommandButton,\n};\n\n// Selection widgets carry no hub data \u2014 they read the ambient candidate provider + selection\n// callback and drive a slot binding. `null` marks the empty data channel (see ConnectedWidget).\nexport const SELECTION_WIDGET_REGISTRY: Record<SelectionWidgetType, WidgetComponent<null>> = {\n 'entity-selector': EntitySelector,\n};\n\n// Location widgets read the hub's location channel: the bound devices' last-known\n// positions, plus the refusal state the `location:read` authority produces.\nexport const LOCATION_WIDGET_REGISTRY: Record<LocationWidgetType, WidgetComponent<LocationStreamState>> = {\n map: MapWidget,\n};\n\n// Whether a widget type binds a DATASOURCE \u2014 the entity whose data it reads.\n//\n// Not derivable from the channel: label and image sit on the measurement channel\n// (ConnectedWidget's default branch) and read no stream at all, while the alarm and\n// control widgets carry one as a SCOPE rather than a value selector. An authoring UI\n// needs this to decide whether to offer a datasource picker, and it had it as a\n// hand-written set that no compiler checked.\n//\n// `satisfies` keeps it exhaustive, so a new widget type does not compile until it\n// says whether it binds anything \u2014 which is the question the config panel silently\n// answered \"no\" to for every type nobody remembered to add.\nexport const WIDGET_BINDS_DATASOURCE = {\n 'timeseries-chart': true,\n 'latest-card': true,\n gauge: true,\n table: true,\n label: false,\n image: false,\n 'alarm-table': true,\n 'alarm-count': true,\n 'command-button': true,\n 'entity-selector': false,\n // A map binds a datasource, and needs it: the selector names both the devices whose\n // positions to plot and (in its own field) the location series to read.\n map: true,\n} as const satisfies Record<WidgetType, boolean>;\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// ConnectedWidget is the one place the imperative DashboardHub meets the pure widget\n// contract. It dispatches on the widget's data CHANNEL (WIDGET_CHANNEL): a measurement\n// widget subscribes its datasource (windowed per its options); an alarm widget\n// subscribes its scope+filters through the hub's alarm channel. Either way it resolves\n// the data and renders the pure widget with { widget, data }. A dashboard renderer maps\n// over its widgets rendering one ConnectedWidget each.\n\nimport type {\n AlarmSubscription,\n CommandSubscription,\n LocationSubscription,\n MeasurementSample,\n WidgetActions,\n WidgetDataSource,\n WidgetInstance,\n} from '@devicechain/dashboards';\nimport type { ReactNode } from 'react';\n\nimport { WidgetFrame } from './frame';\nimport {\n useAlarmStream,\n useCommandStream,\n useDatasourceAvailability,\n useLocationStream,\n useMeasurementStream,\n} from './hooks';\nimport {\n ALARM_WIDGET_REGISTRY,\n CONTROL_WIDGET_REGISTRY,\n LOCATION_WIDGET_REGISTRY,\n SELECTION_WIDGET_REGISTRY,\n WIDGET_CHANNEL,\n WIDGET_REGISTRY,\n} from './registry';\nimport { optNumber, optString } from './widget';\n\n// Rows an alarm table shows before scrolling; also the default page size the count\n// query reads (the count itself uses the server total, independent of this).\nconst DEFAULT_ALARM_ROWS = 50;\n\n// Recent commands a command-button shows in its history before scrolling.\nconst DEFAULT_COMMAND_ROWS = 20;\n\nexport interface ConnectedWidgetProps {\n widget: WidgetInstance;\n hub: WidgetDataSource;\n // The action seam (writes), threaded to acting widgets. Undefined = read-only.\n actions?: WidgetActions;\n // Optional history backfill for this widget (from bucketedMeasurements), seeded\n // under the live tail so a chart isn't blank until the first live sample. Ignored by\n // alarm widgets (their channel reconciles from a query, not a history seed).\n initialSamples?: MeasurementSample[];\n}\n\nexport function ConnectedWidget({ widget, hub, actions, initialSamples }: ConnectedWidgetProps) {\n // A widget bound to a since-deleted device (its stable token no longer resolves)\n // shows an \"unavailable\" pane rather than a blank one \u2014 checked optimistically, so a\n // valid widget renders from its stream without waiting on this. Uniform across every\n // channel, so it wraps the dispatch below.\n const availability = useDatasourceAvailability(hub, widget.datasource);\n if (availability === 'unavailable') {\n return <WidgetUnavailableFrame widget={widget} />;\n }\n\n const channel = WIDGET_CHANNEL[widget.type];\n if (channel === 'alarm') {\n return <AlarmConnectedWidget widget={widget} hub={hub} actions={actions} />;\n }\n if (channel === 'control') {\n return <ControlConnectedWidget widget={widget} hub={hub} actions={actions} />;\n }\n if (channel === 'selection') {\n return <SelectionConnectedWidget widget={widget} />;\n }\n if (channel === 'location') {\n return <LocationConnectedWidget widget={widget} hub={hub} />;\n }\n return <MeasurementConnectedWidget widget={widget} hub={hub} initialSamples={initialSamples} />;\n}\n\n// A selection widget (entity-selector) consumes no hub data \u2014 it reads the ambient candidate\n// provider + selection callback. It carries no datasource, so the availability check above is\n// trivially satisfied and there is no stream to bind.\nfunction SelectionConnectedWidget({ widget }: { widget: WidgetInstance }) {\n const Component = SELECTION_WIDGET_REGISTRY[widget.type as keyof typeof SELECTION_WIDGET_REGISTRY];\n if (!Component) return null;\n return <Component widget={widget} data={null} />;\n}\n\nfunction MeasurementConnectedWidget({ widget, hub, initialSamples }: ConnectedWidgetProps) {\n const data = useMeasurementStream(hub, widget.datasource, {\n window: optNumber(widget.options, 'window'),\n initialSamples,\n });\n\n if (data.error) return <WidgetErrorFrame widget={widget} error={data.error} />;\n\n const Component = WIDGET_REGISTRY[widget.type as keyof typeof WIDGET_REGISTRY];\n if (!Component) return null; // unknown/misclassified type in a hand-edited definition\n return <Component widget={widget} data={data} />;\n}\n\nfunction AlarmConnectedWidget({\n widget,\n hub,\n actions,\n}: {\n widget: WidgetInstance;\n hub: WidgetDataSource;\n actions?: WidgetActions;\n}) {\n const data = useAlarmStream(hub, alarmSubscription(widget));\n\n // Show the error pane ONLY when there's nothing to display (the initial load failed).\n // Once a snapshot has loaded, a later transient poll/query error keeps the last-good\n // rows on screen \u2014 the channel re-queries every 30s and self-heals, so blanking a\n // populated table on one blip is the exact blink the console page avoids.\n if (data.error && data.alarms.length === 0 && data.total === 0) {\n return <WidgetErrorFrame widget={widget} error={data.error} />;\n }\n\n const Component = ALARM_WIDGET_REGISTRY[widget.type as keyof typeof ALARM_WIDGET_REGISTRY];\n if (!Component) return null;\n return <Component widget={widget} data={data} actions={actions} />;\n}\n\nfunction ControlConnectedWidget({\n widget,\n hub,\n actions,\n}: {\n widget: WidgetInstance;\n hub: WidgetDataSource;\n actions?: WidgetActions;\n}) {\n const data = useCommandStream(hub, commandSubscription(widget));\n\n // Unlike the read-only widgets, a command-button is NEVER replaced by the error pane:\n // its primary control is the send form (a separate authority from reading history), so\n // a history poll error must not tear the form down. The widget surfaces the error\n // inline instead.\n const Component = CONTROL_WIDGET_REGISTRY[widget.type as keyof typeof CONTROL_WIDGET_REGISTRY];\n if (!Component) return null;\n return <Component widget={widget} data={data} actions={actions} />;\n}\n\nfunction LocationConnectedWidget({\n widget,\n hub,\n}: {\n widget: WidgetInstance;\n hub: WidgetDataSource;\n}) {\n const data = useLocationStream(hub, locationSubscription(widget));\n\n // Same rule as the alarm channel: show the error pane ONLY when there is nothing to\n // display. Once a snapshot has loaded, a later transient poll error keeps the last\n // known positions on screen \u2014 the channel re-polls and self-heals, and blanking a\n // populated map on one blip is worse than a marker being a few seconds stale.\n //\n // \uD83D\uDD34 A REFUSAL IS NOT AN ERROR AND MUST NOT REACH THIS BRANCH. The hub delivers it as\n // a snapshot state with `error` null, so the widget renders its own permission copy \u2014\n // if it were surfaced as an error, every ordinary member would see \"Data unavailable\"\n // where the platform is working exactly as designed.\n if (data.error && data.locations.length === 0) {\n return <WidgetErrorFrame widget={widget} error={data.error} />;\n }\n\n const Component = LOCATION_WIDGET_REGISTRY[widget.type as keyof typeof LOCATION_WIDGET_REGISTRY];\n if (!Component) return null;\n return <Component widget={widget} data={data} />;\n}\n\n// locationSubscription reads a map widget's selector from its definition: the devices to\n// plot AND the location series to read (the selector's own `location` field). The tile\n// source lives in the widget's options, not here \u2014 it is a rendering choice, not part of\n// what the widget asks the platform for.\nfunction locationSubscription(widget: WidgetInstance): LocationSubscription {\n return { datasource: widget.datasource };\n}\n\n// commandSubscription reads a command widget's scope (the target device) from its\n// definition; the command name + parameter schema live in the widget's options (baked by\n// the console), not the subscription. pageSize bounds the recent-command history shown.\nfunction commandSubscription(widget: WidgetInstance): CommandSubscription {\n return {\n datasource: widget.datasource,\n pageSize: Math.max(1, optNumber(widget.options, 'maxRows') ?? DEFAULT_COMMAND_ROWS),\n };\n}\n\n// alarmSubscription reads an alarm widget's scope+filters from its definition: the\n// datasource (undefined = tenant-wide) plus the state/severity/acknowledged filters. A\n// count reports the server total (independent of pageSize) but still reads a page so its\n// worst-severity accent samples the current alarms rather than only the single newest;\n// a table reads its configured, clamped row cap.\nfunction alarmSubscription(widget: WidgetInstance): AlarmSubscription {\n const pageSize =\n widget.type === 'alarm-count'\n ? DEFAULT_ALARM_ROWS\n : Math.max(1, optNumber(widget.options, 'maxRows') ?? DEFAULT_ALARM_ROWS);\n return {\n datasource: widget.datasource,\n state: optString(widget.options, 'state'),\n severity: optString(widget.options, 'severity'),\n acknowledged: parseAcknowledged(optString(widget.options, 'acknowledged')),\n pageSize,\n };\n}\n\n// The acknowledged filter is stored as 'true' / 'false' (or absent = any).\nfunction parseAcknowledged(value: string | undefined): boolean | undefined {\n if (value === 'true') return true;\n if (value === 'false') return false;\n return undefined;\n}\n\n// WidgetUnavailableFrame is shown when a widget's bound device no longer exists (a\n// deleted device's stable token). Distinct from the error frame: not a transient\n// failure to retry, but a settled \"this entity is gone\" \u2014 so it reads as a state, not\n// an error.\nfunction WidgetUnavailableFrame({ widget }: { widget: WidgetInstance }): ReactNode {\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 4,\n width: '100%',\n height: '100%',\n padding: 12,\n boxSizing: 'border-box',\n textAlign: 'center',\n color: 'hsl(var(--muted-foreground))',\n }}\n >\n <div style={{ fontSize: 20, lineHeight: 1 }} aria-hidden=\"true\">\u2298</div>\n <div style={{ fontSize: 13, fontWeight: 600 }}>Device unavailable</div>\n <div style={{ fontSize: 11, opacity: 0.8 }}>The bound device no longer exists.</div>\n </div>\n </WidgetFrame>\n );\n}\n\n// A resolution/socket error surfaces as a muted error state rather than a blank pane.\n// Shared by both the measurement and alarm channels.\nfunction WidgetErrorFrame({ widget, error }: { widget: WidgetInstance; error: unknown }): ReactNode {\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 4,\n width: '100%',\n height: '100%',\n padding: 12,\n boxSizing: 'border-box',\n textAlign: 'center',\n color: 'hsl(var(--muted-foreground))',\n }}\n >\n <div style={{ fontSize: 20, lineHeight: 1 }}>\u26A0</div>\n <div style={{ fontSize: 13, fontWeight: 600 }}>Data unavailable</div>\n <div style={{ fontSize: 11, opacity: 0.8, wordBreak: 'break-word' }}>{String(error)}</div>\n </div>\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// WIDGET_OPTIONS \u2014 the typed schema for each widget type's `options` bag.\n//\n// A WidgetInstance's `options` is `Record<string, unknown>` in the stored definition:\n// dashboard-management keeps a definition as opaque JSON, and the definition parser does\n// not look inside `options` at all. That leaves every option key owned by NOTHING \u2014 a\n// renamed key breaks no build on either side of the wire, and a wrong-typed value is read\n// back as `undefined` by optString/optNumber and rendered as the default, so a\n// misconfigured widget is indistinguishable from a correctly-configured one.\n//\n// This table is that missing owner. It declares, per widget type, every option the\n// RENDERER actually reads, with its type and legal values. `as const satisfies\n// Record<WidgetType, \u2026>` makes it exhaustive the same way WIDGET_CHANNEL is: a new widget\n// type does not compile until it declares an option schema, and an empty schema is then a\n// deliberate statement rather than an omission.\n//\n// Two consumers, one table:\n// \u2022 validateWidgetOptions() \u2014 the runtime check, for anything that PRODUCES a definition\n// (a generated dashboard must not ship an options blob the renderer ignores).\n// \u2022 WidgetOptions<T> \u2014 the compile-time shape, for code that builds options.\n//\n// The table describes the RENDERER, because that is the contract deciding what a viewer\n// actually sees. Where the console's authoring UI offers fewer keys than this \u2014 it does:\n// `align`, `fontSize`, `fit`, `window` and `measurement` are all honored by the renderer\n// and not authorable in the config panel \u2014 the gap is in the panel, not here.\n\nimport { type WidgetType } from '@devicechain/dashboards';\n\n// ---- Spec vocabulary --------------------------------------------------------\n\n// `required` marks an option whose ABSENCE leaves the widget with nothing to show.\n// The renderer never throws on a missing option \u2014 an image with no `url` renders \"No\n// image URL\", a command-button with no `commandName` renders \"No command configured\" \u2014\n// which is deliberate graceful degradation for a hand-edited definition, but is exactly\n// the failure a generated dashboard must not ship. So `required` is an AUTHORING\n// completeness contract, not a claim that the renderer rejects the option's absence.\n//\n// Absent here means absent AS THE RENDERER SEES IT, which for a required string includes\n// the EMPTY string: every one of the four required options is read into a truthiness test\n// (`url ? \u2026 : 'No image URL'`, `!commandName`, `!target`), so `''` produces the same\n// placeholder as an omitted key. Treating `''` as present would let a generated dashboard\n// carrying `\"url\": \"\"` validate clean and render nothing.\ninterface OptionSpecBase {\n // What the option does. Short: this feeds the config panel's help text and the\n // published \"available widgets\" page.\n doc: string;\n required?: true;\n}\n\nexport interface StringOptionSpec extends OptionSpecBase {\n kind: 'string';\n // 'json' additionally requires the string to parse as JSON. Used for the command\n // button's baked parameterSchema, where a malformed schema is silently swallowed\n // (parseParameterSchema returns []) and renders as a bare Send button.\n format?: 'json';\n}\n\nexport interface NumberOptionSpec extends OptionSpecBase {\n kind: 'number';\n min?: number;\n max?: number;\n integer?: true;\n}\n\nexport interface BooleanOptionSpec extends OptionSpecBase {\n kind: 'boolean';\n}\n\nexport interface EnumOptionSpec<V extends string = string> extends OptionSpecBase {\n kind: 'enum';\n values: readonly V[];\n}\n\nexport type OptionSpec = StringOptionSpec | NumberOptionSpec | BooleanOptionSpec | EnumOptionSpec;\n\nexport type WidgetOptionSpecs = Record<string, OptionSpec>;\n\n// ---- Shared option specs ----------------------------------------------------\n// Declared once and referenced by each widget that reads them, so the same key always\n// carries the same type and bounds. They are still listed PER WIDGET below rather than\n// merged in from a base: which widget reads which option is the fact this table exists\n// to record, and a base object would hide it.\n\nconst TITLE = {\n kind: 'string',\n doc: 'Heading shown in the widget frame. Blank hides the frame heading.',\n} as const satisfies OptionSpec;\n\n// gauge and latest-card fall back to the displayed measurement's name when no title is set.\nconst TITLE_OR_MEASUREMENT = {\n kind: 'string',\n doc: 'Heading shown in the widget frame. Defaults to the displayed measurement name.',\n} as const satisfies OptionSpec;\n\n// toFixed()'s domain is 0..100 \u2014 outside it throws a RangeError, which would take the\n// whole widget subtree down rather than degrading.\nconst PRECISION = {\n kind: 'number',\n integer: true,\n min: 0,\n max: 100,\n doc: 'Decimal places to round displayed values to. Unset shows the raw value.',\n} as const satisfies OptionSpec;\n\nconst FLASH_ON_CHANGE = {\n kind: 'boolean',\n doc: 'Briefly tint a value green on a rise and red on a fall when it changes.',\n} as const satisfies OptionSpec;\n\n// Read by MeasurementConnectedWidget for every measurement-channel widget, and declared on\n// each one that binds a datasource. It is NOT declared on label/image, which carry no\n// datasource at all, so there is no stream for it to size.\n//\n// It bounds the RETAINED buffer, not what is drawn: useMeasurementStream keeps `latest`\n// per measurement unconditionally and trims only `samples`. Only the chart draws\n// `samples`, so on a card, gauge or table this is a memory bound with no visible effect \u2014\n// declared because the renderer honors it, documented so nobody expects it to show.\n//\n// The trim is GLOBAL across the widget's measurements, not per measurement: `samples` is\n// one interleaved list, so a chart drawing N series retains roughly window/N of each. An\n// earlier version of this doc said \"per measurement\", and a caller sized a window off it\n// and got half the history it intended.\nconst WINDOW = {\n kind: 'number',\n integer: true,\n min: 1,\n doc: 'Samples retained across ALL of the widget\\'s measurements before the oldest are dropped. Bounds memory; only the chart draws them. Default 300.',\n} as const satisfies OptionSpec;\n\nconst UNIT = {\n kind: 'string',\n doc: 'Unit suffix shown beside the value.',\n} as const satisfies OptionSpec;\n\nconst ALARM_STATE = {\n kind: 'enum',\n values: ['ACTIVE', 'CLEARED'],\n doc: 'Show only alarms in this state. Unset shows every state.',\n} as const satisfies OptionSpec;\n\nconst ALARM_SEVERITY = {\n kind: 'enum',\n values: ['CRITICAL', 'MAJOR', 'MINOR', 'WARNING', 'INDETERMINATE'],\n doc: 'Show only alarms at this severity. Unset shows every severity.',\n} as const satisfies OptionSpec;\n\n// Stored as the STRING 'true'/'false', not a boolean \u2014 alarmSubscription() reads it with\n// optString and maps it through parseAcknowledged, so a real boolean here is read as\n// undefined and the filter silently disappears. Typed as an enum of those two strings so\n// that mistake is an error rather than a no-op.\nconst ALARM_ACKNOWLEDGED = {\n kind: 'enum',\n values: ['true', 'false'],\n doc: \"Show only acknowledged ('true') or unacknowledged ('false') alarms. Unset shows both.\",\n} as const satisfies OptionSpec;\n\nconst SELECTION_TARGET = {\n kind: 'string',\n doc: 'Name of the slot this widget re-points when the viewer picks an entity.',\n} as const satisfies OptionSpec;\n\n// ---- The table --------------------------------------------------------------\n\nexport const WIDGET_OPTIONS = {\n 'timeseries-chart': {\n title: TITLE,\n window: WINDOW,\n },\n\n 'latest-card': {\n title: TITLE_OR_MEASUREMENT,\n measurement: {\n kind: 'string',\n doc: 'Which measurement to display. Defaults to the first the datasource selects.',\n },\n unit: UNIT,\n precision: PRECISION,\n flashOnChange: FLASH_ON_CHANGE,\n window: WINDOW,\n },\n\n gauge: {\n title: TITLE_OR_MEASUREMENT,\n measurement: {\n kind: 'string',\n doc: 'Which measurement to display. Defaults to the first the datasource selects.',\n },\n min: {\n kind: 'number',\n doc: 'Low end of the gauge scale. Default 0.',\n },\n max: {\n kind: 'number',\n doc: 'High end of the gauge scale. Default 100.',\n },\n unit: UNIT,\n flashOnChange: FLASH_ON_CHANGE,\n window: WINDOW,\n },\n\n table: {\n title: TITLE,\n precision: PRECISION,\n flashOnChange: FLASH_ON_CHANGE,\n window: WINDOW,\n },\n\n // label and image carry no datasource: they sit on the measurement channel only\n // because that is ConnectedWidget's default branch, and read no stream data at all.\n label: {\n title: TITLE,\n text: {\n kind: 'string',\n required: true,\n doc: 'The text to show. Rendered as plain text \u2014 markup is never interpreted.',\n },\n align: {\n kind: 'enum',\n values: ['left', 'center', 'right'],\n doc: 'Horizontal alignment of the text. Default center.',\n },\n fontSize: {\n kind: 'number',\n min: 1,\n doc: 'Text size in pixels. Default 16.',\n },\n },\n\n image: {\n title: TITLE,\n url: {\n kind: 'string',\n required: true,\n doc: 'Source URL of the image.',\n },\n alt: {\n kind: 'string',\n doc: 'Alternative text describing the image for assistive technology.',\n },\n fit: {\n kind: 'enum',\n values: ['contain', 'cover', 'fill'],\n doc: 'How the image fills its box. Default contain.',\n },\n },\n\n 'alarm-table': {\n title: TITLE,\n state: ALARM_STATE,\n severity: ALARM_SEVERITY,\n acknowledged: ALARM_ACKNOWLEDGED,\n maxRows: {\n kind: 'number',\n integer: true,\n min: 1,\n doc: 'Alarms loaded into the table. Default 50.',\n },\n precision: PRECISION,\n flashOnChange: FLASH_ON_CHANGE,\n selectionTarget: SELECTION_TARGET,\n },\n\n // No maxRows: alarmSubscription() forces the default page size for a count, because the\n // number it shows is the SERVER total and the page is read only to sample the worst\n // loaded severity. An authored maxRows here would be read by nothing.\n 'alarm-count': {\n title: TITLE,\n state: ALARM_STATE,\n severity: ALARM_SEVERITY,\n acknowledged: ALARM_ACKNOWLEDGED,\n },\n\n 'command-button': {\n title: TITLE,\n commandName: {\n kind: 'string',\n required: true,\n doc: 'Command key issued to the target device. Without it the widget cannot send.',\n },\n commandLabel: {\n kind: 'string',\n doc: 'Text on the send button. Defaults to the command key.',\n },\n parameterSchema: {\n kind: 'string',\n format: 'json',\n doc: \"The command's parameter descriptors, baked in as JSON at author time. Drives the typed form.\",\n },\n maxRows: {\n kind: 'number',\n integer: true,\n min: 1,\n doc: 'Recent commands shown in the history. Default 20.',\n },\n },\n\n 'entity-selector': {\n title: TITLE,\n selectionTarget: { ...SELECTION_TARGET, required: true },\n },\n\n // \uD83D\uDD34 tileUrl is NOT `required`, and that is the decision rather than an oversight.\n // `required` means \"absent leaves the widget with nothing to show\", and a map without\n // a basemap still shows every position it was asked to \u2014 on a plain panel, saying why\n // there is no map behind them. Marking it required would report a correctly-rendering\n // widget as broken on every board that names no provider of its own \u2014 which is most\n // of them, because the usual and recommended answer is to inherit the tenant's\n // basemap rather than to configure tiles per board.\n map: {\n title: TITLE,\n tileUrl: {\n kind: 'string',\n doc: \"Raster tile URL template, e.g. https://tiles.example.com/{z}/{x}/{y}.png. Leave empty to inherit the tenant's basemap; setting one overrides it for this board only.\",\n },\n attribution: {\n kind: 'string',\n doc: 'Credit line the tile source requires, shown on the map. Set whatever the provider you chose asks for.',\n },\n },\n} as const satisfies Record<WidgetType, WidgetOptionSpecs>;\n\n// ---- Derived compile-time types ---------------------------------------------\n\n// The value type a spec accepts. Enum first: an EnumOptionSpec's values carry the literal\n// union, which is the whole point of declaring one.\ntype SpecValue<S> = S extends { kind: 'enum'; values: readonly (infer V)[] }\n ? V extends string\n ? V\n : never\n : S extends { kind: 'number' }\n ? number\n : S extends { kind: 'boolean' }\n ? boolean\n : S extends { kind: 'string' }\n ? string\n : never;\n\ntype RequiredOptionKeys<S> = {\n [K in keyof S]: S[K] extends { required: true } ? K : never;\n}[keyof S];\n\ntype OptionsFor<T extends WidgetType> = {\n [K in RequiredOptionKeys<(typeof WIDGET_OPTIONS)[T]>]: SpecValue<(typeof WIDGET_OPTIONS)[T][K]>;\n} & {\n [K in Exclude<keyof (typeof WIDGET_OPTIONS)[T], RequiredOptionKeys<(typeof WIDGET_OPTIONS)[T]>>]?: SpecValue<\n (typeof WIDGET_OPTIONS)[T][K]\n >;\n};\n\n// WidgetOptions<'gauge'> is the typed options bag for a gauge: every declared key at its\n// declared type, required ones mandatory. Code that BUILDS a definition should type its\n// options with this \u2014 a typo is then a compile error rather than a key nothing reads.\n//\n// DISTRIBUTIVE over T, which is the whole reason it is written as a conditional rather\n// than inlining OptionsFor. `keyof` a union of the ten tables is their INTERSECTION \u2014 only\n// `title` \u2014 and RequiredOptionKeys over that union collapses to never, so the undistributed\n// form silently degenerates for any generic T: it would reject `{ text: 'body' }` as an\n// excess property while accepting `{}` for a widget that requires an option. That is\n// exactly the position a generator writing one widget of each type sits in. Distributing\n// makes WidgetOptions<WidgetType> the union of the ten per-type bags instead.\nexport type WidgetOptions<T extends WidgetType> = T extends WidgetType ? OptionsFor<T> : never;\n\n// ---- Runtime validation -----------------------------------------------------\n\nexport type OptionIssueCode =\n // a key the renderer for this widget type reads nothing from\n | 'unknown'\n // present, but the wrong JavaScript type \u2014 read back as undefined, rendered as default\n | 'type'\n // a string/number outside the option's declared legal values or bounds\n | 'enum'\n | 'range'\n // a `required` option absent, so the widget renders its \"not configured\" placeholder\n | 'missing'\n // a format:'json' string that does not parse\n | 'json'\n // a rule spanning more than one option (see CROSS_FIELD_RULES)\n | 'invariant';\n\nexport interface OptionIssue {\n widgetType: WidgetType;\n // The offending option key. For a cross-field rule, the key the fix belongs on.\n key: string;\n code: OptionIssueCode;\n message: string;\n}\n\ntype CrossFieldRule = (options: Record<string, unknown>) => Pick<OptionIssue, 'key' | 'code' | 'message'> | undefined;\n\n// Rules that read more than one option, so they cannot live on a single spec. Deliberately\n// Partial: most widgets have none, and an exhaustive map would be nine empty entries of\n// noise. The table above stays pure data so it can generate the docs page and a\n// cross-language type list; these functions are the one exception, kept separate for that\n// reason.\nconst CROSS_FIELD_RULES: Partial<Record<WidgetType, readonly CrossFieldRule[]>> = {\n gauge: [\n (options) => {\n const min = options.min;\n const max = options.max;\n if (typeof min !== 'number' || typeof max !== 'number') return undefined;\n if (!Number.isFinite(min) || !Number.isFinite(max)) return undefined;\n if (max > min) return undefined;\n return {\n key: 'max',\n code: 'invariant',\n message: `max (${max}) must be greater than min (${min})`,\n };\n },\n ],\n};\n\n// validateWidgetOptions reports every way a widget's stored options bag disagrees with\n// what the renderer reads. An empty result means every key present is one the renderer\n// honors, at a value it accepts, and no required option is missing.\n//\n// It is deliberately a REPORT rather than a throw: the renderer is tolerant by design and\n// must stay that way for a hand-edited definition, so the strictness belongs to whoever\n// produces definitions (fixtures, generators, the config panel) and to the tests that\n// gate them. Callers decide which codes are fatal \u2014 a stored dashboard carrying an\n// 'unknown' leftover is untidy; a generated one carrying 'missing' is broken.\nexport function validateWidgetOptions(\n widgetType: WidgetType,\n options: Record<string, unknown> | undefined,\n): OptionIssue[] {\n const specs: WidgetOptionSpecs = WIDGET_OPTIONS[widgetType];\n const issues: OptionIssue[] = [];\n const bag = options ?? {};\n\n for (const [key, spec] of Object.entries(specs)) {\n const value = bag[key];\n // '' counts as absent for a required option: the renderer reads all four into a\n // truthiness test, so an empty string renders the same placeholder as an omitted key.\n if (value === undefined || (spec.required && value === '')) {\n if (spec.required) {\n issues.push({ widgetType, key, code: 'missing', message: `${key} is required but not set` });\n }\n continue;\n }\n issues.push(...checkValue(widgetType, key, spec, value));\n }\n\n for (const key of Object.keys(bag)) {\n // An OWN-property test, not `in`: every specs table inherits Object.prototype, so `in`\n // reports 'toString', 'constructor' and friends as declared and waves them through. A\n // stored definition arrives via JSON.parse, which makes '__proto__' an ordinary own\n // key \u2014 so this is a shape a real definition can carry, not a synthetic one.\n // (hasOwnProperty.call rather than Object.hasOwn: the package targets ES2020.)\n if (Object.prototype.hasOwnProperty.call(specs, key)) continue;\n issues.push({\n widgetType,\n key,\n code: 'unknown',\n message: `${key} is not read by a ${widgetType} widget`,\n });\n }\n\n for (const rule of CROSS_FIELD_RULES[widgetType] ?? []) {\n const issue = rule(bag);\n if (issue) issues.push({ widgetType, ...issue });\n }\n\n return issues;\n}\n\nfunction checkValue(widgetType: WidgetType, key: string, spec: OptionSpec, value: unknown): OptionIssue[] {\n const issue = (code: OptionIssueCode, message: string): OptionIssue[] => [{ widgetType, key, code, message }];\n\n switch (spec.kind) {\n case 'string':\n if (typeof value !== 'string') {\n return issue('type', `${key} must be a string, got ${describe(value)}`);\n }\n if (spec.format === 'json' && !parsesAsJson(value)) {\n return issue('json', `${key} must be valid JSON`);\n }\n return [];\n\n case 'number':\n // Number.isFinite mirrors optNumber: NaN and \u00B1Infinity survive JSON round-trips\n // through some producers and are read back as undefined by the renderer.\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return issue('type', `${key} must be a finite number, got ${describe(value)}`);\n }\n if (spec.integer && !Number.isInteger(value)) {\n return issue('range', `${key} must be a whole number, got ${value}`);\n }\n if (spec.min !== undefined && value < spec.min) {\n return issue('range', `${key} must be at least ${spec.min}, got ${value}`);\n }\n if (spec.max !== undefined && value > spec.max) {\n return issue('range', `${key} must be at most ${spec.max}, got ${value}`);\n }\n return [];\n\n case 'boolean':\n // optBoolean treats anything but literal `true` as false, so a wrong type here is a\n // silently-disabled feature rather than a visible error.\n if (typeof value !== 'boolean') {\n return issue('type', `${key} must be a boolean, got ${describe(value)}`);\n }\n return [];\n\n case 'enum':\n if (typeof value !== 'string') {\n return issue('type', `${key} must be a string, got ${describe(value)}`);\n }\n if (!spec.values.includes(value)) {\n return issue('enum', `${key} must be one of ${spec.values.join(', ')}, got ${JSON.stringify(value)}`);\n }\n return [];\n }\n}\n\nfunction parsesAsJson(value: string): boolean {\n try {\n JSON.parse(value);\n return true;\n } catch {\n return false;\n }\n}\n\n// describe names the offending value's type in a message, quoting a string so an empty\n// one is visible and distinguishing null from an object.\nfunction describe(value: unknown): string {\n if (value === null) return 'null';\n if (typeof value === 'string') return `string ${JSON.stringify(value)}`;\n if (Array.isArray(value)) return 'an array';\n return `${typeof value} ${JSON.stringify(value)}`;\n}\n\n// numberOptionSpec returns the declared bounds for a numeric option, so an authoring\n// UI can constrain its input from the schema rather than from a second, hand-written\n// copy of the same numbers.\n//\n// This exists because the console's numeric inputs were unbounded: a `precision` of\n// -1 is a legal number, reaches Number.prototype.toFixed, and throws a RangeError\n// DURING RENDER. formatValue clamps so the renderer survives, but the definition is\n// still wrong and the author had no way to know. A panel that reads the schema cannot\n// offer the value at all.\nexport function numberOptionSpec(type: WidgetType, key: string): NumberOptionSpec | undefined {\n const specs: WidgetOptionSpecs = WIDGET_OPTIONS[type];\n if (!Object.prototype.hasOwnProperty.call(specs, key)) return undefined;\n const spec = specs[key];\n return spec.kind === 'number' ? spec : undefined;\n}\n\n// clampNumberOption folds a raw number into what a numeric option's schema allows, or\n// returns undefined when it is not a number at all.\n//\n// Separate from the input that calls it because the correction is the part worth\n// testing: an authoring UI's keystroke handling is awkward to exercise, and the\n// question that actually matters \u2014 what value gets STORED for a given input \u2014 is a\n// pure function of the schema and the number.\nexport function clampNumberOption(spec: NumberOptionSpec | undefined, value: number): number | undefined {\n if (!Number.isFinite(value)) return undefined;\n if (!spec) return value;\n let next = spec.integer ? Math.trunc(value) : value;\n if (spec.min !== undefined) next = Math.max(spec.min, next);\n if (spec.max !== undefined) next = Math.min(spec.max, next);\n return next;\n}\n\n// enumOptionValues returns the legal values of an enum option, so an authoring UI can\n// build its dropdown from the schema instead of a private copy of the same list.\n//\n// A dropdown offering a value the schema rejects lets a panel author a definition the\n// validator refuses; one MISSING a value silently removes a filter an author could\n// previously express. Both are invisible without a shared source.\nexport function enumOptionValues(type: WidgetType, key: string): readonly string[] | undefined {\n const specs: WidgetOptionSpecs = WIDGET_OPTIONS[type];\n if (!Object.prototype.hasOwnProperty.call(specs, key)) return undefined;\n const spec = specs[key];\n return spec.kind === 'enum' ? spec.values : undefined;\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The definition-level layer over validateWidgetOptions: \"is every widget on this\n// board configured with options the renderer actually reads?\"\n//\n// WHY IT IS A SEPARATE FUNCTION RATHER THAN A LOOP AT EACH CALL SITE. Every consumer\n// wants the same three things \u2014 walk the widgets, keep the issues, and know WHICH\n// widget each belongs to \u2014 and the third is the one a hand-rolled loop forgets. An\n// issue reported without its widget id tells an author that `commandName` is missing\n// on a board with three command buttons, which is a report they cannot act on.\n//\n// \uD83D\uDD11 THE OPTION SCHEMAS ARE TYPESCRIPT, AND THAT DECIDES WHERE THE GATE CAN LIVE.\n// dashboard-management stores a definition as opaque JSON and checks only that it is\n// a JSON object; it is Go, so it cannot run these schemas, and a Go copy of them\n// would be a THIRD representation of the same table (the reason ADR-076 declined a\n// cross-language widget manifest). So the strictness belongs to whoever PRODUCES a\n// definition, and \uD83D\uDD34 EACH PRODUCER HAS TO WIRE IT FOR ITSELF \u2014 there is no chokepoint\n// downstream that would catch a producer nobody thought about:\n//\n// \u2022 the console, by its publish action (apps/console/src/lib/api/dashboards.ts);\n// \u2022 the sim, by the golden-fixture gate over frontend/testdata/sim-dashboards, which\n// covers every dashboard of every REGISTERED scenario. It was widgetlab-only when\n// this function was written, and the sentence here claimed otherwise \u2014 buildingpulse,\n// the demo board, was gated by nothing. Two reviewers found the same overclaim\n// independently. The gate walks sim.Registry now, so the claim is true by\n// construction rather than by a list someone has to remember to extend.\n//\n// Anything ELSE writing a definition \u2014 raw GraphQL, a future importer \u2014 is unchecked,\n// because dashboard-management deliberately validates nothing. This is not the\n// server-side, ADR-044-style publish gate it superficially resembles, and calling it one\n// would misdescribe exactly which documents are protected.\n\nimport { type DashboardDefinition, type WidgetInstance } from '@devicechain/dashboards';\n\nimport { WIDGET_OPTIONS, validateWidgetOptions, type OptionIssue } from './options';\n\n// A DefinitionOptionIssue is an OptionIssue plus the widget that carries it.\n//\n// It deliberately carries the ID and nothing friendlier. An earlier version also read\n// `options.title` so a consumer could label the issue \u2014 and that one read, purely for\n// presentation, put this file into the option-read scan in options.test.ts, where every\n// other entry is evidence that a RENDERER reads a key. It needed a paragraph explaining\n// why it did not mean what its neighbours meant. A consumer that wants a title already\n// holds the definition and can join on the id; the scan goes back to meaning one thing.\nexport interface DefinitionOptionIssue extends OptionIssue {\n widgetId: string;\n}\n\n// A definition as this module reads it: only the widget list matters, so the fixture\n// tests and the console's in-flight editor state both satisfy it without either having\n// to be a fully parsed DashboardDefinition.\ntype WidgetCarrier = Pick<DashboardDefinition, 'widgets'>;\n\n// validateDefinitionOptions reports every option issue on the board, in widget order.\n// An empty result means every widget's bag is one the renderer honors in full.\nexport function validateDefinitionOptions(definition: WidgetCarrier): DefinitionOptionIssue[] {\n return definition.widgets.flatMap((widget) =>\n validateWidgetOptions(widget.type, widget.options).map((issue) => ({ ...issue, widgetId: widget.id })),\n );\n}\n\n// stripUnknownOptions removes every option key no widget of that type reads, leaving\n// everything else untouched.\n//\n// \uD83D\uDD34 IT EXISTS BECAUSE 'unknown' IS THE ONE ISSUE CODE AN AUTHOR CANNOT FIX IN THE UI.\n// This is the canonical statement of that reasoning; the call sites point here.\n// Every other code sits on a DECLARED key, so the config panel renders a control for it\n// and the author edits or clears it. An unknown key is by definition one the panel has\n// no control for \u2014 and the panel preserves the bag it did not write (it spreads\n// `widget.options` on every edit), so the key survives every subsequent change. Without\n// this, a publish gate that treats 'unknown' as fatal is a dead end: a dashboard that\n// acquired a leftover key \u2014 from a version predating an option rename, or from a\n// producer other than the console \u2014 could never be published again through the UI.\n//\n// Returns the SAME object when there is nothing to strip, so a caller can detect a\n// no-op with `next === before`.\n//\n// \uD83D\uDD34 THE OBVIOUS REASON TO WANT THIS IS NOT THE REAL ONE, and the first version of this\n// comment gave the wrong one: it claimed a fresh-but-equal object would read as an\n// unsaved edit. It would not \u2014 the console's isDirty compares SERIALIZATIONS\n// (dashboards/src/definition.ts), so an equal-but-new object is not dirty. What the\n// identity actually buys is smaller and still worth having: the caller can tell \"there\n// was nothing to remove\" from \"I removed something\" without diffing, and it avoids a\n// state write that would re-render the editor for no change.\nexport function stripUnknownOptions<T extends WidgetCarrier>(definition: T): T {\n let changed = false;\n const widgets = definition.widgets.map((widget) => {\n const stripped = stripUnknownWidgetOptions(widget);\n if (stripped !== widget) changed = true;\n return stripped;\n });\n return changed ? { ...definition, widgets } : definition;\n}\n\nfunction stripUnknownWidgetOptions(widget: WidgetInstance): WidgetInstance {\n const bag = widget.options;\n if (!bag) return widget;\n const specs = WIDGET_OPTIONS[widget.type];\n // hasOwnProperty.call, not `in` \u2014 see validateWidgetOptions: every specs table\n // inherits Object.prototype, so `in` would report '__proto__' and 'constructor' as\n // declared and leave exactly the keys a JSON.parse'd definition can smuggle in.\n const keys = Object.keys(bag);\n const kept = keys.filter((key) => Object.prototype.hasOwnProperty.call(specs, key));\n if (kept.length === keys.length) return widget;\n const options: Record<string, unknown> = {};\n for (const key of kept) options[key] = bag[key];\n return { ...widget, options };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["useEffect", "useState", "jsx", "jsxs", "useState", "useEffect", "useRef", "useState", "jsx", "useRef", "useState", "useEffect", "jsx", "jsxs", "useState", "useEffect", "useMemo", "useRef", "useState", "UNKNOWN_COLOR", "Fragment", "jsx", "jsxs", "useMemo", "useState", "useRef", "useEffect", "useEffect", "useState", "Fragment", "jsx", "jsxs", "hint", "useState", "useEffect", "useMemo", "clamp", "useEffect", "useRef", "jsx", "jsx", "useMemo", "jsx", "jsx", "jsx", "jsxs", "clamp", "useEffect", "useRef", "useState", "createContext", "useContext", "jsx", "createContext", "useContext", "jsx", "jsx", "jsxs", "useRef", "useState", "useEffect", "jsx", "jsxs", "cell", "headCell", "useMemo", "jsx", "useMemo", "jsx", "jsxs", "jsx", "useState", "useEffect"]
7
+ }