@camstack/types 1.2.393 → 1.2.394
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/capabilities/index.d.ts +1 -1
- package/dist/capabilities/notification-rules.cap.d.ts +13 -0
- package/dist/capabilities/storage-admin.cap.d.ts +5 -34
- package/dist/client.d.ts +1 -1
- package/dist/client.js +190 -0
- package/dist/client.mjs +186 -1
- package/dist/generated/addon-api.d.ts +0 -7
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/geometry/subject-path.d.ts +104 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +224 -59
- package/dist/index.mjs +216 -58
- package/dist/notification/media-image.d.ts +8 -1
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* subject-path — the ONE geometry of "where did this subject go": the path a
|
|
3
|
+
* track's positions draw over a frame.
|
|
4
|
+
*
|
|
5
|
+
* Two surfaces draw it: the event detail of the debug views (admin-ui, an SVG
|
|
6
|
+
* over the hero frame) and the notification picture (addon-post-analysis, an
|
|
7
|
+
* SVG burnt into the JPEG by `sharp`, when a rule sets
|
|
8
|
+
* `media.showSubjectPath`). They must draw the SAME line for the same track,
|
|
9
|
+
* so the point list, its simplification and the mapping into a crop live here
|
|
10
|
+
* once. `@camstack/ui-library` re-exports this module for the UI side.
|
|
11
|
+
*
|
|
12
|
+
* It lives in `@camstack/types` and not in ui-library because the server
|
|
13
|
+
* addon that renders the notification picture cannot take ui-library (a React
|
|
14
|
+
* bundle for the browser), while `@camstack/types` is already the
|
|
15
|
+
* framework-free contract every side imports — the hub, the admin UI and the
|
|
16
|
+
* viewer (`@camstack/types/client`). No React, no DOM, no `sharp`: pure.
|
|
17
|
+
*
|
|
18
|
+
* ## Space
|
|
19
|
+
*
|
|
20
|
+
* Every input and output here is NORMALISED 0..1 in the frame the positions
|
|
21
|
+
* were captured in. A caller holding pixel positions divides by that frame
|
|
22
|
+
* first (the detection frame, never a native-res image it happens to show —
|
|
23
|
+
* the two share the aspect, which is what makes one normalised path land on
|
|
24
|
+
* both).
|
|
25
|
+
*
|
|
26
|
+
* ## Honesty rules (from real tracks, device 615, 2026-08-02)
|
|
27
|
+
*
|
|
28
|
+
* - A near-FULL-FRAME box is a motion placeholder, not a localisation: its
|
|
29
|
+
* centre is an artefact, so it is never a path point (it is counted).
|
|
30
|
+
* - A still subject repeats one box many times; consecutive samples closer
|
|
31
|
+
* than {@link SUBJECT_PATH_DWELL_EPS} collapse into one node carrying the
|
|
32
|
+
* count and the time span. The path always ENDS on the last sample.
|
|
33
|
+
* - A long track is downsampled to {@link SUBJECT_PATH_MAX_POINTS}, first and
|
|
34
|
+
* last kept.
|
|
35
|
+
* - Fewer than two nodes is no path: a caller draws nothing.
|
|
36
|
+
*/
|
|
37
|
+
/** A rectangle in normalised 0..1 space. */
|
|
38
|
+
export interface SubjectPathRect {
|
|
39
|
+
readonly x: number;
|
|
40
|
+
readonly y: number;
|
|
41
|
+
readonly w: number;
|
|
42
|
+
readonly h: number;
|
|
43
|
+
}
|
|
44
|
+
/** A point in normalised 0..1 space. */
|
|
45
|
+
export interface SubjectPathPoint {
|
|
46
|
+
readonly x: number;
|
|
47
|
+
readonly y: number;
|
|
48
|
+
}
|
|
49
|
+
/** One position of the subject: its normalised CENTRE, the box extent when known, the instant. */
|
|
50
|
+
export interface SubjectPathSample {
|
|
51
|
+
readonly x: number;
|
|
52
|
+
readonly y: number;
|
|
53
|
+
/** Normalised box width — absent when only the centre is known. */
|
|
54
|
+
readonly w?: number;
|
|
55
|
+
/** Normalised box height — absent when only the centre is known. */
|
|
56
|
+
readonly h?: number;
|
|
57
|
+
/** Capture instant (ms). Absent = order of the input. */
|
|
58
|
+
readonly timestamp?: number;
|
|
59
|
+
}
|
|
60
|
+
/** One drawn node: a normalised centre and the samples that collapsed into it. */
|
|
61
|
+
export interface SubjectPathNode extends SubjectPathPoint {
|
|
62
|
+
readonly samples: number;
|
|
63
|
+
readonly firstTs: number;
|
|
64
|
+
readonly lastTs: number;
|
|
65
|
+
}
|
|
66
|
+
/** The drawable path of one track. */
|
|
67
|
+
export interface SubjectPath {
|
|
68
|
+
/** Oldest → newest. Fewer than 2 = nothing to draw. */
|
|
69
|
+
readonly nodes: readonly SubjectPathNode[];
|
|
70
|
+
/** Samples left out as motion placeholders (near-full-frame boxes). */
|
|
71
|
+
readonly unlocalized: number;
|
|
72
|
+
}
|
|
73
|
+
/** What to build the path from. */
|
|
74
|
+
export interface SubjectPathOptions {
|
|
75
|
+
/** Only samples at or before this instant (the frame the path is drawn on). */
|
|
76
|
+
readonly untilTimestamp?: number;
|
|
77
|
+
/** Downsample to at most this many nodes. Default {@link SUBJECT_PATH_MAX_POINTS}. */
|
|
78
|
+
readonly maxPoints?: number;
|
|
79
|
+
}
|
|
80
|
+
/** A box covering at least this fraction of BOTH frame sides is a placeholder. */
|
|
81
|
+
export declare const SUBJECT_PATH_FULL_FRAME_COVERAGE = 0.85;
|
|
82
|
+
/** Consecutive centres closer than this (normalised euclidean) are one dwell node. */
|
|
83
|
+
export declare const SUBJECT_PATH_DWELL_EPS = 0.005;
|
|
84
|
+
/** A path is downsampled to at most this many nodes. */
|
|
85
|
+
export declare const SUBJECT_PATH_MAX_POINTS = 120;
|
|
86
|
+
/** The sample of a normalised box: its centre, its extent. */
|
|
87
|
+
export declare function subjectPathSampleFromBox(bbox: SubjectPathRect, timestamp?: number): SubjectPathSample;
|
|
88
|
+
/**
|
|
89
|
+
* The path of one track from its samples (normalised, oldest → newest).
|
|
90
|
+
* Pure; never throws; malformed samples are skipped.
|
|
91
|
+
*/
|
|
92
|
+
export declare function buildSubjectPath(samples: readonly SubjectPathSample[], opts?: SubjectPathOptions): SubjectPath;
|
|
93
|
+
/** True when the path has something to draw (two nodes or more). */
|
|
94
|
+
export declare function isDrawableSubjectPath(path: SubjectPath): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* The path as seen through `view` — a normalised rectangle of the frame (the
|
|
97
|
+
* whole frame `{0,0,1,1}`, or the rectangle a crop cut). Points are re-mapped
|
|
98
|
+
* into the view's own 0..1 space and the line is CLIPPED to it: a path that
|
|
99
|
+
* leaves the crop and comes back is two runs. Runs of fewer than two points
|
|
100
|
+
* are dropped, so an empty result means "nothing to draw".
|
|
101
|
+
*/
|
|
102
|
+
export declare function projectSubjectPath(points: readonly SubjectPathPoint[], view: SubjectPathRect): ReadonlyArray<readonly SubjectPathPoint[]>;
|
|
103
|
+
/** An SVG `points` attribute for a run in a `width` × `height` drawing ("x,y x,y …"). */
|
|
104
|
+
export declare function subjectPathSvgPoints(run: readonly SubjectPathPoint[], width: number, height: number): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -261,8 +261,9 @@ export { isAudioClassName, isBaseConditionKey, knownValues, NC_BASE_CONDITION_KE
|
|
|
261
261
|
export { type PreparedAction, type PreparedAttachment, type PreparedNotification, prepareNotification, type ResolvedLevel, } from './notification/degrade-engine.js';
|
|
262
262
|
export { htmlToText, markdownToHtmlLite, markdownToText, type NotificationFormat as NotificationBodyFormat, resolveFormat, textToHtml, transcodeBody, } from './notification/format-transcode.js';
|
|
263
263
|
export { conditionExclusionReason, conditionVisibleForKind, defaultDeliveryForSection, droppedConditionsForKind, isAudioRule, isOccupancyRule, isSystemDelivery, isSystemRuleKind, kindsForDiscriminator, NC_AUDIO_SEED, NC_CAMERA_SCOPE_CAP_NAME, NC_OCCUPANCY_DEFAULTS, NC_RULE_EDITOR_SECTION_ORDER, NC_RULE_KIND_SPECS, NC_RULE_SECTIONS, NC_SYSTEM_DELIVERY, NC_SYSTEM_RULE_KINDS, type NcDroppedCondition, type NcKindVisibilitySubject, type NcRuleEditorSection, type NcRuleEditorSectionKey, type NcRuleKind, type NcRuleKindSpec, type NcRuleOwningSectionKey, type NcRuleSection, type NcRuleSectionKey, type NcRuleConditionsSubject, type NcRuleSectionSubject, type NcRuleSeed, parseRuleSection, ruleDeviceCompatCapNames, ruleDeviceCompatCapQueries, ruleEditorSectionsForKind, ruleGroupingApplies, ruleKindOf, ruleKindSpec, ruleMatchesSection, ruleSection, ruleSectionOf, ruleSeedForSection, systemFamilyOfKind, systemRuleKindOf, } from './notification/rule-kinds.js';
|
|
264
|
-
export { imageModesForKind, legacyImageMode, NC_MEDIA_IMAGE_MODES, type NcMediaRuleSubject, ncMediaPolicyError, normalizeNcMediaPolicy, resolveNcMediaImage, ruleTriggerZoneIds, } from './notification/media-image.js';
|
|
264
|
+
export { imageModesForKind, legacyImageMode, NC_MEDIA_IMAGE_MODES, type NcMediaRuleSubject, ncMediaPolicyError, normalizeNcMediaPolicy, resolveNcMediaImage, ruleTriggerZoneIds, subjectHasPath, } from './notification/media-image.js';
|
|
265
265
|
export { isScheduleActive } from './notification/schedule.js';
|
|
266
|
+
export { buildSubjectPath, isDrawableSubjectPath, projectSubjectPath, SUBJECT_PATH_DWELL_EPS, SUBJECT_PATH_FULL_FRAME_COVERAGE, SUBJECT_PATH_MAX_POINTS, type SubjectPath, type SubjectPathNode, type SubjectPathOptions, type SubjectPathPoint, type SubjectPathRect, type SubjectPathSample, subjectPathSampleFromBox, subjectPathSvgPoints, } from './geometry/subject-path.js';
|
|
266
267
|
export { isAlarmControllableCap, NC_ALARM_CONTROLLABLE_CAPS, NC_ALARM_OPENING_EVENT_KINDS, NC_SENSOR_TRANSITION_NEVER_FIRES_WARNING, type NcAlarmControllableCap, type NcAlarmMembershipSubject, type NcAlarmOpeningEventKind, ruleAutoTriggersAlarm, ruleBelongsToAlarm, ruleGatedOnArmedState, sensorTransitionNeverFires, } from './notification/alarm-membership.js';
|
|
267
268
|
export { alarmDeviceBlocksArming, alarmDeviceSecureOnArm, type AlarmDeviceBlocksInput, type AlarmDeviceSecureOnArmInput, type NcAlarmSecureActuation, secureActuationFor, } from './notification/alarm-controllable.js';
|
|
268
269
|
export { alarmPatchLanded, applyBlockingEdit, applyModeDelaysEdits, applyModeDeviceListEdit, applyNonBlockingEdit, applySecureOnArmEdit, derivePostTriggeredAction, NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, NC_ALARM_DELAY_FIELDS, NC_ALARM_NOTIFY_FIELDS, type NcAlarmDelayField, type NcAlarmDelaySettings, type NcAlarmDelaySource, type NcAlarmModeDeviceListEditResult, type NcAlarmModeNotifyPolicy, type NcAlarmNonBlockingEditResult, type NcAlarmNotifyField, type NcAlarmResolvedDelays, resolveModeDelays, resolveModeNotifyPolicy, } from './notification/alarm-settings.js';
|
package/dist/index.js
CHANGED
|
@@ -14698,36 +14698,13 @@ var StorageKindSchema = zod.z.object({
|
|
|
14698
14698
|
declaration: require_sleep.RecordKindDeclarationSchema
|
|
14699
14699
|
});
|
|
14700
14700
|
/**
|
|
14701
|
-
*
|
|
14702
|
-
*
|
|
14703
|
-
*
|
|
14704
|
-
* these, the per-addon scoped `addon-settings` / `addon-devices` / `sections`
|
|
14705
|
-
* tables, and the tables of owners that are no longer installed - nothing else.
|
|
14706
|
-
* The records-migration leftovers (D871 `stats-5m`, the phase-0 tables) were
|
|
14707
|
-
* dropped by hand on the operator hubs and left this list with D900.
|
|
14708
|
-
*/
|
|
14709
|
-
var STORAGE_ADMIN_MOVED_COLLECTIONS = [
|
|
14710
|
-
"stream-broker:probe-snapshots",
|
|
14711
|
-
"stream-broker:profile-map",
|
|
14712
|
-
"stream-broker:rtsp-tokens",
|
|
14713
|
-
"notification-center:device-mutes",
|
|
14714
|
-
"recorder:recording-configs",
|
|
14715
|
-
"pipeline-orchestrator:device-features"
|
|
14716
|
-
];
|
|
14717
|
-
/** The per-addon blob tables a scoped name may carry. */
|
|
14718
|
-
var STORAGE_ADMIN_SCOPED_BASES = [
|
|
14719
|
-
"addon-settings",
|
|
14720
|
-
"addon-devices",
|
|
14721
|
-
"sections"
|
|
14722
|
-
];
|
|
14723
|
-
/**
|
|
14724
|
-
* storage-admin - the operator's maintenance reach into the settings database:
|
|
14725
|
-
* list its tables, drop one that nothing owns any more (D870).
|
|
14701
|
+
* storage-admin - the operator's maintenance reach into the databases: list
|
|
14702
|
+
* their tables and record kinds, read a kind for diagnosis. Read-only: a table
|
|
14703
|
+
* the code no longer creates is dropped by hand, offline (D901).
|
|
14726
14704
|
*
|
|
14727
14705
|
* Hub-only, internal and `auth: 'admin'` explicitly (`internal` does not gate
|
|
14728
|
-
* the mount, see `data-store-provider`).
|
|
14729
|
-
* with the
|
|
14730
|
-
* migration and is marked for removal once the last hub has run it.
|
|
14706
|
+
* the mount, see `data-store-provider`). `listTables` is LOGGED by the engine
|
|
14707
|
+
* with the counts.
|
|
14731
14708
|
*
|
|
14732
14709
|
* `listTables` is READ-ONLY: it never creates a table, which `count` and `get`
|
|
14733
14710
|
* on an undeclared scoped name used to do.
|
|
@@ -14743,26 +14720,6 @@ var storageAdminCapability = {
|
|
|
14743
14720
|
* optionally only those owned by `ownerPrefix`. Reads only.
|
|
14744
14721
|
*/
|
|
14745
14722
|
listTables: require_sleep.method(zod.z.object({ ownerPrefix: zod.z.string().min(1).optional() }), zod.z.object({ tables: zod.z.array(StorageTableSchema) }), { auth: "admin" }),
|
|
14746
|
-
/**
|
|
14747
|
-
* Drop one table. REFUSES when an addon declared it since the engine
|
|
14748
|
-
* started (a running addon still owns it), when it is an engine or
|
|
14749
|
-
* canonical table, when it does not exist, or when its row count is not
|
|
14750
|
-
* `expectRows` - the caller states what it believes it is deleting. The
|
|
14751
|
-
* engine also refuses anything but `<owner>:(addon-settings|addon-devices|
|
|
14752
|
-
* sections)`, the retired collections by name, and tables of owners that
|
|
14753
|
-
* are not installed (and refuses when it cannot read the installed list).
|
|
14754
|
-
* Removed with the D870 migration script once the last hub ran it.
|
|
14755
|
-
*/
|
|
14756
|
-
dropCollection: require_sleep.method(zod.z.object({
|
|
14757
|
-
collection: zod.z.string().min(1),
|
|
14758
|
-
expectRows: zod.z.number().int().nonnegative()
|
|
14759
|
-
}), zod.z.object({
|
|
14760
|
-
dropped: zod.z.literal(true),
|
|
14761
|
-
rows: zod.z.number().int().nonnegative()
|
|
14762
|
-
}), {
|
|
14763
|
-
kind: "mutation",
|
|
14764
|
-
auth: "admin"
|
|
14765
|
-
}),
|
|
14766
14723
|
/** Every record kind of both files with rows, a size estimate, class and retention (D881). */
|
|
14767
14724
|
listKinds: require_sleep.method(zod.z.object({
|
|
14768
14725
|
owner: zod.z.string().min(1).optional(),
|
|
@@ -44414,6 +44371,15 @@ var NcMediaPolicySchema = zod.z.object({
|
|
|
44414
44371
|
* Replaces `zoneCrop`.
|
|
44415
44372
|
*/
|
|
44416
44373
|
showTriggerZone: zod.z.boolean().optional(),
|
|
44374
|
+
/**
|
|
44375
|
+
* Draw the SUBJECT'S PATH on the picture — a line in the subject's colour
|
|
44376
|
+
* through the track's positions up to the frame shown, the same path the
|
|
44377
|
+
* debug views draw (`buildSubjectPath`, D903). Every picture mode a subject
|
|
44378
|
+
* is drawn on: a `crop` maps the path into the crop and clips it. Valid only
|
|
44379
|
+
* on a rule whose subject is a tracked object (refused on a sound rule);
|
|
44380
|
+
* dropped on `image: 'none'`. Absent = off.
|
|
44381
|
+
*/
|
|
44382
|
+
showSubjectPath: zod.z.boolean().optional(),
|
|
44417
44383
|
/** @deprecated D897 — accepted from an editor that predates `image`, folded into it on save. */
|
|
44418
44384
|
attach: zod.z.enum([
|
|
44419
44385
|
"best",
|
|
@@ -85053,12 +85019,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
85053
85019
|
addonId: null,
|
|
85054
85020
|
access: "create"
|
|
85055
85021
|
},
|
|
85056
|
-
"storageAdmin.dropCollection": {
|
|
85057
|
-
capName: "storage-admin",
|
|
85058
|
-
capScope: "system",
|
|
85059
|
-
addonId: null,
|
|
85060
|
-
access: "delete"
|
|
85061
|
-
},
|
|
85062
85022
|
"storageAdmin.listKinds": {
|
|
85063
85023
|
capName: "storage-admin",
|
|
85064
85024
|
capScope: "system",
|
|
@@ -96356,22 +96316,35 @@ function resolveNcMediaImage(media, kind) {
|
|
|
96356
96316
|
}
|
|
96357
96317
|
/**
|
|
96358
96318
|
* The stored shape of a media policy: `image` set, the legacy triple gone.
|
|
96319
|
+
* `showSubjectPath` survives on a kind with a tracked subject and a picture.
|
|
96359
96320
|
* `showTriggerZone` survives only where it can be drawn — a zone trigger and a
|
|
96360
96321
|
* mode other than `crop`/`none`; a legacy `zoneCrop` on a zone rule becomes it.
|
|
96361
96322
|
* Every other field (gif, clip, rolls, speed, profile, linked cameras) is
|
|
96362
96323
|
* carried through untouched. Pure; never throws.
|
|
96363
96324
|
*/
|
|
96364
96325
|
function normalizeNcMediaPolicy(media, rule) {
|
|
96365
|
-
const
|
|
96366
|
-
const
|
|
96326
|
+
const kind = ruleKindOf(rule);
|
|
96327
|
+
const image = resolveNcMediaImage(media, kind);
|
|
96328
|
+
const { attach: _attach, frame: _frame, zoneCrop, showTriggerZone, showSubjectPath, image: _image, ...rest } = media;
|
|
96367
96329
|
const drawable = (showTriggerZone === true || media.image === void 0 && zoneCrop === true) && image !== "crop" && image !== "none" && ruleTriggerZoneIds(rule.conditions).length > 0;
|
|
96330
|
+
const pathDrawable = showSubjectPath === true && image !== "none" && subjectHasPath(kind);
|
|
96368
96331
|
return {
|
|
96369
96332
|
...rest,
|
|
96370
96333
|
image,
|
|
96371
|
-
...drawable ? { showTriggerZone: true } : {}
|
|
96334
|
+
...drawable ? { showTriggerZone: true } : {},
|
|
96335
|
+
...pathDrawable ? { showSubjectPath: true } : {}
|
|
96372
96336
|
};
|
|
96373
96337
|
}
|
|
96374
96338
|
/**
|
|
96339
|
+
* True when a rule of this kind has a TRACKED subject whose path can be drawn
|
|
96340
|
+
* (`showSubjectPath`): the kinds whose subject owns a region of the frame. A
|
|
96341
|
+
* sound owns none, and a kind that carries no media has no picture.
|
|
96342
|
+
*/
|
|
96343
|
+
function subjectHasPath(kind) {
|
|
96344
|
+
const spec = ruleKindSpec(kind);
|
|
96345
|
+
return spec.carriesMedia && spec.carriesSubjectCrop;
|
|
96346
|
+
}
|
|
96347
|
+
/**
|
|
96375
96348
|
* Why a media policy cannot be SAVED on this rule, or null. Server-side and
|
|
96376
96349
|
* editor alike: `showTriggerZone` on a rule whose trigger is not a zone, and a
|
|
96377
96350
|
* mode the rule's kind cannot honour.
|
|
@@ -96383,6 +96356,7 @@ function ncMediaPolicyError(media, rule) {
|
|
|
96383
96356
|
if (offered.length > 0 && !offered.includes(media.image)) return `media.image '${media.image}' is not available for a ${kind} rule (offered: ${offered.join(", ")})`;
|
|
96384
96357
|
}
|
|
96385
96358
|
if (media.showTriggerZone === true && ruleTriggerZoneIds(rule.conditions).length === 0) return "media.showTriggerZone needs a rule triggered by a zone (a zones condition or an occupancy zone)";
|
|
96359
|
+
if (media.showSubjectPath === true && !subjectHasPath(kind)) return `media.showSubjectPath needs a rule whose subject is a tracked object (not a ${kind} rule)`;
|
|
96386
96360
|
return null;
|
|
96387
96361
|
}
|
|
96388
96362
|
//#endregion
|
|
@@ -96468,6 +96442,190 @@ function isScheduleActive(schedule, atMs) {
|
|
|
96468
96442
|
return schedule.invert === true ? !inside : inside;
|
|
96469
96443
|
}
|
|
96470
96444
|
//#endregion
|
|
96445
|
+
//#region src/geometry/subject-path.ts
|
|
96446
|
+
/** A box covering at least this fraction of BOTH frame sides is a placeholder. */
|
|
96447
|
+
var SUBJECT_PATH_FULL_FRAME_COVERAGE = .85;
|
|
96448
|
+
/** Consecutive centres closer than this (normalised euclidean) are one dwell node. */
|
|
96449
|
+
var SUBJECT_PATH_DWELL_EPS = .005;
|
|
96450
|
+
/** A path is downsampled to at most this many nodes. */
|
|
96451
|
+
var SUBJECT_PATH_MAX_POINTS = 120;
|
|
96452
|
+
var clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
96453
|
+
/** The sample of a normalised box: its centre, its extent. */
|
|
96454
|
+
function subjectPathSampleFromBox(bbox, timestamp) {
|
|
96455
|
+
return {
|
|
96456
|
+
x: bbox.x + bbox.w / 2,
|
|
96457
|
+
y: bbox.y + bbox.h / 2,
|
|
96458
|
+
w: bbox.w,
|
|
96459
|
+
h: bbox.h,
|
|
96460
|
+
...timestamp !== void 0 ? { timestamp } : {}
|
|
96461
|
+
};
|
|
96462
|
+
}
|
|
96463
|
+
function isPlaceholder(s) {
|
|
96464
|
+
return s.w !== void 0 && s.h !== void 0 && s.w >= .85 && s.h >= .85;
|
|
96465
|
+
}
|
|
96466
|
+
function downsample(xs, max) {
|
|
96467
|
+
if (xs.length <= max || max < 2) return xs;
|
|
96468
|
+
const step = (xs.length - 1) / (max - 1);
|
|
96469
|
+
const out = [];
|
|
96470
|
+
for (let i = 0; i < max - 1; i++) {
|
|
96471
|
+
const x = xs[Math.round(i * step)];
|
|
96472
|
+
if (x !== void 0) out.push(x);
|
|
96473
|
+
}
|
|
96474
|
+
const last = xs[xs.length - 1];
|
|
96475
|
+
if (last !== void 0) out.push(last);
|
|
96476
|
+
return out;
|
|
96477
|
+
}
|
|
96478
|
+
function isFiniteSample(s) {
|
|
96479
|
+
return Number.isFinite(s.x) && Number.isFinite(s.y);
|
|
96480
|
+
}
|
|
96481
|
+
/**
|
|
96482
|
+
* The path of one track from its samples (normalised, oldest → newest).
|
|
96483
|
+
* Pure; never throws; malformed samples are skipped.
|
|
96484
|
+
*/
|
|
96485
|
+
function buildSubjectPath(samples, opts) {
|
|
96486
|
+
const until = opts?.untilTimestamp;
|
|
96487
|
+
const nodes = [];
|
|
96488
|
+
let unlocalized = 0;
|
|
96489
|
+
let tail = null;
|
|
96490
|
+
for (let index = 0; index < samples.length; index++) {
|
|
96491
|
+
const s = samples[index];
|
|
96492
|
+
if (s === void 0 || !isFiniteSample(s)) continue;
|
|
96493
|
+
if (until !== void 0 && s.timestamp !== void 0 && s.timestamp > until) continue;
|
|
96494
|
+
if (isPlaceholder(s)) {
|
|
96495
|
+
unlocalized += 1;
|
|
96496
|
+
continue;
|
|
96497
|
+
}
|
|
96498
|
+
const ts = s.timestamp !== void 0 && Number.isFinite(s.timestamp) ? s.timestamp : index;
|
|
96499
|
+
const x = clamp01(s.x);
|
|
96500
|
+
const y = clamp01(s.y);
|
|
96501
|
+
const prev = nodes[nodes.length - 1];
|
|
96502
|
+
if (prev !== void 0 && Math.hypot(x - prev.x, y - prev.y) < .005) {
|
|
96503
|
+
nodes[nodes.length - 1] = {
|
|
96504
|
+
...prev,
|
|
96505
|
+
samples: prev.samples + 1,
|
|
96506
|
+
lastTs: Math.max(prev.lastTs, ts)
|
|
96507
|
+
};
|
|
96508
|
+
tail = {
|
|
96509
|
+
x,
|
|
96510
|
+
y,
|
|
96511
|
+
samples: 1,
|
|
96512
|
+
firstTs: ts,
|
|
96513
|
+
lastTs: ts
|
|
96514
|
+
};
|
|
96515
|
+
continue;
|
|
96516
|
+
}
|
|
96517
|
+
tail = null;
|
|
96518
|
+
nodes.push({
|
|
96519
|
+
x,
|
|
96520
|
+
y,
|
|
96521
|
+
samples: 1,
|
|
96522
|
+
firstTs: ts,
|
|
96523
|
+
lastTs: ts
|
|
96524
|
+
});
|
|
96525
|
+
}
|
|
96526
|
+
const end = nodes[nodes.length - 1];
|
|
96527
|
+
if (tail !== null && end !== void 0 && end.samples > 1 && (tail.x !== end.x || tail.y !== end.y)) {
|
|
96528
|
+
nodes[nodes.length - 1] = {
|
|
96529
|
+
...end,
|
|
96530
|
+
samples: end.samples - 1
|
|
96531
|
+
};
|
|
96532
|
+
nodes.push(tail);
|
|
96533
|
+
}
|
|
96534
|
+
return {
|
|
96535
|
+
nodes: downsample(nodes, opts?.maxPoints ?? 120),
|
|
96536
|
+
unlocalized
|
|
96537
|
+
};
|
|
96538
|
+
}
|
|
96539
|
+
/** True when the path has something to draw (two nodes or more). */
|
|
96540
|
+
function isDrawableSubjectPath(path) {
|
|
96541
|
+
return path.nodes.length >= 2;
|
|
96542
|
+
}
|
|
96543
|
+
function inside(p) {
|
|
96544
|
+
return p.x >= 0 && p.x <= 1 && p.y >= 0 && p.y <= 1;
|
|
96545
|
+
}
|
|
96546
|
+
/** Liang–Barsky: the part of segment a→b inside the unit square, or null. */
|
|
96547
|
+
function clipSegment(a, b) {
|
|
96548
|
+
const dx = b.x - a.x;
|
|
96549
|
+
const dy = b.y - a.y;
|
|
96550
|
+
let t0 = 0;
|
|
96551
|
+
let t1 = 1;
|
|
96552
|
+
const edges = [
|
|
96553
|
+
[-dx, a.x],
|
|
96554
|
+
[dx, 1 - a.x],
|
|
96555
|
+
[-dy, a.y],
|
|
96556
|
+
[dy, 1 - a.y]
|
|
96557
|
+
];
|
|
96558
|
+
for (const [p, q] of edges) {
|
|
96559
|
+
if (p === 0) {
|
|
96560
|
+
if (q < 0) return null;
|
|
96561
|
+
continue;
|
|
96562
|
+
}
|
|
96563
|
+
const r = q / p;
|
|
96564
|
+
if (p < 0) {
|
|
96565
|
+
if (r > t1) return null;
|
|
96566
|
+
if (r > t0) t0 = r;
|
|
96567
|
+
} else {
|
|
96568
|
+
if (r < t0) return null;
|
|
96569
|
+
if (r < t1) t1 = r;
|
|
96570
|
+
}
|
|
96571
|
+
}
|
|
96572
|
+
return [{
|
|
96573
|
+
x: a.x + t0 * dx,
|
|
96574
|
+
y: a.y + t0 * dy
|
|
96575
|
+
}, {
|
|
96576
|
+
x: a.x + t1 * dx,
|
|
96577
|
+
y: a.y + t1 * dy
|
|
96578
|
+
}];
|
|
96579
|
+
}
|
|
96580
|
+
function samePoint(a, b) {
|
|
96581
|
+
return Math.abs(a.x - b.x) < 1e-9 && Math.abs(a.y - b.y) < 1e-9;
|
|
96582
|
+
}
|
|
96583
|
+
/**
|
|
96584
|
+
* The path as seen through `view` — a normalised rectangle of the frame (the
|
|
96585
|
+
* whole frame `{0,0,1,1}`, or the rectangle a crop cut). Points are re-mapped
|
|
96586
|
+
* into the view's own 0..1 space and the line is CLIPPED to it: a path that
|
|
96587
|
+
* leaves the crop and comes back is two runs. Runs of fewer than two points
|
|
96588
|
+
* are dropped, so an empty result means "nothing to draw".
|
|
96589
|
+
*/
|
|
96590
|
+
function projectSubjectPath(points, view) {
|
|
96591
|
+
if (points.length < 2 || !(view.w > 0) || !(view.h > 0)) return [];
|
|
96592
|
+
const local = points.map((p) => ({
|
|
96593
|
+
x: (p.x - view.x) / view.w,
|
|
96594
|
+
y: (p.y - view.y) / view.h
|
|
96595
|
+
}));
|
|
96596
|
+
const runs = [];
|
|
96597
|
+
let run = [];
|
|
96598
|
+
for (let i = 1; i < local.length; i++) {
|
|
96599
|
+
const a = local[i - 1];
|
|
96600
|
+
const b = local[i];
|
|
96601
|
+
if (a === void 0 || b === void 0) continue;
|
|
96602
|
+
const seg = clipSegment(a, b);
|
|
96603
|
+
if (seg === null) {
|
|
96604
|
+
if (run.length >= 2) runs.push(run);
|
|
96605
|
+
run = [];
|
|
96606
|
+
continue;
|
|
96607
|
+
}
|
|
96608
|
+
const [from, to] = seg;
|
|
96609
|
+
const last = run[run.length - 1];
|
|
96610
|
+
if (last === void 0 || !samePoint(last, from)) {
|
|
96611
|
+
if (run.length >= 2) runs.push(run);
|
|
96612
|
+
run = [from];
|
|
96613
|
+
}
|
|
96614
|
+
run.push(to);
|
|
96615
|
+
if (!inside(b)) {
|
|
96616
|
+
if (run.length >= 2) runs.push(run);
|
|
96617
|
+
run = [];
|
|
96618
|
+
}
|
|
96619
|
+
}
|
|
96620
|
+
if (run.length >= 2) runs.push(run);
|
|
96621
|
+
return runs;
|
|
96622
|
+
}
|
|
96623
|
+
/** An SVG `points` attribute for a run in a `width` × `height` drawing ("x,y x,y …"). */
|
|
96624
|
+
function subjectPathSvgPoints(run, width, height) {
|
|
96625
|
+
const fmt = (v) => String(Math.round(v * 100) / 100);
|
|
96626
|
+
return run.map((p) => `${fmt(p.x * width)},${fmt(p.y * height)}`).join(" ");
|
|
96627
|
+
}
|
|
96628
|
+
//#endregion
|
|
96471
96629
|
//#region src/notification/alarm-membership.ts
|
|
96472
96630
|
var ARMED_WORD_PREFIX = "armed_";
|
|
96473
96631
|
var ALARM_PANEL_CAP = "alarm-panel";
|
|
@@ -105240,12 +105398,13 @@ exports.STATE_LABELS = STATE_LABELS;
|
|
|
105240
105398
|
exports.STATE_LABEL_KEYS = STATE_LABEL_KEYS;
|
|
105241
105399
|
exports.STATIONARY_OVERLAY_STYLE = STATIONARY_OVERLAY_STYLE;
|
|
105242
105400
|
exports.STORAGE_ACCESS_FALLBACK = STORAGE_ACCESS_FALLBACK;
|
|
105243
|
-
exports.STORAGE_ADMIN_MOVED_COLLECTIONS = STORAGE_ADMIN_MOVED_COLLECTIONS;
|
|
105244
|
-
exports.STORAGE_ADMIN_SCOPED_BASES = STORAGE_ADMIN_SCOPED_BASES;
|
|
105245
105401
|
exports.STORAGE_BYTES_PER_GB = STORAGE_BYTES_PER_GB;
|
|
105246
105402
|
exports.STORAGE_LOCATION_MODES = STORAGE_LOCATION_MODES;
|
|
105247
105403
|
exports.STREAM_PROFILE_META = STREAM_PROFILE_META;
|
|
105248
105404
|
exports.STREAM_QUALITY_LABELS = STREAM_QUALITY_LABELS;
|
|
105405
|
+
exports.SUBJECT_PATH_DWELL_EPS = SUBJECT_PATH_DWELL_EPS;
|
|
105406
|
+
exports.SUBJECT_PATH_FULL_FRAME_COVERAGE = SUBJECT_PATH_FULL_FRAME_COVERAGE;
|
|
105407
|
+
exports.SUBJECT_PATH_MAX_POINTS = SUBJECT_PATH_MAX_POINTS;
|
|
105249
105408
|
exports.SUB_DETECTION_TYPES = SUB_DETECTION_TYPES;
|
|
105250
105409
|
exports.SYSTEM_CAP_NAMES = SYSTEM_CAP_NAMES;
|
|
105251
105410
|
exports.SYSTEM_SCOPE_DEVICE_METHODS = SYSTEM_SCOPE_DEVICE_METHODS;
|
|
@@ -105637,6 +105796,7 @@ exports.buildNcTaxonomy = buildNcTaxonomy;
|
|
|
105637
105796
|
exports.buildRoleScopes = buildRoleScopes;
|
|
105638
105797
|
exports.buildSecretRef = require_sleep.buildSecretRef;
|
|
105639
105798
|
exports.buildStreamParamsConfigSchema = buildStreamParamsConfigSchema;
|
|
105799
|
+
exports.buildSubjectPath = buildSubjectPath;
|
|
105640
105800
|
exports.buildVideoArgs = require_composition.buildVideoArgs;
|
|
105641
105801
|
exports.buildViewerLink = buildViewerLink;
|
|
105642
105802
|
exports.builtCapabilityBlockers = builtCapabilityBlockers;
|
|
@@ -105958,6 +106118,7 @@ exports.isDeviceConfigCap = require_sleep.isDeviceConfigCap;
|
|
|
105958
106118
|
exports.isDeviceScopedCap = require_sleep.isDeviceScopedCap;
|
|
105959
106119
|
exports.isDoActionActuatable = isDoActionActuatable;
|
|
105960
106120
|
exports.isDoPickerDeviceType = isDoPickerDeviceType;
|
|
106121
|
+
exports.isDrawableSubjectPath = isDrawableSubjectPath;
|
|
105961
106122
|
exports.isEmbeddingModelFailed = isEmbeddingModelFailed;
|
|
105962
106123
|
exports.isEvent = require_sleep.isEvent;
|
|
105963
106124
|
exports.isEventKindLabelKey = isEventKindLabelKey;
|
|
@@ -106171,6 +106332,7 @@ exports.pressureSensorCapability = pressureSensorCapability;
|
|
|
106171
106332
|
exports.principalMayReachAddon = principalMayReachAddon;
|
|
106172
106333
|
exports.privacyMaskCapability = privacyMaskCapability;
|
|
106173
106334
|
exports.procedureAuthKey = procedureAuthKey;
|
|
106335
|
+
exports.projectSubjectPath = projectSubjectPath;
|
|
106174
106336
|
exports.providerMotionEvent = providerMotionEvent;
|
|
106175
106337
|
exports.ptzAutotrackCapability = ptzAutotrackCapability;
|
|
106176
106338
|
exports.ptzCapability = ptzCapability;
|
|
@@ -106350,6 +106512,9 @@ exports.streamSignalsCapability = streamSignalsCapability;
|
|
|
106350
106512
|
exports.stripRetiredBandPreBufferSec = stripRetiredBandPreBufferSec;
|
|
106351
106513
|
exports.strippedOfLegacyReadOnly = strippedOfLegacyReadOnly;
|
|
106352
106514
|
exports.subKindsOf = subKindsOf;
|
|
106515
|
+
exports.subjectHasPath = subjectHasPath;
|
|
106516
|
+
exports.subjectPathSampleFromBox = subjectPathSampleFromBox;
|
|
106517
|
+
exports.subjectPathSvgPoints = subjectPathSvgPoints;
|
|
106353
106518
|
exports.summarisePrivacyAudio = summarisePrivacyAudio;
|
|
106354
106519
|
exports.summarizeEffectiveScope = summarizeEffectiveScope;
|
|
106355
106520
|
exports.summarizeReview = summarizeReview;
|