@camstack/addon-agent-ui 1.2.313 → 1.2.314

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,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.314] - 2026-11-02
11
+
12
+ ### Changed
13
+
14
+ - Rebuilt against updated dependencies.
15
+
10
16
  ## [1.2.313] - 2026-11-02
11
17
 
12
18
  ### Changed
@@ -362,13 +362,13 @@
362
362
  "server/server/packages/addon-agent-ui/src/updates-panel.tsx": "ec6b5b3701322bbb68dbe581703409cd373418cf9227ce829ea0945e70e60d54",
363
363
  "server/server/packages/addon-agent-ui/src/use-pairing-status.ts": "46193a1e83a64250dfff75d417448d74ba0159465e710861b996e903b03e427a",
364
364
  "server/server/packages/sdk/dist/index.js": "89409bfb9632c46ed63ee4882c70b7994c1ff90c67d27a5d4b86b0990c41a131",
365
- "server/server/packages/types/dist/client.mjs": "8d2722a6a47194ba322fc16d1536e6ba4e5fef7739789a08c90c6c4d2c7aa30a",
366
- "server/server/packages/types/dist/composition-kKKOdFIG.mjs": "8884b661df300b88fba5ad861a6cb0096cee1a1c1ca8058bea94907caaff28e6",
365
+ "server/server/packages/types/dist/client.mjs": "9e01dabd3ccf8345ffe626226a594a2543bf48ff2b98441c457c6fa4f417d04a",
366
+ "server/server/packages/types/dist/composition-BDUoQsw9.mjs": "8fef3af56a932116cbb03695f20b3c17a9edf69ec25a1a55cf49fc5cbb3e31ca",
367
367
  "server/server/packages/types/dist/device-type-BOWLtoOt.mjs": "cc8447229f79743a89db50f17befd0674934b4c79bc905aa1a7f4e8667453b2c",
368
368
  "server/server/packages/types/dist/event-category-BmPm98n0.mjs": "3617fc3941b34f1dcc8ebd3c1360e8c302747400d43e3ab694388496c2bb568d",
369
- "server/server/packages/types/dist/index.mjs": "b4c9c734fcd0204d4ddf52cb2495840dcc2552d850f0cdea1120a2d93422782c",
369
+ "server/server/packages/types/dist/index.mjs": "d12022d009587530dfcbf90ac0eccd799ea28926aa42b4c8c4f14eb55f5bad18",
370
370
  "server/server/packages/types/dist/node-color.mjs": "942e663074f0a4ed2f8643848ec139eba4b5fd16d0d01c8a23e08a4819a8843b",
371
- "server/server/packages/types/dist/sleep-B-vp7x15.mjs": "26814958a629c9c04f1b47301254025974b7b9ad9d093523348469d4cd03d2ac",
371
+ "server/server/packages/types/dist/sleep-CdJHPm-k.mjs": "9f8cc606eda8ac8b33aacbc6387d378bb1125f6d9c245ef1d3f35ee48256cab6",
372
372
  "server/server/packages/ui-library/src/composites/adaptive-downlink.ts": "6e9698e97721461b72a3750f930dd7214d049be865491bfb5a64b87c032b0e22",
373
373
  "server/server/packages/ui-library/src/composites/camera-stream-player.tsx": "c04809229b4e091fed09e2bcfe643d6b36cb3a39e294229b49f8a095e4e6ebd3",
374
374
  "server/server/packages/ui-library/src/composites/cap-settings/CameraGridLayoutEditor.tsx": "bfe36ccad931ef0981fe24fa315f136d35dbc3164df221346806173447178324",
@@ -479,7 +479,7 @@
479
479
  "server/server/packages/ui-library/src/contexts/vod-playback.tsx": "c07bc3ecc786182ef2f27d13bec3f520288e0b47367edd7ddf1c2e7b4a2bbaee",
480
480
  "server/server/packages/ui-library/src/contexts/widget-registry.tsx": "71a2815c645abe96821a9e9e5668e321187527d569414f5ca197d615856cc17a",
