@camstack/addon-agent-ui 1.2.350 → 1.2.352

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/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.352] - 2026-11-09
11
+
12
+ ### Changed
13
+
14
+ - Rebuilt against updated dependencies.
15
+
16
+ ## [1.2.351] - 2026-11-09
17
+
18
+ ### Changed
19
+
20
+ - Rebuilt against updated dependencies.
21
+
10
22
  ## [1.2.350] - 2026-11-08
11
23
 
12
24
  ### Changed
@@ -367,14 +367,14 @@
367
367
  "server/server/packages/addon-agent-ui/src/updates-panel.tsx": "ec6b5b3701322bbb68dbe581703409cd373418cf9227ce829ea0945e70e60d54",
368
368
  "server/server/packages/addon-agent-ui/src/use-pairing-status.ts": "46193a1e83a64250dfff75d417448d74ba0159465e710861b996e903b03e427a",
369
369
  "server/server/packages/sdk/dist/index.js": "89409bfb9632c46ed63ee4882c70b7994c1ff90c67d27a5d4b86b0990c41a131",
370
- "server/server/packages/types/dist/client.mjs": "88a71d1f1ab574370c175dae7e21b124bc4f45e6b100176e54c7c0ee3819e005",
370
+ "server/server/packages/types/dist/client.mjs": "ab4b63f8faa3c0cef26052a407d63509d0104362261c2b9631b2ea211d618c93",
371
371
  "server/server/packages/types/dist/composition-CXCX78IR.mjs": "3c34431544a6b88404f5f7c855c84ebbf2e5736ad300d4d4dfa2417759f30d46",
372
372
  "server/server/packages/types/dist/convert-BAzH15HU.mjs": "baa969d99051a3b05b2620e8b8e776a6f5539ac74ee9f9dbee1cc6b6a3a892a0",
373
373
  "server/server/packages/types/dist/device-type-CbDhVwFP.mjs": "94089ac4e225fbd1a8109ff7d5db680acff824c0745d60a1ccf52b4e2717451d",
374
374
  "server/server/packages/types/dist/event-category-78tcBemo.mjs": "767f1bb576027d9ee79ec036367001330203d84842c2c2111fa465fa434045e2",
375
- "server/server/packages/types/dist/index.mjs": "b0becaac78393dbab5a6e30167d501bd2a343a0c911ef733745609506fdea146",
375
+ "server/server/packages/types/dist/index.mjs": "c81ce138f6e772774cbf59eca26247d111c9dd956e9295c83accf7bdc5be1087",
376
376
  "server/server/packages/types/dist/node-color.mjs": "942e663074f0a4ed2f8643848ec139eba4b5fd16d0d01c8a23e08a4819a8843b",
377
- "server/server/packages/types/dist/sleep-CS5Xp1iQ.mjs": "8f49002aa65d022d22d0104c15f3b691013ce9b20be91323a81ba058b0827e52",
377
+ "server/server/packages/types/dist/sleep-CkinsFFb.mjs": "ad13e74a69ba822ea871380a133cb56ecbcfcd844c2276a8fe5783c0ed113de3",
378
378
  "server/server/packages/ui-library/src/composites/adaptive-downlink.ts": "6e9698e97721461b72a3750f930dd7214d049be865491bfb5a64b87c032b0e22",
379
379
  "server/server/packages/ui-library/src/composites/camera-stream-player.tsx": "c04809229b4e091fed09e2bcfe643d6b36cb3a39e294229b49f8a095e4e6ebd3",
380
380
  "server/server/packages/ui-library/src/composites/cap-settings/CameraGridLayoutEditor.tsx": "fae93899c4903183e1b78db39e58bd52a3dd4dc2c11ce85d64e6d2a2bab7516d",
@@ -486,7 +486,7 @@
486
486
  "server/server/packages/ui-library/src/contexts/vod-playback.tsx": "c07bc3ecc786182ef2f27d13bec3f520288e0b47367edd7ddf1c2e7b4a2bbaee",
487
487
  "server/server/packages/ui-library/src/contexts/widget-registry.tsx": "71a2815c645abe96821a9e9e5668e321187527d569414f5ca197d615856cc17a",
488
488
  "server/server/packages/ui-library/src/contexts/zone-editing.tsx": "275f680304ab09dd747fd9e859dc5aa0535df33eb2d33eb669a6de66bdd848d0",
489
- "server/server/packages/ui-library/src/generated/system-hooks.ts": "80e1096642baea14bd28d7fc8a0311e9d2cd71d8a25a16c1255b4dfbe5c7de78",
489
+ "server/server/packages/ui-library/src/generated/system-hooks.ts": "f1242a45e5897fbc5318df28fe19dd1c0a72b6ac59b3a07362869cd5851f8eb8",
490
490
  "server/server/packages/ui-library/src/hooks/turn-server-cache.ts": "fab22f069c3761e0d268449e056d9c499b6ea63b879684a40023735b34bad061",
491
491
  "server/server/packages/ui-library/src/hooks/use-device-capability.ts": "b74ede60be8039c45f6c712a407ee838ab208610435870ddabaa8598cb7cd7f3",
492
492
  "server/server/packages/ui-library/src/hooks/use-device-detections.ts": "61cc54cf2e7f53864e5aa533c05679e4c204c4004a0296f63ab169b003888611",
