@camstack/ui-library 1.2.189 → 1.2.191

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.
@@ -0,0 +1,8 @@
1
+ import { CamProfile, Clip } from '@camstack/types';
2
+ export interface ClipPanelProps {
3
+ readonly deviceId: number;
4
+ readonly clip: Clip;
5
+ readonly profile: CamProfile;
6
+ readonly onProfileChange: (profile: CamProfile) => void;
7
+ }
8
+ export declare function ClipPanel({ deviceId, clip, profile, onProfileChange }: ClipPanelProps): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { Clip } from '@camstack/types';
2
+ export interface ClipTileProps {
3
+ readonly clip: Clip;
4
+ readonly selected: boolean;
5
+ readonly onSelect: (clip: Clip) => void;
6
+ }
7
+ export declare function ClipTile({ clip, selected, onSelect }: ClipTileProps): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ export interface ClipsSectionProps {
2
+ readonly deviceId: number;
3
+ }
4
+ export declare function ClipsSection({ deviceId }: ClipsSectionProps): import("react").JSX.Element;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Day navigation for the clips browser — and the window `listClips` is asked
3
+ * for.
4
+ *
5
+ * Every recording timestamp on the wire is the CAMERA's wall clock (D549
6
+ * decision 7), and the operator navigates a calendar, so a "day" here is a
7
+ * LOCAL wall-clock day: from the instant local midnight starts to the instant
8
+ * the next local midnight starts, half-open. Two consequences that are pinned
9
+ * by spec because both are invisible until they are wrong:
10
+ *
11
+ * - Building the window in UTC shifts every list by the browser's offset, and
12
+ * an operator in Berlin would see a 22:00 clip filed under the next day.
13
+ * - Walking days by adding 86 400 000 ms repeats or skips a calendar day
14
+ * across a DST change. The walk goes through `Date`'s own day arithmetic.
15
+ *
16
+ * A day id is `YYYY-MM-DD` — the shape a URL and a `<input type="date">` both
17
+ * take, so navigation never needs a second representation.
18
+ */
19
+ /** The half-open instant range `listClips` is asked for. */
20
+ export interface ClipDayWindow {
21
+ readonly since: number;
22
+ readonly until: number;
23
+ }
24
+ /** The LOCAL calendar day an instant falls in, as `YYYY-MM-DD`. */
25
+ export declare function toDayId(instant: Date): string;
26
+ /** The half-open local-wall-clock window of one day. */
27
+ export declare function dayWindow(dayId: string): ClipDayWindow;
28
+ /** Walk `days` calendar days from `dayId` — DST-safe, because `Date` does it. */
29
+ export declare function shiftDay(dayId: string, days: number): string;
30
+ /**
31
+ * The day, spelled out for the header.
32
+ *
33
+ * An empty list has to say WHICH day it is empty for — "no clips" with no date
34
+ * beside it is the ambiguity this surface exists to remove — so this label is
35
+ * rendered next to every state, not only the populated one.
36
+ */
37
+ export declare function formatDayLabel(dayId: string): string;
@@ -0,0 +1,34 @@
1
+ import { ClipSource } from '@camstack/types';
2
+ /** How loudly a source's state should read. Never the whole message. */
3
+ export type ClipSourceTone = 'ok' | 'warn' | 'error';
4
+ /** A source's state, rendered. */
5
+ export interface ClipSourceNotice {
6
+ /** The source's own id (`analytics`, `native:reolink:hub`, …). */
7
+ readonly source: string;
8
+ /** The operator-facing source name the provider gave it. */
9
+ readonly label: string;
10
+ /** One line, unique per state. Never "no clips". */
11
+ readonly headline: string;
12
+ /** The provider's reason, verbatim, or null when it gave none. */
13
+ readonly detail: string | null;
14
+ readonly tone: ClipSourceTone;
15
+ /**
16
+ * Did this source contribute rows to the list on screen?
17
+ *
18
+ * `sleeping` answers TRUE: a sleeping camera serves its persisted catalog
19
+ * (D549 decision 4), so its clips ARE in the list — only their age is
20
+ * different. The other three contribute nothing, and an empty day must name
21
+ * them as the reason rather than claim the camera was quiet.
22
+ */
23
+ readonly answered: boolean;
24
+ }
25
+ /** Turn one source row into the line the picker and the list show. */
26
+ export declare function describeSource(source: ClipSource): ClipSourceNotice;
27
+ /**
28
+ * How old a source's catalog is, or null when it did not say.
29
+ *
30
+ * Null is NOT "now". A source that omits `catalogAsOf` was read live and has
31
+ * no age to show; inventing "just now" for a source that never told us would
32
+ * be the same lie as drawing a cached list as current (D549 decision 13).
33
+ */
34
+ export declare function formatCatalogAge(catalogAsOf: number | undefined, now: number): string | null;
@@ -0,0 +1,16 @@
1
+ import { ClipTileMedia } from './clip-tile-media.js';
2
+ /** The `fetch` this module uses — injected so the contract is testable. */
3
+ export type ThumbnailFetcher = (url: string, init: RequestInit) => Promise<Response>;
4
+ /** A minted still, or the reason there is none. */
5
+ export type MintedThumbnail = {
6
+ readonly kind: 'image';
7
+ readonly blob: Blob;
8
+ } | ClipTileMedia;
9
+ /**
10
+ * Ask a mint route for one still.
11
+ *
12
+ * Never rejects: a thrown fetch is a refusal like any other, because a tile
13
+ * that throws is a tile that renders nothing and says nothing — the exact
14
+ * failure this module exists to prevent.
15
+ */
16
+ export declare function fetchMintedThumbnail(url: string, fetcher: ThumbnailFetcher): Promise<MintedThumbnail>;
@@ -0,0 +1,27 @@
1
+ import { Clip } from '@camstack/types';
2
+ /** The four things a tile can be. */
3
+ export type ClipTileMedia = {
4
+ readonly kind: 'vouched';
5
+ readonly url: string;
6
+ } | {
7
+ readonly kind: 'mint';
8
+ readonly url: string;
9
+ } | {
10
+ readonly kind: 'unavailable';
11
+ readonly reason: string;
12
+ readonly text: string;
13
+ } | {
14
+ readonly kind: 'none';
15
+ readonly text: string;
16
+ };
17
+ /** What to draw for one clip's tile. */
18
+ export declare function resolveTileMedia(clip: Clip): ClipTileMedia;
19
+ /**
20
+ * A mint that answered 204 — latch its `x-camstack-reason` to this instant.
21
+ *
22
+ * The refusal is about NOW (a full queue, a sleeping camera), so it replaces
23
+ * the offer for as long as the tile is mounted and is not retried behind the
24
+ * operator's back. An unknown reason is shown raw rather than dropped: a
25
+ * firmware inventing a refusal must not become a blank tile.
26
+ */
27
+ export declare function latchMintRefusal(reason: string): ClipTileMedia;
@@ -0,0 +1,32 @@
1
+ import { Clip, ClipSource } from '@camstack/types';
2
+ import { ClipSourceNotice } from './clip-source-state.js';
3
+ /** React Query's settled-ness, narrowed to what this decision needs. */
4
+ export type ClipQueryStatus = 'pending' | 'error' | 'success';
5
+ export interface ClipsViewInput {
6
+ readonly dayId: string;
7
+ readonly clipsStatus: ClipQueryStatus;
8
+ readonly clips: readonly Clip[];
9
+ readonly sourcesStatus: ClipQueryStatus;
10
+ readonly sources: readonly ClipSource[];
11
+ readonly clipsError?: string;
12
+ readonly sourcesError?: string;
13
+ }
14
+ export type ClipsViewState = {
15
+ readonly kind: 'loading';
16
+ } | {
17
+ readonly kind: 'failed';
18
+ readonly message: string;
19
+ } | {
20
+ readonly kind: 'empty';
21
+ readonly dayId: string;
22
+ readonly answered: readonly ClipSourceNotice[];
23
+ readonly silent: readonly ClipSourceNotice[];
24
+ /** No source could answer at all — a different fact from a quiet day. */
25
+ readonly nobodyAnswered: boolean;
26
+ } | {
27
+ readonly kind: 'listed';
28
+ readonly clips: readonly Clip[];
29
+ readonly answered: readonly ClipSourceNotice[];
30
+ readonly silent: readonly ClipSourceNotice[];
31
+ };
32
+ export declare function resolveClipsView(input: ClipsViewInput): ClipsViewState;
@@ -1,7 +1,22 @@
1
1
  import { QualityOption } from './hls-quality';
