@camstack/addon-osd-manager 0.1.170 → 0.1.171

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.
Files changed (19) hide show
  1. package/dist/{MotionZonesSettings-CTFqdJfp.mjs → MotionZonesSettings-CCNLHmnl.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-M3LLrSyZ.mjs → PrivacyMaskSettings-CUsqDja_.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-BWTOg6t0.mjs → SceneMonitorEditor-C8o2i3G6.mjs} +3 -3
  4. package/dist/_stub.js +4428 -4728
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-CGnGPNWo.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-Cf3xN1-E.mjs} +3 -3
  6. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bszex2Bz.mjs +26 -0
  7. package/dist/addon-osd-manager.css +1 -1
  8. package/dist/{hostInit-CXC2ehtI.mjs → hostInit-C1oxS9dH.mjs} +2 -2
  9. package/dist/index.js +55 -61
  10. package/dist/index.mjs +55 -61
  11. package/dist/{player-overlays-DBi0aH1E.mjs → player-overlays-Bwe6_9ow.mjs} +1 -1
  12. package/dist/remoteEntry.js +1 -1
  13. package/dist/{responsive-BoQdxzOM.mjs → responsive-eWIsQGEg.mjs} +1 -1
  14. package/dist/{scene-monitor-copy-CwfDunLo.mjs → scene-monitor-copy-BDpWrVsR.mjs} +1 -1
  15. package/dist/{square-C-adTToZ.mjs → square-CzU7Lokp.mjs} +1 -1
  16. package/dist/{trash-2-DJjBuL5Q.mjs → trash-2-DLk6p3tf.mjs} +1 -1
  17. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-CtQlS867.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-D-Cxu36Z.mjs} +1 -1
  18. package/package.json +1 -1
  19. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B4GSezZ1.mjs +0 -26
