@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 +12 -0
- package/dist/.bundle-modules.json +4 -4
- package/dist/addon.js +280 -80
- package/dist/assets/{MotionZonesSettings-Bql_KGR7.js → MotionZonesSettings-rkIquCK5.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-Cjgq7MXA.js → PrivacyMaskSettings-BOXuNuRk.js} +1 -1
- package/dist/assets/{SceneMonitorEditor-DQpvO_9L.js → SceneMonitorEditor-CUqDYKzv.js} +1 -1
- package/dist/assets/index-CJE_vVNZ.js +145 -0
- package/dist/index.html +1 -1
- package/package.json +2 -2
- package/dist/assets/index-C3JZYknO.js +0 -145
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": "
|
|
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": "
|
|
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-
|
|
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": "
|
|
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-
|
|
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
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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-
|
|
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};
|