package/dist/addon.js CHANGED
@@ -5880,7 +5880,7 @@ var ZodIssueCode = {
5880
5880
  var ZodFirstPartyTypeKind;
5881
5881
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5882
5882
  //#endregion
5883
- //#region ../types/dist/sleep-CS5Xp1iQ.mjs
5883
+ //#region ../types/dist/sleep-CkinsFFb.mjs
5884
5884
  /**
5885
5885
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5886
5886
  * window to float samples (D455).
@@ -8011,16 +8011,6 @@ var BaseAddon = class {
8011
8011
  return null;
8012
8012
  }
8013
8013
  /**
8014
- * The form schema of ONE integration instance, or `null` to use the scoped form.
8015
- *
8016
- * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
8017
- * needs its connection fields, the default integration the addon's own sections. It is the
8018
- * SHAPE of the form only — the values live on the instance record.
8019
- */
8020
- async integrationFormSchema(_instance) {
8021
- return null;
8022
- }
8023
- /**
8024
8014
  * D986 scoped settings: the form shape of the CLUSTER-wide settings one capability this addon
8025
8015
  * provides owns, or `null` when it has none. `cap` is a capability name the addon's manifest
8026
8016
  * declares. Stored as `addon-settings` rows `<addonId>.<field>`. The keys of this schema, of
@@ -8048,8 +8038,7 @@ var BaseAddon = class {
8048
8038
  }
8049
8039
  /**
8050
8040
  * The host's read of the scoped declarations: the schema one capability declares for `scope`,
8051
- * or `null`. Beside {@link getIntegrationFormSchema}, the same shape: the hook is protected, the
8052
- * host asks this method.
8041
+ * or `null`. The hook is protected, the host asks this method.
8053
8042
  */
8054
8043
  async getScopedSettingsSchema(scope, cap) {
8055
8044
  switch (scope) {
@@ -8217,22 +8206,6 @@ var BaseAddon = class {
8217
8206
  return Object.fromEntries([...collectHydratedFieldValues(form)].filter(([, value]) => value !== void 0));
8218
8207
  }
8219
8208
  /**
8220
- * What the create wizard and the integration edit page render for one instance: the addon's
8221
- * per-instance schema when it declares one, else its global schema (what they always showed).
8222
- */
8223
- async getIntegrationFormSchema(instance, nodeId) {
8224
- const scoped = await this.integrationFormSchema(instance);
8225
- if (scoped === null) return this.scopedForm(nodeId);
8226
- return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
8227
- }
8228
- /** The form of the scoped schemas this addon declared, hydrated from its store. */
8229
- async scopedForm(nodeId) {
8230
- await this.loadScopedSchemas();
8231
- const schema = this.scopedFormSchema();
8232
- if (!schema) return { sections: [] };
8233
- return hydrateSchema(schema, await this.readRawProjectedStore(nodeId), { inlineSecretsAsViews: true });
8234
- }
8235
- /**
8236
8209
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
8237
8210
  * every `perNode: true` field carries THAT node's scoped value on its bare
8238
8211
  * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
@@ -8419,12 +8392,6 @@ var BaseAddon = class {
8419
8392
  this._nodeKeysByCap = /* @__PURE__ */ new Map();
8420
8393
  this._addonSecretTree = null;
8421
8394
  }
8422
- /** The form of an addon's scoped schemas: their sections, each shown once. */
8423
- scopedFormSchema(cap) {
8424
- const byId = /* @__PURE__ */ new Map();
8425
- for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
8426
- return byId.size === 0 ? null : { sections: [...byId.values()] };
8427
- }
8428
8395
  /** Every loaded scoped schema of `cap`, or of every loaded capability. */
8429
8396
  loadedScopedSchemas(cap) {
8430
8397
  if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
@@ -9014,6 +8981,10 @@ function normalizeAddonInitResult(result) {
9014
8981
  if (Array.isArray(result)) return { providers: result };
9015
8982
  return result;
9016
8983
  }
8984
+ object({
8985
+ code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
8986
+ params: record(string(), union([string(), number$1()])).optional()
8987
+ });
9017
8988
  object({
9018
8989
  field: string().min(1),
9019
8990
  on: literal("advance")
@@ -9090,12 +9061,6 @@ function systemMethod(input, output, options) {
9090
9061
  systemOnly: true
9091
9062
  };
9092
9063
  }
9093
- /**
9094
- * A method a SOURCE of a collection cap may legitimately not serve — OPTIONAL
9095
- * on `InferProvider`. The `providerOptional: true` literal is what
9096
- * `InferProvider` keys on; see {@link CapabilityMethodSchema.providerOptional}
9097
- * for when this is the honest answer and when it is a soft stub.
9098
- */
9099
9064
  function optionalMethod(input, output, options) {
9100
9065
  return {
9101
9066
  ...method(input, output, options),
@@ -16318,10 +16283,6 @@ Object.freeze({
16318
16283
  "uploadDataset": 72e5
16319
16284
  }
16320
16285
  });
16321
- object({
16322
- code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
16323
- params: record(string(), union([string(), number$1()])).optional()
16324
- });
16325
16286
  //#endregion
16326
16287
  //#region ../types/dist/composition-CXCX78IR.mjs
16327
16288
  /**
@@ -18033,25 +17994,6 @@ var RequiredAddonUpdateSchema = object({
18033
17994
  installed: string().nullable()
18034
17995
  });
18035
17996
  /**
18036
- * Which integration instance a settings form is for. An addon that serves several instances
18037
- * (`instanceMode: multiple`) can show a different form per instance — an NVR integration
18038
- * needs its connection, the default integration needs the addon's own sections.
18039
- *
18040
- * - `integrationId` is `null` in the create wizard: nothing exists yet, the operator is
18041
- * setting up a NEW instance.
18042
- * - `mode` is the wizard mode of the integration type (`getAvailableTypes`).
18043
- * - `candidateStableId` is the discovery candidate a wizard was seeded from, if any.
18044
- */
18045
- var IntegrationInstanceRefSchema = object({
18046
- integrationId: string().nullable(),
18047
- mode: _enum([
18048
- "standalone",
18049
- "account",
18050
- "broker"
18051
- ]),
18052
- candidateStableId: string().optional()
18053
- });
18054
- /**
18055
17997
  * notification-output — canonical, capability-gated notification delivery.
18056
17998
  *
18057
17999
  * Apprise-derived model (see
@@ -44137,10 +44079,6 @@ method(object({
44137
44079
  scope: ScopedSettingsScopeSchema,
44138
44080
  nodeId: string().optional()
44139
44081
  }), record(string(), unknown()).nullable()), method(object({
44140
- addonId: string(),
44141
- nodeId: string().optional(),
44142
- instance: IntegrationInstanceRefSchema
44143
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
44144
44082
  addonId: string(),
44145
44083
  nodeId: string().optional()
44146
44084
  }), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
@@ -46148,7 +46086,14 @@ var CalendarInfoSchema = object({
46148
46086
  id: string().min(1),
46149
46087
  name: string().min(1),
46150
46088
  source: CalendarSourceKindSchema,
46151
- enabled: boolean()
46089
+ enabled: boolean(),
46090
+ /**
46091
+ * The `integration-instances` record that owns this calendar (the record the switch writes).
46092
+ * Every source stamps it from the record it read the calendar from, so a consumer joins on
46093
+ * it instead of rebuilding an id from form values. Optional on the wire so a source addon
46094
+ * that predates the field still lists its calendars: such a row simply has no switch.
46095
+ */
46096
+ instanceId: string().min(1).optional()
46152
46097
  });
46153
46098
  /**
46154
46099
  * Time contract of {@link CalendarEvent}.
@@ -48144,8 +48089,12 @@ var RemoteSourceSchema = discriminatedUnion("kind", [
48144
48089
  object({
48145
48090
  ...sourceBase,
48146
48091
  kind: literal("ir"),
48147
- /** The `ir-transmitter` device the code is sent through. */
48148
- transmitterId: number$1().int().nonnegative(),
48092
+ /**
48093
+ * The `ir-transmitter` device the code is sent through. Absent = the remote's
48094
+ * `defaultTransmitterId` at run time (a source stored before this field was
48095
+ * optional keeps the emitter it names).
48096
+ */
48097
+ transmitterId: number$1().int().nonnegative().optional(),
48149
48098
  /** The code itself; whether a transmitter could send it is `irSourceProblem`. */
48150
48099
  payload: IrPayloadSchema
48151
48100
  })
@@ -54496,6 +54445,12 @@ var IntegrationWithStateSchema = object({
54496
54445
  name: string(),
54497
54446
  /** A projection of the instance body's switch (`setInstanceEnabled` is the only writer). */
54498
54447
  enabled: boolean(),
54448
+ /**
54449
+ * True when the addon that holds the switch did not answer (down, failed, or keeps no record for
54450
+ * this row): `enabled` is then the stored registry flag, a stale value, not the state. A reader
54451
+ * must not draw it as on or off (D315).
54452
+ */
54453
+ switchUnknown: literal(true).optional(),
54499
54454
  info: record(string(), unknown()).readonly(),
54500
54455
  /** The discovery candidate this integration was set up from (D984). */
54501
54456
  originStableId: string().optional(),
@@ -54509,6 +54464,12 @@ var IntegrationLiteSchema = object({
54509
54464
  name: string(),
54510
54465
  /** A projection of the instance body's switch (`setInstanceEnabled` is the only writer). */
54511
54466
  enabled: boolean(),
54467
+ /**
54468
+ * True when the addon that holds the switch did not answer (down, failed, or keeps no record for
54469
+ * this row): `enabled` is then the stored registry flag, a stale value, not the state. A reader
54470
+ * must not draw it as on or off (D315).
54471
+ */
54472
+ switchUnknown: literal(true).optional(),
54512
54473
  info: record(string(), unknown()).readonly(),
54513
54474
  /** The discovery candidate this integration was set up from (D984). */
54514
54475
  originStableId: string().optional(),
@@ -54607,9 +54568,9 @@ var UpdateIntegrationInputSchema = object({
54607
54568
  id: string(),
54608
54569
  name: string().optional(),
54609
54570
  /**
54610
- * Accepted for a FIXED builtin only (`info.fixed`: alarm panel, camera grid, core blocks,
54611
- * terminal), which keeps no instance record: the registry flag is its one store. Any other
54612
- * integration is switched through `integration-instances.setInstanceEnabled`.
54571
+ * Honoured ONLY for a fixed builtin (`info.fixed`), whose switch is the hub's own registry flag
54572
+ * and works while its addon is down. Any other row is refused (a typed `BAD_REQUEST`): its
54573
+ * switch is `integration-instances.setInstanceEnabled`.
54613
54574
  */
54614
54575
  enabled: boolean().optional(),
54615
54576
  info: record(string(), unknown()).optional()
@@ -54643,6 +54604,12 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
54643
54604
  * the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
54644
54605
  * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
54645
54606
  * clears it.
54607
+ *
54608
+ * Who may call (epic #440): every method but `describe` and `instanceFormSchema` is open to any
54609
+ * session (`caller: 'scoped'`) and default-denies. An admin passes everywhere. A non-admin passes
54610
+ * only on a call pinned to one addon whose `describe().userScoped` is true, and that provider
54611
+ * filters every row on the `caller` the router injected (a notification target is somebody's
54612
+ * phone: a user sees and changes only their own). An addon that says nothing keeps the admin door.
54646
54613
  */
54647
54614
  var ConfigSchemaPassthrough$2 = custom();
54648
54615
  var IntegrationInstanceIdSchema = string().min(1);
@@ -54756,7 +54723,15 @@ var IntegrationInstanceDescriptionSchema = object({
54756
54723
  */
54757
54724
  mintsId: boolean().optional(),
54758
54725
  /** The links to other instances the provider's kinds declare; absent means none. */
54759
- links: array(IntegrationInstanceLinkSchema).optional()
54726
+ links: array(IntegrationInstanceLinkSchema).optional(),
54727
+ /**
54728
+ * The rows belong to individual users (a notification target is somebody's phone). The router
54729
+ * then opens `listInstances`, `getInstance`, `createInstance`, `updateInstance`,
54730
+ * `setInstanceEnabled`, `deleteInstance` and `testInstance` to a NON-admin session on a call
54731
+ * pinned to this addon, and hands the provider the `caller` so it keeps a user to their own rows.
54732
+ * Absent means admin only, as it always was.
54733
+ */
54734
+ userScoped: boolean().optional()
54760
54735
  });
54761
54736
  var IntegrationInstanceStatusSchema = object({
54762
54737
  state: _enum([
@@ -54806,7 +54781,13 @@ method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method
54806
54781
  ]).optional(),
54807
54782
  /** The discovery candidate the wizard was seeded from (`stableId`), if any. */
54808
54783
  candidateStableId: string().min(1).optional()
54809
- }), ConfigSchemaPassthrough$2, { auth: "admin" }), method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }), method(object({
54784
+ }), ConfigSchemaPassthrough$2, { auth: "admin" }), method(looseObject({}), array(IntegrationInstanceSchema).readonly(), {
54785
+ auth: "protected",
54786
+ caller: "scoped"
54787
+ }), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), {
54788
+ auth: "protected",
54789
+ caller: "scoped"
54790
+ }), method(object({
54810
54791
  id: IntegrationInstanceIdSchema.optional(),
54811
54792
  name: string().min(1),
54812
54793
  kind: string().min(1).optional(),
@@ -54828,19 +54809,23 @@ method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method
54828
54809
  seed: object({ info: record(string(), unknown()) }).optional()
54829
54810
  }), object({ id: string() }), {
54830
54811
  kind: "mutation",
54831
- auth: "admin"
54812
+ auth: "protected",
54813
+ caller: "scoped"
54832
54814
  }), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), {
54833
54815
  kind: "mutation",
54834
- auth: "admin"
54816
+ auth: "protected",
54817
+ caller: "scoped"
54835
54818
  }), method(object({
54836
54819
  id: IntegrationInstanceIdSchema,
54837
54820
  enabled: boolean()
54838
54821
  }), object({ ok: literal(true) }), {
54839
54822
  kind: "mutation",
54840
- auth: "admin"
54823
+ auth: "protected",
54824
+ caller: "scoped"
54841
54825
  }), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), {
54842
54826
  kind: "mutation",
54843
- auth: "admin"
54827
+ auth: "protected",
54828
+ caller: "scoped"
54844
54829
  }), optionalMethod(object({
54845
54830
  id: IntegrationInstanceIdSchema.nullable(),
54846
54831
  values: record(string(), unknown()),
@@ -54848,7 +54833,8 @@ method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method
54848
54833
  kind: string().min(1).optional()
54849
54834
  }), TestConnectionResultSchema, {
54850
54835
  kind: "mutation",
54851
- auth: "admin"
54836
+ auth: "protected",
54837
+ caller: "scoped"
54852
54838
  });
