@camstack/addon-agent-ui 1.2.337 → 1.2.339

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.339] - 2026-11-07
11
+
12
+ ### Changed
13
+
14
+ - Rebuilt against updated dependencies.
15
+
16
+ ## [1.2.338] - 2026-11-07
17
+
18
+ ### Changed
19
+
20
+ - Rebuilt against updated dependencies.
21
+
10
22
  ## [1.2.337] - 2026-11-07
11
23
 
12
24
  ### Changed
@@ -367,13 +367,13 @@
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": "6de32b198d01e7cd8c25d936effef662421de90c5131c21c3fc3ac1a6fd3e02c",
370
+ "server/server/packages/types/dist/client.mjs": "33a0c9164217dc630c1fdc5f86c9ef693dbc6c944351e4f236ee16ec09302ea5",
371
371
  "server/server/packages/types/dist/composition-BmBHuDTQ.mjs": "0fedd1640f42bfdbb8e59f8f11a7aa597a59c35c2574297820bbc026c6236f74",
372
372
  "server/server/packages/types/dist/device-type-D1fH5kaC.mjs": "4002ae7c812789715d7a35271c91b8133511b710eaeb07fea061d285db3dc14a",
373
373
  "server/server/packages/types/dist/event-category-78tcBemo.mjs": "767f1bb576027d9ee79ec036367001330203d84842c2c2111fa465fa434045e2",
374
- "server/server/packages/types/dist/index.mjs": "da32c317f13d7d9f3f978f58e70d4a783300f8c628348282d734e4c1b492d00a",
374
+ "server/server/packages/types/dist/index.mjs": "17749f2e9eddda05cdbcb9e37985c1e89a1aebc86ecc46b03638fbdb2b2bb7fd",
375
375
  "server/server/packages/types/dist/node-color.mjs": "942e663074f0a4ed2f8643848ec139eba4b5fd16d0d01c8a23e08a4819a8843b",
376
- "server/server/packages/types/dist/sleep-AEnTN3b4.mjs": "832102f82ade2f54d4fc035a68a55a8b7ca645bdfe6ff5d6a546865ac4d78762",
376
+ "server/server/packages/types/dist/sleep-CQP-REJp.mjs": "ef67ec7f4157dc194b75bc52a2d629a6df7bc15f69d14d8ce69b5208237854cc",
377
377
  "server/server/packages/ui-library/src/composites/adaptive-downlink.ts": "6e9698e97721461b72a3750f930dd7214d049be865491bfb5a64b87c032b0e22",
378
378
  "server/server/packages/ui-library/src/composites/camera-stream-player.tsx": "c04809229b4e091fed09e2bcfe643d6b36cb3a39e294229b49f8a095e4e6ebd3",
379
379
  "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": "ff7b9f41e948b3ec798180f0795a0672dc90d6e43d0a7c07abbc4101b4186a5f",
