@camstack/ui-library 1.2.291 → 1.2.292
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{MaskShapeCanvas-DI4BY7W2.js → MaskShapeCanvas-C9cJlxvG.js} +3 -1
- package/dist/{MaskShapeCanvas-BByN3jvt.cjs → MaskShapeCanvas-NCNwu9_5.cjs} +3 -1
- package/dist/{MotionZonesSettings-CiYjd-yl.js → MotionZonesSettings-Bv8XzCa3.js} +45 -48
- package/dist/{MotionZonesSettings-BK9R9NIr.cjs → MotionZonesSettings-CPRH536-.cjs} +44 -47
- package/dist/{PrivacyMaskSettings-DuXUCUev.cjs → PrivacyMaskSettings-BUWvVF_n.cjs} +33 -42
- package/dist/{PrivacyMaskSettings-BmRdUrPp.js → PrivacyMaskSettings-BlkbESYj.js} +34 -43
- package/dist/{SceneMonitorEditor-COgAjtq5.cjs → SceneMonitorEditor-D_jkzQ2o.cjs} +189 -86
- package/dist/{SceneMonitorEditor-J4Ix_0tu.js → SceneMonitorEditor-i3-nXAEU.js} +190 -87
- package/dist/composites/addon-global-settings-form.d.ts +0 -12
- package/dist/composites/cap-settings/AutotrackSection.d.ts +0 -16
- package/dist/composites/cap-settings/CameraGridLayoutEditor.d.ts +0 -21
- package/dist/composites/cap-settings/ConsumablesPanel.d.ts +0 -18
- package/dist/composites/cap-settings/MaskShapeCanvas.d.ts +0 -102
- package/dist/composites/cap-settings/MotionZonesSettings.d.ts +0 -38
- package/dist/composites/cap-settings/SceneReferenceGallery.d.ts +0 -38
- package/dist/composites/cap-settings/scene-monitor-copy.d.ts +12 -6
- package/dist/composites/clips/clip-source-state.d.ts +3 -2
- package/dist/composites/clips/clip-thumbnail-fetch.d.ts +2 -1
- package/dist/composites/clips/clip-tile-media.d.ts +3 -2
- package/dist/composites/clips/clip-view-state.d.ts +2 -1
- package/dist/composites/confirm-dialog.d.ts +0 -62
- package/dist/composites/data-table.d.ts +2 -11
- package/dist/composites/detection-canvas.d.ts +0 -9
- package/dist/composites/detection-result-tree.d.ts +0 -12
- package/dist/composites/device-controls/container-primary-hero.d.ts +0 -17
- package/dist/composites/device-controls/humidifier-control.d.ts +0 -17
- package/dist/composites/device-controls/lawn-mower-control.d.ts +2 -1
- package/dist/composites/device-controls/pet-feeder-panel.d.ts +0 -18
- package/dist/composites/device-controls/script-automation-controls.d.ts +2 -1
- package/dist/composites/device-controls/sensor-hero-card.d.ts +0 -18
- package/dist/composites/device-controls/update-control.d.ts +0 -13
- package/dist/composites/device-controls/vacuum-control.d.ts +2 -1
- package/dist/composites/device-controls/valve-control.d.ts +2 -1
- package/dist/composites/device-controls/weather-control.d.ts +2 -1
- package/dist/composites/device-item/actions.d.ts +0 -22
- package/dist/composites/device-item/helpers.d.ts +2 -1
- package/dist/composites/device-item/index.d.ts +0 -9
- package/dist/composites/device-list/batch-actions-bar.d.ts +0 -11
- package/dist/composites/device-list/filter-chips.d.ts +0 -13
- package/dist/composites/device-list/page-size-select.d.ts +2 -1
- package/dist/composites/device-list/url-state.d.ts +2 -3
- package/dist/composites/device-meta.d.ts +14 -7
- package/dist/composites/device-picker/DevicePicker.d.ts +1 -1
- package/dist/composites/device-picker/device-filter-bar.d.ts +2 -1
- package/dist/composites/device-picker/device-parent.d.ts +2 -1
- package/dist/composites/device-picker/device-selection-rows.d.ts +6 -1
- package/dist/composites/device-selector-picker.d.ts +25 -1
- package/dist/composites/device-step-matrix.d.ts +0 -26
- package/dist/composites/error-box.d.ts +0 -15
- package/dist/composites/event-stream.d.ts +2 -1
- package/dist/composites/extensions/extensions-view-state.d.ts +2 -1
- package/dist/composites/hls-quality.d.ts +3 -2
- package/dist/composites/index.d.ts +0 -2
- package/dist/composites/notification-targets-field.d.ts +2 -2
- package/dist/composites/pipeline-matrix-shared.d.ts +2 -1
- package/dist/composites/provider-entries/AddProviderEntryDialog.d.ts +1 -1
- package/dist/composites/provider-entries/ProviderEntryPills.d.ts +1 -1
- package/dist/composites/ptz-overlay.d.ts +0 -19
- package/dist/composites/qr-code.d.ts +0 -14
- package/dist/composites/scope-picker.d.ts +2 -1
- package/dist/composites/step-inspector/layered-settings-form.d.ts +0 -11
- package/dist/composites/step-inspector/step-inspector-panel.d.ts +0 -6
- package/dist/composites/stream-broker-selector.d.ts +0 -21
- package/dist/composites/training/TrainingBaseModelPicker.d.ts +0 -10
- package/dist/composites/training/retrain-review-badge.d.ts +2 -1
- package/dist/composites/webrtc-diagnostics/endpoint-test.d.ts +2 -1
- package/dist/composites/webrtc-diagnostics/nat-mapping.d.ts +2 -1
- package/dist/composites/webrtc-diagnostics/session-run.d.ts +2 -1
- package/dist/composites/webrtc-diagnostics/session-verdict.d.ts +2 -1
- package/dist/contexts/ui-text.d.ts +49 -0
- package/dist/generated/system-hooks.d.ts +4 -0
- package/dist/i18n/addon-i18n.d.ts +46 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/use-addon-t.d.ts +57 -0
- package/dist/index.cjs +3287 -2356
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3275 -2356
- package/dist/lib/format-last-seen.d.ts +15 -1
- package/dist/lib/phase-config.d.ts +9 -3
- package/dist/lib/pipeline-mirror.d.ts +2 -1
- package/package.json +1 -1
- package/dist/composites/data-table/data-table-header.d.ts +0 -11
- package/dist/composites/data-table/data-table-pagination.d.ts +0 -10
- package/dist/composites/data-table/data-table-row.d.ts +0 -11
- package/dist/composites/data-table/data-table.d.ts +0 -3
- package/dist/composites/data-table/index.d.ts +0 -2
- package/dist/composites/data-table/types.d.ts +0 -34
- package/dist/composites/data-table-layout.d.ts +0 -31
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `AddonGlobalSettingsForm` — mounts an addon's global-settings schema
|
|
3
|
-
* via `api.addonSettings.getGlobalSettings` + `updateGlobalSettings`
|
|
4
|
-
* and renders it with `ConfigFormBuilder`. Single source of truth for
|
|
5
|
-
* the addon's settings — no hardcoded option lists anywhere in the UI.
|
|
6
|
-
*
|
|
7
|
-
* Used wherever an addon's settings need to be edited inline (e.g.
|
|
8
|
-
* benchmark tabs, per-node pipeline editor). The caller passes the
|
|
9
|
-
* tRPC client + (addonId, nodeId); the component handles the fetch
|
|
10
|
-
* + patch loop. `onAfterChange` fires once the patch is applied so
|
|
11
|
-
* consumers can re-read any derived state.
|
|
12
|
-
*/
|
|
13
1
|
import { type JSX } from "react";
|
|
14
2
|
import type { ConfigUISchemaWithValues } from "@camstack/types";
|
|
15
3
|
interface AddonGlobalSettingsFormTrpc {
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Autotrack settings card — rendered inside the PTZ panel when the
|
|
3
|
-
* device has `DeviceFeature.PtzAutotrack`. Three knobs (cross-vendor
|
|
4
|
-
* schema): target type, stop delay, disappear delay, plus an enable /
|
|
5
|
-
* disable toggle that drives the `ptz-autotrack` cap directly.
|
|
6
|
-
*
|
|
7
|
-
* Save semantics:
|
|
8
|
-
* - Toggle (enable/disable) calls `setEnabled` immediately.
|
|
9
|
-
* - Form fields debounce-save on blur — no explicit Save button.
|
|
10
|
-
*
|
|
11
|
-
* Vendor capability hints:
|
|
12
|
-
* - The cap's status carries `currentSettings` from the camera's
|
|
13
|
-
* own GET; rendered on every poll so the operator sees what's
|
|
14
|
-
* really applied vs what they typed.
|
|
15
|
-
* - When the firmware ignored a setting, the field stays editable.
|
|
16
|
-
*/
|
|
17
1
|
import { type JSX } from "react";
|
|
18
2
|
interface AutotrackSectionProps {
|
|
19
3
|
readonly deviceId: number;
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The camera-grid layout editor.
|
|
3
|
-
*
|
|
4
|
-
* A grid's geometry is the one thing a DERIVED settings form cannot express: a
|
|
5
|
-
* generated form renders "cell 1 source = 592, cell 1 crop x = 0.25, cell 1 crop
|
|
6
|
-
* width = 0.5…" and nobody can hold a picture in their head from that. So the
|
|
7
|
-
* geometry gets a widget — the canvas, drawn at the grid's own aspect ratio,
|
|
8
|
-
* with the cells on it — while the knobs that ARE ordinary fields (the name,
|
|
9
|
-
* the canvas size, the frame rate) sit beside it in the same panel.
|
|
10
|
-
*
|
|
11
|
-
* Every rectangle in here is NORMALIZED [0,1], on both sides: the crop taken
|
|
12
|
-
* out of a source, and the cell it lands in. That is how the addon stores them
|
|
13
|
-
* (a source camera can change resolution, and a stored pixel rectangle would
|
|
14
|
-
* quietly start cutting the wrong region), so the editor speaks the same
|
|
15
|
-
* language rather than converting at the boundary.
|
|
16
|
-
*
|
|
17
|
-
* The preview is the source cameras' own snapshots, positioned by CSS from the
|
|
18
|
-
* SAME normalized numbers that will become the ffmpeg `crop`/`overlay`. It costs
|
|
19
|
-
* the server nothing and it is wrong in exactly the ways the real composition
|
|
20
|
-
* would be wrong.
|
|
21
|
-
*/
|
|
22
1
|
import { type JSX } from "react";
|
|
23
2
|
export interface CameraGridLayoutEditorProps {
|
|
24
3
|
readonly deviceId: number;
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConsumablesPanel — maintenance-items surface for the generic
|
|
3
|
-
* `consumables` capability. A cap-settings component (ui-library),
|
|
4
|
-
* mounted as a cap-contributed top-level device-detail tab via the
|
|
5
|
-
* cap-UI contribution mechanism (the `consumables` cap declares
|
|
6
|
-
* `ui: { kind:'widget', widgetId:'host/consumables-panel', tab:'consumables', topTab:true, ... }`).
|
|
7
|
-
*
|
|
8
|
-
* Reads the cap's pushed runtime-state slice via `useDeviceCapability`
|
|
9
|
-
* and renders one row per item: a fill bar + percentage when `level` is
|
|
10
|
-
* known, else an `ok`/`replace` status pill when `status` is known, else
|
|
11
|
-
* a muted em-dash. When a provider also reports `remaining` (a count in
|
|
12
|
-
* days/hours/cycles), its value is shown alongside — `null` renders a
|
|
13
|
-
* muted em-dash, never a fake `0` (D393); absent `remaining` renders
|
|
14
|
-
* nothing, exactly as before this field existed. A per-item "Replaced"
|
|
15
|
-
* button calls `cap.reset` when the item is `resettable`. The cap is
|
|
16
|
-
* inert until a provider sets items — an empty list renders a muted
|
|
17
|
-
* placeholder.
|
|
18
|
-
*/
|
|
19
1
|
import { type JSX } from "react";
|
|
20
2
|
import type { CapSettingsComponentProps } from "./index";
|
|
21
3
|
export declare function ConsumablesPanel({ deviceId }: CapSettingsComponentProps): JSX.Element;
|
|
@@ -1,105 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MaskShapeCanvas — ONE shared, Konva-based on-frame "drawing plane" editor
|
|
3
|
-
* that handles ALL four `MaskShape` kinds: `rect`, `polygon`, `grid`, `line`.
|
|
4
|
-
*
|
|
5
|
-
* It is a generalization of the detection `ZoneCanvas` (orchestrator) so that
|
|
6
|
-
* privacy-mask (rect/polygon), motion-zones (grid), and the
|
|
7
|
-
* pipeline-orchestrator zones/lines editor can eventually all share a single
|
|
8
|
-
* editor instead of three bespoke ones. Every coordinate is normalized 0..1 of
|
|
9
|
-
* the camera frame (top-left origin), like every existing on-frame editor.
|
|
10
|
-
*
|
|
11
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
-
* Behaviours ported (the references are NOT edited — this generalizes them):
|
|
13
|
-
*
|
|
14
|
-
* - From `ZoneCanvas` (orchestrator zone-editor):
|
|
15
|
-
* • ResizeObserver-sized `<Stage>`, `transparent` absolute-fill overlay
|
|
16
|
-
* mode for layering over a stream player, optional `backdrop` slot,
|
|
17
|
-
* pointer-events pass-through while not editing.
|
|
18
|
-
* • polygon draw / vertex-drag / body-drag with optimistic local override
|
|
19
|
-
* + the `useEffect` that drops the override once the controlled prop
|
|
20
|
-
* echoes the commit back, the rAF-throttled cursor ghost-line preview,
|
|
21
|
-
* the `Arrow` tripwire, and the memoised export.
|
|
22
|
-
* - From `PrivacyMaskCanvas`:
|
|
23
|
-
* • rect move + bottom-right-handle resize with 0..1 clamp, polygon
|
|
24
|
-
* vertex add (edge midpoint) / remove (✕, gated by min/max), and the
|
|
25
|
-
* `polygonVertices` {min,max} gate (e.g. Hikvision {min:4,max:4}).
|
|
26
|
-
* - From `MotionGridCanvas`:
|
|
27
|
-
* • grid cell-paint gesture (press-drag paints every touched cell to the
|
|
28
|
-
* value the first-touched cell flipped to), row-major `cells`.
|
|
29
|
-
*
|
|
30
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
31
|
-
* Usage — each consumer maps its own model onto `MaskShapeItem[]` and gates the
|
|
32
|
-
* editor with `supportedShapes`:
|
|
33
|
-
*
|
|
34
|
-
* @example privacy-mask (rect + polygon)
|
|
35
|
-
* ```tsx
|
|
36
|
-
* const items: MaskShapeItem[] = regions.map((r) => ({ id: r.id, shape: r.shape }))
|
|
37
|
-
* <MaskShapeCanvas
|
|
38
|
-
* transparent
|
|
39
|
-
* items={items}
|
|
40
|
-
* supportedShapes={['rect', 'polygon']}
|
|
41
|
-
* polygonVertices={options.polygonVertices} // Hikvision {min:4,max:4}
|
|
42
|
-
* selectedId={selectedId}
|
|
43
|
-
* onSelect={setSelectedId}
|
|
44
|
-
* onShapeChange={(id, shape) =>
|
|
45
|
-
* onRegionsChange(regions.map((r) => (r.id === id ? { ...r, shape } : r)))}
|
|
46
|
-
* onDrawComplete={(shape) => onRegionsChange([...regions, { id: nextId(), shape }])}
|
|
47
|
-
* drawingKind={drawing ? 'rect' : null}
|
|
48
|
-
* />
|
|
49
|
-
* ```
|
|
50
|
-
*
|
|
51
|
-
* @example motion-zones (grid)
|
|
52
|
-
* ```tsx
|
|
53
|
-
* const items: MaskShapeItem[] = [{
|
|
54
|
-
* id: 'motion',
|
|
55
|
-
* shape: { kind: 'grid', gridWidth: w, gridHeight: h, cells },
|
|
56
|
-
* }]
|
|
57
|
-
* <MaskShapeCanvas
|
|
58
|
-
* transparent
|
|
59
|
-
* items={items}
|
|
60
|
-
* supportedShapes={['grid']}
|
|
61
|
-
* grid={{ width: w, height: h }}
|
|
62
|
-
* selectedId="motion"
|
|
63
|
-
* onSelect={() => {}}
|
|
64
|
-
* onShapeChange={(_id, shape) => {
|
|
65
|
-
* if (shape.kind === 'grid') onCellsChange(shape.cells)
|
|
66
|
-
* }}
|
|
67
|
-
* onDrawComplete={() => {}}
|
|
68
|
-
* drawingKind={null}
|
|
69
|
-
* />
|
|
70
|
-
* ```
|
|
71
|
-
*
|
|
72
|
-
* @example orchestrator (polygon + line/tripwire)
|
|
73
|
-
* ```tsx
|
|
74
|
-
* const items: MaskShapeItem[] = zones.map((z) => ({
|
|
75
|
-
* id: z.id,
|
|
76
|
-
* label: z.name,
|
|
77
|
-
* color: z.color,
|
|
78
|
-
* shape: z.kind === 'tripwire'
|
|
79
|
-
* ? { kind: 'line', points: z.points }
|
|
80
|
-
* : { kind: 'polygon', points: z.points },
|
|
81
|
-
* }))
|
|
82
|
-
* <MaskShapeCanvas
|
|
83
|
-
* transparent
|
|
84
|
-
* items={items}
|
|
85
|
-
* supportedShapes={['polygon', 'line']}
|
|
86
|
-
* selectedId={selectedZoneId}
|
|
87
|
-
* onSelect={onSelectZone}
|
|
88
|
-
* onShapeChange={(id, shape) => {
|
|
89
|
-
* if (shape.kind === 'polygon' || shape.kind === 'line')
|
|
90
|
-
* onZonePointsChange(id, shape.points)
|
|
91
|
-
* }}
|
|
92
|
-
* onDrawComplete={(shape) => onZoneComplete(shape)}
|
|
93
|
-
* drawingKind={drawingKind === 'tripwire' ? 'line' : drawingKind}
|
|
94
|
-
* backdrop={<CameraStreamPlayer ... />}
|
|
95
|
-
* />
|
|
96
|
-
* ```
|
|
97
|
-
*
|
|
98
|
-
* Purely controlled — the parent owns the truth. Every edit fires
|
|
99
|
-
* `onShapeChange(id, shape)` (or `onDrawComplete(shape)` for a freshly-drawn
|
|
100
|
-
* shape) with a brand-new immutable shape object; points and cells are never
|
|
101
|
-
* mutated in place.
|
|
102
|
-
*/
|
|
103
1
|
import { type JSX, type MemoExoticComponent, type ReactNode } from "react";
|
|
104
2
|
import type { MaskShape, MaskShapeKind } from "@camstack/types";
|
|
105
3
|
/** One editable item on the drawing plane. */
|
|
@@ -1,41 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MotionZonesSettings — management surface for the on-camera
|
|
3
|
-
* `motion-zones` capability. A cap-settings component (ui-library),
|
|
4
|
-
* mounted into the device-detail Config tab via the cap-UI
|
|
5
|
-
* contribution mechanism (the `motion-zones` cap declares a `ui` block).
|
|
6
|
-
*
|
|
7
|
-
* The on-camera motion mask is a SINGLE `grid` region in the shared
|
|
8
|
-
* MaskShape vocabulary (a row-major boolean cell lattice). This component
|
|
9
|
-
* owns its own control panel (Edit toggle, All on / All off / Invert,
|
|
10
|
-
* count, Revert, Save) and paints the editable lattice over the live frame
|
|
11
|
-
* with the shared `MaskShapeCanvas` drawing-plane — registered as a
|
|
12
|
-
* player-overlay layer ONLY while "Edit grid" is toggled on (OFF by
|
|
13
|
-
* default — nothing draws on mount).
|
|
14
|
-
*
|
|
15
|
-
* Detection enable + sensitivity are NOT edited here — they are the
|
|
16
|
-
* camera's on-board motion master switch, owned by the driver's
|
|
17
|
-
* "On-camera motion" settings section. Saving sends a `cells`-only patch
|
|
18
|
-
* (one grid region); enable / sensitivity are left untouched camera-side.
|
|
19
|
-
*
|
|
20
|
-
* ── Resizable input grid (resampled to the camera grid on save) ──────────────
|
|
21
|
-
* The camera's native motion grid (`getOptions().grid`, e.g. 22×18) is the
|
|
22
|
-
* FINEST resolution that round-trips — painting finer than it can't be stored
|
|
23
|
-
* (not reconstructible). So the camera grid is the FLOOR (×1) and the operator
|
|
24
|
-
* picks BIGGER cells: an integer cell-size multiplier (×1/×2/×3) where a UI cell
|
|
25
|
-
* spans an m×m block of camera cells (×2 → quarter as many, ×3 → ninth). The
|
|
26
|
-
* CAMERA grid stays authoritative:
|
|
27
|
-
* - LOAD: the camera grid is COARSENED into the chosen UI resolution (a UI
|
|
28
|
-
* cell is `true` if ANY camera cell in its block is painted) so existing
|
|
29
|
-
* masks display on the bigger-celled surface.
|
|
30
|
-
* - SAVE: the UI grid is EXPANDED back to camera gridWidth×gridHeight — every
|
|
31
|
-
* camera cell takes the value of the UI cell whose block covers it.
|
|
32
|
-
* At ×1 both transforms are the identity, so the camera-grid round-trip is
|
|
33
|
-
* exact; at higher multipliers `coarsen(expand(uiCells)) === uiCells`, so an
|
|
34
|
-
* edit made at the chosen resolution survives the round-trip exactly. The
|
|
35
|
-
* component's draft (`uiCells`) lives at the UI resolution; the last-saved truth
|
|
36
|
-
* (`committedCamera`) and every comparison stay anchored to the camera-native
|
|
37
|
-
* grid so `dirty`/Save/Revert math is resolution-independent.
|
|
38
|
-
*/
|
|
39
1
|
import { type JSX } from "react";
|
|
40
2
|
import type { CapSettingsComponentProps } from "./index";
|
|
41
3
|
export declare function MotionZonesSettings({ deviceId }: CapSettingsComponentProps): JSX.Element | null;
|
|
@@ -1,41 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SceneReferenceGallery — the photographs a scene is actually judging against,
|
|
3
|
-
* grouped by lighting variant, each individually deletable.
|
|
4
|
-
*
|
|
5
|
-
* ## Why it has to exist
|
|
6
|
-
*
|
|
7
|
-
* A scene's whole answer is "does this region still look like the photograph".
|
|
8
|
-
* Until now the operator could see a COUNT of references and never the
|
|
9
|
-
* references themselves, which makes the two failure modes indistinguishable
|
|
10
|
-
* from the outside: a blurry capture taken while a van was in the way poisons a
|
|
11
|
-
* condition forever, and from a cosine score there is no way to tell which one
|
|
12
|
-
* it was. `deleteReference` shipped with the cap for exactly this and had no
|
|
13
|
-
* caller.
|
|
14
|
-
*
|
|
15
|
-
* ## The image
|
|
16
|
-
*
|
|
17
|
-
* A bare `<img>` against the `event-media` data plane
|
|
18
|
-
* (`/addon/pipeline-analytics/event-media/m/<mediaId>` — D482), which is
|
|
19
|
-
* `access: 'authenticated'` — that accepts the `camstack_session` cookie, and a
|
|
20
|
-
* cookie is the only credential an `<img>` can send. Same mechanism, and the
|
|
21
|
-
* same reasoning, as `use-device-snapshot`; no bearer-token blob fetch is
|
|
22
|
-
* needed and no second copy of the auth plumbing is introduced here.
|
|
23
|
-
*
|
|
24
|
-
* `SceneReference.thumbnailMediaId` finally IS a media row id (D482). It was
|
|
25
|
-
* named `…MediaId` while holding a composite MediaStore key
|
|
26
|
-
* (`scene:<ownerId>:thumbnailSmall:<ts>`), which this component read
|
|
27
|
-
* POSITIONALLY for its owner id — a name that described the right design
|
|
28
|
-
* beside a value that did not, which is the shape of leftover this repo has
|
|
29
|
-
* been bitten by three times. The tile now addresses the row.
|
|
30
|
-
*
|
|
31
|
-
* **A missing blob is normal, not exceptional.** Reference thumbnails written
|
|
32
|
-
* before the `ownerKind: 'scene'` fix carried an `ownerId` no event row backs,
|
|
33
|
-
* which made them eligible for the orphan sweep — so on an existing install
|
|
34
|
-
* some of these ids resolve to nothing. That must degrade to a dated
|
|
35
|
-
* placeholder: the reference is still real, still being scored against, and
|
|
36
|
-
* still deletable. A gallery that blanks or throws over a missing picture would
|
|
37
|
-
* take the delete button away exactly when it is needed.
|
|
38
|
-
*/
|
|
39
1
|
import type { SceneMonitor, SceneReference } from "@camstack/types";
|
|
40
2
|
import { type JSX } from "react";
|
|
41
3
|
/** The hub-proxied prefix for the pipeline-analytics event-media data plane. */
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* lighting the scene has never been captured in.
|
|
9
9
|
*/
|
|
10
10
|
import type { SceneMonitor } from "@camstack/types";
|
|
11
|
+
import { type UiTextFn } from "../../contexts/ui-text";
|
|
11
12
|
/** The lighting variants an operator is OFFERED. The resolver can also report
|
|
12
13
|
* `night`, `dawn` and `dusk`; those are shown when present but never demanded,
|
|
13
14
|
* because most installs only ever see day and IR. */
|
|
@@ -24,8 +25,12 @@ export type SceneCaptureVariant = (typeof SCENE_CAPTURE_VARIANTS)[number];
|
|
|
24
25
|
* `onUncoveredCondition: 'skip'` the night checks are simply not made.
|
|
25
26
|
*/
|
|
26
27
|
export declare const SCENE_OPTIONAL_VARIANT: SceneCaptureVariant;
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
interface VariantLabelEntry {
|
|
29
|
+
readonly labelKey: string;
|
|
30
|
+
readonly labelFallback: string;
|
|
31
|
+
}
|
|
32
|
+
export declare const VARIANT_LABEL: Readonly<Record<string, VariantLabelEntry>>;
|
|
33
|
+
export declare function variantLabel(condition: string, t?: UiTextFn): string;
|
|
29
34
|
/**
|
|
30
35
|
* "Daylight ✓ · Night (IR) ✗ — optional; checks in night (ir) are skipped."
|
|
31
36
|
*
|
|
@@ -39,7 +44,7 @@ export declare function variantLabel(condition: string): string;
|
|
|
39
44
|
* is — the part you do not have to do — and the sentence says what actually
|
|
40
45
|
* happens instead, which differs by the scene's own policy.
|
|
41
46
|
*/
|
|
42
|
-
export declare function coverageLine(monitor: SceneMonitor): string;
|
|
47
|
+
export declare function coverageLine(monitor: SceneMonitor, t?: UiTextFn): string;
|
|
43
48
|
/**
|
|
44
49
|
* The calm sentence for a scene that is sitting the current light out — `null`
|
|
45
50
|
* whenever it is checking normally.
|
|
@@ -49,12 +54,12 @@ export declare function coverageLine(monitor: SceneMonitor): string;
|
|
|
49
54
|
* needs nothing from the operator; the only thing worth saying is which light
|
|
50
55
|
* it is waiting out and that it will pick itself back up.
|
|
51
56
|
*/
|
|
52
|
-
export declare function suspensionLine(monitor: SceneMonitor): string | null;
|
|
57
|
+
export declare function suspensionLine(monitor: SceneMonitor, t?: UiTextFn): string | null;
|
|
53
58
|
/**
|
|
54
59
|
* The one line under a scene's name. It answers, in order: can it judge, what
|
|
55
60
|
* does it say, and how long has it said it.
|
|
56
61
|
*/
|
|
57
|
-
export declare function statusLine(monitor: SceneMonitor, now: number): string;
|
|
62
|
+
export declare function statusLine(monitor: SceneMonitor, now: number, t?: UiTextFn): string;
|
|
58
63
|
/** The badge word. `latched` wins over `verdict`, because the latch is the
|
|
59
64
|
* thing the operator asked to be told and it outlives the live state.
|
|
60
65
|
*
|
|
@@ -62,7 +67,7 @@ export declare function statusLine(monitor: SceneMonitor, now: number): string;
|
|
|
62
67
|
* never `alarm`, and never the `danger` variant the page maps that to. A
|
|
63
68
|
* missing IR reference must not be able to paint a red badge on a scene that
|
|
64
69
|
* is doing exactly what it was configured to do. */
|
|
65
|
-
export declare function badgeFor(monitor: SceneMonitor): {
|
|
70
|
+
export declare function badgeFor(monitor: SceneMonitor, t?: UiTextFn): {
|
|
66
71
|
text: string;
|
|
67
72
|
tone: "alarm" | "ok" | "unknown";
|
|
68
73
|
};
|
|
@@ -76,3 +81,4 @@ export declare function roiTooLarge(roi: {
|
|
|
76
81
|
height: number;
|
|
77
82
|
}): boolean;
|
|
78
83
|
export declare function humanDuration(ms: number): string;
|
|
84
|
+
export {};
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* an operator whether to look at the card, the network or the firmware.
|
|
29
29
|
*/
|
|
30
30
|
import type { ClipSource } from "@camstack/types";
|
|
31
|
+
import { type UiTextFn } from "../../contexts/ui-text.js";
|
|
31
32
|
/** How loudly a source's state should read. Never the whole message. */
|
|
32
33
|
export type ClipSourceTone = "ok" | "warn" | "error";
|
|
33
34
|
/** A source's state, rendered. */
|
|
@@ -52,7 +53,7 @@ export interface ClipSourceNotice {
|
|
|
52
53
|
readonly answered: boolean;
|
|
53
54
|
}
|
|
54
55
|
/** Turn one source row into the line the picker and the list show. */
|
|
55
|
-
export declare function describeSource(source: ClipSource): ClipSourceNotice;
|
|
56
|
+
export declare function describeSource(source: ClipSource, t?: UiTextFn): ClipSourceNotice;
|
|
56
57
|
/**
|
|
57
58
|
* How old a source's catalog is, or null when it did not say.
|
|
58
59
|
*
|
|
@@ -60,4 +61,4 @@ export declare function describeSource(source: ClipSource): ClipSourceNotice;
|
|
|
60
61
|
* no age to show; inventing "just now" for a source that never told us would
|
|
61
62
|
* be the same lie as drawing a cached list as current (D549 decision 13).
|
|
62
63
|
*/
|
|
63
|
-
export declare function formatCatalogAge(catalogAsOf: number | undefined, now: number): string | null;
|
|
64
|
+
export declare function formatCatalogAge(catalogAsOf: number | undefined, now: number, t?: UiTextFn): string | null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type UiTextFn } from "../../contexts/ui-text.js";
|
|
1
2
|
import { type ClipTileMedia } from "./clip-tile-media.js";
|
|
2
3
|
/** The `fetch` this module uses — injected so the contract is testable. */
|
|
3
4
|
export type ThumbnailFetcher = (url: string, init: RequestInit) => Promise<Response>;
|
|
@@ -13,4 +14,4 @@ export type MintedThumbnail = {
|
|
|
13
14
|
* that throws is a tile that renders nothing and says nothing — the exact
|
|
14
15
|
* failure this module exists to prevent.
|
|
15
16
|
*/
|
|
16
|
-
export declare function fetchMintedThumbnail(url: string, fetcher: ThumbnailFetcher): Promise<MintedThumbnail>;
|
|
17
|
+
export declare function fetchMintedThumbnail(url: string, fetcher: ThumbnailFetcher, t?: UiTextFn): Promise<MintedThumbnail>;
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* tested against that build.
|
|
35
35
|
*/
|
|
36
36
|
import type { Clip } from "@camstack/types";
|
|
37
|
+
import { type UiTextFn } from "../../contexts/ui-text.js";
|
|
37
38
|
/** The five things a tile can be. */
|
|
38
39
|
export type ClipTileMedia = {
|
|
39
40
|
readonly kind: "vouched";
|
|
@@ -64,7 +65,7 @@ export interface MintRefusalHeaders {
|
|
|
64
65
|
readonly retryAfterMs?: number | null;
|
|
65
66
|
}
|
|
66
67
|
/** What to draw for one clip's tile. */
|
|
67
|
-
export declare function resolveTileMedia(clip: Clip): ClipTileMedia;
|
|
68
|
+
export declare function resolveTileMedia(clip: Clip, t?: UiTextFn): ClipTileMedia;
|
|
68
69
|
/**
|
|
69
70
|
* A 204, as something the tile can draw.
|
|
70
71
|
*
|
|
@@ -74,4 +75,4 @@ export declare function resolveTileMedia(clip: Clip): ClipTileMedia;
|
|
|
74
75
|
* vocabulary that moves is a tile that stops early, never one that asks for
|
|
75
76
|
* ever.
|
|
76
77
|
*/
|
|
77
|
-
export declare function describeRefusal(headers: MintRefusalHeaders): ClipTileMedia;
|
|
78
|
+
export declare function describeRefusal(headers: MintRefusalHeaders, t?: UiTextFn): ClipTileMedia;
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
import type { Clip, ClipSource } from "@camstack/types";
|
|
32
32
|
import type { ClipBand, ClipBandPlan } from "./clip-band.js";
|
|
33
|
+
import { type UiTextFn } from "../../contexts/ui-text.js";
|
|
33
34
|
import { type ClipSourceNotice } from "./clip-source-state.js";
|
|
34
35
|
/** React Query's settled-ness, narrowed to what this decision needs. */
|
|
35
36
|
export type ClipQueryStatus = "pending" | "error" | "success";
|
|
@@ -83,4 +84,4 @@ export type ClipsViewState = {
|
|
|
83
84
|
readonly answered: readonly ClipSourceNotice[];
|
|
84
85
|
readonly silent: readonly ClipSourceNotice[];
|
|
85
86
|
};
|
|
86
|
-
export declare function resolveClipsView(input: ClipsViewInput): ClipsViewState;
|
|
87
|
+
export declare function resolveClipsView(input: ClipsViewInput, t?: UiTextFn): ClipsViewState;
|
|
@@ -1,65 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The ONE modal dialog of the product — confirm, alert and prompt.
|
|
3
|
-
*
|
|
4
|
-
* ## Why all three live here
|
|
5
|
-
*
|
|
6
|
-
* The admin UI used to reach for `window.confirm` / `window.alert` /
|
|
7
|
-
* `window.prompt` wherever a `useConfirm()` call was inconvenient. A native
|
|
8
|
-
* dialog is wrong on three counts, and the operator hit the worst one on
|
|
9
|
-
* Identity → Users: resetting a password opened `window.prompt`, which
|
|
10
|
-
* **cannot mask its input**. A new password was typed in clear text, into a
|
|
11
|
-
* box the browser may offer to remember. On top of that a native dialog
|
|
12
|
-
* matches none of the product's visual language and the host can neither style
|
|
13
|
-
* nor test it.
|
|
14
|
-
*
|
|
15
|
-
* So the fix is not "a password modal" — a second dialog component is the
|
|
16
|
-
* defect, not the cure. The provider that already served every confirm in the
|
|
17
|
-
* admin UI (and in the viewer, which documents its own copy as "deliberately
|
|
18
|
-
* the same one `@camstack/ui-library` exposes to the admin UI") grew two more
|
|
19
|
-
* intents. One provider, one backdrop, one visual language, one set of
|
|
20
|
-
* keyboard rules.
|
|
21
|
-
*
|
|
22
|
-
* ## The three intents are NOT one control
|
|
23
|
-
*
|
|
24
|
-
* - `confirm` — a destructive yes/no. It names what is destroyed, and for the
|
|
25
|
-
* `danger` variant the destructive button is the NON-default: opening focus
|
|
26
|
-
* lands on Cancel, so a reflexive Enter cancels.
|
|
27
|
-
* - `alert` — an error/notice with a single acknowledge.
|
|
28
|
-
* - `prompt` — one text input with OK/Cancel. `inputType: 'password'` masks it
|
|
29
|
-
* (and opts out of autofill/remember); `readOnly` turns it into a readable,
|
|
30
|
-
* select-all-on-open field, which is the shape the copy-to-clipboard
|
|
31
|
-
* fallback needs — a password box would be actively wrong there.
|
|
32
|
-
*
|
|
33
|
-
* ## Why the surface is a native `<dialog>`
|
|
34
|
-
*
|
|
35
|
-
* These dialogs open OVER surfaces that are themselves native `<dialog>`s
|
|
36
|
-
* opened with `showModal()` — the event detail panel, the similar-groups
|
|
37
|
-
* modal. A `showModal()` dialog is promoted to the TOP LAYER and everything
|
|
38
|
-
* outside it is inert, so a plain `fixed inset-0` overlay rendered at the app
|
|
39
|
-
* root would be drawn behind that backdrop and could not be clicked at all.
|
|
40
|
-
* Confirming a track delete from the detail panel would have been impossible.
|
|
41
|
-
* So this surface is a `<dialog>` of its own: the top layer stacks, the newest
|
|
42
|
-
* `showModal()` wins, and the confirm sits above the panel that asked for it.
|
|
43
|
-
*
|
|
44
|
-
* jsdom implements `<dialog>` but not `showModal()`, so the open falls back to
|
|
45
|
-
* the `open` attribute there. Everything this component does — focus, keys,
|
|
46
|
-
* resolution — is identical on both paths; only the top-layer promotion, which
|
|
47
|
-
* jsdom cannot model at all, is missing. The fallback is asserted below.
|
|
48
|
-
*
|
|
49
|
-
* ## Focus, and why there is NO focus trap
|
|
50
|
-
*
|
|
51
|
-
* A `showModal()` dialog already scopes the tab ring to itself, and the
|
|
52
|
-
* surfaces underneath (`SlideOverPanel`) implement no trap of their own. A
|
|
53
|
-
* second, hand-rolled focus trap on top of that is how a page becomes
|
|
54
|
-
* unescapable, so this component deliberately does not trap. What it does do:
|
|
55
|
-
*
|
|
56
|
-
* - focuses the input (prompt) or the DEFAULT button on open,
|
|
57
|
-
* - restores focus to the previously focused element on close,
|
|
58
|
-
* - handles Escape/Enter from a `keydown` on the dialog itself and calls
|
|
59
|
-
* `stopPropagation()`. React attaches its listeners at the root container, so
|
|
60
|
-
* stopping there keeps the event from reaching the `window` Escape listener
|
|
61
|
-
* of whatever modal is underneath: one Escape closes ONE dialog.
|
|
62
|
-
*/
|
|
63
1
|
import { type JSX, type ReactNode } from "react";
|
|
64
2
|
export type DialogVariant = "danger" | "warning" | "default";
|
|
65
3
|
export interface ConfirmOptions {
|
|
@@ -33,9 +33,7 @@
|
|
|
33
33
|
* The table stays a table. The wrapper is `overflow-x-auto`, so a phone
|
|
34
34
|
* scrolls the columns sideways INSIDE the table — the page body never moves
|
|
35
35
|
* sideways, and the cells stay one line (`whitespace-nowrap`). That is the
|
|
36
|
-
*
|
|
37
|
-
* `auto` still applies the old column-count rule for a surface that wants it.
|
|
38
|
-
* See `resolveTableLayout`.
|
|
36
|
+
* only rendering — there is no card list and no per-table switch.
|
|
39
37
|
*
|
|
40
38
|
* ── Scrolling the BODY, not the page ──────────────────────────────────────
|
|
41
39
|
* `maxBodyHeight` caps the table and scrolls its rows INSIDE the container
|
|
@@ -48,7 +46,6 @@
|
|
|
48
46
|
* the operator moves through the rows.
|
|
49
47
|
*/
|
|
50
48
|
import { type ReactNode, type JSX } from "react";
|
|
51
|
-
import { type TableMobileMode } from "./data-table-layout";
|
|
52
49
|
import { type DataTablePaginationLabels } from "./data-table-pagination";
|
|
53
50
|
import { type DataTableSelection } from "./data-table-selection";
|
|
54
51
|
import { type TableSortState, type TableSortValue } from "./table-sort";
|
|
@@ -111,12 +108,6 @@ export interface DataTableProps<T> {
|
|
|
111
108
|
/** Per-row className resolver (e.g. status-coloured backgrounds). */
|
|
112
109
|
readonly rowClassName?: (row: T, rowIndex: number) => string | undefined;
|
|
113
110
|
/**
|
|
114
|
-
* Narrow-viewport rendering. Default `scroll` — the table stays a table and
|
|
115
|
-
* the wrapper scrolls sideways. Pass `cards` to force the card list,
|
|
116
|
-
* `auto` for the old column-count rule (`CARD_MODE_MIN_COLUMNS`).
|
|
117
|
-
*/
|
|
118
|
-
readonly mobileMode?: TableMobileMode;
|
|
119
|
-
/**
|
|
120
111
|
* Tailwind `max-h-*` class capping the table. The rows then scroll INSIDE
|
|
121
112
|
* this container and the column header is pinned to its top; the page around
|
|
122
113
|
* it does not move. Absent (default) = the table is as tall as its rows and
|
|
@@ -176,4 +167,4 @@ export interface DataTableProps<T> {
|
|
|
176
167
|
* does not need a breakpoint of its own.
|
|
177
168
|
*/
|
|
178
169
|
export declare const TABLE_SCROLL_MAX_H: "max-h-[60vh]";
|
|
179
|
-
export declare function DataTable<T>({ columns, rows, rowKey, onRowClick, minWidthPx, emptyMessage, className, bordered, rowClassName,
|
|
170
|
+
export declare function DataTable<T>({ columns, rows, rowKey, onRowClick, minWidthPx, emptyMessage, className, bordered, rowClassName, maxBodyHeight, fill, defaultSort, sort: sortProp, onSortChange, pageSize, page: pageProp, onPageChange, paginationLabels, selection }: DataTableProps<T>): JSX.Element | null;
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DetectionCanvas — renders an image with overlaid bounding boxes.
|
|
3
|
-
*
|
|
4
|
-
* Adapted to the Phase 6 detection output rework: consumes the new
|
|
5
|
-
* `ObjectDetection` shape (flat at root, `bbox: {x,y,width,height}`,
|
|
6
|
-
* `labels: ScoredLabel[]`, `parentId` for details). Details (face,
|
|
7
|
-
* plate) are drawn as inner boxes correlated with their parent via
|
|
8
|
-
* `parentId`.
|
|
9
|
-
*/
|
|
10
1
|
import { type JSX, type ReactNode } from "react";
|
|
11
2
|
export type { ObjectDetection, ScoredLabel, DetectionBoundingBox } from "@camstack/types";
|
|
12
3
|
import type { ObjectDetection } from "@camstack/types";
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DetectionResultTree — displays detection results as a hierarchical tree.
|
|
3
|
-
*
|
|
4
|
-
* Adapted to the Phase 6 output rework: the tree consumes flat
|
|
5
|
-
* `ObjectDetection[]` and correlates details (face / plate) with
|
|
6
|
-
* their parents via `parentId`. First-level entries are top-level
|
|
7
|
-
* nodes; their details render as indented children. Each node shows:
|
|
8
|
-
* - bbox (rectangle)
|
|
9
|
-
* - macroClass + score badge
|
|
10
|
-
* - `labels[]` (ScoredLabel — own + inherited)
|
|
11
|
-
* - `debug.alternateLabels` (top-N per source) when present
|
|
12
|
-
*/
|
|
13
1
|
import { type JSX } from "react";
|
|
14
2
|
import type { ObjectDetection } from "@camstack/types";
|
|
15
3
|
export interface DetectionResultTreeProps {
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <ContainerPrimaryHero> — renders a CONTAINER device's detail hero AS its
|
|
3
|
-
* primary child.
|
|
4
|
-
*
|
|
5
|
-
* The container has no surface of its own: it resolves a primary child
|
|
6
|
-
* (priority default, or the operator's durable primary-child pick) and renders
|
|
7
|
-
* THAT child's `HeroCard` from the shared control registry, bound to the child's
|
|
8
|
-
* numeric id — identical to opening the child device directly.
|
|
9
|
-
*
|
|
10
|
-
* The override is persisted as the chosen child's re-sync/rename-stable
|
|
11
|
-
* `entityId` (`primaryChildEntityId`); resolution matches on it directly — see
|
|
12
|
-
* `container-primary.ts`. A legacy numeric `linkDeviceId` is still honored as a
|
|
13
|
-
* fallback. Other children remain as detail tabs (owned by the device-detail
|
|
14
|
-
* page); this component only owns the hero surface. The "primary child" picker
|
|
15
|
-
* that overrides the resolution lives in the device's primary config block (see
|
|
16
|
-
* admin-ui `PrimaryChildRow`), wired to `deviceManager.setPrimaryChildEntityId`.
|
|
17
|
-
*/
|
|
18
1
|
import type { ReactNode } from "react";
|
|
19
2
|
import type { UseDeviceProxyTrpc } from "../../hooks/use-device-proxy";
|
|
20
3
|
import { type ContainerChild } from "./container-primary";
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Humidifier controls for a `DeviceType.Humidifier` device.
|
|
3
|
-
*
|
|
4
|
-
* Reads the `humidifier` cap status slice (on / current / target /
|
|
5
|
-
* mode / action / bounds) and renders:
|
|
6
|
-
* - HumidifierInlineControl (device-list row): a compact on/off
|
|
7
|
-
* tap-toggle + a small `current%→target%` readout. Row-narrow.
|
|
8
|
-
* - HumidifierHeroCard (device-detail hero): a bespoke state-tinted
|
|
9
|
-
* droplet + rising-mist SVG in the framed-box language, an on/off
|
|
10
|
-
* toggle, a target-humidity range slider with a `%` readout, a
|
|
11
|
-
* current-humidity readout, and a mode `<select>` (only when modes
|
|
12
|
-
* are available).
|
|
13
|
-
*
|
|
14
|
-
* Mirrors the climate / valve controls' `useDeviceCapability` +
|
|
15
|
-
* `useOptimisticSlice` pattern so the row and hero never diverge.
|
|
16
|
-
* Controls are gated on `available`. The decorative SVG is aria-hidden.
|
|
17
|
-
*/
|
|
18
1
|
import { type ReactNode } from "react";
|
|
19
2
|
import type { DeviceControlProps } from "./control-registry";
|
|
20
3
|
/** The `action` HA attribute drives the droplet / mist tint. */
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { ReactNode } from "react";
|
|
16
16
|
import type { LawnMowerActivity } from "@camstack/types";
|
|
17
|
+
import { type UiTextFn } from "../../contexts/ui-text";
|
|
17
18
|
import type { DeviceControlProps } from "./control-registry";
|
|
18
19
|
export interface LawnMowerActivityMeta {
|
|
19
20
|
readonly label: string;
|
|
@@ -25,7 +26,7 @@ export interface LawnMowerActivityMeta {
|
|
|
25
26
|
* yet) to a human label + colour tone. Exhaustive switch: a new enum
|
|
26
27
|
* member fails the build.
|
|
27
28
|
*/
|
|
28
|
-
export declare function lawnMowerActivityMeta(activity: LawnMowerActivity | undefined): LawnMowerActivityMeta;
|
|
29
|
+
export declare function lawnMowerActivityMeta(activity: LawnMowerActivity | undefined, t?: UiTextFn): LawnMowerActivityMeta;
|
|
29
30
|
/**
|
|
30
31
|
* LawnMowerInlineControl — compact, activity-aware mower controls for a
|
|
31
32
|
* device-list row. Mow / Pause / Dock + battery badge.
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pet-feeder controls for a `DeviceType.PetFeeder` device.
|
|
3
|
-
*
|
|
4
|
-
* Reads the `pet-feeder` cap status slice (foodLevel / food1 / food2 /
|
|
5
|
-
* lowFood / batteryPower / desiccantLeftDays / feeding / status / error /
|
|
6
|
-
* errorCode / isDualHopper + the four persisted settings) and renders:
|
|
7
|
-
* - PetFeederInlineControl (device-list row): a compact food-level +
|
|
8
|
-
* battery readout with a one-tap Feed button (default portion).
|
|
9
|
-
* - PetFeederHeroCard (device-detail hero): the food-bowl fill gauge
|
|
10
|
-
* signature, battery + desiccant readouts, a manual-feed portion
|
|
11
|
-
* stepper honouring the 4-200 g hardware range (per-hopper on
|
|
12
|
-
* dual-hopper models), a maintenance action row (Call pet / Reset
|
|
13
|
-
* desiccant / Mark refilled) and the child-lock / indicator-light /
|
|
14
|
-
* feed-sound / volume settings.
|
|
15
|
-
*
|
|
16
|
-
* Mirrors the AC card's `useDeviceCapability` + inferred-cap pattern so
|
|
17
|
-
* the row and hero never diverge. Actions are gated on `available`.
|
|
18
|
-
*/
|
|
19
1
|
import { type ReactNode } from "react";
|
|
20
2
|
import type { ControlPanelProps } from "./types.js";
|
|
21
3
|
export declare function PetFeederPanel({ trpc, deviceId, layout, optimistic }: ControlPanelProps): ReactNode;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import { type UiTextFn } from "../../contexts/ui-text";
|
|
2
3
|
import type { DeviceControlProps } from "./control-registry";
|
|
3
4
|
/**
|
|
4
5
|
* "3 min ago", or nothing at all.
|
|
@@ -7,7 +8,7 @@ import type { DeviceControlProps } from "./control-registry";
|
|
|
7
8
|
* epoch. "1 Jan 1970" next to a script is the kind of detail that sends an
|
|
8
9
|
* operator looking for a clock problem that does not exist.
|
|
9
10
|
*/
|
|
10
|
-
export declare function lastRunLabel(atMs: number, nowMs: number): string | null;
|
|
11
|
+
export declare function lastRunLabel(atMs: number, nowMs: number, t?: UiTextFn): string | null;
|
|
11
12
|
export declare function ScriptInlineControl({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
|
|
12
13
|
export declare function ScriptHeroCard({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
|
|
13
14
|
export declare function AutomationInlineControl({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
|