@camstack/addon-osd-manager 0.1.170 → 0.1.172

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-3SEt9bVC.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-M3LLrSyZ.mjs → PrivacyMaskSettings-6XhCc_10.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-BWTOg6t0.mjs → SceneMonitorEditor-BOaJdiHv.mjs} +3 -3
  4. package/dist/_stub.js +4430 -4730
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-CGnGPNWo.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-BWC_vxZ4.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DMeEVWZy.mjs +26 -0
  7. package/dist/addon-osd-manager.css +1 -1
  8. package/dist/{hostInit-CXC2ehtI.mjs → hostInit-CSG1YJE4.mjs} +3 -3
  9. package/dist/index.js +156 -94
  10. package/dist/index.mjs +156 -94
  11. package/dist/{player-overlays-DBi0aH1E.mjs → player-overlays-7D6iloq4.mjs} +1 -1
  12. package/dist/remoteEntry.js +1 -1
  13. package/dist/{responsive-BoQdxzOM.mjs → responsive-DFTpb0I2.mjs} +1 -1
  14. package/dist/{scene-monitor-copy-CwfDunLo.mjs → scene-monitor-copy-D6Ypp3Cr.mjs} +1 -1
  15. package/dist/{square-C-adTToZ.mjs → square-CyIowYkn.mjs} +1 -1
  16. package/dist/{trash-2-DJjBuL5Q.mjs → trash-2-B5AYx8ut.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-CzgrkKsK.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.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`. */
@@ -15477,7 +15478,7 @@ var detectionPipelineCapability = {
15477
15478
  * This matters here more than anywhere else in the repo, because every provider
15478
15479
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
15479
15480
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
15480
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
15481
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
15481
15482
  * camera's own page will ask for its own login. That is correct, and pre-
15482
15483
  * filling it is the operator's business, not ours.
15483
15484
  *
@@ -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
@@ -22343,8 +22344,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
22343
22344
  disabledTargetIds: array(string()).default([])
22344
22345
  });
22345
22346
  /**
22346
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
22347
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
22347
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
22348
+ * report a still-true state. `addons` is the crash-looped addons only and
22349
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
22348
22350
  * job is not an open state, and the alarm stays instant.
22349
22351
  */
22350
22352
  var NcStateSummarySectionIdSchema = _enum([
@@ -22356,6 +22358,7 @@ var NcStateSummarySectionIdSchema = _enum([
22356
22358
  "detection-blind",
22357
22359
  "nodes",
22358
22360
  "addons",
22361
+ "addon-updates",
22359
22362
  "updates"
22360
22363
  ]);
22361
22364
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -23372,7 +23375,19 @@ var NcAlarmModeDelaysListSchema = array(object({
23372
23375
  * When this mode may be armed. Absent / null = always allowed (same as a
23373
23376
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
23374
23377
  */
23375
- schedule: NcScheduleSchema.optional()
23378
+ schedule: NcScheduleSchema.optional(),
23379
+ /**
23380
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
23381
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
23382
+ * only one, and the matches ride the triggered notification's mosaic (D705).
23383
+ */
23384
+ notifyDuringEntryDelay: boolean().optional(),
23385
+ /**
23386
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
23387
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
23388
+ * is the one that starts the entry delay — the entry-delay notification already names it.
23389
+ */
23390
+ notifySensorRewatched: boolean().optional()
23376
23391
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
23377
23392
  var NcAlarmModeDelaysEditListSchema = array(object({
23378
23393
  mode: AlarmArmModeSchema,
@@ -23381,7 +23396,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
23381
23396
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
23382
23397
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
23383
23398
  /** Set a schedule, or `null` to clear (mode always allowed again). */
23384
- schedule: NcScheduleSchema.nullable().optional()
23399
+ schedule: NcScheduleSchema.nullable().optional(),
23400
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
23401
+ notifyDuringEntryDelay: boolean().nullable().optional(),
23402
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
23403
+ notifySensorRewatched: boolean().nullable().optional()
23385
23404
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
23386
23405
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
23387
23406
  var NcAlarmNonBlockingEditSchema = object({
@@ -24558,39 +24577,19 @@ Object.freeze(Object.fromEntries([{
24558
24577
  label: "Face recognition model",
24559
24578
  defaultModelId: "auraface-r100",
24560
24579
  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
- ]
24580
+ options: [{
24581
+ id: "auraface-r100",
24582
+ label: "AuraFace R100"
24583
+ }]
24575
24584
  }, {
24576
24585
  stepId: "clip-embedding",
24577
24586
  label: "Semantic search model",
24578
- defaultModelId: "mobileclip-s1",
24587
+ defaultModelId: "siglip2-b16-224",
24579
24588
  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
- ]
24589
+ options: [{
24590
+ id: "siglip2-b16-224",
24591
+ label: "SigLIP2 B/16 (Apache-2.0)"
24592
+ }]
24594
24593
  }].map((s) => [s.stepId, s.defaultModelId])));
24595
24594
  var CLUSTER_STEP_SETTING_FIELDS = [{
24596
24595
  stepId: "face-embedding",
@@ -28334,6 +28333,22 @@ var MotionSourceEnum = _enum([
28334
28333
  "device-activity"
28335
28334
  ]);
28336
28335
  /**
28336
+ * What opened an on-motion detection SESSION: the motion source that passed the
28337
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
28338
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
28339
+ * device name as the orchestrator's camera-sensors resolution last read it;
28340
+ * absent when unknown or when the camera itself triggered.
28341
+ *
28342
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
28343
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
28344
+ * runner never decides anything on it.
28345
+ */
28346
+ var SessionTriggerSchema = object({
28347
+ source: MotionSourceEnum,
28348
+ triggeredBy: MotionTriggeredBySchema,
28349
+ sensorName: string().optional()
28350
+ });
28351
+ /**
28337
28352
  * List of motion sources active on a camera. Empty array is valid:
28338
28353
  * "no source" — happens for battery cams without firmware motion when
28339
28354
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -28585,6 +28600,14 @@ var RunnerCameraConfigSchema = object({
28585
28600
  */
28586
28601
  motionSources: MotionSourcesSchema.default(["analyzer"]),
28587
28602
  /**
28603
+ * What opened this attach, on a SESSION attach only (see
28604
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
28605
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
28606
+ * behind it, and every attach from an orchestrator older than the field — the
28607
+ * runner then names no source on the phase event.
28608
+ */
28609
+ sessionTrigger: SessionTriggerSchema.optional(),
28610
+ /**
28588
28611
  * WHICH root detector runs for this camera. The detection counterpart of
28589
28612
  * {@link motionSources}, same shape and same discipline: a per-camera list
28590
28613
  * of sources the system understands, vendor-agnostic, owned here.
@@ -37685,7 +37708,7 @@ var LocalInterfaceSchema = object({
37685
37708
  name: string(),
37686
37709
  family: _enum(["IPv4", "IPv6"]),
37687
37710
  address: string(),
37688
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
37711
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
37689
37712
  cidr: string(),
37690
37713
  /** OS netmask. Stable across calls so the SDK can hash on it. */
37691
37714
  netmask: string(),
@@ -37739,7 +37762,8 @@ var ConnectionEndpointSchema = object({
37739
37762
  "lan-ipv4",
37740
37763
  "lan-ipv6",
37741
37764
  "public",
37742
- "loopback"
37765
+ "loopback",
37766
+ "app-link"
37743
37767
  ]),
37744
37768
  /**
37745
37769
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -37808,15 +37832,27 @@ var GetConnectionEndpointsResultSchema = object({
37808
37832
  localPort: AdvertisedLocalPortSchema
37809
37833
  });
37810
37834
  /**
37811
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
37812
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
37813
- * what AUTO currently picks, so the UI can show the effective value either way.
37835
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
37836
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
37837
+ * so the UI can show it either way.
37814
37838
  */
37815
37839
  var NotificationEndpointSchema = object({
37816
37840
  /** The operator's explicit choice, or null for AUTO. */
37817
37841
  baseUrl: string().nullable(),
37818
- /** What the ranking currently resolves to (null when nothing is reachable). */
37819
- resolved: string().nullable()
37842
+ /**
37843
+ * The effective flagged address: the explicit choice, else (AUTO) the
37844
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
37845
+ * answered the public tunnel / mesh here, or null.
37846
+ */
37847
+ resolved: string().nullable(),
37848
+ /**
37849
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
37850
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
37851
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
37852
+ * hub advertises neither; the app link is then minted without `hub=`.
37853
+ * Absent on a pre-D709 hub.
37854
+ */
37855
+ appLinkHub: string().nullable()
37820
37856
  });
37821
37857
  /**
37822
37858
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -37884,7 +37920,7 @@ var localNetworkCapability = {
37884
37920
  * every client a second authority on a socket only the hub binds. A viewer
37885
37921
  * configured with a port-less public URL (`https://camstack.example.top`)
37886
37922
  * infers 443 from the scheme, asks with 443, and was handed
37887
- * `https://192.168.x.x:443` for a hub listening on 4443. That candidate
37923
+ * `https://192.168.10.9:443` for a hub listening on 4443. That candidate
37888
37924
  * cannot ever answer, and losing its race reads in the log exactly like
37889
37925
  * "the LAN was tried and it was slower" — so an operator sitting on his own
37890
37926
  * WiFi stayed on the public tunnel for weeks, with the LAN candidate born
@@ -37917,13 +37953,21 @@ var localNetworkCapability = {
37917
37953
  * Pass `'https'` when the caller is itself loaded over HTTPS
37918
37954
  * to avoid mixed-content blocks in the browser. The public
37919
37955
  * tunnel always emits `https://` regardless. */
37920
- scheme: _enum(["http", "https"]).optional()
37956
+ scheme: _enum(["http", "https"]).optional(),
37957
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
37958
+ * an address notification links can be flagged with, not a hub
37959
+ * route. Default `false`: racers and rankers never see it. */
37960
+ includeAppLink: boolean().optional()
37921
37961
  }), GetConnectionEndpointsResultSchema),