481
481
  "server/server/packages/ui-library/src/contexts/zone-editing.tsx": "275f680304ab09dd747fd9e859dc5aa0535df33eb2d33eb669a6de66bdd848d0",
482
- "server/server/packages/ui-library/src/generated/system-hooks.ts": "0c3e5118d73f77cc0f49c451fb38f6530ddb2e9c4af0d4a4c201320d447c0074",
482
+ "server/server/packages/ui-library/src/generated/system-hooks.ts": "cf09d4a765ce36b589615ca6954d1a01adca02906c950ae03fed218e7ce5c5b7",
483
483
  "server/server/packages/ui-library/src/hooks/turn-server-cache.ts": "fab22f069c3761e0d268449e056d9c499b6ea63b879684a40023735b34bad061",
484
484
  "server/server/packages/ui-library/src/hooks/use-device-capability.ts": "b74ede60be8039c45f6c712a407ee838ab208610435870ddabaa8598cb7cd7f3",
485
485
  "server/server/packages/ui-library/src/hooks/use-device-detections.ts": "61cc54cf2e7f53864e5aa533c05679e4c204c4004a0296f63ab169b003888611",
package/dist/addon.js CHANGED
@@ -5840,7 +5840,7 @@ var ZodIssueCode = {
5840
5840
  var ZodFirstPartyTypeKind;
5841
5841
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5842
5842
  //#endregion
5843
- //#region ../types/dist/sleep-B-vp7x15.mjs
5843
+ //#region ../types/dist/sleep-CdJHPm-k.mjs
5844
5844
  /**
5845
5845
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5846
5846
  * window to float samples (D455).
@@ -5957,7 +5957,7 @@ function isKeychainStoredField(field) {
5957
5957
  var SECRET_CLEAR = { $secretClear: true };
5958
5958
  /** The `'***'` token D634 redaction writes for non-admins. */
5959
5959
  var LEGACY_DEVICE_REDACTION = "***";
5960
- function isRecord$6(value) {
5960
+ function isRecord$7(value) {
5961
5961
  return typeof value === "object" && value !== null && !Array.isArray(value);
5962
5962
  }
5963
5963
  function makeSecretMarker(ref) {
@@ -5968,12 +5968,12 @@ function makeSecretMarker(ref) {
5968
5968
  }
5969
5969
  /** A stored marker: an object whose `$secret` is a non-empty string. */
5970
5970
  function isSecretMarker(value) {
5971
- if (!isRecord$6(value)) return false;
5971
+ if (!isRecord$7(value)) return false;
5972
5972
  const ref = value["$secret"];
5973
5973
  return typeof ref === "string" && ref.length > 0;
5974
5974
  }
5975
5975
  function isSecretClear(value) {
5976
- return isRecord$6(value) && value["$secretClear"] === true;
5976
+ return isRecord$7(value) && value["$secretClear"] === true;
5977
5977
  }
5978
5978
  /**
5979
5979
  * The UI view of a stored secret field value: a marker becomes "set", inline
@@ -6679,7 +6679,7 @@ var KeychainWriteRefusedError = class extends Error {
6679
6679
  }
6680
6680
  };
6681
6681
  var KEEP = { kind: "keep" };
6682
- function isRecord$5(value) {
6682
+ function isRecord$6(value) {
6683
6683
  return typeof value === "object" && value !== null && !Array.isArray(value);
6684
6684
  }
6685
6685
  function defaultMintId() {
@@ -6692,7 +6692,7 @@ function collectSecretMarkerRefs(value, out = []) {
6692
6692
  return out;
6693
6693
  }
6694
6694
  if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
6695
- else if (isRecord$5(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6695
+ else if (isRecord$6(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6696
6696
  return out;
6697
6697
  }
6698
6698
  async function storedValueEquals(secrets, marker, candidate) {
@@ -6747,7 +6747,7 @@ function rowId(row, idKey) {
6747
6747
  return null;
6748
6748
  }
6749
6749
  async function writeArray(ctx, node, incoming, stored, fieldPath) {
6750
- const storedRows = Array.isArray(stored) ? stored.filter(isRecord$5) : [];
6750
+ const storedRows = Array.isArray(stored) ? stored.filter(isRecord$6) : [];
6751
6751
  if (!Array.isArray(incoming)) {
6752
6752
  ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
6753
6753
  return {
@@ -6763,7 +6763,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
6763
6763
  const kept = /* @__PURE__ */ new Set();
6764
6764
  const rows = [];
6765
6765
  for (const row of incoming) {
6766
- if (!isRecord$5(row)) {
6766
+ if (!isRecord$6(row)) {
6767
6767
  rows.push(row);
6768
6768
  continue;
6769
6769
  }
@@ -6788,7 +6788,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6788
6788
  case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
6789
6789
  case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
6790
6790
  case "object": {
6791
- if (!isRecord$5(incoming)) {
6791
+ if (!isRecord$6(incoming)) {
6792
6792
  if (incoming === void 0) return KEEP;
6793
6793
  ctx.deletes.push(...collectSecretMarkerRefs(stored));
6794
6794
  return {
@@ -6796,7 +6796,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6796
6796
  value: incoming
6797
6797
  };
6798
6798
  }
6799
- const storedObj = isRecord$5(stored) ? stored : {};
6799
+ const storedObj = isRecord$6(stored) ? stored : {};
6800
6800
  return {
6801
6801
  kind: "value",
6802
6802
  value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
@@ -6919,7 +6919,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
6919
6919
  value: items
6920
6920
  };
6921
6921
  }
6922
- if (isRecord$5(v)) {
6922
+ if (isRecord$6(v)) {
6923
6923
  const out = {};
6924
6924
  for (const [k, child] of Object.entries(v)) {
6925
6925
  const s = substitute(child);
@@ -6951,7 +6951,7 @@ var SecretOverwriteRefusedError = class extends Error {
6951
6951
  this.name = "SecretOverwriteRefusedError";
6952
6952
  }
6953
6953
  };
6954
- function isRecord$4(value) {
6954
+ function isRecord$5(value) {
6955
6955
  return typeof value === "object" && value !== null && !Array.isArray(value);
6956
6956
  }
6957
6957
  function resolvedValue(pass, marker) {
@@ -6980,7 +6980,7 @@ function inArray(pass, incoming, stored) {
6980
6980
  return incoming.map((item, index) => {
6981
6981
  const twin = stored[index];
6982
6982
  if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
6983
- if (isRecord$4(item) && isRecord$4(twin)) return inArrayRow(pass, item, twin);
6983
+ if (isRecord$5(item) && isRecord$5(twin)) return inArrayRow(pass, item, twin);
6984
6984
  if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
6985
6985
  return item;
6986
6986
  });
@@ -6991,7 +6991,7 @@ function inArrayRow(pass, incoming, stored) {
6991
6991
  const twin = stored[key];
6992
6992
  if (isSecretMarker(twin)) {
6993
6993
  if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
6994
- } else if (isRecord$4(value) && isRecord$4(twin)) out[key] = inArrayRow(pass, value, twin);
6994
+ } else if (isRecord$5(value) && isRecord$5(twin)) out[key] = inArrayRow(pass, value, twin);
6995
6995
  else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
6996
6996
  }
6997
6997
  return out;
@@ -6999,7 +6999,7 @@ function inArrayRow(pass, incoming, stored) {
6999
6999
  async function walk(pass, incoming, stored) {
7000
7000
  if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
7001
7001
  if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
7002
- if (!isRecord$4(incoming) || !isRecord$4(stored)) return incoming;
7002
+ if (!isRecord$5(incoming) || !isRecord$5(stored)) return incoming;
7003
7003
  const out = { ...incoming };
7004
7004
  for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
7005
7005
  return out;
@@ -7030,7 +7030,7 @@ async function restoreUnchangedSecrets(input) {
7030
7030
  function isJsonEqual(a, b) {
7031
7031
  if (Object.is(a, b)) return true;
7032
7032
  if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
7033
- if (isRecord$4(a) && isRecord$4(b)) {
7033
+ if (isRecord$5(a) && isRecord$5(b)) {
7034
7034
  const keys = Object.keys(a);
7035
7035
  if (keys.length !== Object.keys(b).length) return false;
7036
7036
  return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
@@ -7054,7 +7054,7 @@ function markerKeysOf(blob, owner) {
7054
7054
  }
7055
7055
  function nodeFromStored(value, owner) {
7056
7056
  if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
7057
- if (!isRecord$4(value)) return null;
7057
+ if (!isRecord$5(value)) return null;
7058
7058
  const children = /* @__PURE__ */ new Map();
7059
7059
  for (const [key, child] of Object.entries(value)) {
7060
7060
  const node = nodeFromStored(child, owner);
@@ -7673,6 +7673,44 @@ var BaseAddon = class {
7673
7673
  return null;
7674
7674
  }
7675
7675
  /**
7676
+ * D986 scoped settings: the form shape of the CLUSTER-wide settings one capability this addon
7677
+ * provides owns, or `null` when it has none. `cap` is a capability name the addon's manifest
7678
+ * declares. Stored as `addon-settings` rows `<addonId>.<field>`. The keys of this schema, of
7679
+ * {@link nodeSettingsSchema} and of {@link deviceDefaultsSchema} are disjoint, and a key never
7680
+ * contains `@` (guard `check-scoped-settings-declared`). SHAPE only: values are read by scope.
7681
+ */
7682
+ async clusterSettingsSchema(_cap) {
7683
+ return null;
7684
+ }
7685
+ /**
7686
+ * D986: the form shape of the settings one capability owns PER NODE, or `null`. Stored as
7687
+ * `addon-settings` rows `<addonId>.<field>@<nodeId>` carrying `nodeId` in the row data, so a
7688
+ * forgotten node's rows go in one central delete.
7689
+ */
7690
+ async nodeSettingsSchema(_cap) {
7691
+ return null;
7692
+ }
7693
+ /**
7694
+ * D986: the form shape of the FLEET DEFAULTS of the per-device settings one capability owns,
7695
+ * or `null`. Stored as `addon-settings` rows `<addonId>.<field>`; a device resolves its value
7696
+ * as device row, then this default, then the schema default (`resolveDeviceSettings`).
7697
+ */
7698
+ async deviceDefaultsSchema(_cap) {
7699
+ return null;
7700
+ }
7701
+ /**
7702
+ * The host's read of the scoped declarations: the schema one capability declares for `scope`,
7703
+ * or `null`. Beside {@link getIntegrationFormSchema}, the same shape: the hook is protected, the
7704
+ * host asks this method.
7705
+ */
7706
+ async getScopedSettingsSchema(scope, cap) {
7707
+ switch (scope) {
7708
+ case "cluster": return this.clusterSettingsSchema(cap);
7709
+ case "node": return this.nodeSettingsSchema(cap);
7710
+ case "device-defaults": return this.deviceDefaultsSchema(cap);
7711
+ }
7712
+ }
7713
+ /**
7676
7714
  * What the create wizard and the integration edit page render for one instance: the addon's
7677
7715
  * per-instance schema when it declares one, else its global schema (what they always showed).
7678
7716
  */
@@ -8466,6 +8504,21 @@ function normalizeAddonInitResult(result) {
8466
8504
  if (Array.isArray(result)) return { providers: result };
8467
8505
  return result;
8468
8506
  }
8507
+ /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8508
+ /** The files a kind may live in. `history` is device-history's (D850), never records. */
8509
+ var RECORD_DATABASES = ["settings", "events"];
8510
+ /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8511
+ var RECORD_CLASSES = [
8512
+ "config",
8513
+ "ledger",
8514
+ "audit",
8515
+ "mirror",
8516
+ "ephemeral"
8517
+ ];
8518
+ var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8519
+ var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8520
+ var RECORD_BATCH_MAX = 1e3;
8521
+ var RECORD_QUERY_MAX = 1e3;
8469
8522
  /**
8470
8523
  * The share-view scope kinds a share-link token can carry. The hub mints
8471
8524
  * and validates them (`ShareViewGrantSchema` builds its
@@ -8490,21 +8543,6 @@ var PeerBytesTicketSchema = object({
8490
8543
  */
8491
8544
  declaredBytes: number().int().nonnegative().nullable()
8492
8545
  });
8493
- /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8494
- /** The files a kind may live in. `history` is device-history's (D850), never records. */
8495
- var RECORD_DATABASES = ["settings", "events"];
8496
- /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8497
- var RECORD_CLASSES = [
8498
- "config",
8499
- "ledger",
8500
- "audit",
8501
- "mirror",
8502
- "ephemeral"
8503
- ];
8504
- var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8505
- var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8506
- var RECORD_BATCH_MAX = 1e3;
8507
- var RECORD_QUERY_MAX = 1e3;
8508
8546
  /**
8509
8547
  * Record kinds (D881): the declaration an addon makes of one kind of fact, the
8510
8548
  * Zod row schema that describes it, and the helper that turns the two into the
@@ -9304,6 +9342,7 @@ var CAP_NAMES = Object.freeze([
9304
9342
  "humidity-sensor",
9305
9343
  "image",
9306
9344
  "image-settings",
9345
+ "integration-instances",
9307
9346
  "integrations",
9308
9347
  "intercom",
9309
9348
  "ir-transmitter",
@@ -11237,7 +11276,7 @@ object({
11237
11276
  params: record(string(), union([string(), number()])).optional()
11238
11277
  });
11239
11278
  //#endregion
11240
- //#region ../types/dist/composition-kKKOdFIG.mjs
11279
+ //#region ../types/dist/composition-BDUoQsw9.mjs
11241
11280
  /**
11242
11281
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11243
11282
  *
@@ -20720,6 +20759,7 @@ var SYSTEM_CAP_VOCABULARY = {
20720
20759
  "load-contribution": system(INTERNAL),
20721
20760
  "local-network": system(NETWORK),
20722
20761
  "log-channels": system(INTERNAL),
20762
+ "integration-instances": system(INTERNAL),
20723
20763
  "log-destination": system(INTERNAL),
20724
20764
  "login-method": system(IDENTITY),
20725
20765
  "mesh-network": system(NETWORK),
@@ -42866,7 +42906,7 @@ var TargetKindCapsSchema = object({
42866
42906
  * the union is large and not meant for runtime validation here; the exported
42867
42907
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
42868
42908
  */
42869
- var ConfigSchemaPassthrough$3 = unknown();
42909
+ var ConfigSchemaPassthrough$4 = unknown();
42870
42910
  var TargetKindSchema = object({
42871
42911
  kind: string(),
42872
42912
  label: string(),
@@ -42903,7 +42943,7 @@ var TargetKindSchema = object({
42903
42943
  * its raster path, which is the safe default for an unknown image.
42904
42944
  */
42905
42945
  iconMediaType: string().optional(),
42906
- configSchema: ConfigSchemaPassthrough$3,
42946
+ configSchema: ConfigSchemaPassthrough$4,
42907
42947
  /**
42908
42948
  * The config keys that decide WHERE this kind's secrets are sent (server,
42909
42949
  * base URL, instance, chat, recipient). A save that changes any of them
@@ -52623,6 +52663,78 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
52623
52663
  auth: "admin"
52624
52664
  });
52625
52665
  /**
52666
+ * `integration-instances` - the connection of an addon that holds ONE or SEVERAL
52667
+ * (an account, a relay, a destination) as typed records (D985, D986). The addon's
52668
+ * own function page reads and writes them (Logging for Loki); the Integrations
52669
+ * page lists only the integrations that bring devices (D996).
52670
+ *
52671
+ * A collection of providers: each addon that holds connections registers one and a
52672
+ * caller reaches a provider with the pinned `addonId`.
52673
+ *
52674
+ * Identity: the instance id is immutable and opaque to the caller. A record the
52675
+ * Loki migration made keeps the `integrations` registry id it was created under
52676
+ * (`int_NNNN`); a single instance made by its function page takes the addon id.
52677
+ * `name` is the editable label.
52678
+ *
52679
+ * Secrets: every field the instance kind marks `.meta({ secret: true })` reaches
52680
+ * the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
52681
+ * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
52682
+ * clears it.
52683
+ */
52684
+ var ConfigSchemaPassthrough$3 = custom();
52685
+ var IntegrationInstanceIdSchema = string().min(1);
52686
+ var IntegrationInstanceDescriptionSchema = object({
52687
+ /** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
52688
+ instanceMode: _enum(["single", "multiple"]),
52689
+ /** What the instance is, for the UI (`destination`, `relay`, `account`). */
52690
+ kind: string(),
52691
+ /** True when `testInstance` really probes the remote. */
52692
+ canTest: boolean()
52693
+ });
52694
+ var IntegrationInstanceStatusSchema = object({
52695
+ state: _enum([
52696
+ "ok",
52697
+ "off",
52698
+ "error",
52699
+ "unknown"
52700
+ ]),
52701
+ detail: string().optional()
52702
+ });
52703
+ var IntegrationInstanceSchema = object({
52704
+ id: IntegrationInstanceIdSchema,
52705
+ name: string(),
52706
+ /** The addon's own rule (an URL is present, a client id is present...). */
52707
+ configured: boolean(),
52708
+ /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
52709
+ formValues: record(string(), unknown()),
52710
+ status: IntegrationInstanceStatusSchema.optional()
52711
+ });
52712
+ var IntegrationInstancePatchSchema = object({
52713
+ id: IntegrationInstanceIdSchema,
52714
+ name: string().min(1).optional(),
52715
+ /** Omitted / '' / redaction token keeps a secret; `{ $secretClear: true }` clears it. */
52716
+ values: record(string(), unknown())
52717
+ });
52718
+ method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }), method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }), method(object({
52719
+ id: IntegrationInstanceIdSchema,
52720
+ name: string().min(1)
52721
+ }), object({ id: string() }), {
52722
+ kind: "mutation",
52723
+ auth: "admin"
52724
+ }), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), {
52725
+ kind: "mutation",
52726
+ auth: "admin"
52727
+ }), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), {
52728
+ kind: "mutation",
52729
+ auth: "admin"
52730
+ }), optionalMethod(object({
52731
+ id: IntegrationInstanceIdSchema.nullable(),
52732
+ values: record(string(), unknown())
52733
+ }), TestConnectionResultSchema$1, {
52734
+ kind: "mutation",
52735
+ auth: "admin"
52736
+ });
52737
+ /**
52626
52738
  * Two-way audio / talk-back cap. Clients open a WebRTC audio-only
52627
52739
  * session (server-initiated offer pattern, identical to the main
52628
52740
  * video WebRTC flow) and the browser captures the mic; the server
@@ -70323,7 +70435,16 @@ DeviceType.Sensor;
70323
70435
  * future fields without breaking the existing call sites.
70324
70436
  */
70325
70437
  var EmailAddressSchema = email();
70438
+ /**
70439
+ * Which relay of a multi-relay provider answers (D996). Absent = the provider's first / default
70440
+ * relay, so a caller written before relays existed keeps working unchanged. A provider with one
70441
+ * fixed relay ignores it. This is the integration instance id (`int_NNNN`), NOT the addon id: the
70442
+ * collection pin `addonId` still picks the provider, `instanceId` picks the relay inside it.
70443
+ */
70444
+ var InstanceIdSchema = string().min(1).optional();
70445
+ var RelayPinSchema = object({ instanceId: InstanceIdSchema });
70326
70446
  var SendEmailInputSchema = object({
70447
+ instanceId: InstanceIdSchema,
70327
70448
  to: union([EmailAddressSchema, array(EmailAddressSchema).min(1)]),
70328
70449
  cc: array(EmailAddressSchema).optional(),
70329
70450
  bcc: array(EmailAddressSchema).optional(),
@@ -70359,11 +70480,11 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
70359
70480
  kind: "mutation",
70360
70481
  auth: "admin",
70361
70482
  access: "create"
70362
- }), method(_void(), SmtpStatusSchema, {
70483
+ }), method(RelayPinSchema, SmtpStatusSchema, {
70363
70484
  kind: "mutation",
70364
70485
  auth: "admin",
70365
70486
  access: "view"
70366
- }), method(_void(), SmtpStatusSchema, { auth: "admin" });
70487
+ }), method(RelayPinSchema, SmtpStatusSchema, { auth: "admin" });
70367
70488
  /**
70368
70489
  * A single device snapshot returned as base64 JPEG/PNG.
70369
70490
  *
@@ -77976,6 +78097,62 @@ Object.freeze({
77976
78097
  access: "create",
77977
78098
  role: "admin"
77978
78099
  },
78100
+ "integrationInstances.createInstance": {
78101
+ capName: "integration-instances",
78102
+ capScope: "system",
78103
+ addonId: null,
78104
+ access: "create",
78105
+ role: "admin"
78106
+ },
78107
+ "integrationInstances.deleteInstance": {
78108
+ capName: "integration-instances",
78109
+ capScope: "system",
78110
+ addonId: null,
78111
+ access: "delete",
78112
+ role: "admin"
78113
+ },
78114
+ "integrationInstances.describe": {
78115
+ capName: "integration-instances",
78116
+ capScope: "system",
78117
+ addonId: null,
78118
+ access: "view",
78119
+ role: "admin"
78120
+ },
78121
+ "integrationInstances.getInstance": {
78122
+ capName: "integration-instances",
78123
+ capScope: "system",
78124
+ addonId: null,
78125
+ access: "view",
78126
+ role: "admin"
78127
+ },
78128
+ "integrationInstances.instanceFormSchema": {
78129
+ capName: "integration-instances",
78130
+ capScope: "system",
78131
+ addonId: null,
78132
+ access: "view",
78133
+ role: "admin"
78134
+ },
78135
+ "integrationInstances.listInstances": {
78136
+ capName: "integration-instances",
78137
+ capScope: "system",
78138
+ addonId: null,
78139
+ access: "view",
78140
+ role: "admin"
78141
+ },
78142
+ "integrationInstances.testInstance": {
78143
+ capName: "integration-instances",
78144
+ capScope: "system",
78145
+ addonId: null,
78146
+ access: "create",
78147
+ role: "admin"
78148
+ },
78149
+ "integrationInstances.updateInstance": {
78150
+ capName: "integration-instances",
78151
+ capScope: "system",
78152
+ addonId: null,
78153
+ access: "create",
78154
+ role: "admin"
78155
+ },
77979
78156
  "integrations.create": {
77980
78157
  capName: "integrations",
77981
78158
  capScope: "system",
@@ -89281,7 +89458,7 @@ var AgentUIAddon = class extends BaseAddon {
89281
89458
  capability: adminUiCapability,
89282
89459
  provider: {
89283
89460
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
89284
- getVersion: async () => ({ version: "1.2.313" })
89461
+ getVersion: async () => ({ version: "1.2.314" })
89285
89462
  }
89286
89463
  }];
89287
89464
  }
@@ -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 a,d as o,g as s,l as c,p as l,t as u,w as d}from"./index-D5vO_ttL.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=d(`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=o(s().trpcClient,e),[d,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;c(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=d?d.grid.width:0,U=d?d.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&d?O(j,H,U,F):null,[j,d,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&&d&&M(D(N,H,U,F))},[N,d,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!d||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,d,H,U]),Q=(0,m.useMemo)(()=>d&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[d,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!d)){R(!0);try{let e=O(j,d.grid.width,d.grid.height,F),t={kind:`grid`,gridWidth:d.grid.width,gridHeight:d.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:[],d.grid.width,d.grid.height);P(t),M(D(t,d.grid.width,d.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,d,F]);u((0,m.useMemo)(()=>z&&!k&&d&&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,d,j,Q,$,W,G]));let ie=!k&&d!==null&&j!==null;return i?(0,h.jsx)(l,{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:`${a} 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:`${a} 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:d.grid.width,height:d.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:`${a} 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:`${a} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:d.grid.width,height:d.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:`${a} 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{c as a,d as o,g as s,l as c,p as l,t as u,w as d}from"./index-DlpbU1Jb.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=d(`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=o(s().trpcClient,e),[d,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;c(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=d?d.grid.width:0,U=d?d.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&d?O(j,H,U,F):null,[j,d,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&&d&&M(D(N,H,U,F))},[N,d,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!d||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,d,H,U]),Q=(0,m.useMemo)(()=>d&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[d,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!d)){R(!0);try{let e=O(j,d.grid.width,d.grid.height,F),t={kind:`grid`,gridWidth:d.grid.width,gridHeight:d.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:[],d.grid.width,d.grid.height);P(t),M(D(t,d.grid.width,d.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,d,F]);u((0,m.useMemo)(()=>z&&!k&&d&&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,d,j,Q,$,W,G]));let ie=!k&&d!==null&&j!==null;return i?(0,h.jsx)(l,{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:`${a} 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:`${a} 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:d.grid.width,height:d.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:`${a} 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:`${a} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:d.grid.width,height:d.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:`${a} 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{S as ee,_ as a,b as te,c as o,d as ne,g as s,l as c,p as re,t as l,v as u,w as d}from"./index-D5vO_ttL.js";import{MaskShapeCanvas as ie}from"./MaskShapeCanvas-BTvl6Rin.js";var ae=d(`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=d(`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(s().trpcClient,e),[d,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;c(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=d?d.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:d===null?null:d.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=d?.supportedShapes??[];l((0,f.useMemo)(()=>O&&!m&&d&&S?{id:ue,order:le,node:(0,h.jsx)(ie,{transparent:!0,items:K,supportedShapes:Q,polygonVertices:d.polygonVertices,selectedId:A,onSelect:G,onShapeChange:q,onDrawComplete:J,drawingKind:M})}:null,[O,m,d,S,K,Q,A,G,q,J,M]));let be=d?.supportedShapes.includes(`rect`)??!1,xe=d?.supportedShapes.includes(`polygon`)??!1,$=d!==null&&(d.maxRegions<=0||d.supportedShapes.length===0),Se=!m&&!$&&!b&&d!==null&&S!==null;return i?(0,h.jsx)(re,{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:u;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)(a,{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)(te,{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{S as ee,_ as a,b as te,c as o,d as ne,g as s,l as c,p as re,t as l,v as u,w as d}from"./index-DlpbU1Jb.js";import{MaskShapeCanvas as ie}from"./MaskShapeCanvas-BTvl6Rin.js";var ae=d(`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=d(`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(s().trpcClient,e),[d,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;c(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=d?d.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:d===null?null:d.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=d?.supportedShapes??[];l((0,f.useMemo)(()=>O&&!m&&d&&S?{id:ue,order:le,node:(0,h.jsx)(ie,{transparent:!0,items:K,supportedShapes:Q,polygonVertices:d.polygonVertices,selectedId:A,onSelect:G,onShapeChange:q,onDrawComplete:J,drawingKind:M})}:null,[O,m,d,S,K,Q,A,G,q,J,M]));let be=d?.supportedShapes.includes(`rect`)??!1,xe=d?.supportedShapes.includes(`polygon`)??!1,$=d!==null&&(d.maxRegions<=0||d.supportedShapes.length===0),Se=!m&&!$&&!b&&d!==null&&S!==null;return i?(0,h.jsx)(re,{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:u;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)(a,{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)(te,{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};