489
+ "server/server/packages/ui-library/src/generated/system-hooks.ts": "b63d79cc6d673831d7c80a1c289c6a1f177e65a2f77503ffac4ebcebf970b23b",
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
@@ -5866,7 +5866,7 @@ var ZodIssueCode = {
5866
5866
  var ZodFirstPartyTypeKind;
5867
5867
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5868
5868
  //#endregion
5869
- //#region ../types/dist/sleep-AEnTN3b4.mjs
5869
+ //#region ../types/dist/sleep-CQP-REJp.mjs
5870
5870
  /**
5871
5871
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5872
5872
  * window to float samples (D455).
@@ -6223,6 +6223,59 @@ Object.fromEntries([
6223
6223
  function isValuelessField(field) {
6224
6224
  return field.type === "separator" || field.type === "info" || field.type === "qr-code" || field.type === "button" || field.type === "object-array" || field.type === "widget" || field.type === "addon-action-button" || field.type === "device-action-button" || field.type === "account-sign-in" || field.type === "secret-presence";
6225
6225
  }
6226
+ /**
6227
+ * Base collector: flatten a hydrated `ConfigUISchemaWithValues` into
6228
+ * `key → { value, default }`, recursing through `group` and `sub-tabs`
6229
+ * layout fields. Keeping `value` and `default` SEPARATE (rather than
6230
+ * pre-resolving) lets callers both read the effective value AND detect
6231
+ * overrides (`value` differs from `default`). Structural over `unknown`;
6232
+ * malformed input yields an empty map, never throws.
6233
+ */
6234
+ function collectHydratedFieldEntries(schema) {
6235
+ const out = /* @__PURE__ */ new Map();
6236
+ if (typeof schema !== "object" || schema === null) return out;
6237
+ const sections = schema.sections;
6238
+ if (!Array.isArray(sections)) return out;
6239
+ for (const section of sections) if (typeof section === "object" && section !== null) collectFieldEntriesInto(section.fields, out);
6240
+ return out;
6241
+ }
6242
+ /** Recursive field walker for {@link collectHydratedFieldEntries}. */
6243
+ function collectFieldEntriesInto(fields, into) {
6244
+ if (!Array.isArray(fields)) return;
6245
+ for (const field of fields) {
6246
+ if (typeof field !== "object" || field === null) continue;
6247
+ const type = field.type;
6248
+ if (type === "group") {
6249
+ collectFieldEntriesInto(field.fields, into);
6250
+ continue;
6251
+ }
6252
+ if (type === "sub-tabs") {
6253
+ const tabs = field.tabs;
6254
+ if (Array.isArray(tabs)) {
6255
+ for (const tab of tabs) if (typeof tab === "object" && tab !== null) collectFieldEntriesInto(tab.fields, into);
6256
+ }
6257
+ continue;
6258
+ }
6259
+ const key = field.key;
6260
+ if (typeof key !== "string" || key.length === 0) continue;
6261
+ if (!("value" in field)) continue;
6262
+ into.set(key, {
6263
+ value: field.value,
6264
+ default: field.default
6265
+ });
6266
+ }
6267
+ }
6268
+ /**
6269
+ * Flatten a hydrated settings payload into `key → EFFECTIVE value`
6270
+ * (`value ?? default`). The read-side inverse of `hydrateSchema` for the
6271
+ * common "just give me the resolved values" case. Built on
6272
+ * {@link collectHydratedFieldEntries}.
6273
+ */
6274
+ function collectHydratedFieldValues(schema) {
6275
+ const out = /* @__PURE__ */ new Map();
6276
+ for (const [key, entry] of collectHydratedFieldEntries(schema)) out.set(key, entry.value ?? entry.default);
6277
+ return out;
6278
+ }
6226
6279
  function hydrateSchema(schema, values, options = {}) {
6227
6280
  return {
6228
6281
  ...schema.tabs ? { tabs: [...schema.tabs] } : {},
@@ -6918,6 +6971,21 @@ var SCOPED_SETTINGS_SCOPES = [
6918
6971
  "device-defaults",
6919
6972
  "node"
6920
6973
  ];
6974
+ /** The one refusal of a key a scope did not declare. */
6975
+ function assertDeclaredKeys(keys, declared) {
6976
+ const undeclared = keys.filter((key) => !declared.has(key));
6977
+ if (undeclared.length > 0) throw new Error(`scoped settings: [${undeclared.join(", ")}] not declared for this scope (declared: [${[...declared].join(", ")}])`);
6978
+ }
6979
+ /** Build the handle over an addon's scoped read, write and unset. */
6980
+ function createScopedSettings(deps) {
6981
+ return {
6982
+ async get() {
6983
+ return deps.schema.parse(await deps.read());
6984
+ },
6985
+ patch: (patch) => deps.write(Object.fromEntries(Object.entries(patch))),
6986
+ unset: (keys) => deps.unset(keys)
6987
+ };
6988
+ }
6921
6989
  /** A write to a secret path could not be stored in the keychain, so it was refused. */
6922
6990
  var KeychainWriteRefusedError = class extends Error {
6923
6991
  fieldPath;
@@ -7493,7 +7561,16 @@ function createHandle(raw, opts) {
7493
7561
  return blob;
7494
7562
  }
7495
7563
  },
7496
- writeAddonStore: writeAddon
7564
+ writeAddonStore: writeAddon,
7565
+ removeClearedKeys: (keys) => serialize("addon", async () => {
7566
+ const stored = await readStoredAddonBlob(raw);
7567
+ index.seen("addon", stored);
7568
+ const removable = keys.filter((key) => collectSecretMarkerRefs(stored[key]).length === 0);
7569
+ if (removable.length === 0) return;
7570
+ const patch = Object.fromEntries(removable.map((key) => [key, void 0]));
7571
+ await raw.writeAddonStore(patch);
7572
+ index.wrote("addon", patch);
7573
+ })
7497
7574
  };
7498
7575
  }
7499
7576
  /** The context the addon sees, plus the private raw view and write outcome. */
@@ -8038,6 +8115,151 @@ var BaseAddon = class {
8038
8115
  }
8039
8116
  }
