@camstack/addon-agent-ui 1.2.224 → 1.2.226

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/addon.js CHANGED
@@ -5388,7 +5388,7 @@ var ZodIssueCode = {
5388
5388
  var ZodFirstPartyTypeKind;
5389
5389
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5390
5390
  //#endregion
5391
- //#region ../types/dist/sleep-BjDbnx6-.mjs
5391
+ //#region ../types/dist/sleep-BBF1iXUi.mjs
5392
5392
  /**
5393
5393
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5394
5394
  * window to float samples (D455).
@@ -7989,6 +7989,21 @@ var ProfileRtspEntrySchema = object({
7989
7989
  codec: string().optional(),
7990
7990
  resolution: CamStreamResolutionSchema.optional()
7991
7991
  });
7992
+ /**
7993
+ * The model weight formats a catalog entry can ship. Schema-free on purpose:
7994
+ * `types/models.ts` builds Zod schemas from it, but a runner-closure consumer
7995
+ * (the model downloader in `@camstack/system`) needs only the list and must
7996
+ * reach it through `@camstack/types/addon` without building the cap schema
7997
+ * graph (D28).
7998
+ */
7999
+ var MODEL_FORMATS = [
8000
+ "onnx",
8001
+ "coreml",
8002
+ "openvino",
8003
+ "tflite",
8004
+ "pt",
8005
+ "gguf"
8006
+ ];
7992
8007
  object({
7993
8008
  field: string().min(1),
7994
8009
  on: literal("advance")
@@ -8480,7 +8495,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8480
8495
  return DeviceRole;
8481
8496
  }({});
8482
8497
  //#endregion
8483
- //#region ../types/dist/composition-Bzr7GwgP.mjs
8498
+ //#region ../types/dist/composition-C-WpVYTX.mjs
8484
8499
  /**
8485
8500
  * The ffmpeg build every CamStack node downloads, per platform and arch.
8486
8501
  *
@@ -8532,7 +8547,7 @@ var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFM
8532
8547
  function portable(target) {
8533
8548
  return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
8534
8549
  }
8535
- portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
8550
+ portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64"), `${BASE}${FFMPEG_RELEASE}`;
8536
8551
  /**
8537
8552
  * THE table "which boolean of this device type counts as HIGH", and the
8538
8553
  * pure evaluator of its EDGE.
@@ -10107,6 +10122,15 @@ discriminatedUnion("type", [
10107
10122
  message: string()
10108
10123
  })
10109
10124
  ]);
10125
+ /** Every status, in the order a summary lists them. */
10126
+ var RETRAIN_REVIEW_STATUSES = [
10127
+ "unchanged",
10128
+ "adjusted",
10129
+ "reclassified",
10130
+ "added",
10131
+ "removed",
10132
+ "model-error"
10133
+ ];
10110
10134
  /**
10111
10135
  * What the gate decided about a birth, from the CALLER's point of view.
10112
10136
  *
@@ -11724,14 +11748,6 @@ var MASK_SUPPLY_STEP_IDS = ["segmentation"];
11724
11748
  ...CROP_SUPPLY_STEP_IDS,
11725
11749
  ...MASK_SUPPLY_STEP_IDS
11726
11750
  ];
11727
- var MODEL_FORMATS = [
11728
- "onnx",
11729
- "coreml",
11730
- "openvino",
11731
- "tflite",
11732
- "pt",
11733
- "gguf"
11734
- ];
11735
11751
  /**
11736
11752
  * Multi-file format payload.
11737
11753
  *
@@ -13878,6 +13894,16 @@ var BrokerClientsSchema = object({
13878
13894
  /** Total encoded + raw-RTP callback subscriber count (sum of `encoded[].length`). */
13879
13895
  encodedSubscribers: number()
13880
13896
  });
