@camstack/ui-library 1.2.191 → 1.2.192
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/ClipBandBar.d.ts +16 -0
- package/dist/composites/clips/clip-band.d.ts +58 -0
- package/dist/composites/clips/clip-day-window.d.ts +21 -22
- package/dist/composites/clips/clip-still-retry.d.ts +50 -0
- package/dist/composites/clips/clip-tile-media.d.ts +25 -7
- package/dist/composites/clips/clip-view-state.d.ts +15 -2
- package/dist/generated/system-hooks.d.ts +2 -0
- package/dist/index.cjs +838 -481
- package/dist/index.js +839 -483
- package/dist/lib/format-duration.d.ts +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DayWindow, DensityBucket } from '../cap-settings/recording-timeline-model.js';
|
|
2
|
+
export interface ClipBandBarProps {
|
|
3
|
+
/** The calendar day the window is inside. */
|
|
4
|
+
readonly day: Date;
|
|
5
|
+
readonly onDayChange: (day: Date) => void;
|
|
6
|
+
/** The visible window — the band, whenever no bucket is picked. */
|
|
7
|
+
readonly view: DayWindow;
|
|
8
|
+
/** The full day, the clamp for every zoom and pan. */
|
|
9
|
+
readonly dayBounds: DayWindow;
|
|
10
|
+
readonly onViewChange: (view: DayWindow) => void;
|
|
11
|
+
readonly buckets: readonly DensityBucket[];
|
|
12
|
+
readonly selectedBucketIndex: number | null;
|
|
13
|
+
readonly onSelectBucket: (index: number) => void;
|
|
14
|
+
readonly densityLoading: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function ClipBandBar({ day, onDayChange, view, dayBounds, onViewChange, buckets, selectedBucketIndex, onSelectBucket, densityLoading, }: ClipBandBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { DayWindow } from '../cap-settings/recording-timeline-model.js';
|
|
2
|
+
/** The half-open instant range `listClips` is asked for. */
|
|
3
|
+
export interface ClipBand {
|
|
4
|
+
readonly fromMs: number;
|
|
5
|
+
readonly toMs: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* How many local days a band may touch and still be asked for.
|
|
9
|
+
*
|
|
10
|
+
* Two, which is every band the timebar can produce: its view window never
|
|
11
|
+
* spans more than one calendar day, and the only way to reach a second is to
|
|
12
|
+
* sit across local midnight. A third is not a gesture this control has, so it
|
|
13
|
+
* is a bug or a future widening — either way the list refuses rather than
|
|
14
|
+
* guesses.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_BAND_LOCAL_DAYS = 2;
|
|
17
|
+
/** What the surface may do with a band, decided once. */
|
|
18
|
+
export type ClipBandPlan = {
|
|
19
|
+
readonly kind: 'askable';
|
|
20
|
+
readonly band: ClipBand;
|
|
21
|
+
readonly localDays: number;
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: 'too-wide';
|
|
24
|
+
readonly band: ClipBand;
|
|
25
|
+
readonly localDays: number;
|
|
26
|
+
readonly maxLocalDays: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* How many LOCAL calendar days the half-open band touches.
|
|
30
|
+
*
|
|
31
|
+
* The end instant is exclusive, so a band that ends exactly at the next local
|
|
32
|
+
* midnight is one day, not two — otherwise every whole-day band would report
|
|
33
|
+
* double what it costs.
|
|
34
|
+
*/
|
|
35
|
+
export declare function localDaysInBand(band: ClipBand): number;
|
|
36
|
+
/** Whether this band may be asked for, and what it costs, in one value. */
|
|
37
|
+
export declare function planClipBand(band: ClipBand): ClipBandPlan;
|
|
38
|
+
/** A density bucket's own window, as `EventHeatmap` hands it back. */
|
|
39
|
+
export interface ClipBandBucket {
|
|
40
|
+
readonly startMs: number;
|
|
41
|
+
readonly endMs: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The band for the current selection: the picked bucket, or — when nothing is
|
|
45
|
+
* picked — the whole visible window.
|
|
46
|
+
*
|
|
47
|
+
* "Nothing picked means everything visible" is safe here and only here,
|
|
48
|
+
* because the visible window is itself bounded by the timebar. It is never
|
|
49
|
+
* "everything the camera holds" (D382's unstated ask).
|
|
50
|
+
*/
|
|
51
|
+
export declare function bandForSelection(view: DayWindow, bucket: ClipBandBucket | null): ClipBand;
|
|
52
|
+
/**
|
|
53
|
+
* The band, spelled out.
|
|
54
|
+
*
|
|
55
|
+
* Every state renders it — an empty list has to say WHICH period it is empty
|
|
56
|
+
* for, the same rule the day label carried before the band replaced it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function formatBandLabel(band: ClipBand): string;
|
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* for.
|
|
2
|
+
* LOCAL calendar-day arithmetic for the clips browser.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The browser no longer NAVIGATES days — it follows a band selected on the
|
|
5
|
+
* recording timebar (D564) — but the local day survived the move as the unit
|
|
6
|
+
* the COST is counted in: a Reolink search is clamped to one camera-local day
|
|
7
|
+
* per call, so `clip-band.ts` counts a band's local days to decide whether it
|
|
8
|
+
* may be asked for. The window builder this file used to own went with the day
|
|
9
|
+
* picker; `recording-timeline-model.ts`'s `dayWindow(date, nowMs)` is the one
|
|
10
|
+
* that builds windows now, and one directory holding two functions of that
|
|
11
|
+
* name was the confusion worth deleting.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* Two properties are pinned by spec because both are invisible until they are
|
|
14
|
+
* wrong:
|
|
15
|
+
*
|
|
16
|
+
* - Deriving the day in UTC shifts every band by the browser's offset, and an
|
|
17
|
+
* operator in Berlin would see a 22:00 clip filed under the next day.
|
|
13
18
|
* - Walking days by adding 86 400 000 ms repeats or skips a calendar day
|
|
14
19
|
* across a DST change. The walk goes through `Date`'s own day arithmetic.
|
|
15
20
|
*
|
|
16
|
-
* A day id is `YYYY-MM-DD` —
|
|
17
|
-
*
|
|
21
|
+
* A day id is `YYYY-MM-DD` — it orders lexicographically, which is what lets
|
|
22
|
+
* the band's day walk terminate on a comparison rather than on a count.
|
|
18
23
|
*/
|
|
19
|
-
/** The half-open instant range `listClips` is asked for. */
|
|
20
|
-
export interface ClipDayWindow {
|
|
21
|
-
readonly since: number;
|
|
22
|
-
readonly until: number;
|
|
23
|
-
}
|
|
24
24
|
/** The LOCAL calendar day an instant falls in, as `YYYY-MM-DD`. */
|
|
25
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
26
|
/** Walk `days` calendar days from `dayId` — DST-safe, because `Date` does it. */
|
|
29
27
|
export declare function shiftDay(dayId: string, days: number): string;
|
|
30
28
|
/**
|
|
31
|
-
* The day, spelled out
|
|
29
|
+
* The day, spelled out — the calendar half of a band's label.
|
|
32
30
|
*
|
|
33
|
-
* An empty list has to say WHICH
|
|
34
|
-
* beside it is the ambiguity this surface exists to remove
|
|
35
|
-
*
|
|
31
|
+
* An empty list has to say WHICH period it is empty for; "no clips" with no
|
|
32
|
+
* date beside it is the ambiguity this surface exists to remove. `formatBandLabel`
|
|
33
|
+
* puts the times around it, and every state renders the result, not only the
|
|
34
|
+
* populated one.
|
|
36
35
|
*/
|
|
37
36
|
export declare function formatDayLabel(dayId: string): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a tile comes back for a still the server said was queued — and when it
|
|
3
|
+
* gives up (D563).
|
|
4
|
+
*
|
|
5
|
+
* ## Why a retry, and not a queue or a push
|
|
6
|
+
*
|
|
7
|
+
* A deferred refusal is FREE. The provider's lane rejects past its capacity
|
|
8
|
+
* without touching the camera, so asking again costs one cheap HTTP round trip
|
|
9
|
+
* to the addon and zero Baichuan commands. That is what rules out the
|
|
10
|
+
* alternatives: a push would need a channel, a subscription and server-side
|
|
11
|
+
* fan-out of 60 tile identities to save requests that cost nothing, and a
|
|
12
|
+
* client-side queue would be a second copy of the authority that already lives
|
|
13
|
+
* where the camera's real constraint is. Scroll-into-view alone does not help
|
|
14
|
+
* either — the 43 tiles measured refusing on 592 are already on screen.
|
|
15
|
+
*
|
|
16
|
+
* ## The shape
|
|
17
|
+
*
|
|
18
|
+
* Exponential, jittered, bounded. Exponential because a fixed interval either
|
|
19
|
+
* hammers or dawdles; JITTERED because 43 tiles refused in the same paint
|
|
20
|
+
* would otherwise come back in the same instant and refuse together again;
|
|
21
|
+
* BOUNDED because a tile that asks for ever is the other way to show the
|
|
22
|
+
* operator nothing.
|
|
23
|
+
*
|
|
24
|
+
* The server's `retry-after` is taken only when it is LONGER than the curve.
|
|
25
|
+
* The curve is the floor, so a provider answering `retry-after: 1` for a whole
|
|
26
|
+
* page cannot turn this into 43 requests a second.
|
|
27
|
+
*
|
|
28
|
+
* Sized against the measurement: one page is 60 tiles and the provider serves
|
|
29
|
+
* one mint in flight per host, so the last tile is ~60 mints away. The budget
|
|
30
|
+
* below outlasts that; a shorter one would be theatre.
|
|
31
|
+
*/
|
|
32
|
+
/** How many times a tile comes back before it says so and stops. */
|
|
33
|
+
export declare const STILL_RETRY_ATTEMPTS = 4;
|
|
34
|
+
/** First wait, doubling from there. */
|
|
35
|
+
export declare const STILL_RETRY_BASE_MS = 4000;
|
|
36
|
+
/** No single wait is longer than this, whatever the server hints. */
|
|
37
|
+
export declare const STILL_RETRY_MAX_WAIT_MS = 30000;
|
|
38
|
+
export interface StillRetryInput {
|
|
39
|
+
/** 0 for the first retry, 1 for the second, … */
|
|
40
|
+
readonly attempt: number;
|
|
41
|
+
/** The server's `retry-after`, in ms, or `null` when it sent none. */
|
|
42
|
+
readonly serverHintMs: number | null;
|
|
43
|
+
/** Injected so the schedule is assertable; `Math.random` in production. */
|
|
44
|
+
readonly random: () => number;
|
|
45
|
+
}
|
|
46
|
+
export interface StillRetryPlan {
|
|
47
|
+
readonly waitMs: number;
|
|
48
|
+
}
|
|
49
|
+
/** The next wait, or `null` when the budget is spent. */
|
|
50
|
+
export declare function planStillRetry(input: StillRetryInput): StillRetryPlan | null;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { Clip } from '@camstack/types';
|
|
2
|
-
/** The
|
|
2
|
+
/** The five things a tile can be. */
|
|
3
3
|
export type ClipTileMedia = {
|
|
4
4
|
readonly kind: 'vouched';
|
|
5
5
|
readonly url: string;
|
|
6
6
|
} | {
|
|
7
7
|
readonly kind: 'mint';
|
|
8
8
|
readonly url: string;
|
|
9
|
+
}
|
|
10
|
+
/** The still is QUEUED. The tile shows loading and comes back, under a
|
|
11
|
+
* bound — `text` is what it says if the bound runs out. */
|
|
12
|
+
| {
|
|
13
|
+
readonly kind: 'deferred';
|
|
14
|
+
readonly reason: string;
|
|
15
|
+
readonly text: string;
|
|
16
|
+
readonly retryAfterMs: number | null;
|
|
9
17
|
} | {
|
|
10
18
|
readonly kind: 'unavailable';
|
|
11
19
|
readonly reason: string;
|
|
@@ -14,14 +22,24 @@ export type ClipTileMedia = {
|
|
|
14
22
|
readonly kind: 'none';
|
|
15
23
|
readonly text: string;
|
|
16
24
|
};
|
|
25
|
+
/** A 204, as its three headers arrived. `undefined` where a header was absent
|
|
26
|
+
* — which is a real case, not a defect: an addon older than D563 sends only
|
|
27
|
+
* the prose. */
|
|
28
|
+
export interface MintRefusalHeaders {
|
|
29
|
+
readonly code: string | undefined;
|
|
30
|
+
readonly reason: string;
|
|
31
|
+
readonly disposition: string | undefined;
|
|
32
|
+
readonly retryAfterMs?: number | null;
|
|
33
|
+
}
|
|
17
34
|
/** What to draw for one clip's tile. */
|
|
18
35
|
export declare function resolveTileMedia(clip: Clip): ClipTileMedia;
|
|
19
36
|
/**
|
|
20
|
-
* A
|
|
37
|
+
* A 204, as something the tile can draw.
|
|
21
38
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
39
|
+
* `deferred` is the ONLY answer that comes back, and only because the server
|
|
40
|
+
* said so. Everything else — an unknown disposition, an absent one, a code
|
|
41
|
+
* this build has never heard of — is final, so the failure mode of a
|
|
42
|
+
* vocabulary that moves is a tile that stops early, never one that asks for
|
|
43
|
+
* ever.
|
|
26
44
|
*/
|
|
27
|
-
export declare function
|
|
45
|
+
export declare function describeRefusal(headers: MintRefusalHeaders): ClipTileMedia;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { Clip, ClipSource } from '@camstack/types';
|
|
2
|
+
import { ClipBand, ClipBandPlan } from './clip-band.js';
|
|
2
3
|
import { ClipSourceNotice } from './clip-source-state.js';
|
|
3
4
|
/** React Query's settled-ness, narrowed to what this decision needs. */
|
|
4
5
|
export type ClipQueryStatus = 'pending' | 'error' | 'success';
|
|
5
6
|
export interface ClipsViewInput {
|
|
6
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The period the list is about, and whether it may be asked for at all
|
|
9
|
+
* (`clip-band.ts`). A refused plan means NO query was fired, which is why
|
|
10
|
+
* the refusal has to be decided here rather than inferred from a query that
|
|
11
|
+
* will stay `pending` for ever.
|
|
12
|
+
*/
|
|
13
|
+
readonly plan: ClipBandPlan;
|
|
7
14
|
readonly clipsStatus: ClipQueryStatus;
|
|
8
15
|
readonly clips: readonly Clip[];
|
|
9
16
|
readonly sourcesStatus: ClipQueryStatus;
|
|
@@ -16,9 +23,15 @@ export type ClipsViewState = {
|
|
|
16
23
|
} | {
|
|
17
24
|
readonly kind: 'failed';
|
|
18
25
|
readonly message: string;
|
|
26
|
+
} | {
|
|
27
|
+
/** The band is wider than the bound, so nothing was asked. */
|
|
28
|
+
readonly kind: 'refused';
|
|
29
|
+
readonly band: ClipBand;
|
|
30
|
+
readonly localDays: number;
|
|
31
|
+
readonly maxLocalDays: number;
|
|
19
32
|
} | {
|
|
20
33
|
readonly kind: 'empty';
|
|
21
|
-
readonly
|
|
34
|
+
readonly band: ClipBand;
|
|
22
35
|
readonly answered: readonly ClipSourceNotice[];
|
|
23
36
|
readonly silent: readonly ClipSourceNotice[];
|
|
24
37
|
/** No source could answer at all — a different fact from a quiet day. */
|
|
@@ -1267,6 +1267,8 @@ export declare const usePipelineAnalyticsGetTrackMedia: typeof trpc.pipelineAnal
|
|
|
1267
1267
|
export declare const usePipelineAnalyticsListTrackMedia: typeof trpc.pipelineAnalytics.listTrackMedia.useQuery;
|
|
1268
1268
|
/** Generated alias around `trpc.pipelineAnalytics.listEventMedia.useQuery`. */
|
|
1269
1269
|
export declare const usePipelineAnalyticsListEventMedia: typeof trpc.pipelineAnalytics.listEventMedia.useQuery;
|
|
1270
|
+
/** Generated alias around `trpc.pipelineAnalytics.ownersWithMedia.useQuery`. */
|
|
1271
|
+
export declare const usePipelineAnalyticsOwnersWithMedia: typeof trpc.pipelineAnalytics.ownersWithMedia.useQuery;
|
|
1270
1272
|
/** Generated alias around `trpc.pipelineAnalytics.searchObjectEvents.useQuery`. */
|
|
1271
1273
|
export declare const usePipelineAnalyticsSearchObjectEvents: typeof trpc.pipelineAnalytics.searchObjectEvents.useQuery;
|
|
1272
1274
|
/** Generated alias around `trpc.pipelineAnalytics.wipeObjectEmbeddings.useMutation`. */
|