package/dist/index.js CHANGED
@@ -10635,11 +10635,11 @@ var ModelVariantGroupSchema = object({
10635
10635
  /** Quantization axis. Omit ⇒ the fp32 base build. */
10636
10636
  precision: _enum(["fp32", "int8"]).optional(),
10637
10637
  /**
10638
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
10639
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
10640
- * future performance variants plug into.
10638
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
10639
+ * an alternate training of the same family (Scrypted `relu_test`); chip
10640
+ * label is "Test".
10641
10641
  */
10642
- optimization: _enum(["standard", "fast"]).optional(),
10642
+ optimization: _enum(["standard", "test"]).optional(),
10643
10643
  /**
10644
10644
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
10645
10645
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -10662,9 +10662,10 @@ var ModelProviderIdSchema = _enum([
10662
10662
  * are per upstream. A persisted custom-model row written when this field was a
10663
10663
  * string still parses — the string is lifted into `{ weights }`.
10664
10664
  *
10665
- * Every licence surface (the admin "Models and licenses" page,
10666
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
10667
- * these records by `scripts/gen-model-licenses.ts` (D661).
10665
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
10666
+ * model table) is GENERATED from these records by
10667
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
10668
+ * `camstack/docs` → `/docs/detection/models`.
10668
10669
  */
10669
10670
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
10670
10671
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -19767,7 +19768,7 @@ var modelDistributorCapability = {
19767
19768
  mode: "singleton",
19768
19769
  internal: true,
19769
19770
  methods: { distributeModel: method(object({
19770
- nodeId: string(),
19771
+ targetNodeId: string(),
19771
19772
  modelId: string(),
19772
19773
  format: _enum(MODEL_FORMATS),
19773
19774
  entry: ModelCatalogEntrySchema
@@ -24562,39 +24563,19 @@ Object.freeze(Object.fromEntries([{
24562
24563
  label: "Face recognition model",
24563
24564
  defaultModelId: "auraface-r100",
24564
24565
  indexName: "the enrolled face gallery",
24565
- options: [
24566
- {
24567
- id: "auraface-r100",
24568
- label: "AuraFace R100"
24569
- },
24570
- {
24571
- id: "arcface-r100",
24572
- label: "ArcFace (ResNet34)"
24573
- },
24574
- {
24575
- id: "inception-resnet-v1",
24576
- label: "Inception ResNet V1"
24577
- }
24578
- ]
24566
+ options: [{
24567
+ id: "auraface-r100",
24568
+ label: "AuraFace R100"
24569
+ }]
24579
24570
  }, {
24580
24571
  stepId: "clip-embedding",
24581
24572
  label: "Semantic search model",
24582
- defaultModelId: "mobileclip-s1",
24573
+ defaultModelId: "siglip2-b16-224",
24583
24574
  indexName: "the object semantic-search index",
24584
- options: [
24585
- {
24586
- id: "mobileclip-s1",
24587
- label: "MobileCLIP S1"
24588
- },
24589
- {
24590
- id: "mobileclip-s2",
24591
- label: "MobileCLIP S2"
24592
- },
24593
- {
24594
- id: "siglip2-b16-224",
24595
- label: "SigLIP2 B/16 (Apache-2.0)"
24596
- }
24597
- ]
24575
+ options: [{
24576
+ id: "siglip2-b16-224",
24577
+ label: "SigLIP2 B/16 (Apache-2.0)"
24578
+ }]
24598
24579
  }].map((s) => [s.stepId, s.defaultModelId])));
24599
24580
  var CLUSTER_STEP_SETTING_FIELDS = [{
24600
24581
  stepId: "face-embedding",
@@ -39768,7 +39749,13 @@ var OsdStatusSchema = object({ overlays: array(object({
39768
39749
  */
39769
39750
  availablePositions: array(OsdPositionEnum).readonly(),
39770
39751
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
39771
- readOnly: boolean().optional()
39752
+ readOnly: boolean().optional(),
39753
+ /**
39754
+ * Firmware text ceiling for this overlay. Absent = no known limit.
39755
+ * The OSD manager truncates here on every write — never stores its own
39756
+ * copy on the binding.
39757
+ */
39758
+ maxCharacters: number().int().positive().optional()
39772
39759
  })).readonly() });
39773
39760
  var OsdOverlayPatchSchema = object({
39774
39761
  enabled: boolean().optional(),
@@ -39865,7 +39852,9 @@ var OsdTemplateBindingSchema = object({
39865
39852
  valuePath: string().min(1).max(64),
39866
39853
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
39867
39854
  displayUnit: string().max(16).optional(),
39868
- maxDecimals: number().int().min(0).max(4).default(1)
39855
+ maxDecimals: number().int().min(0).max(4).default(1),
39856
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
39857
+ valueMap: record(string(), string()).optional()
39869
39858
  });
39870
39859
  /**
39871
39860
  * A named template: bindings (alias → leaf field) + a parser that
@@ -39915,18 +39904,11 @@ var OsdSlotBindingSchema = object({
39915
39904
  source: OsdSourceSchema,
39916
39905
  /** `${value}` and `${unit}` are substituted; every occurrence. */
39917
39906
  template: string().max(96).default("${value}"),
39918
- /** Truncate with an ellipsis past this length. Absent = no limit. */
39919
- maxCharacters: number().int().min(4).max(64).optional(),
39920
39907
  /**
39921
39908
  * Decimal places for a numeric value. `0` yields an integer — the
39922
39909
  * documented workaround for firmwares that reject `.` in overlay text.
39923
39910
  */
39924
39911
  maxDecimals: number().int().min(0).max(4).default(1),
39925
- /**
39926
- * Appended via `${unit}` when no unit conversion produces a label.
39927
- * Prefer `source.displayUnit` for converted device-state values.
39928
- */
39929
- unitLabel: string().max(8).optional(),
39930
39912
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
39931
39913
  valueMap: record(string(), string()).optional(),
39932
39914
  /** Time windows in which the slot is shown. Absent = always. */
@@ -39935,9 +39917,7 @@ var OsdSlotBindingSchema = object({
39935
39917
  * Display gate, in the notification centre's condition vocabulary.
39936
39918
  * Only the keys reported by `getConditionSupport` are accepted.
39937
39919
  */
39938
- conditions: NcConditionsSchema.optional(),
39939
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
39940
- fallbackText: string().max(64).default("")
39920
+ conditions: NcConditionsSchema.optional()
39941
39921
  });
39942
39922
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
39943
39923
  var OsdSlotViewSchema = object({
@@ -58194,6 +58174,8 @@ var STATE_SUMMARY_VARS = [
58194
58174
  [...RULE_TEXT],
58195
58175
  [...RULE_TEXT],
58196
58176
  [...RULE_TEXT],
58177
+ [...RULE_TEXT],
58178
+ [...RULE_TEXT],
58197
58179
  ...STATE_SUMMARY_VARS
58198
58180
  ];
58199
58181
  new Set([
@@ -58986,8 +58968,8 @@ function applyTemplate(template, value, unit) {
58986
58968
  /** The whole formatting chain: raw value → the text to burn into the slot. */
58987
58969
  function formatSlotText(raw, binding, options = {}) {
58988
58970
  const value = displayValue(options.displayRaw ?? raw, binding);
58989
- const unit = options.unit ?? binding.unitLabel ?? "";
58990
- return limitText(applyTemplate(binding.template, value, unit).trim(), binding.maxCharacters);
58971
+ const unit = options.unit ?? "";
58972
+ return limitText(applyTemplate(binding.template, value, unit).trim(), options.maxCharacters);
58991
58973
  }
58992
58974
  //#endregion
58993
58975
  //#region src/state-leaves.ts
@@ -59252,6 +59234,12 @@ function resolveBindingLive(binding, facts) {
59252
59234
  if (slice === void 0) return { reason: `device ${deviceId} publishes no "${binding.capName}" state` };
59253
59235
  const raw = readStatePath(slice, binding.valuePath);
59254
59236
  if (raw === void 0) return { reason: `"${binding.capName}.${binding.valuePath}" is absent on device ${deviceId}` };
59237
+ const mapped = binding.valueMap?.[String(raw)];
59238
+ if (mapped !== void 0) return {
59239
+ value: mapped,
59240
+ unit: "",
59241
+ formatted: applyTemplateTokenKind(mapped, "", "full")
59242
+ };
59255
59243
  let value;
59256
59244
  let unit = "";
59257
59245
  if (typeof raw === "number") {
@@ -59324,7 +59312,7 @@ function resolveSource(binding, cameraDeviceId, facts) {
59324
59312
  return { value: {
59325
59313
  raw,
59326
59314
  displayRaw: converted.value,
59327
- unit: converted.unit || binding.unitLabel
59315
+ ...converted.unit !== void 0 && converted.unit !== "" ? { unit: converted.unit } : {}
59328
59316
  } };
59329
59317
  }
59330
59318
  return { value: { raw } };
@@ -59335,7 +59323,7 @@ function resolveSource(binding, cameraDeviceId, facts) {
59335
59323
  * write — a slot rewritten every tick is one firmware round-trip per tick
59336
59324
  * per slot, for a string that did not move.
59337
59325
  */
59338
- function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59326
+ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText, maxCharacters) {
59339
59327
  if (binding === null) return {
59340
59328
  slotId,
59341
59329
  outcome: "unbound",
@@ -59348,7 +59336,6 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59348
59336
  text: "",
59349
59337
  reason: "binding is switched off"
59350
59338
  };
59351
- const fallback = limitText(binding.fallbackText, binding.maxCharacters);
59352
59339
  const gate = evaluateGate({
59353
59340
  cameraDeviceId,
59354
59341
  atMs: facts.atMs,
@@ -59356,14 +59343,14 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59356
59343
  conditions: binding.conditions,
59357
59344
  deviceState: facts.deviceState
59358
59345
  });
59359
- if (!gate.open) return settle(slotId, fallback, previousText, "gated", `condition "${gate.failedCondition}"`);
59346
+ if (!gate.open) return settle(slotId, "", previousText, "gated", `condition "${gate.failedCondition}"`);
59360
59347
  const resolved = resolveSource(binding, cameraDeviceId, facts);
59361
- if ("reason" in resolved) return settle(slotId, fallback, previousText, "unreadable", resolved.reason);
59362
- if (resolved.value.preformatted !== void 0 && binding.source.kind === "template") return settle(slotId, limitText(resolved.value.preformatted.trim(), binding.maxCharacters), previousText, "written");
59363
- const unit = resolved.value.unit ?? binding.unitLabel;
59348
+ if ("reason" in resolved) return settle(slotId, "", previousText, "unreadable", resolved.reason);
59349
+ if (resolved.value.preformatted !== void 0 && binding.source.kind === "template") return settle(slotId, limitText(resolved.value.preformatted.trim(), maxCharacters), previousText, "written");
59364
59350
  return settle(slotId, formatSlotText(resolved.value.raw, binding, {
59365
59351
  ...resolved.value.displayRaw !== void 0 ? { displayRaw: resolved.value.displayRaw } : {},
59366
- ...unit !== void 0 ? { unit } : {}
59352
+ ...resolved.value.unit !== void 0 ? { unit: resolved.value.unit } : {},
59353
+ ...maxCharacters !== void 0 ? { maxCharacters } : {}
59367
59354
  }), previousText, "written");
59368
59355
  }
59369
59356
  function settle(slotId, text, previousText, outcome, reason) {
@@ -59882,7 +59869,7 @@ var OsdManager = class {
59882
59869
  previewSlot = async ({ deviceId, slotId, binding }) => {
59883
59870
  if (binding !== void 0) assertSupportedConditions(binding.conditions);
59884
59871
  const effective = binding ?? (await this.readBindings(deviceId))[slotId] ?? null;
59885
- return renderSlot(slotId, effective, deviceId, await this.collectFacts(deviceId, effective === null ? {} : { [slotId]: effective }), void 0);
59872
+ return renderSlot(slotId, effective, deviceId, await this.collectFacts(deviceId, effective === null ? {} : { [slotId]: effective }), void 0, await this.maxCharactersForSlot(deviceId, slotId));
59886
59873
  };
59887
59874
  renderDevice = async ({ deviceId }) => {
59888
59875
  return { results: [...await this.renderDeviceInternal(deviceId, null)] };
@@ -59917,17 +59904,24 @@ var OsdManager = class {
59917
59904
  });
59918
59905
  }
59919
59906
  }
59907
+ /** Firmware text ceiling for one overlay, from the live `osd` status. */
59908
+ async maxCharactersForSlot(deviceId, slotId) {
59909
+ return (await this.deps.readOsdStatus(deviceId))?.overlays.find((o) => o.id === slotId)?.maxCharacters;
59910
+ }
59920
59911
  /** Render (and write) `onlySlots`, or every bound slot when `null`. */
59921
59912
  async renderDeviceInternal(deviceId, onlySlots) {
59922
59913
  const bindings = await this.readBindings(deviceId);
59923
59914
  const slotIds = onlySlots ?? Object.keys(bindings);
59924
59915
  if (slotIds.length === 0) return [];
59925
59916
  const facts = await this.collectFacts(deviceId, bindings);
59917
+ const status = await this.deps.readOsdStatus(deviceId);
59918
+ const maxBySlot = /* @__PURE__ */ new Map();
59919
+ for (const overlay of status?.overlays ?? []) if (overlay.maxCharacters !== void 0) maxBySlot.set(overlay.id, overlay.maxCharacters);
59926
59920
  const results = [];
59927
59921
  for (const slotId of slotIds) {
59928
59922
  const binding = bindings[slotId] ?? null;
59929
59923
  const key = cacheKey(deviceId, slotId);
59930
- const result = renderSlot(slotId, binding, deviceId, facts, this.lastWritten.get(key));
59924
+ const result = renderSlot(slotId, binding, deviceId, facts, this.lastWritten.get(key), maxBySlot.get(slotId));
59931
59925
  results.push(result);
59932
59926
  if (result.outcome === "unchanged") continue;
59933
59927
  if (result.outcome === "unbound" || result.outcome === "disabled") continue;
package/dist/index.mjs CHANGED
@@ -10631,11 +10631,11 @@ var ModelVariantGroupSchema = object({
10631
10631
  /** Quantization axis. Omit ⇒ the fp32 base build. */
10632
10632
  precision: _enum(["fp32", "int8"]).optional(),
10633
10633
  /**
10634
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
10635
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
10636
- * future performance variants plug into.
10634
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
10635
+ * an alternate training of the same family (Scrypted `relu_test`); chip
10636
+ * label is "Test".
10637
10637
  */
10638
- optimization: _enum(["standard", "fast"]).optional(),
10638
+ optimization: _enum(["standard", "test"]).optional(),
10639
10639
  /**
10640
10640
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
10641
10641
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -10658,9 +10658,10 @@ var ModelProviderIdSchema = _enum([
10658
10658
  * are per upstream. A persisted custom-model row written when this field was a
10659
10659
  * string still parses — the string is lifted into `{ weights }`.
10660
10660
  *
10661
- * Every licence surface (the admin "Models and licenses" page,
10662
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
10663
- * these records by `scripts/gen-model-licenses.ts` (D661).
10661
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
10662
+ * model table) is GENERATED from these records by
10663
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
10664
+ * `camstack/docs` → `/docs/detection/models`.
10664
10665
  */
10665
10666
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
10666
10667
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -19763,7 +19764,7 @@ var modelDistributorCapability = {
19763
19764
  mode: "singleton",
19764
19765
  internal: true,
19765
19766
  methods: { distributeModel: method(object({
19766
- nodeId: string(),
19767
+ targetNodeId: string(),
19767
19768
  modelId: string(),
19768
19769
  format: _enum(MODEL_FORMATS),
19769
19770
  entry: ModelCatalogEntrySchema
@@ -24558,39 +24559,19 @@ Object.freeze(Object.fromEntries([{
24558
24559
  label: "Face recognition model",
24559
24560
  defaultModelId: "auraface-r100",
24560
24561
  indexName: "the enrolled face gallery",
24561
- options: [
24562
- {
24563
- id: "auraface-r100",
24564
- label: "AuraFace R100"
24565
- },
24566
- {
24567
- id: "arcface-r100",
24568
- label: "ArcFace (ResNet34)"
24569
- },
24570
- {
24571
- id: "inception-resnet-v1",
24572
- label: "Inception ResNet V1"
24573
- }
24574
- ]
24562
+ options: [{
24563
+ id: "auraface-r100",
24564
+ label: "AuraFace R100"
24565
+ }]
24575
24566
  }, {
24576
24567
  stepId: "clip-embedding",
24577
24568
  label: "Semantic search model",
24578
- defaultModelId: "mobileclip-s1",
24569
+ defaultModelId: "siglip2-b16-224",
24579
24570
  indexName: "the object semantic-search index",
24580
- options: [
24581
- {
24582
- id: "mobileclip-s1",
24583
- label: "MobileCLIP S1"
24584
- },
24585
- {
24586
- id: "mobileclip-s2",
24587
- label: "MobileCLIP S2"
24588
- },
24589
- {
24590
- id: "siglip2-b16-224",
24591
- label: "SigLIP2 B/16 (Apache-2.0)"
24592
- }
24593
- ]
24571
+ options: [{
24572
+ id: "siglip2-b16-224",
24573
+ label: "SigLIP2 B/16 (Apache-2.0)"
24574
+ }]
24594
24575
  }].map((s) => [s.stepId, s.defaultModelId])));
24595
24576
  var CLUSTER_STEP_SETTING_FIELDS = [{
24596
24577
  stepId: "face-embedding",
@@ -39764,7 +39745,13 @@ var OsdStatusSchema = object({ overlays: array(object({
39764
39745
  */
39765
39746
  availablePositions: array(OsdPositionEnum).readonly(),
39766
39747
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
39767
- readOnly: boolean().optional()
39748
+ readOnly: boolean().optional(),
39749
+ /**
39750
+ * Firmware text ceiling for this overlay. Absent = no known limit.
39751
+ * The OSD manager truncates here on every write — never stores its own
39752
+ * copy on the binding.
39753
+ */
39754
+ maxCharacters: number().int().positive().optional()
39768
39755
  })).readonly() });
39769
39756
  var OsdOverlayPatchSchema = object({
39770
39757
  enabled: boolean().optional(),
@@ -39861,7 +39848,9 @@ var OsdTemplateBindingSchema = object({
39861
39848
  valuePath: string().min(1).max(64),
39862
39849
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
39863
39850
  displayUnit: string().max(16).optional(),
39864
- maxDecimals: number().int().min(0).max(4).default(1)
39851
+ maxDecimals: number().int().min(0).max(4).default(1),
39852
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
39853
+ valueMap: record(string(), string()).optional()
39865
39854
  });
39866
39855
  /**
39867
39856
  * A named template: bindings (alias → leaf field) + a parser that
@@ -39911,18 +39900,11 @@ var OsdSlotBindingSchema = object({
39911
39900
  source: OsdSourceSchema,
39912
39901
  /** `${value}` and `${unit}` are substituted; every occurrence. */
39913
39902
  template: string().max(96).default("${value}"),
39914
- /** Truncate with an ellipsis past this length. Absent = no limit. */
39915
- maxCharacters: number().int().min(4).max(64).optional(),
39916
39903
  /**
39917
39904
  * Decimal places for a numeric value. `0` yields an integer — the
39918
39905
  * documented workaround for firmwares that reject `.` in overlay text.
39919
39906
  */
39920
39907
  maxDecimals: number().int().min(0).max(4).default(1),
39921
- /**
39922
- * Appended via `${unit}` when no unit conversion produces a label.
39923
- * Prefer `source.displayUnit` for converted device-state values.
39924
- */
39925
- unitLabel: string().max(8).optional(),
39926
39908
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
39927
39909
  valueMap: record(string(), string()).optional(),
39928
39910
  /** Time windows in which the slot is shown. Absent = always. */
@@ -39931,9 +39913,7 @@ var OsdSlotBindingSchema = object({
39931
39913
  * Display gate, in the notification centre's condition vocabulary.
39932
39914
  * Only the keys reported by `getConditionSupport` are accepted.
39933
39915
  */
39934
- conditions: NcConditionsSchema.optional(),
39935
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
39936
- fallbackText: string().max(64).default("")
39916
+ conditions: NcConditionsSchema.optional()
39937
39917
  });
39938
39918
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
39939
39919
  var OsdSlotViewSchema = object({
@@ -58190,6 +58170,8 @@ var STATE_SUMMARY_VARS = [
58190
58170
  [...RULE_TEXT],
58191
58171
  [...RULE_TEXT],
58192
58172
  [...RULE_TEXT],
58173
+ [...RULE_TEXT],
58174
+ [...RULE_TEXT],
58193
58175
  ...STATE_SUMMARY_VARS
58194
58176
  ];
58195
58177
  new Set([
@@ -58982,8 +58964,8 @@ function applyTemplate(template, value, unit) {
58982
58964
  /** The whole formatting chain: raw value → the text to burn into the slot. */
58983
58965
  function formatSlotText(raw, binding, options = {}) {
58984
58966
  const value = displayValue(options.displayRaw ?? raw, binding);
58985
- const unit = options.unit ?? binding.unitLabel ?? "";
58986
- return limitText(applyTemplate(binding.template, value, unit).trim(), binding.maxCharacters);
58967
+ const unit = options.unit ?? "";
58968
+ return limitText(applyTemplate(binding.template, value, unit).trim(), options.maxCharacters);
58987
58969
  }
58988
58970
  //#endregion
58989
58971
  //#region src/state-leaves.ts
@@ -59248,6 +59230,12 @@ function resolveBindingLive(binding, facts) {
59248
59230
  if (slice === void 0) return { reason: `device ${deviceId} publishes no "${binding.capName}" state` };
59249
59231
  const raw = readStatePath(slice, binding.valuePath);
59250
59232
  if (raw === void 0) return { reason: `"${binding.capName}.${binding.valuePath}" is absent on device ${deviceId}` };
59233
+ const mapped = binding.valueMap?.[String(raw)];
59234
+ if (mapped !== void 0) return {
59235
+ value: mapped,
59236
+ unit: "",
59237
+ formatted: applyTemplateTokenKind(mapped, "", "full")
59238
+ };
59251
59239
  let value;
59252
59240
  let unit = "";
59253
59241
  if (typeof raw === "number") {
@@ -59320,7 +59308,7 @@ function resolveSource(binding, cameraDeviceId, facts) {
59320
59308
  return { value: {
59321
59309
  raw,
59322
59310
  displayRaw: converted.value,
59323
- unit: converted.unit || binding.unitLabel
59311
+ ...converted.unit !== void 0 && converted.unit !== "" ? { unit: converted.unit } : {}
59324
59312
  } };
59325
59313
  }
59326
59314
  return { value: { raw } };
@@ -59331,7 +59319,7 @@ function resolveSource(binding, cameraDeviceId, facts) {
59331
59319
  * write — a slot rewritten every tick is one firmware round-trip per tick
59332
59320
  * per slot, for a string that did not move.
59333
59321
  */
59334
- function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59322
+ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText, maxCharacters) {
59335
59323
  if (binding === null) return {
59336
59324
  slotId,
59337
59325
  outcome: "unbound",
@@ -59344,7 +59332,6 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59344
59332
  text: "",
59345
59333
  reason: "binding is switched off"
59346
59334
  };
59347
- const fallback = limitText(binding.fallbackText, binding.maxCharacters);
59348
59335
  const gate = evaluateGate({
59349
59336
  cameraDeviceId,
59350
59337
  atMs: facts.atMs,
@@ -59352,14 +59339,14 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59352
59339
  conditions: binding.conditions,
59353
59340
  deviceState: facts.deviceState
59354
59341
  });
59355
- if (!gate.open) return settle(slotId, fallback, previousText, "gated", `condition "${gate.failedCondition}"`);
59342
+ if (!gate.open) return settle(slotId, "", previousText, "gated", `condition "${gate.failedCondition}"`);
59356
59343
  const resolved = resolveSource(binding, cameraDeviceId, facts);
59357
- if ("reason" in resolved) return settle(slotId, fallback, previousText, "unreadable", resolved.reason);
59358
- if (resolved.value.preformatted !== void 0 && binding.source.kind === "template") return settle(slotId, limitText(resolved.value.preformatted.trim(), binding.maxCharacters), previousText, "written");
59359
- const unit = resolved.value.unit ?? binding.unitLabel;
59344
+ if ("reason" in resolved) return settle(slotId, "", previousText, "unreadable", resolved.reason);
59345
+ if (resolved.value.preformatted !== void 0 && binding.source.kind === "template") return settle(slotId, limitText(resolved.value.preformatted.trim(), maxCharacters), previousText, "written");
59360
59346
  return settle(slotId, formatSlotText(resolved.value.raw, binding, {
59361
59347
  ...resolved.value.displayRaw !== void 0 ? { displayRaw: resolved.value.displayRaw } : {},
59362
- ...unit !== void 0 ? { unit } : {}
59348
+ ...resolved.value.unit !== void 0 ? { unit: resolved.value.unit } : {},
59349
+ ...maxCharacters !== void 0 ? { maxCharacters } : {}
59363
59350
  }), previousText, "written");
59364
59351
  }
59365
59352
  function settle(slotId, text, previousText, outcome, reason) {
@@ -59878,7 +59865,7 @@ var OsdManager = class {
59878
59865
  previewSlot = async ({ deviceId, slotId, binding }) => {
59879
59866
  if (binding !== void 0) assertSupportedConditions(binding.conditions);
59880
59867
  const effective = binding ?? (await this.readBindings(deviceId))[slotId] ?? null;
59881
- return renderSlot(slotId, effective, deviceId, await this.collectFacts(deviceId, effective === null ? {} : { [slotId]: effective }), void 0);
59868
+ return renderSlot(slotId, effective, deviceId, await this.collectFacts(deviceId, effective === null ? {} : { [slotId]: effective }), void 0, await this.maxCharactersForSlot(deviceId, slotId));
59882
59869
  };
59883
59870
  renderDevice = async ({ deviceId }) => {
59884
59871
  return { results: [...await this.renderDeviceInternal(deviceId, null)] };
@@ -59913,17 +59900,24 @@ var OsdManager = class {
59913
59900
  });
59914
59901
  }
59915
59902
  }
59903
+ /** Firmware text ceiling for one overlay, from the live `osd` status. */
59904
+ async maxCharactersForSlot(deviceId, slotId) {
59905
+ return (await this.deps.readOsdStatus(deviceId))?.overlays.find((o) => o.id === slotId)?.maxCharacters;
59906
+ }
59916
59907
  /** Render (and write) `onlySlots`, or every bound slot when `null`. */
59917
59908
  async renderDeviceInternal(deviceId, onlySlots) {
59918
59909
  const bindings = await this.readBindings(deviceId);
59919
59910
  const slotIds = onlySlots ?? Object.keys(bindings);
59920
59911
  if (slotIds.length === 0) return [];
59921
59912
  const facts = await this.collectFacts(deviceId, bindings);
59913
+ const status = await this.deps.readOsdStatus(deviceId);
59914
+ const maxBySlot = /* @__PURE__ */ new Map();
59915
+ for (const overlay of status?.overlays ?? []) if (overlay.maxCharacters !== void 0) maxBySlot.set(overlay.id, overlay.maxCharacters);
59922
59916
  const results = [];
59923
59917
  for (const slotId of slotIds) {
59924
59918
  const binding = bindings[slotId] ?? null;
59925
59919
  const key = cacheKey(deviceId, slotId);
59926
- const result = renderSlot(slotId, binding, deviceId, facts, this.lastWritten.get(key));
59920
+ const result = renderSlot(slotId, binding, deviceId, facts, this.lastWritten.get(key), maxBySlot.get(slotId));
59927
59921
  results.push(result);
59928
59922
  if (result.outcome === "unchanged") continue;
59929
59923
  if (result.outcome === "unbound" || result.outcome === "disabled") continue;
@@ -1,5 +1,5 @@
1
1
  import { b as e, g as t, l as n, u as r } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react__loadShare__.js-C5jUMd7X.mjs";
2
- import { c as i, s as a } from "./responsive-BoQdxzOM.mjs";
2
+ import { c as i, s as a } from "./responsive-eWIsQGEg.mjs";
3
3
  import { n as o, r as s, t as c } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DalfdIDw.mjs";
4
4
  var l = i("chevron-down", [["path", {
5
5
  d: "m6 9 6 6 6-6",
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-CtQlS867.mjs";
1
+ import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-D-Cxu36Z.mjs";
2
2
  export { t as get, e as init };
@@ -1,6 +1,6 @@
1
1
  import { _ as e, b as t, c as n, g as r, l as i, m as a, n as o, r as s, t as c, u as l } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react__loadShare__.js-C5jUMd7X.mjs";
2
2
  import "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DalfdIDw.mjs";
3
- import { i as u } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B4GSezZ1.mjs";
3
+ import { i as u } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bszex2Bz.mjs";
4
4
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
5
5
  t();
6
6
  var d = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), f = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), p = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), m = (e) => {
@@ -1,5 +1,5 @@
1
1
  import { b as e, c as t, g as n, m as r } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react__loadShare__.js-C5jUMd7X.mjs";
2
- import { c as i } from "./responsive-BoQdxzOM.mjs";
2
+ import { c as i } from "./responsive-eWIsQGEg.mjs";
3
3
  var a = i("camera", [["path", {
4
4
  d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z",
5
5
  key: "18u6gg"
@@ -1,4 +1,4 @@
1
- import { c as e } from "./responsive-BoQdxzOM.mjs";
1
+ import { c as e } from "./responsive-eWIsQGEg.mjs";
2
2
  var t = e("eye-off", [
3
3
  ["path", {
4
4
  d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",
@@ -1,4 +1,4 @@
1
- import { c as e } from "./responsive-BoQdxzOM.mjs";
1
+ import { c as e } from "./responsive-eWIsQGEg.mjs";
2
2
  var t = e("trash-2", [
3
3
  ["path", {
4
4
  d: "M10 11v6",
@@ -2753,7 +2753,7 @@ async function rr(e) {
2753
2753
  }
2754
2754
  }
2755
2755
  async function ir() {
2756
- return tr ||= rr(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-CGnGPNWo.mjs")).catch((e) => {
2756
+ return tr ||= rr(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-Cf3xN1-E.mjs")).catch((e) => {
2757
2757
  throw tr = void 0, e;
2758
2758
  }), tr;
2759
2759
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-osd-manager",
3
- "version": "0.1.170",
3
+ "version": "0.1.171",
4
4
  "description": "Binds camera on-screen-display slots to live state and recognitions",
5
5
  "keywords": [
6
6
  "camstack",