@camstack/ui-library 0.1.57 → 0.1.58
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/composites/battery-badge.d.ts +3 -0
- package/dist/composites/camera-stream-player.d.ts +62 -2
- package/dist/composites/cap-settings/ConsumablesPanel.d.ts +2 -0
- package/dist/composites/cap-settings/CoverageTrack.d.ts +12 -0
- package/dist/composites/cap-settings/EventBucketStrip.d.ts +25 -0
- package/dist/composites/cap-settings/EventHeatmap.d.ts +10 -0
- package/dist/composites/cap-settings/RecordingPanel.d.ts +2 -0
- package/dist/composites/cap-settings/RecordingRulesEditor.d.ts +7 -0
- package/dist/composites/cap-settings/RecordingSettings.d.ts +8 -0
- package/dist/composites/cap-settings/RecordingTimeline.d.ts +5 -0
- package/dist/composites/cap-settings/TimelineControls.d.ts +21 -0
- package/dist/composites/cap-settings/event-thumb.d.ts +11 -0
- package/dist/composites/cap-settings/index.d.ts +4 -0
- package/dist/composites/cap-settings/recording-config-form.d.ts +5 -0
- package/dist/composites/cap-settings/recording-spans.d.ts +22 -0
- package/dist/composites/cap-settings/recording-timeline-model.d.ts +84 -0
- package/dist/composites/device-activity-panel.d.ts +5 -1
- package/dist/composites/device-controls/alarm-hero-card.d.ts +24 -0
- package/dist/composites/device-controls/atoms.d.ts +81 -0
- package/dist/composites/device-controls/brightness-panel.d.ts +3 -0
- package/dist/composites/device-controls/button-control.d.ts +18 -0
- package/dist/composites/device-controls/climate-panel.d.ts +16 -0
- package/dist/composites/device-controls/container-primary-hero.d.ts +24 -0
- package/dist/composites/device-controls/container-primary.d.ts +46 -0
- package/dist/composites/device-controls/control-panel.d.ts +10 -0
- package/dist/composites/device-controls/control-registry.d.ts +74 -0
- package/dist/composites/device-controls/cover-hero-card.d.ts +27 -0
- package/dist/composites/device-controls/cover-inline.d.ts +27 -0
- package/dist/composites/device-controls/cover-panel.d.ts +3 -0
- package/dist/composites/device-controls/dummy-hero-card.d.ts +6 -0
- package/dist/composites/device-controls/fan-hero-card.d.ts +21 -0
- package/dist/composites/device-controls/fan-panel.d.ts +3 -0
- package/dist/composites/device-controls/humidifier-control.d.ts +37 -0
- package/dist/composites/device-controls/image-control.d.ts +24 -0
- package/dist/composites/device-controls/index.d.ts +33 -0
- package/dist/composites/device-controls/lawn-mower-control.d.ts +24 -0
- package/dist/composites/device-controls/light-hero-card.d.ts +16 -0
- package/dist/composites/device-controls/lock-hero-card.d.ts +15 -0
- package/dist/composites/device-controls/lock-panel.d.ts +3 -0
- package/dist/composites/device-controls/media-player-hero-card.d.ts +17 -0
- package/dist/composites/device-controls/media-player-panel.d.ts +9 -0
- package/dist/composites/device-controls/offline-badge.d.ts +11 -0
- package/dist/composites/device-controls/panel-controls.d.ts +17 -0
- package/dist/composites/device-controls/popover-row-action.d.ts +9 -0
- package/dist/composites/device-controls/primary-child.d.ts +18 -0
- package/dist/composites/device-controls/radial-gauge.d.ts +20 -0
- package/dist/composites/device-controls/sensor-hero-card.d.ts +10 -0
- package/dist/composites/device-controls/sensor-inline-control.d.ts +11 -0
- package/dist/composites/device-controls/sensor-value-atom.d.ts +106 -0
- package/dist/composites/device-controls/switch-hero-card.d.ts +13 -0
- package/dist/composites/device-controls/switch-panel.d.ts +3 -0
- package/dist/composites/device-controls/tap-toggle.d.ts +17 -0
- package/dist/composites/device-controls/thermostat-hero-card.d.ts +17 -0
- package/dist/composites/device-controls/types.d.ts +17 -0
- package/dist/composites/device-controls/update-control.d.ts +11 -0
- package/dist/composites/device-controls/vacuum-control.d.ts +36 -0
- package/dist/composites/device-controls/valve-control.d.ts +46 -0
- package/dist/composites/device-controls/water-heater-control.d.ts +41 -0
- package/dist/composites/device-controls/weather-control.d.ts +41 -0
- package/dist/composites/device-item/actions.d.ts +12 -1
- package/dist/composites/device-item/child-layout.d.ts +32 -0
- package/dist/composites/device-item/child-section-accordion.d.ts +9 -0
- package/dist/composites/device-item/container-children-context.d.ts +15 -0
- package/dist/composites/device-item/device-delete-action.d.ts +8 -0
- package/dist/composites/device-item/header.d.ts +8 -1
- package/dist/composites/device-item/helpers.d.ts +108 -2
- package/dist/composites/device-item/index.d.ts +4 -0
- package/dist/composites/device-item/preview.d.ts +3 -3
- package/dist/composites/device-item/reboot-quick-action.d.ts +9 -0
- package/dist/composites/device-item/status-dot.d.ts +4 -1
- package/dist/composites/device-list/batch-actions-bar.d.ts +15 -0
- package/dist/composites/device-list/batch-toolbar.d.ts +15 -0
- package/dist/composites/device-list/cards-layout.d.ts +18 -0
- package/dist/composites/device-list/columns.d.ts +68 -0
- package/dist/composites/device-list/device-mode.d.ts +115 -0
- package/dist/composites/device-list/filter-chips.d.ts +15 -6
- package/dist/composites/device-list/fuzzy-match.d.ts +27 -0
- package/dist/composites/device-list/generic-mode.d.ts +81 -0
- package/dist/composites/device-list/group.d.ts +19 -0
- package/dist/composites/device-list/hardware-cell.d.ts +7 -0
- package/dist/composites/device-list/hardware.d.ts +26 -0
- package/dist/composites/device-list/icon-action.d.ts +17 -0
- package/dist/composites/device-list/index.d.ts +23 -39
- package/dist/composites/device-list/multi-select.d.ts +22 -0
- package/dist/composites/device-list/sort.d.ts +18 -0
- package/dist/composites/device-list/sortable-header.d.ts +10 -0
- package/dist/composites/device-list/table-layout.d.ts +25 -0
- package/dist/composites/device-list/type-filter.d.ts +19 -0
- package/dist/composites/device-list/url-state.d.ts +7 -2
- package/dist/composites/device-meta.d.ts +38 -0
- package/dist/composites/discovery-panel.d.ts +3 -3
- package/dist/composites/hls-quality.d.ts +35 -0
- package/dist/composites/hls-video.d.ts +26 -0
- package/dist/composites/hover-zoom-image.d.ts +18 -0
- package/dist/composites/index.d.ts +12 -1
- package/dist/composites/log-stream-scroll.d.ts +32 -0
- package/dist/composites/log-stream.d.ts +8 -1
- package/dist/composites/stream-panel.d.ts +45 -10
- package/dist/composites/timezone-selector.d.ts +18 -0
- package/dist/composites/widget-panel.d.ts +28 -0
- package/dist/contexts/vod-playback.d.ts +17 -0
- package/dist/generated/system-hooks.d.ts +330 -56
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/use-device-cap-slice.d.ts +19 -0
- package/dist/hooks/use-device-capability.d.ts +12 -0
- package/dist/hooks/use-device-list-page-size.d.ts +14 -0
- package/dist/hooks/use-device-webrtc.d.ts +36 -4
- package/dist/hooks/use-optimistic-slice.d.ts +11 -0
- package/dist/index.cjs +52175 -10927
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +51796 -10813
- package/dist/index.js.map +1 -1
- package/dist/lib/format-control-datetime.d.ts +18 -0
- package/dist/lib/format-last-seen.d.ts +12 -0
- package/dist/lib/format-numeric.d.ts +9 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/primitives/dialog.d.ts +13 -0
- package/dist/primitives/tooltip.d.ts +9 -3
- package/package.json +3 -1
|
@@ -2,6 +2,9 @@ export interface BatteryStatusLike {
|
|
|
2
2
|
readonly percentage: number;
|
|
3
3
|
readonly charging: 'dc' | 'solar' | 'none';
|
|
4
4
|
readonly sleeping: boolean;
|
|
5
|
+
/** True for a binary LOW_BAT source with no real % — render "Normal"/"Low"
|
|
6
|
+
* instead of a misleading exact percentage. */
|
|
7
|
+
readonly binary?: boolean;
|
|
5
8
|
}
|
|
6
9
|
export interface BatteryBadgeProps {
|
|
7
10
|
readonly status: BatteryStatusLike | null | undefined;
|
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
import { CamProfile } from '@camstack/types';
|
|
3
|
+
type WebkitPresentationMode = 'inline' | 'picture-in-picture' | 'fullscreen';
|
|
4
|
+
declare global {
|
|
5
|
+
interface HTMLVideoElement {
|
|
6
|
+
webkitSetPresentationMode?: (mode: WebkitPresentationMode) => void;
|
|
7
|
+
readonly webkitPresentationMode?: WebkitPresentationMode;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
2
10
|
export type PlayerConnectionState = 'connecting' | 'playing' | 'error' | 'disconnected' | 'idle';
|
|
11
|
+
/**
|
|
12
|
+
* Discriminated WebRTC target — same shape as `WebrtcStreamTarget` on the
|
|
13
|
+
* backend cap; the `profile` tier uses the canonical `CamProfile` enum.
|
|
14
|
+
* Carried by `getSessionState`'s `pendingRenegotiation` and handed back to
|
|
15
|
+
* `reoffer` verbatim during an adaptive re-offer.
|
|
16
|
+
*/
|
|
17
|
+
export type PlayerWebrtcTarget = {
|
|
18
|
+
readonly kind: 'adaptive';
|
|
19
|
+
} | {
|
|
20
|
+
readonly kind: 'profile';
|
|
21
|
+
readonly profile: CamProfile;
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: 'cam-stream';
|
|
24
|
+
readonly camStreamId: string;
|
|
25
|
+
};
|
|
26
|
+
/** Live signaling state polled mid-stream — the adaptive re-offer signal. */
|
|
27
|
+
export interface SessionSignalingState {
|
|
28
|
+
readonly pendingRenegotiation: {
|
|
29
|
+
readonly target: PlayerWebrtcTarget;
|
|
30
|
+
readonly epoch: number;
|
|
31
|
+
} | null;
|
|
32
|
+
}
|
|
3
33
|
/**
|
|
4
34
|
* Signaling function for WebRTC connection.
|
|
5
35
|
* Called by the player to establish the WebRTC session.
|
|
@@ -101,7 +131,7 @@ export interface CameraStreamPlayerProps {
|
|
|
101
131
|
* Main/High (incl. 4K) source pass through with NO transcode — the only
|
|
102
132
|
* path that lets iOS decode High. The caller closes over the target.
|
|
103
133
|
*/
|
|
104
|
-
handleOffer?: (sdpOffer: string) => Promise<ClientOfferResult>;
|
|
134
|
+
handleOffer?: (sdpOffer: string, sessionId?: string) => Promise<ClientOfferResult>;
|
|
105
135
|
/**
|
|
106
136
|
* Fetch the browser-side ICE servers (STUN + TURN) for client-offer mode.
|
|
107
137
|
* Called BEFORE the offer is built so a remote/CGNAT viewer can gather a
|
|
@@ -117,6 +147,35 @@ export interface CameraStreamPlayerProps {
|
|
|
117
147
|
}>;
|
|
118
148
|
/** Explicitly close the WebRTC session on server (cleanup on unmount). */
|
|
119
149
|
closeSession?: (sessionId: string) => Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Poll the live signaling state of a session (client-offer mode only).
|
|
152
|
+
* After the server swaps the broker source to a different tier (adaptive
|
|
153
|
+
* downgrade), this returns a non-null `pendingRenegotiation` with the new
|
|
154
|
+
* target + a monotonic `epoch`. The player then transparently re-offers
|
|
155
|
+
* on the SAME sessionId — building a fresh PeerConnection in the
|
|
156
|
+
* background and only swapping the live `<video>` to the new stream once
|
|
157
|
+
* it delivers media, so the last decoded frame stays on screen with no
|
|
158
|
+
* loader. Best-effort: omit it to disable adaptive re-offer entirely.
|
|
159
|
+
*/
|
|
160
|
+
getSessionState?: (sessionId: string) => Promise<SessionSignalingState>;
|
|
161
|
+
/**
|
|
162
|
+
* Re-offer signaling for an adaptive tier switch (client-offer mode).
|
|
163
|
+
* Posts a fresh browser-built offer for an EXISTING `sessionId` against
|
|
164
|
+
* the new `target` (from `getSessionState`'s `pendingRenegotiation`). The
|
|
165
|
+
* server closes the stale session and creates a fresh one under the same
|
|
166
|
+
* id, bound to the new tier's source. Distinct from `handleOffer`, whose
|
|
167
|
+
* target is fixed at the player's selected stream — a re-offer must carry
|
|
168
|
+
* the server-chosen downgrade target instead. Required (alongside
|
|
169
|
+
* `getSessionState`) for adaptive re-offer to run.
|
|
170
|
+
*/
|
|
171
|
+
reoffer?: (sessionId: string, sdpOffer: string, target: PlayerWebrtcTarget) => Promise<ClientOfferResult>;
|
|
172
|
+
/**
|
|
173
|
+
* Optional poster image (snapshot data-URL) shown by the <video> before
|
|
174
|
+
* the FIRST-EVER frame is decoded. Only a cold-start fallback — once a
|
|
175
|
+
* frame is showing, the player keeps the last decoded frame across
|
|
176
|
+
* renegotiations and never falls back to the poster. Caller-supplied.
|
|
177
|
+
*/
|
|
178
|
+
posterUrl?: string;
|
|
120
179
|
/**
|
|
121
180
|
* Override client hints sent to the server at session creation.
|
|
122
181
|
* Values provided here replace the auto-computed browser values.
|
|
@@ -124,4 +183,5 @@ export interface CameraStreamPlayerProps {
|
|
|
124
183
|
*/
|
|
125
184
|
hintsOverride?: Partial<ClientStreamHints>;
|
|
126
185
|
}
|
|
127
|
-
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, className, onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, hintsOverride, }: CameraStreamPlayerProps): import("react/jsx-runtime").JSX.Element;
|
|
186
|
+
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, className, onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, }: CameraStreamPlayerProps): import("react/jsx-runtime").JSX.Element;
|
|
187
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PlaybackSpan } from './recording-spans';
|
|
2
|
+
interface CoverageTrackProps {
|
|
3
|
+
readonly windowFrom: number;
|
|
4
|
+
readonly windowTo: number;
|
|
5
|
+
readonly spans: readonly PlaybackSpan[];
|
|
6
|
+
/** Last-seeked position to mark with the playhead, or null. */
|
|
7
|
+
readonly playheadMs: number | null;
|
|
8
|
+
/** Called with the absolute ms a click maps to within the window. */
|
|
9
|
+
readonly onSeek: (clickMs: number) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function CoverageTrack({ windowFrom, windowTo, spans, playheadMs, onSeek, }: CoverageTrackProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ClipKind } from './event-thumb';
|
|
2
|
+
export interface BucketClip {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly timestamp: number;
|
|
5
|
+
readonly kind: ClipKind;
|
|
6
|
+
readonly label: string;
|
|
7
|
+
readonly mediaUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
interface EventBucketStripProps {
|
|
10
|
+
readonly clips: readonly BucketClip[];
|
|
11
|
+
readonly loading: boolean;
|
|
12
|
+
/** Key of the clip whose manifest is resolving, for the spinner. */
|
|
13
|
+
readonly busyKey: string | null;
|
|
14
|
+
readonly onPlay: (clip: BucketClip) => void;
|
|
15
|
+
/** Which kinds are currently enabled for display. */
|
|
16
|
+
readonly enabledKinds: ReadonlySet<ClipKind>;
|
|
17
|
+
/** Called when the user toggles a kind checkbox. */
|
|
18
|
+
readonly onToggleKind: (kind: ClipKind) => void;
|
|
19
|
+
/** True when more results may exist beyond the current page. */
|
|
20
|
+
readonly hasMore: boolean;
|
|
21
|
+
/** Called when the user clicks "Load more". */
|
|
22
|
+
readonly onLoadMore: () => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function EventBucketStrip({ clips, loading, busyKey, onPlay, enabledKinds, onToggleKind, hasMore, onLoadMore }: EventBucketStripProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DensityBucket } from './recording-timeline-model';
|
|
2
|
+
interface EventHeatmapProps {
|
|
3
|
+
readonly windowFrom: number;
|
|
4
|
+
readonly windowTo: number;
|
|
5
|
+
readonly buckets: readonly DensityBucket[];
|
|
6
|
+
readonly selectedIndex: number | null;
|
|
7
|
+
readonly onSelectBucket: (index: number) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function EventHeatmap({ windowFrom, windowTo, buckets, selectedIndex, onSelectBucket }: EventHeatmapProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RecordingRule } from '@camstack/types';
|
|
2
|
+
interface RecordingRulesEditorProps {
|
|
3
|
+
readonly rules: readonly RecordingRule[];
|
|
4
|
+
readonly onChange: (rules: RecordingRule[]) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function RecordingRulesEditor({ rules, onChange }: RecordingRulesEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RecordingConfig } from '@camstack/types';
|
|
2
|
+
interface RecordingSettingsProps {
|
|
3
|
+
readonly initial: RecordingConfig;
|
|
4
|
+
readonly saving: boolean;
|
|
5
|
+
readonly onSave: (config: RecordingConfig) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function RecordingSettings({ initial, saving, onSave }: RecordingSettingsProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DayWindow } from './recording-timeline-model';
|
|
2
|
+
interface TimelineControlsProps {
|
|
3
|
+
/** Currently selected calendar day. */
|
|
4
|
+
readonly day: Date;
|
|
5
|
+
/** Called when the user picks a new day. */
|
|
6
|
+
readonly onDayChange: (d: Date) => void;
|
|
7
|
+
/** Current zoom+pan view window. */
|
|
8
|
+
readonly view: DayWindow;
|
|
9
|
+
/** Full day bounds (used for zoom/pan clamping). */
|
|
10
|
+
readonly dayBounds: DayWindow;
|
|
11
|
+
/** Called when zoom changes the view. */
|
|
12
|
+
readonly onViewChange: (w: DayWindow) => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Date selector + zoom-in / zoom-out buttons for the recording timeline.
|
|
16
|
+
* Zoom cycles through ZOOM_LEVELS (widest → narrowest), centred on the
|
|
17
|
+
* current view midpoint. Horizontal pan is wired via wheel events in
|
|
18
|
+
* RecordingTimeline directly.
|
|
19
|
+
*/
|
|
20
|
+
export declare function TimelineControls({ day, onDayChange, view, dayBounds, onViewChange, }: TimelineControlsProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ClipKind = 'motion' | 'object' | 'audio';
|
|
2
|
+
interface EventThumbProps {
|
|
3
|
+
readonly mediaUrl?: string;
|
|
4
|
+
readonly kind: ClipKind;
|
|
5
|
+
}
|
|
6
|
+
export declare function EventThumb({ mediaUrl, kind }: EventThumbProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function EventKindIcon({ kind, large }: {
|
|
8
|
+
readonly kind: ClipKind;
|
|
9
|
+
readonly large?: boolean;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export { PtzPanel } from './PtzPanel';
|
|
2
|
+
export { ConsumablesPanel } from './ConsumablesPanel';
|
|
2
3
|
export { AutotrackSection } from './AutotrackSection';
|
|
3
4
|
export { MotionZonesSettings } from './MotionZonesSettings';
|
|
4
5
|
export { PrivacyMaskSettings } from './PrivacyMaskSettings';
|
|
6
|
+
export { RecordingPanel } from './RecordingPanel';
|
|
7
|
+
export { RecordingSettings } from './RecordingSettings';
|
|
8
|
+
export { RecordingRulesEditor } from './RecordingRulesEditor';
|
|
5
9
|
export { MaskShapeCanvas } from './MaskShapeCanvas';
|
|
6
10
|
export type { MaskShapeItem, MaskShapeCanvasProps } from './MaskShapeCanvas';
|
|
7
11
|
/**
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { RecordingRule, RecordingMode } from '@camstack/types';
|
|
2
|
+
export declare function emptyRule(): RecordingRule;
|
|
3
|
+
export declare function isTriggerMode(mode: RecordingMode): boolean;
|
|
4
|
+
export declare function withRuleAt(rules: readonly RecordingRule[], index: number, patch: Partial<RecordingRule>): RecordingRule[];
|
|
5
|
+
export declare function removeRuleAt(rules: readonly RecordingRule[], index: number): RecordingRule[];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface AvailabilityRange {
|
|
2
|
+
readonly profile: string;
|
|
3
|
+
readonly startMs: number;
|
|
4
|
+
readonly endMs: number;
|
|
5
|
+
}
|
|
6
|
+
export interface PlaybackSpan {
|
|
7
|
+
readonly startMs: number;
|
|
8
|
+
readonly endMs: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Merge availability ranges (across every profile) into the union of distinct
|
|
12
|
+
* time windows, ascending by start. Overlapping or touching intervals coalesce;
|
|
13
|
+
* zero/negative-length inputs are dropped. Newest-first ordering is the caller's
|
|
14
|
+
* concern (it reverses for display).
|
|
15
|
+
*/
|
|
16
|
+
export declare function mergeAvailabilitySpans(ranges: readonly AvailabilityRange[]): PlaybackSpan[];
|
|
17
|
+
/**
|
|
18
|
+
* Pick the right unit for a byte count and render it with a sensible number of
|
|
19
|
+
* significant digits (binary prefixes, matching recording sizes elsewhere).
|
|
20
|
+
* KB and up render one decimal under 100, none above; bytes never get a decimal.
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatBytes(bytes: number): string;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { PlaybackSpan } from './recording-spans';
|
|
2
|
+
/** Fixed heatmap bucket size: 15 minutes → 96 buckets over 24h. */
|
|
3
|
+
export declare const BUCKET_MS: number;
|
|
4
|
+
/** Event kinds the timeline understands. */
|
|
5
|
+
export type TimelineKind = 'motion' | 'object' | 'audio';
|
|
6
|
+
export interface DensityBucket {
|
|
7
|
+
readonly index: number;
|
|
8
|
+
readonly startMs: number;
|
|
9
|
+
readonly endMs: number;
|
|
10
|
+
readonly counts: Readonly<Record<TimelineKind, number>>;
|
|
11
|
+
readonly total: number;
|
|
12
|
+
/** Highest-count kind, or null when the bucket is empty. */
|
|
13
|
+
readonly dominant: TimelineKind | null;
|
|
14
|
+
}
|
|
15
|
+
/** Row returned by the server `getEventDensity` call (one per non-empty bucket). */
|
|
16
|
+
export interface EventDensityRow {
|
|
17
|
+
readonly bucketStart: number;
|
|
18
|
+
readonly motion: number;
|
|
19
|
+
readonly object: number;
|
|
20
|
+
readonly audio: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert server-side density rows into `DensityBucket[]` for the timeline
|
|
24
|
+
* heatmap. Rows with total === 0 are skipped (defensive; server already omits
|
|
25
|
+
* them). Returns a sparse, ascending-by-index array — EventHeatmap positions
|
|
26
|
+
* each bar by `b.startMs` rather than array index, so gaps are fine.
|
|
27
|
+
*/
|
|
28
|
+
export declare function densityRowsToBuckets(rows: readonly EventDensityRow[], windowFrom: number, bucketMs: number): DensityBucket[];
|
|
29
|
+
export interface SeekTarget {
|
|
30
|
+
readonly fromMs: number;
|
|
31
|
+
readonly toMs: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a playback window for a click at `clickMs` against recorded `spans`.
|
|
35
|
+
* Inside a span → play from the click to that span's end. In a gap / outside →
|
|
36
|
+
* snap to the WHOLE nearest span. No spans → null (nothing to play).
|
|
37
|
+
* `spans` are expected ascending by start (as `mergeAvailabilitySpans` returns).
|
|
38
|
+
*/
|
|
39
|
+
export declare function seekTargetForSpans(clickMs: number, spans: readonly PlaybackSpan[]): SeekTarget | null;
|
|
40
|
+
/**
|
|
41
|
+
* Position `ms` inside the window `[fromMs, toMs]` as a clamped percentage
|
|
42
|
+
* (0–100). A zero/negative-width window yields 0. Callers use this for `left:`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function pctForMs(ms: number, fromMs: number, toMs: number): number;
|
|
45
|
+
/** A [fromMs, toMs) window spanning (at most) one calendar day. */
|
|
46
|
+
export interface DayWindow {
|
|
47
|
+
readonly fromMs: number;
|
|
48
|
+
readonly toMs: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build a DayWindow for the given calendar `date` using local time.
|
|
52
|
+
* For any day strictly in the past: `fromMs` = local midnight, `toMs` =
|
|
53
|
+
* next local midnight (exactly 24 h).
|
|
54
|
+
* For today (or any future day that contains `nowMs`): `toMs = nowMs` so the
|
|
55
|
+
* window never extends into the future.
|
|
56
|
+
*/
|
|
57
|
+
export declare function dayWindow(date: Date, nowMs: number): DayWindow;
|
|
58
|
+
/** Available zoom levels in ms, from widest (24h) to narrowest (15 min). */
|
|
59
|
+
export declare const ZOOM_LEVELS: readonly number[];
|
|
60
|
+
/**
|
|
61
|
+
* Produce a `levelMs`-wide DayWindow centred on `centerMs`, clamped within
|
|
62
|
+
* `dayBounds`. If `levelMs >= dayBounds span`, returns `dayBounds` unchanged.
|
|
63
|
+
*/
|
|
64
|
+
export declare function zoomWindow(_current: DayWindow, dayBounds: DayWindow, levelMs: number, centerMs: number): DayWindow;
|
|
65
|
+
/**
|
|
66
|
+
* Shift `win` by `deltaMs`, preserving width, clamped within `dayBounds`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function panWindow(win: DayWindow, dayBounds: DayWindow, deltaMs: number): DayWindow;
|
|
69
|
+
/**
|
|
70
|
+
* Choose a sensible heatmap bucket size for a visible span of `spanMs`,
|
|
71
|
+
* targeting ~96-120 buckets. Always picks from `BUCKET_CANDIDATES`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function bucketMsForSpan(spanMs: number): number;
|
|
74
|
+
/** One labelled tick on the time axis. */
|
|
75
|
+
export interface AxisTick {
|
|
76
|
+
readonly pct: number;
|
|
77
|
+
readonly label: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Produce `count` evenly-spaced axis ticks spanning [fromMs, toMs].
|
|
81
|
+
* - `pct` is via pctForMs (first=0, last=100).
|
|
82
|
+
* - `label` is a localised HH:MM clock string.
|
|
83
|
+
*/
|
|
84
|
+
export declare function axisTicks(fromMs: number, toMs: number, count: number): AxisTick[];
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export interface DeviceActivityPanelProps {
|
|
2
2
|
readonly deviceId: number;
|
|
3
|
+
/** When this device is a Container, its own id — the Logs tab then shows the
|
|
4
|
+
* whole subtree (container + every child) by filtering `containerDeviceId`
|
|
5
|
+
* instead of `deviceId`. Omit/undefined for a non-container. */
|
|
6
|
+
readonly containerDeviceId?: number;
|
|
3
7
|
/** Initial event categories the user wants tracked. Defaults to "all". */
|
|
4
8
|
readonly defaultEventCategories?: readonly string[];
|
|
5
9
|
/** Initial state-cap whitelist. Empty = show every cap that fires. */
|
|
@@ -25,5 +29,5 @@ declare const TABS: readonly [{
|
|
|
25
29
|
readonly icon: import('react').ForwardRefExoticComponent<Omit<import('lucide-react').LucideProps, "ref"> & import('react').RefAttributes<SVGSVGElement>>;
|
|
26
30
|
}];
|
|
27
31
|
export type ActivityTabId = (typeof TABS)[number]['id'];
|
|
28
|
-
export declare function DeviceActivityPanel({ deviceId, defaultEventCategories, defaultStateCaps, initialTab, className, maxHeight, }: DeviceActivityPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export declare function DeviceActivityPanel({ deviceId, containerDeviceId, defaultEventCategories, defaultStateCaps, initialTab, className, maxHeight, }: DeviceActivityPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
29
33
|
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { DeviceControlProps } from './control-registry';
|
|
3
|
+
/**
|
|
4
|
+
* Row-sized alarm control: the shared Disarm + arm-mode segment strip only —
|
|
5
|
+
* no glyph, no state label, no card padding. Cap-gated on `alarm-panel`.
|
|
6
|
+
* Drops into a 200 px control column without towering over sibling rows.
|
|
7
|
+
*/
|
|
8
|
+
export declare function AlarmInlineControl({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* AlarmHeroCard — segmented arm/disarm hero for AlarmPanel devices.
|
|
11
|
+
*
|
|
12
|
+
* Renders (Spec §3):
|
|
13
|
+
* - A shield glyph + state label tinted by lifecycle state (disarmed=muted,
|
|
14
|
+
* armed=green, arming/pending=amber, triggered=red).
|
|
15
|
+
* - The shared `AlarmSegments` strip: a Disarm button plus one arm button per
|
|
16
|
+
* `availableModes` entry (Home / Away / Night / …). The currently-active
|
|
17
|
+
* segment is highlighted; transitioning states highlight none.
|
|
18
|
+
*
|
|
19
|
+
* Cap-gated on `alarm-panel` — returns null when the device lacks the cap.
|
|
20
|
+
* PIN entry (when `requiresCode`) is deferred to a later task; the segments
|
|
21
|
+
* call arm/disarm without a code, and the provider rejects when a code is
|
|
22
|
+
* required — surfaced upstream.
|
|
23
|
+
*/
|
|
24
|
+
export declare function AlarmHeroCard({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
2
|
+
export declare const FILL: "fill";
|
|
3
|
+
export declare const RIGHT: "right";
|
|
4
|
+
export declare const CENTER: "center";
|
|
5
|
+
export declare const FIT: "fit";
|
|
6
|
+
export type ControlAlign = typeof FILL | typeof RIGHT | typeof CENTER | typeof FIT;
|
|
7
|
+
interface ControlColumnProps extends ComponentPropsWithoutRef<'div'> {
|
|
8
|
+
readonly align: ControlAlign;
|
|
9
|
+
readonly children: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/** The one control column every device-row/hero control sits in.
|
|
12
|
+
* FILL → the child stretches to the full fixed column width (steppers, selects, buttons, cover triad).
|
|
13
|
+
* RIGHT → the child keeps its natural width, flush to the right edge of the fixed column (readouts, toggles — card view).
|
|
14
|
+
* CENTER → the child keeps its natural width, centered in the fixed column (readouts, toggles).
|
|
15
|
+
* FIT → the column DROPS its fixed width and sizes to its content (table view). The control takes
|
|
16
|
+
* its natural width and lays out in sequence with the trailing trash + status dot, so a wide
|
|
17
|
+
* control (climate stepper + mode select, cover triad + %) never overflows a fixed box and
|
|
18
|
+
* collides with the neighbours. FILL has no effect under FIT — a stretchy child in a
|
|
19
|
+
* content-width wrapper simply takes its natural width.
|
|
20
|
+
* FILL, RIGHT and CENTER share the fixed `w-[140px]` box (card / minimal / hero views). */
|
|
21
|
+
export declare function ControlColumn({ align, children, className, ...rest }: ControlColumnProps): ReactNode;
|
|
22
|
+
interface ValueReadoutProps {
|
|
23
|
+
readonly value: string;
|
|
24
|
+
readonly unit?: string;
|
|
25
|
+
}
|
|
26
|
+
/** A value + inline unit suffix (no separator). E.g. `60s`, `22%`. */
|
|
27
|
+
export declare function ValueReadout({ value, unit }: ValueReadoutProps): ReactNode;
|
|
28
|
+
interface StepperProps {
|
|
29
|
+
readonly value: number | string;
|
|
30
|
+
readonly unit?: string;
|
|
31
|
+
readonly onDelta: (d: 1 | -1) => void;
|
|
32
|
+
}
|
|
33
|
+
/** Increment/decrement control with value + unit display. */
|
|
34
|
+
export declare function Stepper({ value, unit, onDelta }: StepperProps): ReactNode;
|
|
35
|
+
interface SlideToggleProps {
|
|
36
|
+
readonly on: boolean;
|
|
37
|
+
readonly targetLabel: string;
|
|
38
|
+
readonly onConfirm: () => void;
|
|
39
|
+
}
|
|
40
|
+
/** Safety slide: onConfirm fires only after the thumb is dragged past threshold, never on a plain tap. */
|
|
41
|
+
export declare function SlideToggle({ on, targetLabel, onConfirm }: SlideToggleProps): ReactNode;
|
|
42
|
+
interface ArcKnobProps {
|
|
43
|
+
readonly fraction: number;
|
|
44
|
+
readonly color: string;
|
|
45
|
+
/**
|
|
46
|
+
* Distance in px from the rotating container's top edge to the knob's top
|
|
47
|
+
* edge. The knob is 20px tall, so its CENTER sits at `knobTopPx + 10` px
|
|
48
|
+
* from the top. For the knob center to ride the ring's centre radius the
|
|
49
|
+
* caller passes `(containerCenter − ringRadius) − 10`. Defaults to the
|
|
50
|
+
* thermostat gauge value (176px container, radius 76 → 88 − 76 − 10 = 2).
|
|
51
|
+
*/
|
|
52
|
+
readonly knobTopPx?: number;
|
|
53
|
+
/**
|
|
54
|
+
* When provided, the knob becomes interactive: dragging it reports the new
|
|
55
|
+
* arc fraction (0..1) to the parent, which owns the value and re-renders the
|
|
56
|
+
* knob at the new `fraction`. When omitted the knob is a display-only
|
|
57
|
+
* indicator (no grab cursor, no pointer handlers).
|
|
58
|
+
*/
|
|
59
|
+
readonly onDragFraction?: (fraction: number) => void;
|
|
60
|
+
}
|
|
61
|
+
/** Half the ArcKnob's 20px (h-5) height. Callers seat the knob centre on a
|
|
62
|
+
* ring by computing `(containerCentre − ringRadius) − KNOB_HALF_PX`. */
|
|
63
|
+
export declare const KNOB_HALF_PX = 10;
|
|
64
|
+
/**
|
|
65
|
+
* White knob riding a circular arc at `fraction` (0..1). For thermostat/light/fan.
|
|
66
|
+
*
|
|
67
|
+
* Display-only by default. Pass `onDragFraction` to make the knob draggable:
|
|
68
|
+
* pointer capture is taken on the knob, and pointer moves are mapped to a new
|
|
69
|
+
* arc fraction reported back to the parent (the parent owns the value).
|
|
70
|
+
*/
|
|
71
|
+
export declare function ArcKnob({ fraction, color, knobTopPx, onDragFraction }: ArcKnobProps): ReactNode;
|
|
72
|
+
interface GripTrackProps {
|
|
73
|
+
readonly fraction: number;
|
|
74
|
+
readonly color?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Horizontal track with a white grip thumb at `fraction` (0..1). For seek/volume/brightness.
|
|
78
|
+
* @remarks display-only indicator; interactive drag is deferred to a later task.
|
|
79
|
+
*/
|
|
80
|
+
export declare function GripTrack({ fraction, color }: GripTrackProps): ReactNode;
|
|
81
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { DeviceControlProps } from './control-registry';
|
|
3
|
+
/**
|
|
4
|
+
* ButtonControl (inline) — compact "Press" trigger for a device-list row.
|
|
5
|
+
*
|
|
6
|
+
* The `button` cap is fire-only (no status / runtimeState), so we bypass
|
|
7
|
+
* `useDeviceCapability` (which requires a slice) and resolve the cap
|
|
8
|
+
* handle directly from the device proxy. We guard on `cap === undefined`
|
|
9
|
+
* (device has no button cap) and render nothing in that case.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ButtonControl({ trpc, deviceId }: DeviceControlProps): ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* ButtonHeroCard — large press-actuator card for the device-detail hero area.
|
|
14
|
+
*
|
|
15
|
+
* Renders a centred "Press" button with real design-token classes. The cap is
|
|
16
|
+
* fire-only so no state display is needed — the affordance IS the control.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ButtonHeroCard({ trpc, deviceId }: DeviceControlProps): ReactNode;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ControlPanelProps } from './types.js';
|
|
3
|
+
import { HvacMode } from '@camstack/types';
|
|
4
|
+
/**
|
|
5
|
+
* Exhaustive colour map — one entry per HvacMode. Build will fail if a
|
|
6
|
+
* mode is added to the enum and not reflected here (Record<HvacMode, ...>
|
|
7
|
+
* + `satisfies` means TypeScript enforces full coverage).
|
|
8
|
+
*/
|
|
9
|
+
export declare const MODE_COLOR: Record<HvacMode, string>;
|
|
10
|
+
/**
|
|
11
|
+
* Mode text-accent colour — applied to the compact (inline) temperature value
|
|
12
|
+
* as a subtle cue, replacing the old bordered/filled mode box so the control
|
|
13
|
+
* matches the borderless cover/lock inline visual language.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MODE_TEXT_COLOR: Record<HvacMode, string>;
|
|
16
|
+
export declare function ClimatePanel({ trpc, deviceId, features: _features, layout, optimistic }: ControlPanelProps): ReactNode;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { UseDeviceProxyTrpc } from '../../hooks/use-device-proxy';
|
|
3
|
+
import { ContainerChild } from './container-primary';
|
|
4
|
+
/** A child row the hero can render + offer in the picker. */
|
|
5
|
+
export interface ContainerHeroChild extends ContainerChild {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly features?: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export interface ContainerPrimaryHeroProps {
|
|
10
|
+
readonly trpc: UseDeviceProxyTrpc;
|
|
11
|
+
/** The container's entity-children (from `deviceManager.getChildren`). */
|
|
12
|
+
readonly children: readonly ContainerHeroChild[];
|
|
13
|
+
/** Container's LEGACY persisted primary-child override (numeric child id) —
|
|
14
|
+
* consulted only when no durable `primaryChildEntityId` is set. */
|
|
15
|
+
readonly linkDeviceId: number | null | undefined;
|
|
16
|
+
/** Container's DURABLE primary-child override (chosen child's entityId) —
|
|
17
|
+
* null/undefined ⇒ no durable override (falls back to linkDeviceId, then
|
|
18
|
+
* priority default). */
|
|
19
|
+
readonly primaryChildEntityId?: string | null | undefined;
|
|
20
|
+
/** Persist the chosen primary child. Called with the picked child's
|
|
21
|
+
* re-sync/rename-stable `entityId`. */
|
|
22
|
+
readonly onSetPrimary: (childEntityId: string) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function ContainerPrimaryHero({ trpc, children, linkDeviceId, primaryChildEntityId, onSetPrimary, }: ContainerPrimaryHeroProps): ReactNode;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ChildRef } from './primary-child';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal shape this module needs from a child device row. Structurally
|
|
4
|
+
* compatible with `DeviceItemDevice` / `DeviceInfo` — callers pass the raw
|
|
5
|
+
* `getChildren` rows through.
|
|
6
|
+
*/
|
|
7
|
+
export interface ContainerChild {
|
|
8
|
+
readonly id: number;
|
|
9
|
+
readonly stableId: string;
|
|
10
|
+
readonly type: string;
|
|
11
|
+
readonly sourceInfo?: {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
} | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Re-sync-stable identity of a child. Prefers the upstream-system dispatch key
|
|
17
|
+
* (`sourceInfo.id` — e.g. HA `entity_id`); falls back to `stableId` for
|
|
18
|
+
* providers that never populated SourceInfo. Both survive a re-sync that may
|
|
19
|
+
* reallocate the numeric `id`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function childEntityId(child: ContainerChild): string;
|
|
22
|
+
/** Project a raw child row onto the pure resolver's {@link ChildRef} shape. */
|
|
23
|
+
export declare function containerChildToRef(child: ContainerChild): ChildRef;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the entityId override from the container's persisted `linkDeviceId`.
|
|
26
|
+
* Returns the linked child's stable entityId, or `undefined` when there is no
|
|
27
|
+
* link or the link is stale (points to a numeric id no longer present) — in
|
|
28
|
+
* which case the resolver falls back to the priority default.
|
|
29
|
+
*/
|
|
30
|
+
export declare function overrideEntityIdFromLink(children: readonly ContainerChild[], linkDeviceId: number | null | undefined): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Full container → primary-child resolution: project children to refs, resolve
|
|
33
|
+
* the override entityId, and delegate to the pure resolver. Returns `null` for a
|
|
34
|
+
* container with no children (caller degrades gracefully).
|
|
35
|
+
*
|
|
36
|
+
* Override precedence:
|
|
37
|
+
* 1. `primaryChildEntityId` — the DURABLE override (re-sync/rename-stable),
|
|
38
|
+
* passed straight to the resolver which matches on `entityId`. Wins when a
|
|
39
|
+
* non-empty string is supplied.
|
|
40
|
+
* 2. `linkDeviceId` — LEGACY numeric override, re-derived to an entityId from
|
|
41
|
+
* the live child set. Consulted only when there is no durable override.
|
|
42
|
+
*
|
|
43
|
+
* When neither resolves to a live child, the resolver falls back to the
|
|
44
|
+
* priority default.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveContainerPrimary(children: readonly ContainerChild[], linkDeviceId: number | null | undefined, primaryChildEntityId?: string | null | undefined): ChildRef | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ControlPanelProps } from './types.js';
|
|
3
|
+
/** Trim an HA date value to the `<input type=date>` shape ('YYYY-MM-DD'). */
|
|
4
|
+
export declare function toDateInput(value: string): string;
|
|
5
|
+
/** Trim an HA time value ('HH:MM:SS') to the `<input type=time>` shape ('HH:MM'). */
|
|
6
|
+
export declare function toTimeInput(value: string): string;
|
|
7
|
+
/** Trim an HA datetime value to the `<input type=datetime-local>` shape
|
|
8
|
+
* ('YYYY-MM-DDTHH:MM'). Accepts both 'T'- and space-separated forms. */
|
|
9
|
+
export declare function toDateTimeInput(value: string): string;
|
|
10
|
+
export declare function ControlPanel({ trpc, deviceId, features: _features, layout, optimistic }: ControlPanelProps): ReactNode;
|