13897
+ /**
13898
+ * How many people are watching one camera LIVE right now — see
13899
+ * `getLiveViewerCounts`. A camera with nobody watching is absent from the list
13900
+ * (so `viewers` is always at least 1): the listing carries the cameras that
13901
+ * have viewers, never a row of zeros.
13902
+ */
13903
+ var LiveViewerCountSchema = object({
13904
+ deviceId: number().int().nonnegative(),
13905
+ viewers: number().int().positive()
13906
+ });
13881
13907
  _enum([
13882
13908
  "reconnecting",
13883
13909
  "sleeping",
@@ -14238,7 +14264,7 @@ method(object({
14238
14264
  }), {
14239
14265
  kind: "mutation",
14240
14266
  auth: "admin"
14241
- }), method(object({ brokerId: string() }), BrokerClientsSchema), method(object({
14267
+ }), method(object({ brokerId: string() }), BrokerClientsSchema), method(_void(), array(LiveViewerCountSchema).readonly()), method(object({
14242
14268
  brokerId: string(),
14243
14269
  channel: _enum(["rtsp", "audio"]),
14244
14270
  handle: string()
@@ -15186,6 +15212,38 @@ var BulkRecordSchema = object({
15186
15212
  data: record(string(), unknown())
15187
15213
  });
15188
15214
  /**
15215
+ * One write inside a {@link settingsStoreCapability.methods.transact} call.
15216
+ * Only the three that a "replace this set, bump that revision" write needs.
15217
+ */
15218
+ var TransactOpSchema = discriminatedUnion("op", [
15219
+ object({
15220
+ op: literal("deleteWhere"),
15221
+ collection: string(),
15222
+ filter: MutationFilterSchema
15223
+ }),
15224
+ object({
15225
+ op: literal("insertMany"),
15226
+ collection: string(),
15227
+ records: array(BulkRecordSchema).readonly()
15228
+ }),
15229
+ object({
15230
+ op: literal("update"),
15231
+ collection: string(),
15232
+ id: string(),
15233
+ data: record(string(), unknown())
15234
+ })
15235
+ ]);
15236
+ /**
15237
+ * The precondition of a transaction: the row `key` of `collection` must hold
15238
+ * `equals` in `column`, read INSIDE the transaction.
15239
+ */
15240
+ var TransactGuardSchema = object({
15241
+ collection: string(),
15242
+ key: string(),
15243
+ column: string(),
15244
+ equals: unknown()
15245
+ });
15246
+ /**
15189
15247
  * Column declaration for a structured (SQL-backed) collection.
15190
15248
  *
15191
15249
  * Logical types — the backend translates each to the matching SQLite
@@ -15276,6 +15334,16 @@ id: union([string(), number()]) }), {
15276
15334
  }), object({ inserted: number().int() }), {
15277
15335
  kind: "mutation",
15278
15336
  auth: "admin"
15337
+ }), method(object({
15338
+ namespace: string().optional(),
15339
+ guard: TransactGuardSchema.optional(),
15340
+ ops: array(TransactOpSchema).readonly()
15341
+ }), object({
15342
+ applied: boolean(),
15343
+ current: unknown().optional()
15344
+ }), {
15345
+ kind: "mutation",
15346
+ auth: "admin"
15279
15347
  }), method(object({
15280
15348
  namespace: string().optional(),
15281
15349
  collection: string(),
@@ -15424,6 +15492,16 @@ id: union([string(), number()]) }), {
15424
15492
  }), object({ inserted: number().int() }), {
15425
15493
  kind: "mutation",
15426
15494
  auth: "admin"
15495
+ }), method(object({
15496
+ namespace: string().optional(),
15497
+ guard: TransactGuardSchema.optional(),
15498
+ ops: array(TransactOpSchema).readonly()
15499
+ }), object({
15500
+ applied: boolean(),
15501
+ current: unknown().optional()
15502
+ }), {
15503
+ kind: "mutation",
15504
+ auth: "admin"
15427
15505
  }), method(object({
15428
15506
  namespace: string().optional(),
15429
15507
  collection: string(),
@@ -23836,6 +23914,55 @@ var RetrainBboxSchema = object({
23836
23914
  h: number()
23837
23915
  });
23838
23916
  /**
23917
+ * What a box LOOKED LIKE when it was offered, snapshotted at the moment the
23918
+ * operator accepted it. The saved row keeps only the final rectangle; this is
23919
+ * the other half of the pair, and the pair is what says whether the operator
23920
+ * accepted, nudged, relabelled or replaced a proposal (`retrain-review.ts`).
23921
+ *
23922
+ * `ref` names the proposal inside its frame: `pipeline:<trackId>` for a box the
23923
+ * live pipeline drew, `assist:<n>` for the n-th box of a detector or LLM run,
23924
+ * and anything unique for an operator-drawn box (`operator:<n>`). Re-derived
23925
+ * pipeline boxes carry the same ref every time, which is what lets a dismissal
23926
+ * stay dismissed.
23927
+ */
23928
+ var RetrainOriginSchema = object({
23929
+ source: RetrainAnnotationSourceSchema,
23930
+ ref: string().min(1),
23931
+ bbox: RetrainBboxSchema,
23932
+ macroClass: RetrainMacroClassSchema,
23933
+ label: string().optional(),
23934
+ score: number().optional(),
23935
+ modelId: string().optional()
23936
+ });
23937
+ /**
23938
+ * A proposal the operator deleted. Durable on the frame, so a deleted pipeline
23939
+ * box does not come back as a draft every time the frame is reopened, and so
23940
+ * the deletion itself is a recorded fact rather than an absence.
23941
+ */
23942
+ var RetrainDismissedSchema = object({
23943
+ ref: string().min(1),
23944
+ snapshot: RetrainOriginSchema
23945
+ });
23946
+ /** What the operator did with a proposal (see `retrain-review.ts`). */
23947
+ var RetrainReviewStatusSchema = _enum(RETRAIN_REVIEW_STATUSES);
23948
+ var RetrainFrameReviewSchema = object({
23949
+ boxes: array(object({
23950
+ ref: string(),
23951
+ savedId: string().optional(),
23952
+ status: RetrainReviewStatusSchema,
23953
+ /** The baseline was re-derived, not snapshotted (a pre-origin row). */
23954
+ approximate: boolean(),
23955
+ origin: RetrainOriginSchema.optional(),
23956
+ iou: number().optional()
23957
+ })).readonly(),
23958
+ summary: object({
23959
+ counts: record(RetrainReviewStatusSchema, number().int()),
23960
+ total: number().int(),
23961
+ changed: number().int(),
23962
+ approximate: boolean()
23963
+ })
23964
+ });
23965
+ /**
23839
23966
  * One annotated subject.
23840
23967
  *
23841
23968
  * `bbox` is normalised against the full frame, ALWAYS. The per-model shapes
@@ -23859,6 +23986,9 @@ var RetrainAnnotationSchema = object({
23859
23986
  /** Which model proposed this box — or, on a `model_error`, drew the phantom. */
23860
23987
  assistModelId: string().optional(),
23861
23988
  assistScore: number().optional(),
23989
+ /** The proposal this box started from, snapshotted at accept time. `null` on
23990
+ * a row saved before origins existed; absent on a draft nobody snapshotted. */
23991
+ origin: RetrainOriginSchema.nullable().optional(),
23862
23992
  exportedInBatch: string().optional(),
23863
23993
  createdAt: number()
23864
23994
  });
@@ -23951,7 +24081,19 @@ var RetrainFrameSchema = object({
23951
24081
  * empty when nothing the track store holds can be placed on it. */
23952
24082
  pipelineBoxes: array(RetrainPipelineBoxSchema).readonly().optional(),
23953
24083
  /** The named collections this frame belongs to. Present on list answers. */
23954
- collectionIds: array(string()).readonly().optional()
24084
+ collectionIds: array(string()).readonly().optional(),
24085
+ /** Bumped by every annotation save. A save that names a stale
24086
+ * `expectedRevision` is refused, so two surfaces cannot overwrite each other. */
24087
+ revision: number().int().optional(),
24088
+ /** Proposals the operator deleted — never re-seeded as drafts. */
24089
+ dismissed: array(RetrainDismissedSchema).readonly().optional(),
24090
+ /** What the operator did with the proposals, computed by the server. */
24091
+ review: RetrainFrameReviewSchema.optional(),
24092
+ /** The frame's pixels over the admin data plane (full size). Fetch with the
24093
+ * admin bearer token; relative to the hub root. */
24094
+ imageUrl: string().optional(),
24095
+ /** The same, as a ~480 px thumbnail for grids. */
24096
+ thumbnailUrl: string().optional()
23955
24097
  });
23956
24098
  /** Why a copy-on-select could not be honoured — named, never a silent skip. */
23957
24099
  var RetrainCopyRefusalSchema = _enum([
@@ -25030,10 +25172,33 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
25030
25172
  /** The frame's track, when it has one — checked against the frame. */
25031
25173
  trackId: string().optional(),
25032
25174
  frameId: string(),
25033
- annotations: array(RetrainAnnotationDraftSchema)
25175
+ annotations: array(RetrainAnnotationDraftSchema),
25176
+ /** The frame's `revision` the caller saw. A different current revision
25177
+ * refuses the save with a `retrain-revision-conflict` error
25178
+ * (`parseRetrainRevisionConflict`). Absent ⇒ last write wins. */
25179
+ expectedRevision: number().int().optional(),
25180
+ /** The proposals the operator deleted. Absent ⇒ keep the frame's
25181
+ * current set; present ⇒ replace it (an empty array un-dismisses). */
25182
+ dismissed: array(RetrainDismissedSchema).optional()
25034
25183
  }), array(RetrainAnnotationSchema).readonly(), {
25035
25184
  kind: "mutation",
25036
25185
  auth: "admin"
25186
+ }), method(object({
25187
+ deviceId: number(),
25188
+ trackId: string().optional(),
25189
+ frameId: string(),
25190
+ expectedRevision: number().int().optional(),
25191
+ origins: array(object({
25192
+ annotationId: string(),
25193
+ origin: RetrainOriginSchema
25194
+ })).min(1).max(500)
25195
+ }), object({
25196
+ updated: number().int(),
25197
+ skipped: number().int(),
25198
+ revision: number().int()
25199
+ }), {
25200
+ kind: "mutation",
25201
+ auth: "admin"
25037
25202
  }), method(object({
25038
25203
  deviceId: number(),
25039
25204
  trackId: string()
@@ -27649,6 +27814,12 @@ var CameraStepOverridePatchSchema = object({
27649
27814
  settings: record(string(), unknown()).readonly().optional()
27650
27815
  });
27651
27816
  var CameraStepSettingsSchema = record(string(), record(string(), unknown()).readonly());
27817
+ /** The three root-detector choices a camera has (D507: at most one root). */
27818
+ var CameraDetectionSourceSchema = _enum([
27819
+ "pipeline",
27820
+ "onboard",
27821
+ "none"
27822
+ ]);
27652
27823
  var CameraPipelineSettingsSchema = object({
27653
27824
  stepToggles: record(string(), boolean()).optional(),
27654
27825
  stepSettings: CameraStepSettingsSchema.optional(),
@@ -28226,6 +28397,26 @@ method(object({
28226
28397
  }), object({ success: literal(true) }), {
28227
28398
  kind: "mutation",
28228
28399
  auth: "admin"
28400
+ }), method(object({ deviceId: number() }), object({
28401
+ source: CameraDetectionSourceSchema,
28402
+ explicit: boolean(),
28403
+ onboard: object({
28404
+ /** The camera has an onboard detector this system can read. */
28405
+ available: boolean(),
28406
+ /** What its detections carry — a camera with no box cannot be a root step. */
28407
+ geometry: _enum([
28408
+ "boxed",
28409
+ "while-streaming",
28410
+ "flag-only",
28411
+ "unknown"
28412
+ ])
28413
+ })
28414
+ })), method(object({
28415
+ deviceId: number(),
28416
+ source: CameraDetectionSourceSchema
28417
+ }), object({ success: literal(true) }), {
28418
+ kind: "mutation",
28419
+ auth: "admin"
28229
28420
  }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
28230
28421
  deviceId: number(),
28231
28422
  agentNodeId: string(),
@@ -31557,6 +31748,17 @@ var BatteryStatusSchema = object({
31557
31748
  */
31558
31749
  lastContactAt: number().optional(),
31559
31750
  /**
31751
+ * Ms epoch of the last COMPLETE scheduled refresh — every fact the provider
31752
+ * holds for the device (level, charging, link, device info, state slices) and
31753
+ * its cached snapshot, read together. Absent = never since this slice was
31754
+ * born. Unlike `lastUpdated` (any write, even a push of one field) this is
31755
+ * the staleness an operator can trust: the provider owns the device fact and
31756
+ * so owns the window on its age (D224, D847). Not a clock in the
31757
+ * runtime-state-durability sense: it moves once per window and must survive
31758
+ * a restart, or every restart would owe the camera a wake.
31759
+ */
31760
+ lastFullRefreshAt: number().optional(),
31761
+ /**
31560
31762
  * True when the source is a BINARY low-battery indicator (HA
31561
31763
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
31562
31764
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -34772,7 +34974,14 @@ var TopologyNodeSchema = object({
34772
34974
  * completed a mutual-TLS handshake, `clear` otherwise. Absent for the hub,
34773
34975
  * for offline rows and when the hub cannot tell.
34774
34976
  */
34775
- meshTransport: _enum(["tls", "clear"]).optional()
34977
+ meshTransport: _enum(["tls", "clear"]).optional(),
34978
+ /**
34979
+ * Whether the hub holds this online AGENT's capability registration (its
34980
+ * `registerNode` manifest). `false` = online in the mesh but not ready: the
34981
+ * hub dropped the registration on a disconnect only it saw and the agent has
34982
+ * not re-sent it. Absent for the hub and offline rows.
34983
+ */
34984
+ capabilitiesRegistered: boolean().optional()
34776
34985
  });
34777
34986
  var CapUsageEdgeSchema = object({
34778
34987
  callerAddonId: string(),
@@ -41310,6 +41519,12 @@ Object.freeze({
41310
41519
  addonId: null,
41311
41520
  access: "create"
41312
41521
  },
41522
+ "dataStoreProvider.transact": {
41523
+ capName: "data-store-provider",
41524
+ capScope: "system",
41525
+ addonId: null,
41526
+ access: "create"
41527
+ },
41313
41528
  "dataStoreProvider.update": {
41314
41529
  capName: "data-store-provider",
41315
41530
  capScope: "system",
@@ -44154,6 +44369,12 @@ Object.freeze({
44154
44369
  addonId: null,
44155
44370
  access: "create"
44156
44371
  },
44372
+ "pipelineAnalytics.backfillRetrainOrigins": {
44373
+ capName: "pipeline-analytics",
44374
+ capScope: "device",
44375
+ addonId: null,
44376
+ access: "create"
44377
+ },
44157
44378
  "pipelineAnalytics.cancelRelocateMedia": {
44158
44379
  capName: "pipeline-analytics",
44159
44380
  capScope: "device",
@@ -44934,6 +45155,12 @@ Object.freeze({
44934
45155
  addonId: null,
44935
45156
  access: "view"
44936
45157
  },
45158
+ "pipelineOrchestrator.getCameraDetectionSource": {
45159
+ capName: "pipeline-orchestrator",
45160
+ capScope: "system",
45161
+ addonId: null,
45162
+ access: "view"
45163
+ },
44937
45164
  "pipelineOrchestrator.getCameraMetrics": {
44938
45165
  capName: "pipeline-orchestrator",
44939
45166
  capScope: "system",
@@ -45162,6 +45389,12 @@ Object.freeze({
45162
45389
  addonId: null,
45163
45390
  access: "create"
45164
45391
  },
45392
+ "pipelineOrchestrator.setCameraDetectionSource": {
45393
+ capName: "pipeline-orchestrator",
45394
+ capScope: "system",
45395
+ addonId: null,
45396
+ access: "create"
45397
+ },
45165
45398
  "pipelineOrchestrator.setCameraPipelineForAgent": {
45166
45399
  capName: "pipeline-orchestrator",
45167
45400
  capScope: "system",
@@ -46134,6 +46367,12 @@ Object.freeze({
46134
46367
  addonId: null,
46135
46368
  access: "create"
46136
46369
  },
46370
+ "settingsStore.transact": {
46371
+ capName: "settings-store",
46372
+ capScope: "system",
46373
+ addonId: null,
46374
+ access: "create"
46375
+ },
46137
46376
  "settingsStore.update": {
46138
46377
  capName: "settings-store",
46139
46378
  capScope: "system",
@@ -46614,6 +46853,12 @@ Object.freeze({
46614
46853
  addonId: null,
46615
46854
  access: "view"
46616
46855
  },
46856
+ "streamBroker.getLiveViewerCounts": {
46857
+ capName: "stream-broker",
46858
+ capScope: "system",
46859
+ addonId: null,
46860
+ access: "view"
46861
+ },
46617
46862
  "streamBroker.getPreBufferInfo": {
46618
46863
  capName: "stream-broker",
46619
46864
  capScope: "system",
@@ -50454,6 +50699,11 @@ Object.freeze({
50454
50699
  form: "single",
50455
50700
  optional: false
50456
50701
  }],
50702
+ "pipelineAnalytics.backfillRetrainOrigins": [{
50703
+ name: "deviceId",
50704
+ form: "single",
50705
+ optional: false
50706
+ }],
50457
50707
  "pipelineAnalytics.clearTrackRelation": [{
50458
50708
  name: "deviceId",
50459
50709
  form: "single",
@@ -50759,6 +51009,11 @@ Object.freeze({
50759
51009
  form: "single",
50760
51010
  optional: false
50761
51011
  }],
51012
+ "pipelineOrchestrator.getCameraDetectionSource": [{
51013
+ name: "deviceId",
51014
+ form: "single",
51015
+ optional: false
51016
+ }],
50762
51017
  "pipelineOrchestrator.getCameraMetrics": [{
50763
51018
  name: "deviceId",
50764
51019
  form: "single",
@@ -50819,6 +51074,11 @@ Object.freeze({
50819
51074
  form: "single",
50820
51075
  optional: false
50821
51076
  }],
51077
+ "pipelineOrchestrator.setCameraDetectionSource": [{
51078
+ name: "deviceId",
51079
+ form: "single",
51080
+ optional: false
51081
+ }],
50822
51082
  "pipelineOrchestrator.setCameraPipelineForAgent": [{
50823
51083
  name: "deviceId",
50824
51084
  form: "single",
@@ -52817,7 +53077,7 @@ var AgentUIAddon = class extends BaseAddon {
52817
53077
  capability: adminUiCapability,
52818
53078
  provider: {
52819
53079
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
52820
- getVersion: async () => ({ version: "1.2.224" })
53080
+ getVersion: async () => ({ version: "1.2.226" })
52821
53081
  }
52822
53082
  }];
52823
53083
  }
@@ -1 +1 @@
1
- import{n as e,o as t,r as n}from"./scheduler-mSeF_D00.js";import{C as r,c as i,d as a,h as o,l as s,p as c,t as l}from"./index-kzmpb15D.js";import{MaskShapeCanvas as u}from"./MaskShapeCanvas-u4Ka7P5T.js";var d=r(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),f=t(n(),1),p=t(e(),1),m=110,h=`motion-zones`,g=0,_=[1,2,3],v=1,y=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,b=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function x(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function S(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function C(e,t){return Math.ceil(e/t)}function w(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function T(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+w(n,t,i)]=!0)}return o}function E(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+w(n,t,i)]===!0}return o}function D({deviceId:e}){let t=a(o().trpcClient,e),[n,r]=(0,f.useState)(null),[w,D]=(0,f.useState)(!1),[O,k]=(0,f.useState)(null),[A,j]=(0,f.useState)(null),[M,N]=(0,f.useState)(v),[P,F]=(0,f.useState)(!1),[I,L]=(0,f.useState)(!1),R=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!t)return;let e=!1;return R.current=!1,r(null),D(!1),k(null),j(null),N(v),L(!1),(async()=>{try{let n=await t.motionZones?.getOptions({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(r(n),R.current)return;let i=await t.motionZones?.getStatus({});if(e)return;if(!i)throw Error(`device proxy not ready`);R.current=!0;let a=i.regions.find(e=>e.shape.kind===`grid`),o=x(a?a.shape.cells:[],n.grid.width,n.grid.height);j(o),k(T(o,n.grid.width,n.grid.height,v))}catch(t){if(e)return;s(t)?D(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[t]);let z=n?n.grid.width:0,B=n?n.grid.height:0,V=C(z,M),H=C(B,M),U=(0,f.useMemo)(()=>O&&n?E(O,z,B,M):null,[O,n,z,B,M]),W=(0,f.useMemo)(()=>U!==null&&A!==null&&!S(U,A),[U,A]),G=(0,f.useMemo)(()=>U?U.reduce((e,t)=>t?e+1:e,0):0,[U]),K=z*B,q=V*H,J=(0,f.useCallback)(e=>{q>0&&k(Array.from({length:q},()=>e))},[q]),Y=(0,f.useCallback)(()=>{k(e=>e&&e.map(e=>!e))},[]),X=(0,f.useCallback)(()=>{A&&n&&k(T(A,z,B,M))},[A,n,z,B,M]),Z=(0,f.useCallback)(e=>{e===M||!n||k(t=>{if(!t)return N(e),t;let n=T(E(t,z,B,M),z,B,e);return N(e),n})},[M,n,z,B]),Q=(0,f.useMemo)(()=>n&&O?[{id:g,shape:{kind:`grid`,gridWidth:V,gridHeight:H,cells:[...O]}}]:[],[n,O,V,H]),$=(0,f.useCallback)((e,t)=>{t.kind===`grid`&&k(t.cells)},[]),ee=(0,f.useCallback)(async()=>{if(!(!t||!O||!n)){F(!0);try{let e=E(O,n.grid.width,n.grid.height,M),r={kind:`grid`,gridWidth:n.grid.width,gridHeight:n.grid.height,cells:e};await t.motionZones?.setZone({patch:{regions:[{id:g,enabled:!0,shape:r}]}});let i=await t.motionZones?.getStatus({});if(i){let e=i.regions.find(e=>e.shape.kind===`grid`),t=x(e?e.shape.cells:[],n.grid.width,n.grid.height);j(t),k(T(t,n.grid.width,n.grid.height,M))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{F(!1)}}},[t,O,n,M]);l((0,f.useMemo)(()=>I&&!w&&n&&O?{id:h,order:m,node:(0,p.jsx)(u,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:V,height:H},selectedId:g,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[I,w,n,O,Q,$,V,H]));let te=!w&&n!==null&&O!==null;return t?(0,p.jsx)(c,{title:`Motion Zones`,icon:(0,p.jsx)(d,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,p.jsx)(`div`,{className:`flex flex-col gap-3`,children:w?(0,p.jsx)(`p`,{className:`${i} leading-relaxed`,children:`This camera doesn't expose an on-board motion zones grid.`}):te?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`p`,{className:`${i} leading-relaxed`,children:[`Toggle `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Edit grid`}),` to paint the region directly on the live frame, then `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Save`}),` to push the mask to the camera. Pick a bigger`,` `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Cell size`}),` for quicker, broad-stroke painting — it's resampled to the camera's native `,n.grid.width,`×`,n.grid.height,` grid on save (×1 is the finest).`]}),(0,p.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,p.jsx)(`button`,{type:`button`,onClick:()=>L(e=>!e),disabled:P,"aria-pressed":I,className:I?b:y,children:I?`Done editing`:`Edit grid`}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>J(!0),disabled:P,className:y,children:`All on`}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>J(!1),disabled:P,className:y,children:`All off`}),(0,p.jsx)(`button`,{type:`button`,onClick:Y,disabled:P,className:y,children:`Invert`}),(0,p.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":`Cell size`,children:[(0,p.jsx)(`span`,{className:`${i} mr-0.5`,children:`Cell size`}),_.map(e=>(0,p.jsxs)(`button`,{type:`button`,onClick:()=>Z(e),disabled:P,"aria-pressed":M===e,title:e===1?`Camera grid ${z}×${B} (finest)`:`${C(z,e)}×${C(B,e)} painting grid · cells ×${e} bigger`,className:M===e?b:y,children:[`×`,e]},e))]}),(0,p.jsxs)(`span`,{className:`${i} ml-1 tabular-nums`,children:[G,` / `,K,` cells · `,n.grid.width,`×`,n.grid.height,M===1?``:` · paint ${V}×${H}`]}),(0,p.jsx)(`span`,{className:`flex-1`}),(0,p.jsx)(`button`,{type:`button`,onClick:X,disabled:P||!W,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:`Revert`}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>void ee(),disabled:P||!W,className:b,children:P?`Saving…`:`Save`})]})]}):(0,p.jsx)(`p`,{className:`${i} leading-relaxed`,children:`Loading the camera's grid…`})})}):null}export{D as MotionZonesSettings};
1
+ import{n as e,o as t,r as n}from"./scheduler-mSeF_D00.js";import{C as r,c as i,d as a,h as o,l as s,p as c,t as l}from"./index-DD7GAk3Q.js";import{MaskShapeCanvas as u}from"./MaskShapeCanvas-u4Ka7P5T.js";var d=r(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),f=t(n(),1),p=t(e(),1),m=110,h=`motion-zones`,g=0,_=[1,2,3],v=1,y=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,b=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function x(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function S(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function C(e,t){return Math.ceil(e/t)}function w(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function T(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+w(n,t,i)]=!0)}return o}function E(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+w(n,t,i)]===!0}return o}function D({deviceId:e}){let t=a(o().trpcClient,e),[n,r]=(0,f.useState)(null),[w,D]=(0,f.useState)(!1),[O,k]=(0,f.useState)(null),[A,j]=(0,f.useState)(null),[M,N]=(0,f.useState)(v),[P,F]=(0,f.useState)(!1),[I,L]=(0,f.useState)(!1),R=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!t)return;let e=!1;return R.current=!1,r(null),D(!1),k(null),j(null),N(v),L(!1),(async()=>{try{let n=await t.motionZones?.getOptions({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(r(n),R.current)return;let i=await t.motionZones?.getStatus({});if(e)return;if(!i)throw Error(`device proxy not ready`);R.current=!0;let a=i.regions.find(e=>e.shape.kind===`grid`),o=x(a?a.shape.cells:[],n.grid.width,n.grid.height);j(o),k(T(o,n.grid.width,n.grid.height,v))}catch(t){if(e)return;s(t)?D(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[t]);let z=n?n.grid.width:0,B=n?n.grid.height:0,V=C(z,M),H=C(B,M),U=(0,f.useMemo)(()=>O&&n?E(O,z,B,M):null,[O,n,z,B,M]),W=(0,f.useMemo)(()=>U!==null&&A!==null&&!S(U,A),[U,A]),G=(0,f.useMemo)(()=>U?U.reduce((e,t)=>t?e+1:e,0):0,[U]),K=z*B,q=V*H,J=(0,f.useCallback)(e=>{q>0&&k(Array.from({length:q},()=>e))},[q]),Y=(0,f.useCallback)(()=>{k(e=>e&&e.map(e=>!e))},[]),X=(0,f.useCallback)(()=>{A&&n&&k(T(A,z,B,M))},[A,n,z,B,M]),Z=(0,f.useCallback)(e=>{e===M||!n||k(t=>{if(!t)return N(e),t;let n=T(E(t,z,B,M),z,B,e);return N(e),n})},[M,n,z,B]),Q=(0,f.useMemo)(()=>n&&O?[{id:g,shape:{kind:`grid`,gridWidth:V,gridHeight:H,cells:[...O]}}]:[],[n,O,V,H]),$=(0,f.useCallback)((e,t)=>{t.kind===`grid`&&k(t.cells)},[]),ee=(0,f.useCallback)(async()=>{if(!(!t||!O||!n)){F(!0);try{let e=E(O,n.grid.width,n.grid.height,M),r={kind:`grid`,gridWidth:n.grid.width,gridHeight:n.grid.height,cells:e};await t.motionZones?.setZone({patch:{regions:[{id:g,enabled:!0,shape:r}]}});let i=await t.motionZones?.getStatus({});if(i){let e=i.regions.find(e=>e.shape.kind===`grid`),t=x(e?e.shape.cells:[],n.grid.width,n.grid.height);j(t),k(T(t,n.grid.width,n.grid.height,M))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{F(!1)}}},[t,O,n,M]);l((0,f.useMemo)(()=>I&&!w&&n&&O?{id:h,order:m,node:(0,p.jsx)(u,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:V,height:H},selectedId:g,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[I,w,n,O,Q,$,V,H]));let te=!w&&n!==null&&O!==null;return t?(0,p.jsx)(c,{title:`Motion Zones`,icon:(0,p.jsx)(d,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,p.jsx)(`div`,{className:`flex flex-col gap-3`,children:w?(0,p.jsx)(`p`,{className:`${i} leading-relaxed`,children:`This camera doesn't expose an on-board motion zones grid.`}):te?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`p`,{className:`${i} leading-relaxed`,children:[`Toggle `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Edit grid`}),` to paint the region directly on the live frame, then `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Save`}),` to push the mask to the camera. Pick a bigger`,` `,(0,p.jsx)(`strong`,{className:`text-foreground`,children:`Cell size`}),` for quicker, broad-stroke painting — it's resampled to the camera's native `,n.grid.width,`×`,n.grid.height,` grid on save (×1 is the finest).`]}),(0,p.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,p.jsx)(`button`,{type:`button`,onClick:()=>L(e=>!e),disabled:P,"aria-pressed":I,className:I?b:y,children:I?`Done editing`:`Edit grid`}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>J(!0),disabled:P,className:y,children:`All on`}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>J(!1),disabled:P,className:y,children:`All off`}),(0,p.jsx)(`button`,{type:`button`,onClick:Y,disabled:P,className:y,children:`Invert`}),(0,p.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":`Cell size`,children:[(0,p.jsx)(`span`,{className:`${i} mr-0.5`,children:`Cell size`}),_.map(e=>(0,p.jsxs)(`button`,{type:`button`,onClick:()=>Z(e),disabled:P,"aria-pressed":M===e,title:e===1?`Camera grid ${z}×${B} (finest)`:`${C(z,e)}×${C(B,e)} painting grid · cells ×${e} bigger`,className:M===e?b:y,children:[`×`,e]},e))]}),(0,p.jsxs)(`span`,{className:`${i} ml-1 tabular-nums`,children:[G,` / `,K,` cells · `,n.grid.width,`×`,n.grid.height,M===1?``:` · paint ${V}×${H}`]}),(0,p.jsx)(`span`,{className:`flex-1`}),(0,p.jsx)(`button`,{type:`button`,onClick:X,disabled:P||!W,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:`Revert`}),(0,p.jsx)(`button`,{type:`button`,onClick:()=>void ee(),disabled:P||!W,className:b,children:P?`Saving…`:`Save`})]})]}):(0,p.jsx)(`p`,{className:`${i} leading-relaxed`,children:`Loading the camera's grid…`})})}):null}export{D as MotionZonesSettings};
@@ -1 +1 @@
1
- import{n as e,o as t,r as n}from"./scheduler-mSeF_D00.js";import{C as r,_ as i,c as a,d as o,g as ee,h as te,l as s,p as c,t as l,x as ne,y as u}from"./index-kzmpb15D.js";import{MaskShapeCanvas as re}from"./MaskShapeCanvas-u4Ka7P5T.js";var ie=r(`hexagon`,[[`path`,{d:`M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z`,key:`yt0hxn`}]]),d=r(`mic-off`,[[`path`,{d:`M12 19v3`,key:`npa21l`}],[`path`,{d:`M15 9.34V5a3 3 0 0 0-5.68-1.33`,key:`1gzdoj`}],[`path`,{d:`M16.95 16.95A7 7 0 0 1 5 12v-2`,key:`cqa7eg`}],[`path`,{d:`M18.89 13.23A7 7 0 0 0 19 12v-2`,key:`16hl24`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M9 9v3a3 3 0 0 0 5.12 2.12`,key:`r2i35w`}]]),f=t(n(),1),p=`Microphone`,m=`The camera encodes no audio at all — live view, recordings, exports and the audio analyzer are all silent.`;function h(e){let t=e.supportsAudioMute===!0,n=t?e.audioEnabled:null,r=e.pendingOn??null;return{label:p,supported:t,on:n===null?null:r??n,available:t&&n!==null,costWhenOff:m}}function ae(e){return!e.available||e.on===null?null:{enabled:!e.on}}var g=t(e(),1),oe=120,_=`privacy-mask`,v=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,y=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function b(e){return e.kind===`rect`||e.kind===`polygon`?e:null}function se(e){let t=new Set(e.map(e=>e.id)),n=0;for(;t.has(n);)n+=1;return n}function ce(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(JSON.stringify(e[n])!==JSON.stringify(t[n]))return!1;return!0}function x({deviceId:e}){let t=o(te().trpcClient,e),[n,r]=(0,f.useState)(null),[p,m]=(0,f.useState)(!1),[x,S]=(0,f.useState)(!1),[C,w]=(0,f.useState)(null),[T,E]=(0,f.useState)(null),[D,O]=(0,f.useState)(!1),[k,A]=(0,f.useState)(!1),[j,M]=(0,f.useState)(null),[N,P]=(0,f.useState)(null),[le,F]=(0,f.useState)(null),[I,L]=(0,f.useState)(null),[R,z]=(0,f.useState)(null),B=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!t)return;let e=!1;return B.current=!1,r(null),m(!1),S(!1),w(null),E(null),A(!1),M(null),P(null),F(null),L(null),z(null),(async()=>{try{let n=await t.privacyMask?.getOptions({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(r(n),B.current)return;let i=await t.privacyMask?.getStatus({});if(e)return;if(!i)throw Error(`device proxy not ready`);if(i.regions===null){S(!0),F(i.audioEnabled);return}B.current=!0;let a={enabled:i.enabled,regions:i.regions};E(a),w(a),F(i.audioEnabled)}catch(t){if(e)return;s(t)?m(!0):console.error(`Privacy Mask load failed`,t)}})(),()=>{e=!0}},[t]);let V=(0,f.useMemo)(()=>C!==null&&T!==null&&(C.enabled!==T.enabled||!ce(C.regions,T.regions)),[C,T]),H=C?C.regions.length:0,U=n?n.maxRegions:0,W=(0,f.useRef)(0);(0,f.useEffect)(()=>{W.current=U},[U]);let G=U>0&&H>=U,ue=(0,f.useCallback)(()=>{w(e=>e&&{...e,enabled:!e.enabled})},[]),K=(0,f.useCallback)(e=>{M(typeof e==`number`?e:null)},[]),q=(0,f.useMemo)(()=>C?C.regions.map(e=>({id:e.id,shape:e.shape,enabled:e.enabled,label:`Zone ${String(e.id)}`})):[],[C]),J=(0,f.useCallback)((e,t)=>{let n=b(t);n&&w(t=>t&&{...t,regions:t.regions.map(t=>t.id===e?{...t,shape:n}:t)})},[]),Y=(0,f.useCallback)(e=>{let t=b(e);t&&(P(null),w(e=>{if(!e)return e;let n=W.current;if(n>0&&e.regions.length>=n)return e;let r=se(e.regions),i={id:r,enabled:!0,shape:t};return M(r),{...e,regions:[...e.regions,i]}}))},[]),X=(0,f.useCallback)(e=>{A(!0),M(null),P(e)},[]),de=(0,f.useCallback)(e=>{A(!0),P(null),M(e)},[]),fe=(0,f.useCallback)(e=>{M(t=>t===e?null:t),w(t=>t&&{...t,regions:t.regions.filter(t=>t.id!==e)})},[]),pe=(0,f.useCallback)(()=>{P(null),M(null),T&&w({enabled:T.enabled,regions:T.regions})},[T]),me=(0,f.useCallback)(async()=>{if(!(!t||!C)){O(!0);try{await t.privacyMask?.setMask({patch:{enabled:C.enabled,regions:[...C.regions]}});let e=await t.privacyMask?.getStatus({});if(e&&e.regions!==null){let t={enabled:e.enabled,regions:e.regions};E(t),w(t)}}catch(e){console.error(`Privacy Mask save failed`,e)}finally{O(!1)}}},[t,C]),Z=h({supportsAudioMute:n===null?null:n.supportsAudioMute,audioEnabled:le,pendingOn:I}),he=(0,f.useCallback)(async()=>{let e=ae(Z);if(!(!t||e===null)){L(e.enabled),z(null);try{await t.privacyMask?.setAudioEnabled(e);let n=await t.privacyMask?.getStatus({});n&&F(n.audioEnabled)}catch(e){console.error(`Privacy microphone write failed — reverting`,e),z(e instanceof Error?e.message:String(e))}finally{L(null)}}},[t,Z]),ge=(0,f.useCallback)(()=>{A(e=>(e&&(P(null),M(null)),!e))},[]),Q=n?.supportedShapes??[];l((0,f.useMemo)(()=>k&&!p&&n&&C?{id:_,order:oe,node:(0,g.jsx)(re,{transparent:!0,items:q,supportedShapes:Q,polygonVertices:n.polygonVertices,selectedId:j,onSelect:K,onShapeChange:J,onDrawComplete:Y,drawingKind:N})}:null,[k,p,n,C,q,Q,j,K,J,Y,N]));let _e=n?.supportedShapes.includes(`rect`)??!1,ve=n?.supportedShapes.includes(`polygon`)??!1,$=n!==null&&(n.maxRegions<=0||n.supportedShapes.length===0),ye=!p&&!$&&!x&&n!==null&&C!==null;return t?(0,g.jsx)(c,{title:`Privacy Mask`,icon:(0,g.jsx)(ne,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,g.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[p||$?(0,g.jsx)(`p`,{className:`${a} leading-relaxed`,children:`This camera doesn't support an on-board privacy mask.`}):x?(0,g.jsx)(`p`,{className:`${a} leading-relaxed`,children:`This camera hasn't answered, so what it currently masks is unknown — the editor stays closed rather than showing an empty frame you could save over. A battery camera answers once it wakes; otherwise check that it is reachable.`}):ye?(0,g.jsxs)(g.Fragment,{children:[(0,g.jsxs)(`p`,{className:`${a} leading-relaxed`,children:[`Toggle `,(0,g.jsx)(`strong`,{className:`text-foreground`,children:`Edit mask`}),` to draw blanked-out zones on the live frame, then `,(0,g.jsx)(`strong`,{className:`text-foreground`,children:`Save`}),` to push them to the camera. Drag a rectangle to move, its corner to resize; drag polygon vertices, click an edge midpoint to add one, or right-click a vertex to remove it.`]}),(0,g.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,g.jsx)(`button`,{type:`button`,onClick:ge,disabled:D,"aria-pressed":k,className:k?y:v,children:k?`Done editing`:`Edit mask`}),(0,g.jsx)(`button`,{type:`button`,onClick:ue,disabled:D,"aria-pressed":C.enabled,className:C.enabled?y:v,children:C.enabled?`Mask on`:`Mask off`}),_e&&(0,g.jsx)(`button`,{type:`button`,onClick:()=>X(`rect`),disabled:D||G,"aria-pressed":N===`rect`,className:N===`rect`?y:v,title:G?`Maximum zones reached`:`Add a rectangle zone`,children:`+ Rect`}),ve&&(0,g.jsx)(`button`,{type:`button`,onClick:()=>X(`polygon`),disabled:D||G,"aria-pressed":N===`polygon`,className:N===`polygon`?y:v,title:G?`Maximum zones reached`:`Add a polygon zone`,children:`+ Polygon`}),(0,g.jsxs)(`span`,{className:`${a} ml-1 tabular-nums`,children:[H,` / `,U,` zones`]}),(0,g.jsx)(`span`,{className:`flex-1`}),(0,g.jsx)(`button`,{type:`button`,onClick:pe,disabled:D||!V,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:`Revert`}),(0,g.jsx)(`button`,{type:`button`,onClick:()=>void me(),disabled:D||!V,className:y,children:D?`Saving…`:`Save`})]}),H>0?(0,g.jsx)(`div`,{className:`flex flex-col gap-1`,children:C.regions.map(e=>{let t=j===e.id,n=e.shape.kind===`polygon`?ie:i;return(0,g.jsxs)(`div`,{className:`flex items-center gap-2 rounded-md border px-2 py-1 transition-colors ${t?`border-primary/50 bg-primary/10`:`border-border bg-surface`}`,children:[(0,g.jsxs)(`button`,{type:`button`,onClick:()=>de(e.id),disabled:D,className:`flex flex-1 items-center gap-2 text-left text-[11px] font-medium text-foreground-subtle hover:text-foreground disabled:opacity-40 transition-colors`,children:[(0,g.jsx)(n,{className:`h-3.5 w-3.5 shrink-0`}),(0,g.jsxs)(`span`,{children:[`Zone `,e.id]}),(0,g.jsx)(`span`,{className:`text-foreground-faint capitalize`,children:e.shape.kind})]}),(0,g.jsx)(`button`,{type:`button`,onClick:()=>fe(e.id),disabled:D,"aria-label":`Delete zone ${String(e.id)}`,title:`Delete zone`,className:`inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:border-red-400/40 hover:bg-red-500/10 hover:text-red-400 disabled:opacity-40 transition-colors`,children:(0,g.jsx)(ee,{className:`h-3.5 w-3.5`})})]},e.id)})}):null]}):(0,g.jsx)(`p`,{className:`${a} leading-relaxed`,children:`Loading the camera's privacy mask…`}),Z.supported&&(0,g.jsxs)(`div`,{className:`flex items-start gap-3 rounded-md border border-border bg-surface px-2 py-2`,children:[(0,g.jsx)(`button`,{type:`button`,role:`switch`,"aria-label":Z.label,"aria-checked":Z.on===!0,disabled:!Z.available||I!==null,onClick:()=>void he(),title:Z.on===null?`Microphone — state unknown; the camera did not answer.`:Z.on?`The camera is capturing sound. Turning it off: ${Z.costWhenOff}`:`The camera is silent. ${Z.costWhenOff}`,className:Z.on===!1||Z.on===null?v:y,children:(0,g.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[Z.on===!1?(0,g.jsx)(d,{className:`h-3.5 w-3.5`}):(0,g.jsx)(u,{className:`h-3.5 w-3.5`}),Z.on===null?`Microphone unknown`:Z.on?`Microphone on`:`Microphone off`]})}),(0,g.jsx)(`p`,{className:`text-[10px] text-foreground-subtle flex-1 leading-relaxed`,children:R===null?Z.on===null?`The camera did not report its microphone, so this state is unknown.`:Z.costWhenOff:`Write failed: ${R}`})]})]})}):null}export{x as PrivacyMaskSettings};
1
+ import{n as e,o as t,r as n}from"./scheduler-mSeF_D00.js";import{C as r,_ as i,c as a,d as o,g as ee,h as te,l as s,p as c,t as l,x as ne,y as u}from"./index-DD7GAk3Q.js";import{MaskShapeCanvas as re}from"./MaskShapeCanvas-u4Ka7P5T.js";var ie=r(`hexagon`,[[`path`,{d:`M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z`,key:`yt0hxn`}]]),d=r(`mic-off`,[[`path`,{d:`M12 19v3`,key:`npa21l`}],[`path`,{d:`M15 9.34V5a3 3 0 0 0-5.68-1.33`,key:`1gzdoj`}],[`path`,{d:`M16.95 16.95A7 7 0 0 1 5 12v-2`,key:`cqa7eg`}],[`path`,{d:`M18.89 13.23A7 7 0 0 0 19 12v-2`,key:`16hl24`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M9 9v3a3 3 0 0 0 5.12 2.12`,key:`r2i35w`}]]),f=t(n(),1),p=`Microphone`,m=`The camera encodes no audio at all — live view, recordings, exports and the audio analyzer are all silent.`;function h(e){let t=e.supportsAudioMute===!0,n=t?e.audioEnabled:null,r=e.pendingOn??null;return{label:p,supported:t,on:n===null?null:r??n,available:t&&n!==null,costWhenOff:m}}function ae(e){return!e.available||e.on===null?null:{enabled:!e.on}}var g=t(e(),1),oe=120,_=`privacy-mask`,v=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,y=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function b(e){return e.kind===`rect`||e.kind===`polygon`?e:null}function se(e){let t=new Set(e.map(e=>e.id)),n=0;for(;t.has(n);)n+=1;return n}function ce(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(JSON.stringify(e[n])!==JSON.stringify(t[n]))return!1;return!0}function x({deviceId:e}){let t=o(te().trpcClient,e),[n,r]=(0,f.useState)(null),[p,m]=(0,f.useState)(!1),[x,S]=(0,f.useState)(!1),[C,w]=(0,f.useState)(null),[T,E]=(0,f.useState)(null),[D,O]=(0,f.useState)(!1),[k,A]=(0,f.useState)(!1),[j,M]=(0,f.useState)(null),[N,P]=(0,f.useState)(null),[le,F]=(0,f.useState)(null),[I,L]=(0,f.useState)(null),[R,z]=(0,f.useState)(null),B=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!t)return;let e=!1;return B.current=!1,r(null),m(!1),S(!1),w(null),E(null),A(!1),M(null),P(null),F(null),L(null),z(null),(async()=>{try{let n=await t.privacyMask?.getOptions({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(r(n),B.current)return;let i=await t.privacyMask?.getStatus({});if(e)return;if(!i)throw Error(`device proxy not ready`);if(i.regions===null){S(!0),F(i.audioEnabled);return}B.current=!0;let a={enabled:i.enabled,regions:i.regions};E(a),w(a),F(i.audioEnabled)}catch(t){if(e)return;s(t)?m(!0):console.error(`Privacy Mask load failed`,t)}})(),()=>{e=!0}},[t]);let V=(0,f.useMemo)(()=>C!==null&&T!==null&&(C.enabled!==T.enabled||!ce(C.regions,T.regions)),[C,T]),H=C?C.regions.length:0,U=n?n.maxRegions:0,W=(0,f.useRef)(0);(0,f.useEffect)(()=>{W.current=U},[U]);let G=U>0&&H>=U,ue=(0,f.useCallback)(()=>{w(e=>e&&{...e,enabled:!e.enabled})},[]),K=(0,f.useCallback)(e=>{M(typeof e==`number`?e:null)},[]),q=(0,f.useMemo)(()=>C?C.regions.map(e=>({id:e.id,shape:e.shape,enabled:e.enabled,label:`Zone ${String(e.id)}`})):[],[C]),J=(0,f.useCallback)((e,t)=>{let n=b(t);n&&w(t=>t&&{...t,regions:t.regions.map(t=>t.id===e?{...t,shape:n}:t)})},[]),Y=(0,f.useCallback)(e=>{let t=b(e);t&&(P(null),w(e=>{if(!e)return e;let n=W.current;if(n>0&&e.regions.length>=n)return e;let r=se(e.regions),i={id:r,enabled:!0,shape:t};return M(r),{...e,regions:[...e.regions,i]}}))},[]),X=(0,f.useCallback)(e=>{A(!0),M(null),P(e)},[]),de=(0,f.useCallback)(e=>{A(!0),P(null),M(e)},[]),fe=(0,f.useCallback)(e=>{M(t=>t===e?null:t),w(t=>t&&{...t,regions:t.regions.filter(t=>t.id!==e)})},[]),pe=(0,f.useCallback)(()=>{P(null),M(null),T&&w({enabled:T.enabled,regions:T.regions})},[T]),me=(0,f.useCallback)(async()=>{if(!(!t||!C)){O(!0);try{await t.privacyMask?.setMask({patch:{enabled:C.enabled,regions:[...C.regions]}});let e=await t.privacyMask?.getStatus({});if(e&&e.regions!==null){let t={enabled:e.enabled,regions:e.regions};E(t),w(t)}}catch(e){console.error(`Privacy Mask save failed`,e)}finally{O(!1)}}},[t,C]),Z=h({supportsAudioMute:n===null?null:n.supportsAudioMute,audioEnabled:le,pendingOn:I}),he=(0,f.useCallback)(async()=>{let e=ae(Z);if(!(!t||e===null)){L(e.enabled),z(null);try{await t.privacyMask?.setAudioEnabled(e);let n=await t.privacyMask?.getStatus({});n&&F(n.audioEnabled)}catch(e){console.error(`Privacy microphone write failed — reverting`,e),z(e instanceof Error?e.message:String(e))}finally{L(null)}}},[t,Z]),ge=(0,f.useCallback)(()=>{A(e=>(e&&(P(null),M(null)),!e))},[]),Q=n?.supportedShapes??[];l((0,f.useMemo)(()=>k&&!p&&n&&C?{id:_,order:oe,node:(0,g.jsx)(re,{transparent:!0,items:q,supportedShapes:Q,polygonVertices:n.polygonVertices,selectedId:j,onSelect:K,onShapeChange:J,onDrawComplete:Y,drawingKind:N})}:null,[k,p,n,C,q,Q,j,K,J,Y,N]));let _e=n?.supportedShapes.includes(`rect`)??!1,ve=n?.supportedShapes.includes(`polygon`)??!1,$=n!==null&&(n.maxRegions<=0||n.supportedShapes.length===0),ye=!p&&!$&&!x&&n!==null&&C!==null;return t?(0,g.jsx)(c,{title:`Privacy Mask`,icon:(0,g.jsx)(ne,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,g.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[p||$?(0,g.jsx)(`p`,{className:`${a} leading-relaxed`,children:`This camera doesn't support an on-board privacy mask.`}):x?(0,g.jsx)(`p`,{className:`${a} leading-relaxed`,children:`This camera hasn't answered, so what it currently masks is unknown — the editor stays closed rather than showing an empty frame you could save over. A battery camera answers once it wakes; otherwise check that it is reachable.`}):ye?(0,g.jsxs)(g.Fragment,{children:[(0,g.jsxs)(`p`,{className:`${a} leading-relaxed`,children:[`Toggle `,(0,g.jsx)(`strong`,{className:`text-foreground`,children:`Edit mask`}),` to draw blanked-out zones on the live frame, then `,(0,g.jsx)(`strong`,{className:`text-foreground`,children:`Save`}),` to push them to the camera. Drag a rectangle to move, its corner to resize; drag polygon vertices, click an edge midpoint to add one, or right-click a vertex to remove it.`]}),(0,g.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,g.jsx)(`button`,{type:`button`,onClick:ge,disabled:D,"aria-pressed":k,className:k?y:v,children:k?`Done editing`:`Edit mask`}),(0,g.jsx)(`button`,{type:`button`,onClick:ue,disabled:D,"aria-pressed":C.enabled,className:C.enabled?y:v,children:C.enabled?`Mask on`:`Mask off`}),_e&&(0,g.jsx)(`button`,{type:`button`,onClick:()=>X(`rect`),disabled:D||G,"aria-pressed":N===`rect`,className:N===`rect`?y:v,title:G?`Maximum zones reached`:`Add a rectangle zone`,children:`+ Rect`}),ve&&(0,g.jsx)(`button`,{type:`button`,onClick:()=>X(`polygon`),disabled:D||G,"aria-pressed":N===`polygon`,className:N===`polygon`?y:v,title:G?`Maximum zones reached`:`Add a polygon zone`,children:`+ Polygon`}),(0,g.jsxs)(`span`,{className:`${a} ml-1 tabular-nums`,children:[H,` / `,U,` zones`]}),(0,g.jsx)(`span`,{className:`flex-1`}),(0,g.jsx)(`button`,{type:`button`,onClick:pe,disabled:D||!V,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:`Revert`}),(0,g.jsx)(`button`,{type:`button`,onClick:()=>void me(),disabled:D||!V,className:y,children:D?`Saving…`:`Save`})]}),H>0?(0,g.jsx)(`div`,{className:`flex flex-col gap-1`,children:C.regions.map(e=>{let t=j===e.id,n=e.shape.kind===`polygon`?ie:i;return(0,g.jsxs)(`div`,{className:`flex items-center gap-2 rounded-md border px-2 py-1 transition-colors ${t?`border-primary/50 bg-primary/10`:`border-border bg-surface`}`,children:[(0,g.jsxs)(`button`,{type:`button`,onClick:()=>de(e.id),disabled:D,className:`flex flex-1 items-center gap-2 text-left text-[11px] font-medium text-foreground-subtle hover:text-foreground disabled:opacity-40 transition-colors`,children:[(0,g.jsx)(n,{className:`h-3.5 w-3.5 shrink-0`}),(0,g.jsxs)(`span`,{children:[`Zone `,e.id]}),(0,g.jsx)(`span`,{className:`text-foreground-faint capitalize`,children:e.shape.kind})]}),(0,g.jsx)(`button`,{type:`button`,onClick:()=>fe(e.id),disabled:D,"aria-label":`Delete zone ${String(e.id)}`,title:`Delete zone`,className:`inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:border-red-400/40 hover:bg-red-500/10 hover:text-red-400 disabled:opacity-40 transition-colors`,children:(0,g.jsx)(ee,{className:`h-3.5 w-3.5`})})]},e.id)})}):null]}):(0,g.jsx)(`p`,{className:`${a} leading-relaxed`,children:`Loading the camera's privacy mask…`}),Z.supported&&(0,g.jsxs)(`div`,{className:`flex items-start gap-3 rounded-md border border-border bg-surface px-2 py-2`,children:[(0,g.jsx)(`button`,{type:`button`,role:`switch`,"aria-label":Z.label,"aria-checked":Z.on===!0,disabled:!Z.available||I!==null,onClick:()=>void he(),title:Z.on===null?`Microphone — state unknown; the camera did not answer.`:Z.on?`The camera is capturing sound. Turning it off: ${Z.costWhenOff}`:`The camera is silent. ${Z.costWhenOff}`,className:Z.on===!1||Z.on===null?v:y,children:(0,g.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[Z.on===!1?(0,g.jsx)(d,{className:`h-3.5 w-3.5`}):(0,g.jsx)(u,{className:`h-3.5 w-3.5`}),Z.on===null?`Microphone unknown`:Z.on?`Microphone on`:`Microphone off`]})}),(0,g.jsx)(`p`,{className:`text-[10px] text-foreground-subtle flex-1 leading-relaxed`,children:R===null?Z.on===null?`The camera did not report its microphone, so this state is unknown.`:Z.costWhenOff:`Write failed: ${R}`})]})]})}):null}export{x as PrivacyMaskSettings};