8040
8117
  /**
8118
+ * Hook: values layered over the stored ones when the FORM of one scope is served
8119
+ * ({@link getScopedSettings}) — a preview that derives dependent options without writing.
8120
+ * Never applied to {@link ScopedSettings.get} or to a write. `null` ⇒ no overlay.
8121
+ */
8122
+ async scopedSettingsOverlay(_scope, _cap, _nodeId) {
8123
+ return null;
8124
+ }
8125
+ /**
8126
+ * Hook: a write of one scope changed stored values (not called when the store reports no change,
8127
+ * e.g. the write-back of a stored secret). `keys` are the bare field keys of the write, node keys included.
8128
+ */
8129
+ async onScopedSettingsChanged(_scope, _cap, _keys) {}
8130
+ /** Typed handle over the CLUSTER settings `cap` declares (`clusterSettingsSchema`). */
8131
+ clusterSettings(cap, schema) {
8132
+ return this.scopedHandle("cluster", cap, schema);
8133
+ }
8134
+ /** Typed handle over the fleet defaults of the per-device settings `cap` declares. */
8135
+ deviceDefaults(cap, schema) {
8136
+ return this.scopedHandle("device-defaults", cap, schema);
8137
+ }
8138
+ /**
8139
+ * Typed handle over the settings `cap` owns PER NODE. `nodeId` omitted ⇒ the local node. A node
8140
+ * with no row yields the schema defaults — never a bare-key fallback.
8141
+ */
8142
+ nodeSettings(cap, schema, nodeId) {
8143
+ return this.scopedHandle("node", cap, schema, nodeId);
8144
+ }
8145
+ /**
8146
+ * {@link state} for a value that is kept per node: stored under `<key>@<nodeId>`, and the
8147
+ * settings store carries `nodeId` in the row data (D986 §1), so a forgotten node's rows go in one
8148
+ * central delete. `nodeId` omitted ⇒ the local node.
8149
+ */
8150
+ nodeState(key, schema, fallback, nodeId) {
8151
+ this._declaredStateKeys.add(key);
8152
+ return createDurableState({
8153
+ key: nodeScopedKey(key, nodeId ?? this._ctx?.kernel?.localNodeId ?? ""),
8154
+ schema,
8155
+ fallback,
8156
+ read: async () => this.resolveStoredSecrets(await this.readAddonStoreWithRetry()),
8157
+ write: async (patch) => {
8158
+ await this._ctx?.settings?.writeAddonStore(patch);
8159
+ },
8160
+ onParseError: (k, e) => this._ctx?.logger?.warn?.(`durable-state: stored "${k}" failed validation — using fallback`, { meta: {
8161
+ addonId: this._ctx?.id,
8162
+ key: k,
8163
+ error: String(e)
8164
+ } })
8165
+ });
8166
+ }
8167
+ /** The loaded schema `cap` declares for `scope`, or `null`. */
8168
+ async declaredScopedSchema(scope, cap) {
8169
+ await this.loadScopedSchemas(cap);
8170
+ return this.loadedScopedSchemas(cap).find((entry) => entry.scope === scope)?.schema ?? null;
8171
+ }
8172
+ async requireScopedSchema(scope, cap) {
8173
+ const schema = await this.declaredScopedSchema(scope, cap);
8174
+ if (schema === null) throw new Error(`${this.constructor.name}: cap "${cap}" declares no ${scope} settings`);
8175
+ return schema;
8176
+ }
8177
+ scopedKeys(schema) {
8178
+ return new Set(schema.sections.flatMap((section) => collectValueFieldKeys(section.fields)));
8179
+ }
8180
+ /** `store` narrowed to the keys `schema` declares. */
8181
+ narrowToScope(store, schema) {
8182
+ const keys = this.scopedKeys(schema);
8183
+ return Object.fromEntries(Object.entries(store).filter(([key]) => keys.has(key)));
8184
+ }
8185
+ scopedHandle(scope, cap, schema, nodeId) {
8186
+ return createScopedSettings({
8187
+ schema,
8188
+ read: async () => {
8189
+ const declared = await this.requireScopedSchema(scope, cap);
8190
+ const stored = this.narrowToScope(await this.resolveGlobalStore(nodeId, cap), declared);
8191
+ const effective = collectHydratedFieldValues(hydrateSchema(declared, stored));
8192
+ return {
8193
+ ...Object.fromEntries([...effective].filter(([, value]) => value !== void 0)),
8194
+ ...stored
8195
+ };
8196
+ },
8197
+ write: (patch) => this.writeScoped(scope, cap, patch, nodeId),
8198
+ unset: (keys) => this.unsetScoped(scope, cap, keys, nodeId)
8199
+ });
8200
+ }
8201
+ /** The one write path of the scoped API: declared keys only, then the global patch. */
8202
+ async writeScoped(scope, cap, patch, nodeId) {
8203
+ assertDeclaredKeys(Object.keys(patch), this.scopedKeys(await this.requireScopedSchema(scope, cap)));
8204
+ if (await this.applyGlobalPatch(patch, scope === "node" ? nodeId : void 0, cap)) await this.onScopedSettingsChanged(scope, cap, Object.keys(patch));
8205
+ }
8206
+ /**
8207
+ * Remove declared keys of one scope. A secret goes through {@link clearSecrets} — its keychain
8208
+ * value is released exactly as there — and the emptied row is then removed; every other key is
8209
+ * removed by an `undefined` patch (which a secret transform would read as "keep").
8210
+ */
8211
+ async unsetScoped(scope, cap, keys, nodeId) {
8212
+ assertDeclaredKeys(keys, this.scopedKeys(await this.requireScopedSchema(scope, cap)));
8213
+ const target = scope === "node" ? nodeId : void 0;
8214
+ const tree = this.addonSecretTree();
8215
+ const secretKeys = keys.filter((key) => tree.has(key));
8216
+ const plainKeys = keys.filter((key) => !tree.has(key));
8217
+ let changed = false;
8218
+ if (plainKeys.length > 0) {
8219
+ const patch = Object.fromEntries(plainKeys.map((key) => [key, void 0]));
8220
+ changed = await this.applyGlobalPatch(patch, target, cap) || changed;
8221
+ }
8222
+ if (secretKeys.length > 0) {
8223
+ await this.clearSecretKeys(secretKeys, target, cap);
8224
+ const node = normalizeNodeId(target ?? this._ctx?.kernel?.localNodeId);
8225
+ const rows = secretKeys.map((key) => scope === "node" ? nodeScopedKey(key, node) : key);
8226
+ if (this._secretSettings !== null) await this._secretSettings.removeClearedKeys(rows);
8227
+ else await this._ctx?.settings?.writeAddonStore(Object.fromEntries(rows.map((row) => [row, void 0])));
8228
+ changed = true;
8229
+ }
8230
+ if (changed) await this.onScopedSettingsChanged(scope, cap, [...keys]);
8231
+ }
8232
+ /**
8233
+ * The hydrated form of one scope of one capability's settings (any
8234
+ * {@link scopedSettingsOverlay} applied), or `null` when the capability declares nothing there.
8235
+ */
8236
+ async getScopedSettings(scope, cap, nodeId) {
8237
+ const schema = await this.declaredScopedSchema(scope, cap);
8238
+ if (schema === null) return null;
8239
+ const stored = this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema);
8240
+ const overlay = await this.scopedSettingsOverlay(scope, cap, nodeId);
8241
+ return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(stored, overlay) : stored, { inlineSecretsAsViews: true });
8242
+ }
8243
+ /** The OPERATOR's write of declared keys of one scope (D787 secret semantics). */
8244
+ async updateScopedSettings(scope, cap, patch, nodeId) {
8245
+ this._operatorWriteDepth++;
8246
+ try {
8247
+ await this.writeScoped(scope, cap, patch, nodeId);
8248
+ } finally {
8249
+ this._operatorWriteDepth--;
8250
+ }
8251
+ }
8252
+ /**
8253
+ * The effective values (`stored ?? default`) of one scope, a secret as its `set` view — never
8254
+ * its value. `null` when the capability declares nothing for the scope.
8255
+ */
8256
+ async readScopedValues(scope, cap, nodeId) {
8257
+ const schema = await this.declaredScopedSchema(scope, cap);
8258
+ if (schema === null) return null;
8259
+ const form = hydrateSchema(schema, this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema), { inlineSecretsAsViews: true });
8260
+ return Object.fromEntries([...collectHydratedFieldValues(form)].filter(([, value]) => value !== void 0));
8261
+ }
8262
+ /**
8041
8263
  * What the create wizard and the integration edit page render for one instance: the addon's
8042
8264
  * per-instance schema when it declares one, else its global schema (what they always showed).
8043
8265
  */