2
2
  export interface HlsVideoProps {
3
- /** Master `.m3u8` URL (typically relative, through the hub data-plane). */
3
+ /** Media URL (typically relative, through the hub data-plane) — an HLS
4
+ * master `.m3u8`, or a progressive `.mp4` when {@link format} says so. */
4
5
  readonly url: string;
6
+ /**
7
+ * What {@link url} is. Absent means `hls` — every existing caller is
8
+ * unchanged by construction.
9
+ *
10
+ * `mp4` is the NATIVE-CLIP path (D549 22): a vendor addon fetched the
11
+ * camera's own file, muxed it once with `+faststart` and serves it with
12
+ * `Range`, so a plain `<video src>` seeks natively and hls.js is not
13
+ * involved at all — handing it an MP4 would have it parse the file as a
14
+ * manifest and fail. **Nothing is appended to the URL**: the hub's
15
+ * `/addon/<id>/<prefix>` proxy takes the same-origin session cookie, which
16
+ * is what the native-Safari branch below already relies on, so the URL is
17
+ * never a credential.
18
+ */
19
+ readonly format?: 'hls' | 'mp4';
5
20
  /** Bearer token for the admin-gated data-plane route (optional). */
6
21
  readonly authToken?: string | null;
7
22
  /** Poster painted until the first frame decodes (optional). */
@@ -23,4 +38,4 @@ export interface HlsVideoProps {
23
38
  */
24
39
  readonly selectedLevel?: number;
25
40
  }
26
- export declare function HlsVideo({ url, authToken, posterUrl, autoPlay, className, onQualityOptions, selectedLevel, }: HlsVideoProps): import("react").JSX.Element;
41
+ export declare function HlsVideo({ url, format, authToken, posterUrl, autoPlay, className, onQualityOptions, selectedLevel, }: HlsVideoProps): import("react").JSX.Element;
@@ -83,6 +83,13 @@ export interface StreamPanelProps {
83
83
  * player. Clearing it returns the panel to live mode.
84
84
  */
85
85
  readonly playbackUrl?: string;
86
+ /**
87
+ * What {@link playbackUrl} is. Absent means `hls` — the recorder's manifest,
88
+ * which is what every caller passed before native clips existed. A vendor
89
+ * addon's own clip is `mp4` and the hero plays it as a plain media URL
90
+ * (D549 22); passing it as `hls` would hand an MP4 to a manifest parser.
91
+ */
92
+ readonly playbackFormat?: 'hls' | 'mp4';
86
93
  /** Bearer token for the admin-gated data-plane playback route (optional). */
87
94
  readonly playbackAuthToken?: string | null;
88
95
  /**
@@ -202,4 +209,4 @@ export interface StreamPanelProps {
202
209
  readonly chromeless?: boolean;
203
210
  readonly className?: string;
204
211
  }
205
- export declare function StreamPanel({ serverUrl, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, playbackUrl, playbackAuthToken, onExitPlayback, posterUrl, streams, activeStreamId: controlledStreamId, onStreamChange, deviceName, phase, pipelineMetrics, detections, stationaryObjects, defaultShowDetections, defaultShowMotion, streamStats, autoPlay, showPlayStop, snapshotSrc, snapshotLoading, onRefreshSnapshot, showStreamStats, children, extraOverlay, ptzAvailable, ptzShown, ptzOverlay, onPtzToggle, intercomAvailable, intercomShown, onIntercomToggle, chromeless, className, }: StreamPanelProps): import("react").JSX.Element;
212
+ export declare function StreamPanel({ serverUrl, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, playbackUrl, playbackFormat, playbackAuthToken, onExitPlayback, posterUrl, streams, activeStreamId: controlledStreamId, onStreamChange, deviceName, phase, pipelineMetrics, detections, stationaryObjects, defaultShowDetections, defaultShowMotion, streamStats, autoPlay, showPlayStop, snapshotSrc, snapshotLoading, onRefreshSnapshot, showStreamStats, children, extraOverlay, ptzAvailable, ptzShown, ptzOverlay, onPtzToggle, intercomAvailable, intercomShown, onIntercomToggle, chromeless, className, }: StreamPanelProps): import("react").JSX.Element;
@@ -1,7 +1,19 @@
1
1
  import { ReactNode } from 'react';
2
2
  export interface VodPlaybackRequest {
3
- /** Master `.m3u8` URL through the hub data-plane (relative). */
3
+ /** Media URL through the hub data-plane (relative) — an HLS master `.m3u8`,
4
+ * or a progressive `.mp4` when {@link format} says so. */
4
5
  readonly url: string;
6
+ /**
7
+ * What {@link url} is. Absent means `hls`, so every existing controller is
8
+ * unchanged.
9
+ *
10
+ * A NATIVE clip (a camera's own file, fetched and muxed by its vendor addon)
11
+ * is `mp4`: the hero player sets `video.src` instead of handing it to
12
+ * hls.js, which would parse the file as a manifest and fail (D549 22). The
13
+ * value comes straight from `videoclips.getClipPlayback().format` — the
14
+ * provider is the only thing that knows.
15
+ */
16
+ readonly format?: 'hls' | 'mp4';
5
17
  /** Short human label shown in the hero while playing (e.g. a timestamp). */
6
18
  readonly label?: string;
7
19
  }
@@ -2067,6 +2067,8 @@ export declare const useValveGetStatus: typeof trpc.valve.getStatus.useQuery;
2067
2067
  export declare const useVibrationGetStatus: typeof trpc.vibration.getStatus.useQuery;
2068
2068
  /** Generated alias around `trpc.videoclips.listClips.useQuery`. */
2069
2069
  export declare const useVideoclipsListClips: typeof trpc.videoclips.listClips.useQuery;
2070
+ /** Generated alias around `trpc.videoclips.listSources.useQuery`. */
2071
+ export declare const useVideoclipsListSources: typeof trpc.videoclips.listSources.useQuery;
2070
2072
  /** Generated alias around `trpc.videoclips.getClipPlayback.useQuery`. */
2071
2073
  export declare const useVideoclipsGetClipPlayback: typeof trpc.videoclips.getClipPlayback.useQuery;
2072
2074
  /** Generated alias around `trpc.waterHeater.setTargetTemp.useMutation`. */
@@ -0,0 +1,32 @@
1
+ import { CapabilityInfo } from '@camstack/types';
2
+ /**
3
+ * What is known about a capability's presence in this cluster.
4
+ *
5
+ * - `pending` — the registry has not answered. Render progress, never absence.
6
+ * - `present` — the registry answered and at least one addon provides it.
7
+ * - `absent` — the registry answered, and nothing provides it (either the
8
+ * capability is undeclared, or it is declared with no provider).
9
+ * - `unknown` — the read FAILED. Not an answer, so it is not `absent` either;
10
+ * a surface that cannot reach the registry says so.
11
+ */
12
+ export type CapabilityPresence = 'pending' | 'present' | 'absent' | 'unknown';
13
+ /**
14
+ * Classify a settled registry answer.
15
+ *
16
+ * `null` is "no such capability". A row with an empty `providers` list is a
17
+ * capability that is DECLARED but that no addon supplies — indistinguishable
18
+ * to an operator from not having it, and the panel behind it would have
19
+ * nothing to call. Both are `absent`.
20
+ *
21
+ * Exported for the unit test: the mapping is the whole decision, and it is
22
+ * worth pinning without a React tree around it.
23
+ */
24
+ export declare function classifyCapability(row: CapabilityInfo | null): 'present' | 'absent';
25
+ /**
26
+ * Ask the hub's capability registry whether `capabilityName` has a provider.
27
+ *
28
+ * The four states map straight off React Query's `status`, so there is no
29
+ * place for a nullish fallback to turn "no answer" into "no providers" — the
30
+ * exact collapse this hook exists to make unrepresentable.
31
+ */
32
+ export declare function useCapabilityPresence(capabilityName: string): CapabilityPresence;