37922
37962
  /**
37923
- * The endpoint OUTBOUND artifacts must be addressed by — notification
37924
- * attachment links and deep links. `baseUrl: null` means AUTO: the first
37925
- * public candidate (a configured origin, then a connected tunnel, then the
37926
- * LAN), never loopback.
37963
+ * The address notification links are flagged with. `baseUrl: null` means
37964
+ * AUTO, which is the CamStack app link (D709): viewer taps go through
37965
+ * `https://camstack.zentik.app/…?hub=<appLinkHub>` and open the app. Any
37966
+ * hub address the palette lists can be flagged instead; then viewer taps,
37967
+ * attachment links and action buttons all use it. Signed media and
37968
+ * buttons never use the app link — with it flagged (or AUTO) they keep
37969
+ * the ranked hub ladder (a configured origin, a connected tunnel, then
37970
+ * the LAN), never loopback.
37927
37971
  *
37928
37972
  * Why an explicit marker: the automatic ranking cannot know which of
37929
37973
  * several reachable addresses the RECIPIENT can actually open. A link is
@@ -38316,7 +38360,7 @@ var MeshPeerSchema = object({
38316
38360
  tags: array(string()).readonly(),
38317
38361
  /** True when the peer advertises itself as an exit-node candidate. */
38318
38362
  exitNodeOption: boolean(),