54853
54839
  /**
54854
54840
  * Two-way audio / talk-back cap. Clients open a WebRTC audio-only
@@ -71309,7 +71295,6 @@ var RemoteCustomButtonSchema = object({
71309
71295
  /** What the operator calls it, and what a voice command may name. */
71310
71296
  name: string().min(1).max(60)
71311
71297
  });
71312
- var RemotePowerModeSchema = _enum(["toggle", "discrete"]);
71313
71298
  /**
71314
71299
  * The source rules: every step role is bound, ids and names are unique, a
71315
71300
  * channel number belongs to one `digits` source, and no name or id is another
@@ -71390,7 +71375,6 @@ var RemoteConfigSchema = object({
71390
71375
  sources: array(RemoteSourceSchema).max(200).default([]),
71391
71376
  /** The pause between two steps of a source: the television's receiver, not a repeat. */
71392
71377
  stepDelayMs: number$1().int().min(100).max(REMOTE_STEP_DELAY_MAX_MS).default(500),
71393
- power: object({ mode: RemotePowerModeSchema }),
71394
71378
  /**
71395
71379
  * READ-ONLY: sources a stored configuration carries that no longer parse,
71396
71380
  * left out of `sources` with the reason (`readStoredRemoteConfig`). Never
@@ -71494,6 +71478,11 @@ var RemoteLayoutButtonSchema = object({
71494
71478
  /** Absent = the plain rendering every layout had before shapes existed. */
