@camstack/ui-library 1.2.189 → 1.2.190
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/clips/ClipPanel.d.ts +8 -0
- package/dist/composites/clips/ClipSilentSources.d.ts +4 -0
- package/dist/composites/clips/ClipSourceRow.d.ts +9 -0
- package/dist/composites/clips/ClipTile.d.ts +7 -0
- package/dist/composites/clips/ClipsBrowser.d.ts +4 -0
- package/dist/composites/clips/clip-day-window.d.ts +37 -0
- package/dist/composites/clips/clip-source-state.d.ts +34 -0
- package/dist/composites/clips/clip-thumbnail-fetch.d.ts +16 -0
- package/dist/composites/clips/clip-tile-media.d.ts +27 -0
- package/dist/composites/clips/clip-view-state.d.ts +34 -0
- package/dist/composites/hls-video.d.ts +17 -2
- package/dist/composites/stream-panel.d.ts +8 -1
- package/dist/contexts/vod-playback.d.ts +13 -1
- package/dist/generated/system-hooks.d.ts +2 -0
- package/dist/hooks/use-capability-presence.d.ts +32 -0
- package/dist/index.cjs +796 -43
- package/dist/index.d.ts +1 -0
- package/dist/index.js +794 -44
- package/package.json +1 -1
|
@@ -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,9 @@
|
|
|
1
|
+
import { ClipSourceNotice } from './clip-source-state.js';
|
|
2
|
+
export interface ClipSourceRowProps {
|
|
3
|
+
readonly notice: ClipSourceNotice;
|
|
4
|
+
/** How old this source's catalog is, or null when it was read live. */
|
|
5
|
+
readonly catalogAge: string | null;
|
|
6
|
+
readonly checked: boolean;
|
|
7
|
+
readonly onToggle: (source: string, next: boolean) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function ClipSourceRow({ notice, catalogAge, checked, onToggle }: ClipSourceRowProps): 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,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,34 @@
|
|
|
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
|
+
/** The picker's selection. Absent means every source (D554 decision 3). */
|
|
12
|
+
readonly selected?: readonly string[];
|
|
13
|
+
readonly clipsError?: string;
|
|
14
|
+
readonly sourcesError?: string;
|
|
15
|
+
}
|
|
16
|
+
export type ClipsViewState = {
|
|
17
|
+
readonly kind: 'loading';
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: 'failed';
|
|
20
|
+
readonly message: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'empty';
|
|
23
|
+
readonly dayId: string;
|
|
24
|
+
readonly answered: readonly ClipSourceNotice[];
|
|
25
|
+
readonly silent: readonly ClipSourceNotice[];
|
|
26
|
+
/** No source could answer at all — a different fact from a quiet day. */
|
|
27
|
+
readonly nobodyAnswered: boolean;
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: 'listed';
|
|
30
|
+
readonly clips: readonly Clip[];
|
|
31
|
+
readonly answered: readonly ClipSourceNotice[];
|
|
32
|
+
readonly silent: readonly ClipSourceNotice[];
|
|
33
|
+
};
|
|
34
|
+
export declare function resolveClipsView(input: ClipsViewInput): ClipsViewState;
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { QualityOption } from './hls-quality';
|
|
2
2
|
export interface HlsVideoProps {
|
|
3
|
-
/**
|
|
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
|
-
/**
|
|
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;
|