@@ -8189,6 +8411,9 @@ var BaseAddon = class {
8189
8411
  * a keychain (an older kernel) it writes `''`, as such code always did.
8190
8412
  */
8191
8413
  async clearSecrets(keys, nodeId) {
8414
+ await this.clearSecretKeys(keys, nodeId);
8415
+ }
8416
+ async clearSecretKeys(keys, nodeId, cap) {
8192
8417
  const tree = this.addonSecretTree();
8193
8418
  const keychain = this._ctx?.secrets !== void 0;
8194
8419
  const stored = keychain ? await this.readAddonStoreWithRetry() : {};
@@ -8201,25 +8426,27 @@ var BaseAddon = class {
8201
8426
  const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
8202
8427
  patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
8203
8428
  }
8204
- await this.applyGlobalPatch(patch, nodeId);
8429
+ await this.applyGlobalPatch(patch, nodeId, cap);
8205
8430
  }
8206
- async applyGlobalPatch(barePatch, nodeId) {
8207
- await this.loadScopedSchemas();
8208
- const keys = this.perNodeKeys();
8431
+ /** Resolves `true` when the write changed stored values. */
8432
+ async applyGlobalPatch(barePatch, nodeId, cap) {
8433
+ await this.loadScopedSchemas(cap);
8434
+ const keys = this.perNodeKeys(cap);
8209
8435
  const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
8210
8436
  const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
8211
8437
  const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
8212
8438
  const handle = this._secretSettings;
8213
8439
  if (handle !== null) {
8214
8440
  const { changed } = await handle.writeAddonStore(scoped);
8215
- if (!changed) return;
8441
+ if (!changed) return false;
8216
8442
  } else await this._ctx?.settings?.writeAddonStore(scoped);
8217
- if (target !== localNode) return;
8443
+ if (target !== localNode) return true;
8218
8444
  await this.resolveConfig();
8219
8445
  await this.onConfigChanged();
8220
8446
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
8221
8447
  const legacy = this.globalSettingsSchema();
8222
8448
  this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
8449
+ return true;
8223
8450
  }
8224
8451
  /**
8225
8452
  * The set of field keys the global settings schema declares `perNode: true`
@@ -43860,6 +44087,8 @@ var SettingsSchemaWithValuesSchema = object({
43860
44087
  });
43861
44088
  /** Patch object — keys are field names, values are the new field values. */
43862
44089
  var SettingsPatchSchema = record(string(), unknown());
44090
+ /** The scope of a scoped-settings call (D986): where the capability declared the values. */
44091
+ var ScopedSettingsScopeSchema = _enum(SCOPED_SETTINGS_SCOPES);
43863
44092
  /** Standard success response for update operations. */
43864
44093
  var SettingsUpdateResultSchema = object({ success: literal(true) });
43865
44094
  method(object({
@@ -43875,6 +44104,25 @@ method(object({
43875
44104
  kind: "mutation",
43876
44105
  auth: "admin"
43877
44106
  }), method(object({
44107
+ addonId: string(),
44108
+ cap: string(),
44109
+ scope: ScopedSettingsScopeSchema,
44110
+ nodeId: string().optional()
44111
+ }), SettingsSchemaWithValuesSchema.nullable()), method(object({
44112
+ addonId: string(),
44113
+ cap: string(),
44114
+ scope: ScopedSettingsScopeSchema,
44115
+ nodeId: string().optional(),
44116
+ patch: SettingsPatchSchema
44117
+ }), SettingsUpdateResultSchema, {
44118
+ kind: "mutation",
44119
+ auth: "admin"
44120
+ }), method(object({
44121
+ addonId: string(),
44122
+ cap: string(),
44123
+ scope: ScopedSettingsScopeSchema,
44124
+ nodeId: string().optional()
44125
+ }), record(string(), unknown()).nullable()), method(object({
43878
44126
  addonId: string(),
43879
44127
  nodeId: string().optional()
43880
44128
  }), SettingsSchemaWithValuesSchema.nullable()), method(object({
@@ -72837,7 +73085,7 @@ var TerminalInstanceInfoSchema = object({
72837
73085
  /**
72838
73086
  * False while the node the instance is bound to is not in the cluster
72839
73087
  * topology (forgotten, renamed, re-paired). The instance is kept; the
72840
- * operator re-points it with `updateInstance({ targetNodeId })`.
73088
+ * operator re-points it on the Terminal page.
72841
73089
  */
72842
73090
  nodeInCluster: boolean(),
72843
73091
  profileId: string(),
@@ -72875,41 +73123,7 @@ var TerminalOutputBatchSchema = object({
72875
73123
  snapshot: string().optional(),
72876
73124
  events: array(TerminalOutputEventSchema).readonly()
72877
73125
  });
72878
- method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
72879
- targetNodeId: string().min(1),
72880
- profileId: string().min(1),
72881
- name: string().trim().min(1).max(160).optional(),
72882
- executable: string().max(1024).optional(),
72883
- args: array(string().max(2048)).max(64).optional(),
72884
- cwd: string().max(1024).optional(),
72885
- environment: array(string().max(4096)).max(64).optional(),
72886
- profileSettings: ProfileSettingsBagSchema.optional()
72887
- }), TerminalInstanceInfoSchema, {
72888
- kind: "mutation",
72889
- auth: "admin"
72890
- }), method(object({
72891
- instanceId: string().min(1),
72892
- /** Re-point the instance to another node; the profile must exist there. */
72893
- targetNodeId: string().min(1).max(256).optional(),
72894
- name: string().trim().min(1).max(160).optional(),
72895
- executable: string().max(1024).optional(),
72896
- args: array(string().max(2048)).max(64).optional(),
72897
- cwd: string().max(1024).optional(),
72898
- environment: array(string().max(4096)).max(64).optional(),
72899
- profileSettings: ProfileSettingsBagSchema.optional()
72900
- }), TerminalInstanceInfoSchema, {
72901
- kind: "mutation",
72902
- auth: "admin"
72903
- }), method(object({ instanceId: string().min(1) }), _void(), {
72904
- kind: "mutation",
72905
- auth: "admin"
72906
- }), method(object({
72907
- instanceId: string().min(1),
72908
- enabled: boolean()
72909
- }), TerminalInstanceInfoSchema, {
72910
- kind: "mutation",
72911
- auth: "admin"
72912
- }), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
73126
+ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
72913
73127
  stableId: string().min(1),
72914
73128
  name: string().trim().min(1).max(160).optional()
72915
73129
  }), TerminalInstanceInfoSchema, {
@@ -75727,6 +75941,20 @@ Object.freeze({
75727
75941
  access: "view",
75728
75942
  role: "admin"
75729
75943
  },
75944
+ "addonSettings.getScopedSettings": {
75945
+ capName: "addon-settings",
75946
+ capScope: "system",
75947
+ addonId: null,
75948
+ access: "view",
75949
+ role: "admin"
75950
+ },
75951
+ "addonSettings.readScopedValues": {
75952
+ capName: "addon-settings",
75953
+ capScope: "system",
75954
+ addonId: null,
75955
+ access: "view",
75956
+ role: "admin"
75957
+ },
75730
75958
  "addonSettings.unsetStoredSettingsKeys": {
75731
75959
  capName: "addon-settings",
75732
75960
  capScope: "system",
@@ -75748,6 +75976,13 @@ Object.freeze({
75748
75976
  access: "create",
75749
75977
  role: "admin"
75750
75978
  },
75979
+ "addonSettings.updateScopedSettings": {
75980
+ capName: "addon-settings",
75981
+ capScope: "system",
75982
+ addonId: null,
75983
+ access: "create",
75984
+ role: "admin"
75985
+ },
75751
75986
  "addonWidgets.listWidgets": {
75752
75987
  capName: "addon-widgets",
75753
75988
  capScope: "system",
@@ -84638,27 +84873,6 @@ Object.freeze({
84638
84873
  access: "create",
84639
84874
  role: "admin"
84640
84875
  },
84641
- "terminalSession.createInstance": {
84642
- capName: "terminal-session",
84643
- capScope: "system",
84644
- addonId: null,
84645
- access: "create",
84646
- role: "admin"
84647
- },
84648
- "terminalSession.deleteInstance": {
84649
- capName: "terminal-session",
84650
- capScope: "system",
84651
- addonId: null,
84652
- access: "delete",
84653
- role: "admin"
84654
- },
84655
- "terminalSession.listInstances": {
84656
- capName: "terminal-session",
84657
- capScope: "system",
84658
- addonId: null,
84659
- access: "view",
84660
- role: "admin"
84661
- },
84662
84876
  "terminalSession.listLegacyCameras": {
84663
84877
  capName: "terminal-session",
84664
84878
  capScope: "system",
@@ -84701,20 +84915,6 @@ Object.freeze({
84701
84915
  access: "create",
84702
84916
  role: "admin"
84703
84917
  },
84704
- "terminalSession.setInstanceEnabled": {
84705
- capName: "terminal-session",
84706
- capScope: "system",
84707
- addonId: null,
84708
- access: "create",
84709
- role: "admin"
84710
- },
84711
- "terminalSession.updateInstance": {
84712
- capName: "terminal-session",
84713
- capScope: "system",
84714
- addonId: null,
84715
- access: "create",
84716
- role: "admin"
84717
- },
84718
84918
  "terminalSession.writeInput": {
84719
84919
  capName: "terminal-session",
84720
84920
  capScope: "system",
@@ -90032,7 +90232,7 @@ var AgentUIAddon = class extends BaseAddon {
90032
90232
  capability: adminUiCapability,
90033
90233
  provider: {
90034
90234
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
90035
- getVersion: async () => ({ version: "1.2.337" })
90235
+ getVersion: async () => ({ version: "1.2.339" })
90036
90236
  }
90037
90237
  }];
90038
90238
  }
@@ -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-C3JZYknO.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-CJE_vVNZ.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-C3JZYknO.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-CJE_vVNZ.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};