71495
71479
  shape: RemoteButtonShapeSchema.optional(),
71496
71480
  tone: RemoteButtonToneSchema.optional(),
71481
+ /**
71482
+ * Draw the label on a colour-toned key. A colour key shows only its colour
71483
+ * by default; a remote whose plastic prints letters on them (A B C D) sets it.
71484
+ */
71485
+ showLegend: boolean().optional(),
71497
71486
  /** The group this button belongs to; it must lie inside that group's rectangle. */
71498
71487
  groupId: string().min(1).max(40).optional()
71499
71488
  });
@@ -71631,7 +71620,6 @@ var RemoteBoundCommandSchema = object({
71631
71620
  });
71632
71621
  var RemoteCommandListSchema = object({
71633
71622
  layoutTemplateId: string().nullable(),
71634
- power: object({ mode: RemotePowerModeSchema }),
71635
71623
  /** Only the roles that are BOUND — the ones a press can reach. */
71636
71624
  commands: array(RemoteBoundCommandSchema),
71637
71625
  /** The sources the operator declared; empty when none (planning#460). */
@@ -71644,7 +71632,8 @@ var RemoteRefusalReasonSchema = _enum([
71644
71632
  "unknown-source",
71645
71633
  "unbound-step",
71646
71634
  "undeclared-channel",
71647
- "press-timeout"
71635
+ "press-timeout",
71636
+ "no-transmitter"
71648
71637
  ]);
71649
71638
  var RemoteSendResultSchema = discriminatedUnion("status", [
71650
71639
  object({
@@ -71737,7 +71726,8 @@ var RemoteComponentButtonSchema = object({
71737
71726
  icon: string().min(1).max(60).optional(),
71738
71727
  label: string().min(1).max(40).optional(),
71739
71728
  shape: RemoteButtonShapeSchema.optional(),
71740
- tone: RemoteButtonToneSchema.optional()
71729
+ tone: RemoteButtonToneSchema.optional(),
71730
+ showLegend: boolean().optional()
71741
71731
  });
71742
71732
  var RemoteLayoutComponentSchema = object({
71743
71733
  id: string().max(80).regex(COMPONENT_ID_RE, { message: "<namespace>:<name>, lowercase" }),
@@ -72692,6 +72682,11 @@ var SendEmailResultSchema = object({
72692
72682
  var SmtpStatusSchema = object({
72693
72683
  /** True iff the addon has successfully verified the relay. */
72694
72684
  ready: boolean(),
72685
+ /**
72686
+ * The tri-state behind `ready` (D315): true when the last send worked, false when it failed,
72687
+ * ABSENT when the relay has not sent yet (unknown, never "not verified").
72688
+ */
72689
+ verified: boolean().optional(),
72695
72690
  /** Operator-visible host string (no credentials). */
72696
72691
  host: string(),
72697
72692
  /** Last error message reported by the relay, when not ready. */
@@ -72703,10 +72698,6 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
72703
72698
  kind: "mutation",
72704
72699
  auth: "admin",
72705
72700
  access: "create"
72706
- }), method(RelayPinSchema, SmtpStatusSchema, {
72707
- kind: "mutation",
72708
- auth: "admin",
72709
- access: "view"
72710
72701
  }), method(RelayPinSchema, SmtpStatusSchema, { auth: "admin" });
72711
72702
  /**
72712
72703
  * A single device snapshot returned as base64 JPEG/PNG.
@@ -79375,13 +79366,6 @@ Object.freeze({
79375
79366
  access: "view",
79376
79367
  role: "admin"
79377
79368
  },
79378
- "addonSettings.getIntegrationFormSchema": {
79379
- capName: "addon-settings",
79380
- capScope: "system",
79381
- addonId: null,
79382
- access: "view",
79383
- role: "admin"
79384
- },
79385
79369
  "addonSettings.getScopedSettings": {
79386
79370
  capName: "addon-settings",
79387
79371
  capScope: "system",
@@ -87404,13 +87388,6 @@ Object.freeze({
87404
87388
  access: "create",
87405
87389
  role: "admin"
87406
87390
  },
87407
- "smtpProvider.verify": {
87408
- capName: "smtp-provider",
87409
- capScope: "system",
87410
- addonId: null,
87411
- access: "view",
87412
- role: "admin"
87413
- },
87414
87391
  "snapshot.applyDeviceSettingsPatch": {
87415
87392
  capName: "snapshot",
87416
87393
  capScope: "device",
@@ -93786,7 +93763,7 @@ var AgentUIAddon = class extends BaseAddon {
93786
93763
  capability: adminUiCapability,
93787
93764
  provider: {
93788
93765
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
93789
- getVersion: async () => ({ version: "1.2.350" })
93766
+ getVersion: async () => ({ version: "1.2.352" })
93790
93767
  }
93791
93768
  }];
93792
93769
  }
@@ -1 +1 @@
1
- import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{T as a,_ as o,c as s,d as c,l,p as u,t as d}from"./index-DIZdNkiY.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=a(`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`}]]),m=n(i(),1),h=n(e(),1),g=110,_=`motion-zones`,v=0,y=[1,2,3],b=1,x=`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`,S=`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 C(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 w(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 T(e,t){return Math.ceil(e/t)}function E(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function D(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+E(n,t,i)]=!0)}return o}function O(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+E(n,t,i)]===!0}return o}function k({deviceId:e}){let n=t(),i=c(o().trpcClient,e),[a,E]=(0,m.useState)(null),[k,A]=(0,m.useState)(!1),[j,M]=(0,m.useState)(null),[N,P]=(0,m.useState)(null),[F,I]=(0,m.useState)(b),[L,R]=(0,m.useState)(!1),[z,B]=(0,m.useState)(!1),V=(0,m.useRef)(!1);(0,m.useEffect)(()=>{if(!i)return;let e=!1;return V.current=!1,E(null),A(!1),M(null),P(null),I(b),B(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(E(t),V.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);V.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=C(r?r.shape.cells:[],t.grid.width,t.grid.height);P(a),M(D(a,t.grid.width,t.grid.height,b))}catch(t){if(e)return;l(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=a?a.grid.width:0,U=a?a.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&a?O(j,H,U,F):null,[j,a,H,U,F]),q=(0,m.useMemo)(()=>K!==null&&N!==null&&!w(K,N),[K,N]),J=(0,m.useMemo)(()=>K?K.reduce((e,t)=>t?e+1:e,0):0,[K]),Y=H*U,X=W*G,Z=(0,m.useCallback)(e=>{X>0&&M(Array.from({length:X},()=>e))},[X]),ee=(0,m.useCallback)(()=>{M(e=>e&&e.map(e=>!e))},[]),te=(0,m.useCallback)(()=>{N&&a&&M(D(N,H,U,F))},[N,a,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!a||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,a,H,U]),Q=(0,m.useMemo)(()=>a&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[a,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!a)){R(!0);try{let e=O(j,a.grid.width,a.grid.height,F),t={kind:`grid`,gridWidth:a.grid.width,gridHeight:a.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:v,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=C(e?e.shape.cells:[],a.grid.width,a.grid.height);P(t),M(D(t,a.grid.width,a.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,a,F]);d((0,m.useMemo)(()=>z&&!k&&a&&j?{id:_,order:g,node:(0,h.jsx)(f,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:W,height:G},selectedId:v,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[z,k,a,j,Q,$,W,G]));let ie=!k&&a!==null&&j!==null;return i?(0,h.jsx)(u,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,h.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsx)(`div`,{className:`flex flex-col gap-3`,children:k?(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.motionZones.intro`,fallback:`Toggle <0>Edit grid</0> to paint the region directly on the live frame, then <1>Save</1> to push the mask to the camera. Pick a bigger <2>Cell size</2> for quicker, broad-stroke painting — it's resampled to the camera's native {{width}}×{{height}} grid on save (×1 is the finest).`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`e`),(0,h.jsx)(`strong`,{className:`text-foreground`},`s`),(0,h.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:a.grid.width,height:a.grid.height}})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>B(e=>!e),disabled:L,"aria-pressed":z,className:z?S:x,children:z?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!0),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!1),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,h.jsx)(`button`,{type:`button`,onClick:ee,disabled:L,className:x,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,h.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,h.jsx)(`span`,{className:`${s} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),y.map(e=>(0,h.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:L,"aria-pressed":F===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:H,height:U}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:T(H,e),height:T(U,e),factor:e}),className:F===e?S:x,children:[`×`,e]},e))]}),(0,h.jsxs)(`span`,{className:`${s} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:a.grid.width,height:a.grid.height}),F===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:W,height:G})]}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:te,disabled:L||!q,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:n(`uiLibrary.motionZones.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:L||!q,className:S,children:L?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};
1
+ import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{T as a,_ as o,c as s,d as c,l,p as u,t as d}from"./index-BePyQ_Ek.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=a(`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`}]]),m=n(i(),1),h=n(e(),1),g=110,_=`motion-zones`,v=0,y=[1,2,3],b=1,x=`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`,S=`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 C(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 w(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 T(e,t){return Math.ceil(e/t)}function E(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function D(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+E(n,t,i)]=!0)}return o}function O(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+E(n,t,i)]===!0}return o}function k({deviceId:e}){let n=t(),i=c(o().trpcClient,e),[a,E]=(0,m.useState)(null),[k,A]=(0,m.useState)(!1),[j,M]=(0,m.useState)(null),[N,P]=(0,m.useState)(null),[F,I]=(0,m.useState)(b),[L,R]=(0,m.useState)(!1),[z,B]=(0,m.useState)(!1),V=(0,m.useRef)(!1);(0,m.useEffect)(()=>{if(!i)return;let e=!1;return V.current=!1,E(null),A(!1),M(null),P(null),I(b),B(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(E(t),V.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);V.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=C(r?r.shape.cells:[],t.grid.width,t.grid.height);P(a),M(D(a,t.grid.width,t.grid.height,b))}catch(t){if(e)return;l(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=a?a.grid.width:0,U=a?a.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&a?O(j,H,U,F):null,[j,a,H,U,F]),q=(0,m.useMemo)(()=>K!==null&&N!==null&&!w(K,N),[K,N]),J=(0,m.useMemo)(()=>K?K.reduce((e,t)=>t?e+1:e,0):0,[K]),Y=H*U,X=W*G,Z=(0,m.useCallback)(e=>{X>0&&M(Array.from({length:X},()=>e))},[X]),ee=(0,m.useCallback)(()=>{M(e=>e&&e.map(e=>!e))},[]),te=(0,m.useCallback)(()=>{N&&a&&M(D(N,H,U,F))},[N,a,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!a||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,a,H,U]),Q=(0,m.useMemo)(()=>a&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[a,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!a)){R(!0);try{let e=O(j,a.grid.width,a.grid.height,F),t={kind:`grid`,gridWidth:a.grid.width,gridHeight:a.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:v,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=C(e?e.shape.cells:[],a.grid.width,a.grid.height);P(t),M(D(t,a.grid.width,a.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,a,F]);d((0,m.useMemo)(()=>z&&!k&&a&&j?{id:_,order:g,node:(0,h.jsx)(f,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:W,height:G},selectedId:v,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[z,k,a,j,Q,$,W,G]));let ie=!k&&a!==null&&j!==null;return i?(0,h.jsx)(u,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,h.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsx)(`div`,{className:`flex flex-col gap-3`,children:k?(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.motionZones.intro`,fallback:`Toggle <0>Edit grid</0> to paint the region directly on the live frame, then <1>Save</1> to push the mask to the camera. Pick a bigger <2>Cell size</2> for quicker, broad-stroke painting — it's resampled to the camera's native {{width}}×{{height}} grid on save (×1 is the finest).`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`e`),(0,h.jsx)(`strong`,{className:`text-foreground`},`s`),(0,h.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:a.grid.width,height:a.grid.height}})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>B(e=>!e),disabled:L,"aria-pressed":z,className:z?S:x,children:z?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!0),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!1),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,h.jsx)(`button`,{type:`button`,onClick:ee,disabled:L,className:x,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,h.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,h.jsx)(`span`,{className:`${s} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),y.map(e=>(0,h.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:L,"aria-pressed":F===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:H,height:U}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:T(H,e),height:T(U,e),factor:e}),className:F===e?S:x,children:[`×`,e]},e))]}),(0,h.jsxs)(`span`,{className:`${s} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:a.grid.width,height:a.grid.height}),F===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:W,height:G})]}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:te,disabled:L||!q,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:n(`uiLibrary.motionZones.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:L||!q,className:S,children:L?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};
@@ -1 +1 @@
1
- import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{C as ee,T as a,_ as te,c as o,d as ne,l as s,p as c,t as re,v as l,x as u,y as d}from"./index-DIZdNkiY.js";import{MaskShapeCanvas as ie}from"./MaskShapeCanvas-BTvl6Rin.js";var ae=a(`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`}]]),oe=a(`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=n(i(),1),p=`Microphone`,m=`The camera encodes no audio at all — live view, recordings, exports and the audio analyzer are all silent.`;function se(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 ce(e){return!e.available||e.on===null?null:{enabled:!e.on}}var h=n(e(),1),le=120,ue=`privacy-mask`,g=`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`,_=`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 v(e){return e.kind===`rect`||e.kind===`polygon`?e:null}function de(e){let t=new Set(e.map(e=>e.id)),n=0;for(;t.has(n);)n+=1;return n}function fe(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 y({deviceId:e}){let n=t(),i=ne(te().trpcClient,e),[a,p]=(0,f.useState)(null),[m,y]=(0,f.useState)(!1),[b,x]=(0,f.useState)(!1),[S,C]=(0,f.useState)(null),[w,T]=(0,f.useState)(null),[E,D]=(0,f.useState)(!1),[O,k]=(0,f.useState)(!1),[A,j]=(0,f.useState)(null),[M,N]=(0,f.useState)(null),[pe,P]=(0,f.useState)(null),[F,I]=(0,f.useState)(null),[L,R]=(0,f.useState)(null),z=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!i)return;let e=!1;return z.current=!1,p(null),y(!1),x(!1),C(null),T(null),k(!1),j(null),N(null),P(null),I(null),R(null),(async()=>{try{let t=await i.privacyMask?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(p(t),z.current)return;let n=await i.privacyMask?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(n.regions===null){x(!0),P(n.audioEnabled);return}z.current=!0;let r={enabled:n.enabled,regions:n.regions};T(r),C(r),P(n.audioEnabled)}catch(t){if(e)return;s(t)?y(!0):console.error(`Privacy Mask load failed`,t)}})(),()=>{e=!0}},[i]);let B=(0,f.useMemo)(()=>S!==null&&w!==null&&(S.enabled!==w.enabled||!fe(S.regions,w.regions)),[S,w]),V=S?S.regions.length:0,H=a?a.maxRegions:0,U=(0,f.useRef)(0);(0,f.useEffect)(()=>{U.current=H},[H]);let W=H>0&&V>=H,me=(0,f.useCallback)(()=>{C(e=>e&&{...e,enabled:!e.enabled})},[]),G=(0,f.useCallback)(e=>{j(typeof e==`number`?e:null)},[]),K=(0,f.useMemo)(()=>S?S.regions.map(e=>({id:e.id,shape:e.shape,enabled:e.enabled,label:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})})):[],[S,n]),q=(0,f.useCallback)((e,t)=>{let n=v(t);n&&C(t=>t&&{...t,regions:t.regions.map(t=>t.id===e?{...t,shape:n}:t)})},[]),J=(0,f.useCallback)(e=>{let t=v(e);t&&(N(null),C(e=>{if(!e)return e;let n=U.current;if(n>0&&e.regions.length>=n)return e;let r=de(e.regions),i={id:r,enabled:!0,shape:t};return j(r),{...e,regions:[...e.regions,i]}}))},[]),Y=(0,f.useCallback)(e=>{k(!0),j(null),N(e)},[]),he=(0,f.useCallback)(e=>{k(!0),N(null),j(e)},[]),ge=(0,f.useCallback)(e=>{j(t=>t===e?null:t),C(t=>t&&{...t,regions:t.regions.filter(t=>t.id!==e)})},[]),_e=(0,f.useCallback)(()=>{N(null),j(null),w&&C({enabled:w.enabled,regions:w.regions})},[w]),X=(0,f.useCallback)(async()=>{if(!(!i||!S)){D(!0);try{await i.privacyMask?.setMask({patch:{enabled:S.enabled,regions:[...S.regions]}});let e=await i.privacyMask?.getStatus({});if(e&&e.regions!==null){let t={enabled:e.enabled,regions:e.regions};T(t),C(t)}}catch(e){console.error(`Privacy Mask save failed`,e)}finally{D(!1)}}},[i,S]),Z=se({supportsAudioMute:a===null?null:a.supportsAudioMute,audioEnabled:pe,pendingOn:F}),ve=(0,f.useCallback)(async()=>{let e=ce(Z);if(!(!i||e===null)){I(e.enabled),R(null);try{await i.privacyMask?.setAudioEnabled(e);let t=await i.privacyMask?.getStatus({});t&&P(t.audioEnabled)}catch(e){console.error(`Privacy microphone write failed — reverting`,e),R(e instanceof Error?e.message:String(e))}finally{I(null)}}},[i,Z]),ye=(0,f.useCallback)(()=>{k(e=>(e&&(N(null),j(null)),!e))},[]),Q=a?.supportedShapes??[];re((0,f.useMemo)(()=>O&&!m&&a&&S?{id:ue,order:le,node:(0,h.jsx)(ie,{transparent:!0,items:K,supportedShapes:Q,polygonVertices:a.polygonVertices,selectedId:A,onSelect:G,onShapeChange:q,onDrawComplete:J,drawingKind:M})}:null,[O,m,a,S,K,Q,A,G,q,J,M]));let be=a?.supportedShapes.includes(`rect`)??!1,xe=a?.supportedShapes.includes(`polygon`)??!1,$=a!==null&&(a.maxRegions<=0||a.supportedShapes.length===0),Se=!m&&!$&&!b&&a!==null&&S!==null;return i?(0,h.jsx)(c,{title:n(`uiLibrary.privacyMaskSettings.title`,`Privacy Mask`),icon:(0,h.jsx)(ee,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[m||$?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unsupported`,`This camera doesn't support an on-board privacy mask.`)}):b?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unreadable`,`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.`)}):Se?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.privacyMaskSettings.help`,fallback:`Toggle <0>Edit mask</0> to draw blanked-out zones on the live frame, then <1>Save</1> 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.`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`edit`),(0,h.jsx)(`strong`,{className:`text-foreground`},`save`)]})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:ye,disabled:E,"aria-pressed":O,className:O?_:g,children:O?n(`uiLibrary.privacyMaskSettings.doneEditing`,`Done editing`):n(`uiLibrary.privacyMaskSettings.editMask`,`Edit mask`)}),(0,h.jsx)(`button`,{type:`button`,onClick:me,disabled:E,"aria-pressed":S.enabled,className:S.enabled?_:g,children:S.enabled?n(`uiLibrary.privacyMaskSettings.maskOn`,`Mask on`):n(`uiLibrary.privacyMaskSettings.maskOff`,`Mask off`)}),be&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`rect`),disabled:E||W,"aria-pressed":M===`rect`,className:M===`rect`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addRect`,`Add a rectangle zone`),children:n(`uiLibrary.privacyMaskSettings.rectButton`,`+ Rect`)}),xe&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`polygon`),disabled:E||W,"aria-pressed":M===`polygon`,className:M===`polygon`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addPolygon`,`Add a polygon zone`),children:n(`uiLibrary.privacyMaskSettings.polygonButton`,`+ Polygon`)}),(0,h.jsx)(`span`,{className:`${o} ml-1 tabular-nums`,children:n(`uiLibrary.privacyMaskSettings.zoneCount`,`{{active}} / {{max}} zones`,{active:V,max:H})}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:_e,disabled:E||!B,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:n(`uiLibrary.privacyMaskSettings.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void X(),disabled:E||!B,className:_,children:E?n(`uiLibrary.privacyMaskSettings.saving`,`Saving…`):n(`uiLibrary.privacyMaskSettings.save`,`Save`)})]}),V>0?(0,h.jsx)(`div`,{className:`flex flex-col gap-1`,children:S.regions.map(e=>{let t=A===e.id,r=e.shape.kind===`polygon`?ae:d;return(0,h.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,h.jsxs)(`button`,{type:`button`,onClick:()=>he(e.id),disabled:E,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,h.jsx)(r,{className:`h-3.5 w-3.5 shrink-0`}),(0,h.jsx)(`span`,{children:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})}),(0,h.jsx)(`span`,{className:`text-foreground-faint capitalize`,children:e.shape.kind})]}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>ge(e.id),disabled:E,"aria-label":n(`uiLibrary.privacyMaskSettings.deleteZoneN`,`Delete zone {{id}}`,{id:e.id}),title:n(`uiLibrary.privacyMaskSettings.deleteZone`,`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,h.jsx)(l,{className:`h-3.5 w-3.5`})})]},e.id)})}):null]}):(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.loading`,`Loading the camera's privacy mask…`)}),Z.supported&&(0,h.jsxs)(`div`,{className:`flex items-start gap-3 rounded-md border border-border bg-surface px-2 py-2`,children:[(0,h.jsx)(`button`,{type:`button`,role:`switch`,"aria-label":Z.label,"aria-checked":Z.on===!0,disabled:!Z.available||F!==null,onClick:()=>void ve(),title:Z.on===null?n(`uiLibrary.privacyMaskSettings.micTitleUnknown`,`Microphone — state unknown; the camera did not answer.`):Z.on?n(`uiLibrary.privacyMaskSettings.micTitleOn`,`The camera is capturing sound. Turning it off: {{cost}}`,{cost:Z.costWhenOff}):n(`uiLibrary.privacyMaskSettings.micTitleOff`,`The camera is silent. {{cost}}`,{cost:Z.costWhenOff}),className:Z.on===!1||Z.on===null?g:_,children:(0,h.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[Z.on===!1?(0,h.jsx)(oe,{className:`h-3.5 w-3.5`}):(0,h.jsx)(u,{className:`h-3.5 w-3.5`}),Z.on===null?n(`uiLibrary.privacyMaskSettings.micUnknown`,`Microphone unknown`):Z.on?n(`uiLibrary.privacyMaskSettings.micOn`,`Microphone on`):n(`uiLibrary.privacyMaskSettings.micOff`,`Microphone off`)]})}),(0,h.jsx)(`p`,{className:`text-[10px] text-foreground-subtle flex-1 leading-relaxed`,children:L===null?Z.on===null?n(`uiLibrary.privacyMaskSettings.micNotReported`,`The camera did not report its microphone, so this state is unknown.`):Z.costWhenOff:n(`uiLibrary.privacyMaskSettings.writeFailed`,`Write failed: {{reason}}`,{reason:L})})]})]})}):null}export{y as PrivacyMaskSettings};
1
+ import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{C as ee,T as a,_ as te,c as o,d as ne,l as s,p as c,t as re,v as l,x as u,y as d}from"./index-BePyQ_Ek.js";import{MaskShapeCanvas as ie}from"./MaskShapeCanvas-BTvl6Rin.js";var ae=a(`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`}]]),oe=a(`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=n(i(),1),p=`Microphone`,m=`The camera encodes no audio at all — live view, recordings, exports and the audio analyzer are all silent.`;function se(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 ce(e){return!e.available||e.on===null?null:{enabled:!e.on}}var h=n(e(),1),le=120,ue=`privacy-mask`,g=`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`,_=`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 v(e){return e.kind===`rect`||e.kind===`polygon`?e:null}function de(e){let t=new Set(e.map(e=>e.id)),n=0;for(;t.has(n);)n+=1;return n}function fe(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 y({deviceId:e}){let n=t(),i=ne(te().trpcClient,e),[a,p]=(0,f.useState)(null),[m,y]=(0,f.useState)(!1),[b,x]=(0,f.useState)(!1),[S,C]=(0,f.useState)(null),[w,T]=(0,f.useState)(null),[E,D]=(0,f.useState)(!1),[O,k]=(0,f.useState)(!1),[A,j]=(0,f.useState)(null),[M,N]=(0,f.useState)(null),[pe,P]=(0,f.useState)(null),[F,I]=(0,f.useState)(null),[L,R]=(0,f.useState)(null),z=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!i)return;let e=!1;return z.current=!1,p(null),y(!1),x(!1),C(null),T(null),k(!1),j(null),N(null),P(null),I(null),R(null),(async()=>{try{let t=await i.privacyMask?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(p(t),z.current)return;let n=await i.privacyMask?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(n.regions===null){x(!0),P(n.audioEnabled);return}z.current=!0;let r={enabled:n.enabled,regions:n.regions};T(r),C(r),P(n.audioEnabled)}catch(t){if(e)return;s(t)?y(!0):console.error(`Privacy Mask load failed`,t)}})(),()=>{e=!0}},[i]);let B=(0,f.useMemo)(()=>S!==null&&w!==null&&(S.enabled!==w.enabled||!fe(S.regions,w.regions)),[S,w]),V=S?S.regions.length:0,H=a?a.maxRegions:0,U=(0,f.useRef)(0);(0,f.useEffect)(()=>{U.current=H},[H]);let W=H>0&&V>=H,me=(0,f.useCallback)(()=>{C(e=>e&&{...e,enabled:!e.enabled})},[]),G=(0,f.useCallback)(e=>{j(typeof e==`number`?e:null)},[]),K=(0,f.useMemo)(()=>S?S.regions.map(e=>({id:e.id,shape:e.shape,enabled:e.enabled,label:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})})):[],[S,n]),q=(0,f.useCallback)((e,t)=>{let n=v(t);n&&C(t=>t&&{...t,regions:t.regions.map(t=>t.id===e?{...t,shape:n}:t)})},[]),J=(0,f.useCallback)(e=>{let t=v(e);t&&(N(null),C(e=>{if(!e)return e;let n=U.current;if(n>0&&e.regions.length>=n)return e;let r=de(e.regions),i={id:r,enabled:!0,shape:t};return j(r),{...e,regions:[...e.regions,i]}}))},[]),Y=(0,f.useCallback)(e=>{k(!0),j(null),N(e)},[]),he=(0,f.useCallback)(e=>{k(!0),N(null),j(e)},[]),ge=(0,f.useCallback)(e=>{j(t=>t===e?null:t),C(t=>t&&{...t,regions:t.regions.filter(t=>t.id!==e)})},[]),_e=(0,f.useCallback)(()=>{N(null),j(null),w&&C({enabled:w.enabled,regions:w.regions})},[w]),X=(0,f.useCallback)(async()=>{if(!(!i||!S)){D(!0);try{await i.privacyMask?.setMask({patch:{enabled:S.enabled,regions:[...S.regions]}});let e=await i.privacyMask?.getStatus({});if(e&&e.regions!==null){let t={enabled:e.enabled,regions:e.regions};T(t),C(t)}}catch(e){console.error(`Privacy Mask save failed`,e)}finally{D(!1)}}},[i,S]),Z=se({supportsAudioMute:a===null?null:a.supportsAudioMute,audioEnabled:pe,pendingOn:F}),ve=(0,f.useCallback)(async()=>{let e=ce(Z);if(!(!i||e===null)){I(e.enabled),R(null);try{await i.privacyMask?.setAudioEnabled(e);let t=await i.privacyMask?.getStatus({});t&&P(t.audioEnabled)}catch(e){console.error(`Privacy microphone write failed — reverting`,e),R(e instanceof Error?e.message:String(e))}finally{I(null)}}},[i,Z]),ye=(0,f.useCallback)(()=>{k(e=>(e&&(N(null),j(null)),!e))},[]),Q=a?.supportedShapes??[];re((0,f.useMemo)(()=>O&&!m&&a&&S?{id:ue,order:le,node:(0,h.jsx)(ie,{transparent:!0,items:K,supportedShapes:Q,polygonVertices:a.polygonVertices,selectedId:A,onSelect:G,onShapeChange:q,onDrawComplete:J,drawingKind:M})}:null,[O,m,a,S,K,Q,A,G,q,J,M]));let be=a?.supportedShapes.includes(`rect`)??!1,xe=a?.supportedShapes.includes(`polygon`)??!1,$=a!==null&&(a.maxRegions<=0||a.supportedShapes.length===0),Se=!m&&!$&&!b&&a!==null&&S!==null;return i?(0,h.jsx)(c,{title:n(`uiLibrary.privacyMaskSettings.title`,`Privacy Mask`),icon:(0,h.jsx)(ee,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[m||$?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unsupported`,`This camera doesn't support an on-board privacy mask.`)}):b?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unreadable`,`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.`)}):Se?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.privacyMaskSettings.help`,fallback:`Toggle <0>Edit mask</0> to draw blanked-out zones on the live frame, then <1>Save</1> 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.`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`edit`),(0,h.jsx)(`strong`,{className:`text-foreground`},`save`)]})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:ye,disabled:E,"aria-pressed":O,className:O?_:g,children:O?n(`uiLibrary.privacyMaskSettings.doneEditing`,`Done editing`):n(`uiLibrary.privacyMaskSettings.editMask`,`Edit mask`)}),(0,h.jsx)(`button`,{type:`button`,onClick:me,disabled:E,"aria-pressed":S.enabled,className:S.enabled?_:g,children:S.enabled?n(`uiLibrary.privacyMaskSettings.maskOn`,`Mask on`):n(`uiLibrary.privacyMaskSettings.maskOff`,`Mask off`)}),be&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`rect`),disabled:E||W,"aria-pressed":M===`rect`,className:M===`rect`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addRect`,`Add a rectangle zone`),children:n(`uiLibrary.privacyMaskSettings.rectButton`,`+ Rect`)}),xe&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`polygon`),disabled:E||W,"aria-pressed":M===`polygon`,className:M===`polygon`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addPolygon`,`Add a polygon zone`),children:n(`uiLibrary.privacyMaskSettings.polygonButton`,`+ Polygon`)}),(0,h.jsx)(`span`,{className:`${o} ml-1 tabular-nums`,children:n(`uiLibrary.privacyMaskSettings.zoneCount`,`{{active}} / {{max}} zones`,{active:V,max:H})}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:_e,disabled:E||!B,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:n(`uiLibrary.privacyMaskSettings.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void X(),disabled:E||!B,className:_,children:E?n(`uiLibrary.privacyMaskSettings.saving`,`Saving…`):n(`uiLibrary.privacyMaskSettings.save`,`Save`)})]}),V>0?(0,h.jsx)(`div`,{className:`flex flex-col gap-1`,children:S.regions.map(e=>{let t=A===e.id,r=e.shape.kind===`polygon`?ae:d;return(0,h.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,h.jsxs)(`button`,{type:`button`,onClick:()=>he(e.id),disabled:E,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,h.jsx)(r,{className:`h-3.5 w-3.5 shrink-0`}),(0,h.jsx)(`span`,{children:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})}),(0,h.jsx)(`span`,{className:`text-foreground-faint capitalize`,children:e.shape.kind})]}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>ge(e.id),disabled:E,"aria-label":n(`uiLibrary.privacyMaskSettings.deleteZoneN`,`Delete zone {{id}}`,{id:e.id}),title:n(`uiLibrary.privacyMaskSettings.deleteZone`,`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,h.jsx)(l,{className:`h-3.5 w-3.5`})})]},e.id)})}):null]}):(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.loading`,`Loading the camera's privacy mask…`)}),Z.supported&&(0,h.jsxs)(`div`,{className:`flex items-start gap-3 rounded-md border border-border bg-surface px-2 py-2`,children:[(0,h.jsx)(`button`,{type:`button`,role:`switch`,"aria-label":Z.label,"aria-checked":Z.on===!0,disabled:!Z.available||F!==null,onClick:()=>void ve(),title:Z.on===null?n(`uiLibrary.privacyMaskSettings.micTitleUnknown`,`Microphone — state unknown; the camera did not answer.`):Z.on?n(`uiLibrary.privacyMaskSettings.micTitleOn`,`The camera is capturing sound. Turning it off: {{cost}}`,{cost:Z.costWhenOff}):n(`uiLibrary.privacyMaskSettings.micTitleOff`,`The camera is silent. {{cost}}`,{cost:Z.costWhenOff}),className:Z.on===!1||Z.on===null?g:_,children:(0,h.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[Z.on===!1?(0,h.jsx)(oe,{className:`h-3.5 w-3.5`}):(0,h.jsx)(u,{className:`h-3.5 w-3.5`}),Z.on===null?n(`uiLibrary.privacyMaskSettings.micUnknown`,`Microphone unknown`):Z.on?n(`uiLibrary.privacyMaskSettings.micOn`,`Microphone on`):n(`uiLibrary.privacyMaskSettings.micOff`,`Microphone off`)]})}),(0,h.jsx)(`p`,{className:`text-[10px] text-foreground-subtle flex-1 leading-relaxed`,children:L===null?Z.on===null?n(`uiLibrary.privacyMaskSettings.micNotReported`,`The camera did not report its microphone, so this state is unknown.`):Z.costWhenOff:n(`uiLibrary.privacyMaskSettings.writeFailed`,`Write failed: {{reason}}`,{reason:L})})]})]})}):null}export{y as PrivacyMaskSettings};