38319
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
38363
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
38320
38364
  advertisedRoutes: array(string()).readonly(),
38321
38365
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
38322
38366
  userLogin: string().nullable(),
@@ -39764,7 +39808,13 @@ var OsdStatusSchema = object({ overlays: array(object({
39764
39808
  */
39765
39809
  availablePositions: array(OsdPositionEnum).readonly(),
39766
39810
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
39767
- readOnly: boolean().optional()
39811
+ readOnly: boolean().optional(),
39812
+ /**
39813
+ * Firmware text ceiling for this overlay. Absent = no known limit.
39814
+ * The OSD manager truncates here on every write — never stores its own
39815
+ * copy on the binding.
39816
+ */
39817
+ maxCharacters: number().int().positive().optional()
39768
39818
  })).readonly() });
39769
39819
  var OsdOverlayPatchSchema = object({
39770
39820
  enabled: boolean().optional(),
@@ -39861,7 +39911,9 @@ var OsdTemplateBindingSchema = object({
39861
39911
  valuePath: string().min(1).max(64),
39862
39912
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
39863
39913
  displayUnit: string().max(16).optional(),
39864
- maxDecimals: number().int().min(0).max(4).default(1)
39914
+ maxDecimals: number().int().min(0).max(4).default(1),
39915
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
39916
+ valueMap: record(string(), string()).optional()
39865
39917
  });
39866
39918
  /**
39867
39919
  * A named template: bindings (alias → leaf field) + a parser that
@@ -39911,18 +39963,11 @@ var OsdSlotBindingSchema = object({
39911
39963
  source: OsdSourceSchema,
39912
39964
  /** `${value}` and `${unit}` are substituted; every occurrence. */
39913
39965
  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
39966
  /**
39917
39967
  * Decimal places for a numeric value. `0` yields an integer — the
39918
39968
  * documented workaround for firmwares that reject `.` in overlay text.
39919
39969
  */
39920
39970
  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
39971
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
39927
39972
  valueMap: record(string(), string()).optional(),
39928
39973
  /** Time windows in which the slot is shown. Absent = always. */
@@ -39931,9 +39976,7 @@ var OsdSlotBindingSchema = object({
39931
39976
  * Display gate, in the notification centre's condition vocabulary.
39932
39977
  * Only the keys reported by `getConditionSupport` are accepted.
39933
39978
  */
39934
- conditions: NcConditionsSchema.optional(),
39935
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
39936
- fallbackText: string().max(64).default("")
39979
+ conditions: NcConditionsSchema.optional()
39937
39980
  });
39938
39981
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
39939
39982
  var OsdSlotViewSchema = object({
@@ -57815,12 +57858,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
57815
57858
  {
57816
57859
  id: "addons",
57817
57860
  family: "system",
57818
- label: "Addons",
57819
- kinds: [
57820
- "addon-crash-loop",
57821
- "addon-update-available",
57822
- "addon-updated"
57823
- ]
57861
+ label: "Addons stopped",
57862
+ kinds: ["addon-crash-loop"]
57863
+ },
57864
+ {
57865
+ id: "addon-updates",
57866
+ family: "system",
57867
+ label: "Addon updates",
57868
+ kinds: ["addon-update-available", "addon-updated"]
57824
57869
  },
57825
57870
  {
57826
57871
  id: "updates",
@@ -58060,13 +58105,15 @@ var STATE_SUMMARY_SECTION_VARS = [
58060
58105
  },
58061
58106
  {
58062
58107
  name: "summaryAddons",
58063
- label: "Summary: addons",
58064
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
58065
- kinds: [
58066
- "addon-crash-loop",
58067
- "addon-update-available",
58068
- "addon-updated"
58069
- ]
58108
+ label: "Summary: addons stopped",
58109
+ example: "hub — recorder stopped after repeated crashes",
58110
+ kinds: ["addon-crash-loop"]
58111
+ },
58112
+ {
58113
+ name: "summaryAddonUpdates",
58114
+ label: "Summary: addon updates",
58115
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
58116
+ kinds: ["addon-update-available", "addon-updated"]
58070
58117
  },
58071
58118
  {
58072
58119
  name: "summaryUpdates",
@@ -58150,6 +58197,7 @@ var STATE_SUMMARY_VARS = [
58150
58197
  [...RULE_BODY_ONLY],
58151
58198
  [...RULE_TEXT],
58152
58199
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
58200
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
58153
58201
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
58154
58202
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
58155
58203
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -58190,6 +58238,8 @@ var STATE_SUMMARY_VARS = [
58190
58238
  [...RULE_TEXT],
58191
58239
  [...RULE_TEXT],
58192
58240
  [...RULE_TEXT],
58241
+ [...RULE_TEXT],
58242
+ [...RULE_TEXT],
58193
58243
  ...STATE_SUMMARY_VARS
58194
58244
  ];
58195
58245
  new Set([
@@ -58982,8 +59032,8 @@ function applyTemplate(template, value, unit) {
58982
59032
  /** The whole formatting chain: raw value → the text to burn into the slot. */
58983
59033
  function formatSlotText(raw, binding, options = {}) {
58984
59034
  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);
59035
+ const unit = options.unit ?? "";
59036
+ return limitText(applyTemplate(binding.template, value, unit).trim(), options.maxCharacters);
58987
59037
  }
58988
59038
  //#endregion
58989
59039
  //#region src/state-leaves.ts
@@ -59248,6 +59298,12 @@ function resolveBindingLive(binding, facts) {
59248
59298
  if (slice === void 0) return { reason: `device ${deviceId} publishes no "${binding.capName}" state` };
59249
59299
  const raw = readStatePath(slice, binding.valuePath);
59250
59300
  if (raw === void 0) return { reason: `"${binding.capName}.${binding.valuePath}" is absent on device ${deviceId}` };
59301
+ const mapped = binding.valueMap?.[String(raw)];
59302
+ if (mapped !== void 0) return {
59303
+ value: mapped,
59304
+ unit: "",
59305
+ formatted: applyTemplateTokenKind(mapped, "", "full")
59306
+ };
59251
59307
  let value;
59252
59308
  let unit = "";
59253
59309
  if (typeof raw === "number") {
@@ -59320,7 +59376,7 @@ function resolveSource(binding, cameraDeviceId, facts) {
59320
59376
  return { value: {
59321
59377
  raw,
59322
59378
  displayRaw: converted.value,
59323
- unit: converted.unit || binding.unitLabel
59379
+ ...converted.unit !== void 0 && converted.unit !== "" ? { unit: converted.unit } : {}
59324
59380
  } };
59325
59381
  }
59326
59382
  return { value: { raw } };
@@ -59331,7 +59387,7 @@ function resolveSource(binding, cameraDeviceId, facts) {
59331
59387
  * write — a slot rewritten every tick is one firmware round-trip per tick
59332
59388
  * per slot, for a string that did not move.
59333
59389
  */
59334
- function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59390
+ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText, maxCharacters) {
59335
59391
  if (binding === null) return {
59336
59392
  slotId,
59337
59393
  outcome: "unbound",
@@ -59344,7 +59400,6 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59344
59400
  text: "",
59345
59401
  reason: "binding is switched off"
59346
59402
  };
59347
- const fallback = limitText(binding.fallbackText, binding.maxCharacters);
59348
59403
  const gate = evaluateGate({
59349
59404
  cameraDeviceId,
59350
59405
  atMs: facts.atMs,
@@ -59352,14 +59407,14 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
59352
59407
  conditions: binding.conditions,
59353
59408
  deviceState: facts.deviceState
59354
59409
  });
59355
- if (!gate.open) return settle(slotId, fallback, previousText, "gated", `condition "${gate.failedCondition}"`);
59410
+ if (!gate.open) return settle(slotId, "", previousText, "gated", `condition "${gate.failedCondition}"`);
59356
59411
  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;
59412
+ if ("reason" in resolved) return settle(slotId, "", previousText, "unreadable", resolved.reason);
59413
+ if (resolved.value.preformatted !== void 0 && binding.source.kind === "template") return settle(slotId, limitText(resolved.value.preformatted.trim(), maxCharacters), previousText, "written");
59360
59414
  return settle(slotId, formatSlotText(resolved.value.raw, binding, {
59361
59415
  ...resolved.value.displayRaw !== void 0 ? { displayRaw: resolved.value.displayRaw } : {},
59362
- ...unit !== void 0 ? { unit } : {}
59416
+ ...resolved.value.unit !== void 0 ? { unit: resolved.value.unit } : {},
59417
+ ...maxCharacters !== void 0 ? { maxCharacters } : {}
59363
59418
  }), previousText, "written");
59364
59419
  }
59365
59420
  function settle(slotId, text, previousText, outcome, reason) {
@@ -59878,7 +59933,7 @@ var OsdManager = class {
59878
59933
  previewSlot = async ({ deviceId, slotId, binding }) => {
59879
59934
  if (binding !== void 0) assertSupportedConditions(binding.conditions);
59880
59935
  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);
59936
+ return renderSlot(slotId, effective, deviceId, await this.collectFacts(deviceId, effective === null ? {} : { [slotId]: effective }), void 0, await this.maxCharactersForSlot(deviceId, slotId));
59882
59937
  };
59883
59938
  renderDevice = async ({ deviceId }) => {
59884
59939
  return { results: [...await this.renderDeviceInternal(deviceId, null)] };
@@ -59913,17 +59968,24 @@ var OsdManager = class {
59913
59968
  });
59914
59969
  }
59915
59970
  }
59971
+ /** Firmware text ceiling for one overlay, from the live `osd` status. */
59972
+ async maxCharactersForSlot(deviceId, slotId) {
59973
+ return (await this.deps.readOsdStatus(deviceId))?.overlays.find((o) => o.id === slotId)?.maxCharacters;
59974
+ }
59916
59975
  /** Render (and write) `onlySlots`, or every bound slot when `null`. */
59917
59976
  async renderDeviceInternal(deviceId, onlySlots) {
59918
59977
  const bindings = await this.readBindings(deviceId);
59919
59978
  const slotIds = onlySlots ?? Object.keys(bindings);
59920
59979
  if (slotIds.length === 0) return [];
59921
59980
  const facts = await this.collectFacts(deviceId, bindings);
59981
+ const status = await this.deps.readOsdStatus(deviceId);
59982
+ const maxBySlot = /* @__PURE__ */ new Map();
59983
+ for (const overlay of status?.overlays ?? []) if (overlay.maxCharacters !== void 0) maxBySlot.set(overlay.id, overlay.maxCharacters);
59922
59984
  const results = [];
59923
59985
  for (const slotId of slotIds) {
59924
59986
  const binding = bindings[slotId] ?? null;
59925
59987
  const key = cacheKey(deviceId, slotId);
59926
- const result = renderSlot(slotId, binding, deviceId, facts, this.lastWritten.get(key));
59988
+ const result = renderSlot(slotId, binding, deviceId, facts, this.lastWritten.get(key), maxBySlot.get(slotId));
59927
59989
  results.push(result);
59928
59990
  if (result.outcome === "unchanged") continue;
59929
59991
  if (result.outcome === "unbound" || result.outcome === "disabled") continue;
@@ -60291,7 +60353,7 @@ function toOsdPickerDevices(devices) {
60291
60353
  return devices.map((d) => ({
60292
60354
  id: d.id,
60293
60355
  name: d.name,
60294
- type: d.type ?? null,
60356
+ ...typeof d.type === "string" ? { type: d.type } : {},
60295
60357
  location: d.location ?? null,
60296
60358
  parentDeviceId: d.parentDeviceId ?? null
60297
60359
  }));
@@ -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-DFTpb0I2.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-CzgrkKsK.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-DMeEVWZy.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-DFTpb0I2.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-DFTpb0I2.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-DFTpb0I2.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-BWC_vxZ4.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.172",
4
4
  "description": "Binds camera on-screen-display slots to live state and recognitions",
5
5
  "keywords": [
6
6
  "camstack",