gps-plus-slam-app-framework 1.2.0 → 1.4.0

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 (230) hide show
  1. package/dist/{app-selectors-CcZ5UV-8.d.ts → app-selectors-CrOucL00.d.ts} +1 -1
  2. package/dist/ar/bresenham3d.d.ts +1 -1
  3. package/dist/ar/camera-blit-capture.d.ts +2 -2
  4. package/dist/ar/camera-blit-capture.js +87 -9
  5. package/dist/ar/capability-checker.d.ts +1 -1
  6. package/dist/ar/capture-failure-tracker.d.ts +1 -1
  7. package/dist/ar/chromium-camera-access-workaround.d.ts +1 -1
  8. package/dist/ar/depth-grid-lookup.d.ts +2 -0
  9. package/dist/ar/depth-grid-lookup.js +43 -0
  10. package/dist/ar/depth-sampler.d.ts +2 -2
  11. package/dist/ar/depth-sampler.js +2 -4
  12. package/dist/ar/depth-unprojection.d.ts +1 -1
  13. package/dist/ar/enable-gps-ar.d.ts +1 -1
  14. package/dist/ar/enable-gps-ar.js +26 -1
  15. package/dist/ar/frame-loop.d.ts +1 -1
  16. package/dist/ar/image-capture.d.ts +2 -2
  17. package/dist/ar/image-capture.js +7 -5
  18. package/dist/ar/index.d.ts +30 -19
  19. package/dist/ar/index.js +15 -4
  20. package/dist/ar/occupancy-grid.d.ts +1 -1
  21. package/dist/ar/occupancy-grid.js +29 -2
  22. package/dist/ar/planar-pnp.d.ts +2 -0
  23. package/dist/ar/planar-pnp.js +425 -0
  24. package/dist/ar/qr-debug-view.d.ts +2 -0
  25. package/dist/ar/qr-debug-view.js +89 -0
  26. package/dist/ar/qr-derived-pose.d.ts +2 -0
  27. package/dist/ar/qr-derived-pose.js +114 -0
  28. package/dist/ar/qr-detection-controller.d.ts +2 -0
  29. package/dist/ar/qr-detection-controller.js +2 -0
  30. package/dist/ar/qr-frontend.d.ts +2 -0
  31. package/dist/ar/qr-frontend.js +60 -0
  32. package/dist/ar/qr-pose.d.ts +2 -0
  33. package/dist/ar/qr-pose.js +267 -0
  34. package/dist/ar/qr-size-depth-context.d.ts +3 -0
  35. package/dist/ar/qr-size-depth-context.js +42 -0
  36. package/dist/ar/qr-size-measurer.d.ts +2 -0
  37. package/dist/ar/qr-size-measurer.js +2 -0
  38. package/dist/ar/replay-scene.d.ts +1 -1
  39. package/dist/ar/replay-scene.js +1 -1
  40. package/dist/ar/scene-node-names.d.ts +1 -1
  41. package/dist/ar/webxr-nue-basis.d.ts +1 -1
  42. package/dist/ar/webxr-session.d.ts +4 -4
  43. package/dist/ar/webxr-session.js +2 -2
  44. package/dist/ar/xr-camera-texture.d.ts +1 -1
  45. package/dist/ar/xr-error-handler.d.ts +1 -1
  46. package/dist/ar/xr-frame-loop.d.ts +1 -1
  47. package/dist/ar-Dc72Ksiu.js +382 -0
  48. package/dist/{ar-world-group-alignment-ColHB_5A.d.ts → ar-world-group-alignment-DDLkgNf_.d.ts} +1 -1
  49. package/dist/{camera-blit-capture-D5_mNhla.d.ts → camera-blit-capture-CqWe1G7s.d.ts} +66 -4
  50. package/dist/concurrency-DqAlKX4j.d.ts +50 -0
  51. package/dist/core/index.d.ts +1 -1
  52. package/dist/{create-slam-app-store-qNXQ-tq5.d.ts → create-slam-app-store-Ce2FMKCp.d.ts} +5 -5
  53. package/dist/depth-grid-lookup-vMDrGTk_.d.ts +21 -0
  54. package/dist/{depth-sampler-Bhanfwla.d.ts → depth-sampler-DwfPYzS7.d.ts} +1 -1
  55. package/dist/{depth-unprojection-Y6i_mJuT.d.ts → depth-unprojection-CK3b9htH.d.ts} +1 -1
  56. package/dist/{enable-gps-ar-CapmADVN.d.ts → enable-gps-ar-CjZ1nEdu.d.ts} +4 -4
  57. package/dist/file-system-utils-Bs7lpeVP.d.ts +40 -0
  58. package/dist/{frame-conversions-BfsafGWR.d.ts → frame-conversions-D_Af8Dt3.d.ts} +1 -1
  59. package/dist/{fused-path-DJoVu98b.d.ts → fused-path-C0FHe67t.d.ts} +1 -1
  60. package/dist/geo/h3-proximity.d.ts +2 -2
  61. package/dist/geo/h3-proximity.js +61 -2
  62. package/dist/geo/index.d.ts +2 -2
  63. package/dist/geo/index.js +2 -2
  64. package/dist/{gps-anchor-dYeCvvp5.d.ts → gps-anchor-C6I9NDHA.d.ts} +1 -1
  65. package/dist/{gps-ar-pose-sampler-DVb96iOy.d.ts → gps-ar-pose-sampler-DaHg2aPZ.d.ts} +1 -1
  66. package/dist/{gps-event-coordinator-BDIQde2x.d.ts → gps-event-coordinator-C7hwHyLA.d.ts} +3 -3
  67. package/dist/{gps-event-markers-Dc9FT-q1.d.ts → gps-event-markers-Dd0G2bD0.d.ts} +18 -0
  68. package/dist/{h3-proximity-I1msGPgE.d.ts → h3-proximity-B57C6rYL.d.ts} +39 -1
  69. package/dist/{image-capture-DZ4HFRgS.d.ts → image-capture-C2Feywet.d.ts} +30 -4
  70. package/dist/index-BvLo2poy.d.ts +399 -0
  71. package/dist/{index-KJq2amHF.d.ts → index-m_LneUSi.d.ts} +1 -1
  72. package/dist/index.d.ts +77 -67
  73. package/dist/index.js +18 -9
  74. package/dist/{leaflet-map-overlay-6CJC-E1l.d.ts → leaflet-map-overlay-bnb2U1NQ.d.ts} +1 -1
  75. package/dist/{map-data-CkloBxIl.d.ts → map-data-DdjPiSav.d.ts} +1 -1
  76. package/dist/{map-overlay-draw-krOKwDAq.d.ts → map-overlay-draw-CPVdAsFq.d.ts} +1 -1
  77. package/dist/{null-storage-backend-CxEjZHRW.d.ts → null-storage-backend-CuSdYFsM.d.ts} +2 -2
  78. package/dist/{occupancy-grid-BRNRaPyT.d.ts → occupancy-grid-Bvi2Gw1U.d.ts} +12 -2
  79. package/dist/{opfs-storage-DYm8WHq7.d.ts → opfs-storage-DMtlt57S.d.ts} +25 -6
  80. package/dist/{opfs-storage-backend-CjSAuqg_.d.ts → opfs-storage-backend-CeQAb0SD.d.ts} +3 -3
  81. package/dist/{persistence-middleware-DwyzgfWr.d.ts → persistence-middleware-5eearhSV.d.ts} +1 -1
  82. package/dist/planar-pnp-cjthe7Be.d.ts +67 -0
  83. package/dist/qr-debug-view-B8gTzjMm.d.ts +29 -0
  84. package/dist/qr-derived-pose-DinKE600.d.ts +122 -0
  85. package/dist/qr-detected-slice-Cpv-dg82.d.ts +166 -0
  86. package/dist/qr-detection-controller-B-tLVd-S.js +119 -0
  87. package/dist/qr-detection-controller-Cd2Ih4cC.d.ts +48 -0
  88. package/dist/qr-frontend-DufZp2xS.d.ts +58 -0
  89. package/dist/qr-pose-Bt2reSw7.d.ts +138 -0
  90. package/dist/qr-pose-aggregation-CFlmLm6w.d.ts +90 -0
  91. package/dist/qr-pose-aggregation-DXR9STHs.js +200 -0
  92. package/dist/qr-size-depth-context-onu36E9s.d.ts +13 -0
  93. package/dist/qr-size-measurer-D7739ZpB.d.ts +165 -0
  94. package/dist/qr-size-measurer-DLb33geH.js +596 -0
  95. package/dist/recording-options-D93kksJU.d.ts +369 -0
  96. package/dist/{recording-replayer-BL-x-7k0.d.ts → recording-replayer-CUEK82tM.d.ts} +2 -2
  97. package/dist/recording-slice-B-uCgGoh.d.ts +55 -0
  98. package/dist/{replay-engine-BRqszuQ9.d.ts → replay-engine-BSVd7Zzb.d.ts} +7 -4
  99. package/dist/{replay-scene-Bw58VL6p.d.ts → replay-scene-CPt56pps.d.ts} +3 -3
  100. package/dist/sensors/gps-error-handler.d.ts +1 -1
  101. package/dist/sensors/gps.d.ts +1 -1
  102. package/dist/sensors/index.d.ts +3 -3
  103. package/dist/sensors/permission-checker.d.ts +1 -1
  104. package/dist/sensors/permission-checker.js +1 -1
  105. package/dist/state/app-selectors.d.ts +1 -1
  106. package/dist/state/app-selectors.js +3 -1
  107. package/dist/state/combined-root-state.d.ts +1 -1
  108. package/dist/state/create-slam-app-store.d.ts +1 -1
  109. package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
  110. package/dist/state/gps-event-coordinator.d.ts +1 -1
  111. package/dist/state/index.d.ts +20 -17
  112. package/dist/state/index.js +3 -2
  113. package/dist/state/persistence-middleware.d.ts +1 -1
  114. package/dist/state/qr-detected-slice.d.ts +4 -0
  115. package/dist/state/qr-detected-slice.js +311 -0
  116. package/dist/state/recording-options.d.ts +2 -2
  117. package/dist/state/recording-options.js +128 -4
  118. package/dist/state/recording-replayer.d.ts +2 -2
  119. package/dist/state/recording-slice.d.ts +1 -1
  120. package/dist/state/recording-slice.js +5 -1
  121. package/dist/state/replay-engine.d.ts +1 -1
  122. package/dist/state/replay-engine.js +7 -3
  123. package/dist/state/store-subscribers.d.ts +2 -2
  124. package/dist/state/subscribe-to-selector.d.ts +1 -1
  125. package/dist/state/tracking-quality.d.ts +1 -1
  126. package/dist/state/tracking-slice.d.ts +1 -1
  127. package/dist/{state-BkIfpiyA.js → state-IEw0ZsBu.js} +1 -0
  128. package/dist/storage/file-system-utils.d.ts +2 -2
  129. package/dist/storage/file-system-utils.js +17 -1
  130. package/dist/storage/index.d.ts +9 -9
  131. package/dist/storage/index.js +4 -4
  132. package/dist/storage/null-storage-backend.d.ts +1 -1
  133. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  134. package/dist/storage/opfs-storage-backend.js +3 -3
  135. package/dist/storage/opfs-storage.d.ts +1 -1
  136. package/dist/storage/opfs-storage.js +9 -8
  137. package/dist/storage/storage-backend.d.ts +1 -1
  138. package/dist/storage/zip-coverage-embed.d.ts +2 -0
  139. package/dist/storage/zip-coverage-embed.js +88 -0
  140. package/dist/storage/zip-export.d.ts +2 -2
  141. package/dist/storage/zip-export.js +38 -60
  142. package/dist/storage/zip-reader.d.ts +1 -1
  143. package/dist/storage/zip-reader.js +1 -1
  144. package/dist/{storage-backend-C9HLFtgz.d.ts → storage-backend-CTJHNdMp.d.ts} +1 -1
  145. package/dist/{store-subscribers-rrkLkFTt.d.ts → store-subscribers-BZP81hjh.d.ts} +2 -2
  146. package/dist/{subscribe-to-selector-CP3BKSbF.d.ts → subscribe-to-selector-Bp3sLx8L.d.ts} +1 -1
  147. package/dist/test-utils/browser-mocks.d.ts +7 -1
  148. package/dist/test-utils/browser-mocks.js +6 -0
  149. package/dist/test-utils/zip-round-trip-helpers.d.ts +9 -1
  150. package/dist/test-utils/zip-round-trip-helpers.js +8 -3
  151. package/dist/{tracking-slice-CdK8c-8P.d.ts → tracking-slice-DJZ9qeGg.d.ts} +1 -1
  152. package/dist/types/ar-types.d.ts +1 -1
  153. package/dist/types/geo-types.d.ts +1 -1
  154. package/dist/types/index.d.ts +2 -2
  155. package/dist/utils/concurrency.d.ts +2 -2
  156. package/dist/utils/concurrency.js +66 -2
  157. package/dist/utils/failure-tracker.d.ts +1 -1
  158. package/dist/utils/format-file-size.d.ts +1 -1
  159. package/dist/utils/fused-path.d.ts +1 -1
  160. package/dist/utils/index.d.ts +6 -6
  161. package/dist/utils/list-formatter.d.ts +1 -1
  162. package/dist/utils/logger.d.ts +1 -1
  163. package/dist/visualization/accuracy-circles.d.ts +1 -1
  164. package/dist/visualization/alignment-lerper.d.ts +1 -1
  165. package/dist/visualization/ar-world-group-alignment.d.ts +1 -1
  166. package/dist/visualization/ar-world-group-alignment.js +1 -1
  167. package/dist/visualization/camera-follower.d.ts +1 -1
  168. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  169. package/dist/visualization/frame-conversions.d.ts +1 -1
  170. package/dist/visualization/frustum-visibility.d.ts +1 -1
  171. package/dist/visualization/gps-anchor.d.ts +1 -1
  172. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  173. package/dist/visualization/gps-event-markers.d.ts +1 -1
  174. package/dist/visualization/gps-event-markers.js +40 -5
  175. package/dist/visualization/hit-test-reticle.d.ts +1 -1
  176. package/dist/visualization/hit-test-reticle.js +1 -0
  177. package/dist/visualization/index.d.ts +18 -18
  178. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  179. package/dist/visualization/leaflet-map-overlay.js +15 -3
  180. package/dist/visualization/lerp-utils.d.ts +1 -1
  181. package/dist/visualization/map-data.d.ts +1 -1
  182. package/dist/visualization/map-overlay-draw.d.ts +1 -1
  183. package/dist/visualization/map-overlay.d.ts +1 -1
  184. package/dist/visualization/three-dispose.d.ts +1 -1
  185. package/dist/visualization/vis-colors.d.ts +1 -1
  186. package/dist/{webxr-session-QhcSSub5.js → webxr-session-CJF9Nijp.js} +234 -9
  187. package/dist/{webxr-session-DtAG9srK.d.ts → webxr-session-COsOjJwn.d.ts} +76 -8
  188. package/dist/zip-coverage-embed-tFohI2J4.d.ts +41 -0
  189. package/dist/{zip-export-D_5HAvFz.d.ts → zip-export-DDWXInTR.d.ts} +28 -24
  190. package/package.json +2 -2
  191. package/dist/concurrency-CO1FQvs3.d.ts +0 -24
  192. package/dist/file-system-C_eqiEA8.d.ts +0 -114
  193. package/dist/file-system-utils-DyAmcuvX.d.ts +0 -24
  194. package/dist/recording-options-DJBXuMxV.d.ts +0 -182
  195. package/dist/recording-slice-89O07ebs.d.ts +0 -36
  196. package/dist/storage/file-system.d.ts +0 -2
  197. package/dist/storage/file-system.js +0 -289
  198. /package/dist/{accuracy-circles-BpwWAs8G.d.ts → accuracy-circles-Pi2YeCrM.d.ts} +0 -0
  199. /package/dist/{alignment-lerper-Dm9G7JJO.d.ts → alignment-lerper-BUTsQ2yu.d.ts} +0 -0
  200. /package/dist/{ar-types-Dan1MVTi.d.ts → ar-types-BckI1E-O.d.ts} +0 -0
  201. /package/dist/{bresenham3d-CFfd7n6B.d.ts → bresenham3d-ByjcmEgM.d.ts} +0 -0
  202. /package/dist/{camera-follower-C2mwEn7c.d.ts → camera-follower-BTb2Yp12.d.ts} +0 -0
  203. /package/dist/{capability-checker-CQXJHxXg.d.ts → capability-checker-CXqoJYtb.d.ts} +0 -0
  204. /package/dist/{capture-failure-tracker-CUeU3L_M.d.ts → capture-failure-tracker-BL5SoG2a.d.ts} +0 -0
  205. /package/dist/{chromium-camera-access-workaround-Jqbn0apH.d.ts → chromium-camera-access-workaround-BNK189P0.d.ts} +0 -0
  206. /package/dist/{css3d-renderer-manager-ptu_t3B3.d.ts → css3d-renderer-manager-m-ab19vp.d.ts} +0 -0
  207. /package/dist/{failure-tracker-Cn8Fo9D4.d.ts → failure-tracker-Cnc_xWUw.d.ts} +0 -0
  208. /package/dist/{format-file-size-CLCZz3lC.d.ts → format-file-size-BFUWvir1.d.ts} +0 -0
  209. /package/dist/{frame-loop-CMewS8cY.d.ts → frame-loop-BEO9_q3k.d.ts} +0 -0
  210. /package/dist/{frustum-visibility-CmI96ESS.d.ts → frustum-visibility-D_RGmH98.d.ts} +0 -0
  211. /package/dist/{geo-types-SaPnIEUM.d.ts → geo-types-DqiBzPO9.d.ts} +0 -0
  212. /package/dist/{gps-VuezJWK3.d.ts → gps-C5inWeNV.d.ts} +0 -0
  213. /package/dist/{gps-compass-cubes-BM1uGiYV.d.ts → gps-compass-cubes-BExxrWJ-.d.ts} +0 -0
  214. /package/dist/{gps-error-handler-CJl_2MVm.d.ts → gps-error-handler-C2bS69oJ.d.ts} +0 -0
  215. /package/dist/{hit-test-reticle-BEzEybxZ.d.ts → hit-test-reticle-Dqak9hiR.d.ts} +0 -0
  216. /package/dist/{index-CF05QV2m.d.ts → index-eaI1Z1KR.d.ts} +0 -0
  217. /package/dist/{lerp-utils-BpV-0Dtk.d.ts → lerp-utils-kLxMJ2Xj.d.ts} +0 -0
  218. /package/dist/{list-formatter-RRcBe8Vg.d.ts → list-formatter-ct8txwvR.d.ts} +0 -0
  219. /package/dist/{logger-BEHXsnnq.d.ts → logger-CzQrCe6C.d.ts} +0 -0
  220. /package/dist/{map-overlay-bcogY5fK.d.ts → map-overlay-Dci4yvm3.d.ts} +0 -0
  221. /package/dist/{permission-checker-GpAiSCwd.d.ts → permission-checker-CtPtPQRq.d.ts} +0 -0
  222. /package/dist/{scene-node-names-Bu0S66H1.d.ts → scene-node-names-CdNeQjOE.d.ts} +0 -0
  223. /package/dist/{session-disposers-M-oashRH.js → session-disposers-D8wnO-uW.js} +0 -0
  224. /package/dist/{three-dispose-D950t71B.d.ts → three-dispose-BCZkSJp2.d.ts} +0 -0
  225. /package/dist/{vis-colors-D6omC64K.d.ts → vis-colors-BZsSLI4i.d.ts} +0 -0
  226. /package/dist/{webxr-nue-basis-CqKQmZzT.d.ts → webxr-nue-basis-CW1wsbm6.d.ts} +0 -0
  227. /package/dist/{xr-camera-texture-DBwCaKTd.d.ts → xr-camera-texture-BmrP7iNZ.d.ts} +0 -0
  228. /package/dist/{xr-error-handler-C0AQRmxs.d.ts → xr-error-handler-BEjRgZjt.d.ts} +0 -0
  229. /package/dist/{xr-frame-loop-BtYytZJg.d.ts → xr-frame-loop-K8ebdjLw.d.ts} +0 -0
  230. /package/dist/{zip-reader-DSEXt7K6.d.ts → zip-reader-Q2ue6zlC.d.ts} +0 -0
@@ -2,6 +2,7 @@ import { buildRawGpsPoint, buildRecordGpsEventPayload, createGpsPositionHandler,
2
2
  import { clearLastRestartedPayload, originReset, poseLost, poseReceived, resetTracking, selectLastRestartedPayload, selectLastSensorOrientation, selectLastValidPose, selectLostFrameCount, selectTrackingPhase, trackingReducer } from "./tracking-slice.js";
3
3
  import { DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, IMAGE_CONSTRAINTS, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateDepthOptions, validateImageOptions, validateRecordingOptions } from "./recording-options.js";
4
4
  import { endSession, recordDepthSample, recordWriteFailure, recordingReducer, startSession } from "./recording-slice.js";
5
+ import { DEFAULT_QR_MAX_HISTORY, clearAllQrMarkers, clearQrMarker, medianQrPosition, pruneQrDetections, qrDetectedReducer, recordQrDetection, recordQrSizeEstimate, selectDerivedQrPlacement, selectLatestQrDetection, selectQrMarker, selectQrMarkers, selectQrPoseStability, selectQrRawObservations, selectQrSize, selectResolvedQrSizeM, selectSolvedQrPose, selectStableQrPose, setQrMaxHistory } from "./qr-detected-slice.js";
5
6
  import { captureGpsAnchorSample } from "./gps-ar-pose-sampler.js";
6
7
  import { selectAlignmentMatrix, selectFrameTilesInWebXR, selectGpsPositions, selectOdometryPositions, selectOdometryRotations, selectZeroReference } from "./app-selectors.js";
7
8
  import { DEFAULT_TRACKING_QUALITY_OPTIONS, computeCompassAgreement, computeConvergence, computeCoverage, computeGpsAccuracy, computeGpsVsFusedDivergence, computeResidualConsensus, computeTrackingQualityReport, createTrackingQualityListenerMiddleware, degradedCountUpdated, firstAgreementReached, matrixDelta, reportUpdated, resetTrackingQuality, selectFirstAgreementObservationIndex, selectRecentAlignments, selectTrackingQuality, snapshotPushed, snapshotsTrimmed, trackingQualityReducer } from "./tracking-quality.js";
@@ -11,5 +12,5 @@ import { replayRecording } from "./recording-replayer.js";
11
12
  import { DEFAULT_MAX_DELAY_MS, ReplayEngine, computeInterActionDelay, extractActionTimestamp } from "./replay-engine.js";
12
13
  import { subscribeToSelector } from "./subscribe-to-selector.js";
13
14
  import { wireStoreSubscribers } from "./store-subscribers.js";
14
- import { a as computeOnboardingGuidance, i as setZeroPos, n as calcRelativeCoordsInMeters, o as selectOnboardingGuidance, r as recordGpsEvent, t as add2dImage } from "../state-BkIfpiyA.js";
15
- export { DEFAULT_MAX_DELAY_MS, DEFAULT_RECORDING_OPTIONS, DEFAULT_TRACKING_QUALITY_OPTIONS, DEPTH_CONSTRAINTS, IMAGE_CONSTRAINTS, ReplayEngine, STORAGE_KEY, add2dImage, buildRawGpsPoint, buildRecordGpsEventPayload, calcRelativeCoordsInMeters, captureGpsAnchorSample, clearLastRestartedPayload, cloneRecordingOptions, computeCompassAgreement, computeConvergence, computeCoverage, computeGpsAccuracy, computeGpsVsFusedDivergence, computeInterActionDelay, computeOnboardingGuidance, computeResidualConsensus, computeTrackingQualityReport, createGpsPositionHandler, createPersistenceMiddleware, createSlamAppStore, createTrackingQualityListenerMiddleware, degradedCountUpdated, endSession, eulerToQuaternion, extractActionTimestamp, extractOdomPosition, extractOdomRotation, firstAgreementReached, getLastDeviceOrientation, loadRecordingOptions, matrixDelta, originReset, poseLost, poseReceived, recordDepthSample, recordGpsEvent, recordWriteFailure, recordingReducer, replayRecording, reportUpdated, resetCoordinatorState, resetRecordingOptions, resetTracking, resetTrackingQuality, saveRecordingOptions, selectAlignmentMatrix, selectFirstAgreementObservationIndex, selectFrameTilesInWebXR, selectGpsPositions, selectLastRestartedPayload, selectLastSensorOrientation, selectLastValidPose, selectLostFrameCount, selectOdometryPositions, selectOdometryRotations, selectOnboardingGuidance, selectRecentAlignments, selectTrackingPhase, selectTrackingQuality, selectZeroReference, setZeroPos, slicePrefixOf, snapshotPushed, snapshotsTrimmed, startSession, subscribeToSelector, trackingQualityReducer, trackingReducer, updateDeviceOrientation, validateDepthOptions, validateImageOptions, validateRecordingOptions, wireStoreSubscribers };
15
+ import { a as computeOnboardingGuidance, i as setZeroPos, n as calcRelativeCoordsInMeters, o as selectOnboardingGuidance, r as recordGpsEvent, t as add2dImage } from "../state-IEw0ZsBu.js";
16
+ export { DEFAULT_MAX_DELAY_MS, DEFAULT_QR_MAX_HISTORY, DEFAULT_RECORDING_OPTIONS, DEFAULT_TRACKING_QUALITY_OPTIONS, DEPTH_CONSTRAINTS, IMAGE_CONSTRAINTS, ReplayEngine, STORAGE_KEY, add2dImage, buildRawGpsPoint, buildRecordGpsEventPayload, calcRelativeCoordsInMeters, captureGpsAnchorSample, clearAllQrMarkers, clearLastRestartedPayload, clearQrMarker, cloneRecordingOptions, computeCompassAgreement, computeConvergence, computeCoverage, computeGpsAccuracy, computeGpsVsFusedDivergence, computeInterActionDelay, computeOnboardingGuidance, computeResidualConsensus, computeTrackingQualityReport, createGpsPositionHandler, createPersistenceMiddleware, createSlamAppStore, createTrackingQualityListenerMiddleware, degradedCountUpdated, endSession, eulerToQuaternion, extractActionTimestamp, extractOdomPosition, extractOdomRotation, firstAgreementReached, getLastDeviceOrientation, loadRecordingOptions, matrixDelta, medianQrPosition, originReset, poseLost, poseReceived, pruneQrDetections, qrDetectedReducer, recordDepthSample, recordGpsEvent, recordQrDetection, recordQrSizeEstimate, recordWriteFailure, recordingReducer, replayRecording, reportUpdated, resetCoordinatorState, resetRecordingOptions, resetTracking, resetTrackingQuality, saveRecordingOptions, selectAlignmentMatrix, selectDerivedQrPlacement, selectFirstAgreementObservationIndex, selectFrameTilesInWebXR, selectGpsPositions, selectLastRestartedPayload, selectLastSensorOrientation, selectLastValidPose, selectLatestQrDetection, selectLostFrameCount, selectOdometryPositions, selectOdometryRotations, selectOnboardingGuidance, selectQrMarker, selectQrMarkers, selectQrPoseStability, selectQrRawObservations, selectQrSize, selectRecentAlignments, selectResolvedQrSizeM, selectSolvedQrPose, selectStableQrPose, selectTrackingPhase, selectTrackingQuality, selectZeroReference, setQrMaxHistory, setZeroPos, slicePrefixOf, snapshotPushed, snapshotsTrimmed, startSession, subscribeToSelector, trackingQualityReducer, trackingReducer, updateDeviceOrientation, validateDepthOptions, validateImageOptions, validateRecordingOptions, wireStoreSubscribers };
@@ -1,2 +1,2 @@
1
- import { n as createPersistenceMiddleware, r as slicePrefixOf, t as PersistenceMiddlewareOptions } from "../persistence-middleware-DwyzgfWr.js";
1
+ import { n as createPersistenceMiddleware, r as slicePrefixOf, t as PersistenceMiddlewareOptions } from "../persistence-middleware-5eearhSV.js";
2
2
  export { PersistenceMiddlewareOptions, createPersistenceMiddleware, slicePrefixOf };
@@ -0,0 +1,4 @@
1
+ import { d as QrSizeStatus, l as QrSizeEstimate } from "../qr-size-measurer-D7739ZpB.js";
2
+ import { a as QrPoseStability, o as QrPoseStabilityOptions, s as QrPoseStabilityStatus } from "../qr-pose-aggregation-CFlmLm6w.js";
3
+ import { C as setQrMaxHistory, S as selectStableQrPose, _ as selectQrPoseStability, a as RootWithQrDetected, b as selectResolvedQrSizeM, c as medianQrPosition, d as recordQrDetection, f as recordQrSizeEstimate, g as selectQrMarkers, h as selectQrMarker, i as QrMarkerState, l as pruneQrDetections, m as selectLatestQrDetection, n as QrDetectedState, o as clearAllQrMarkers, p as selectDerivedQrPlacement, r as QrDetectionEntry, s as clearQrMarker, t as DEFAULT_QR_MAX_HISTORY, u as qrDetectedReducer, v as selectQrRawObservations, x as selectSolvedQrPose, y as selectQrSize } from "../qr-detected-slice-Cpv-dg82.js";
4
+ export { DEFAULT_QR_MAX_HISTORY, QrDetectedState, QrDetectionEntry, QrMarkerState, QrPoseStability, QrPoseStabilityOptions, QrPoseStabilityStatus, QrSizeEstimate, QrSizeStatus, RootWithQrDetected, clearAllQrMarkers, clearQrMarker, medianQrPosition, pruneQrDetections, qrDetectedReducer, recordQrDetection, recordQrSizeEstimate, selectDerivedQrPlacement, selectLatestQrDetection, selectQrMarker, selectQrMarkers, selectQrPoseStability, selectQrRawObservations, selectQrSize, selectResolvedQrSizeM, selectSolvedQrPose, selectStableQrPose, setQrMaxHistory };
@@ -0,0 +1,311 @@
1
+ import { deriveQrPlacement, deriveSolvedQrPose } from "../ar/qr-derived-pose.js";
2
+ import { i as evaluateQrPoseStability } from "../qr-pose-aggregation-DXR9STHs.js";
3
+ import { createSlice } from "@reduxjs/toolkit";
4
+ //#region ../src/state/qr-detected-slice.ts
5
+ /**
6
+ * `qrDetected` Redux slice — Note 3 of the QR-tracking follow-up plan
7
+ * (docs `2026-06-15-followup-qr-tracking-generalization-overlay-and-north.md`).
8
+ *
9
+ * A detection-agnostic, framework-level store of "what was detected, where, in
10
+ * 3D" that overlay / trigger / anchor consumers subscribe to INDEPENDENTLY of
11
+ * the GPS fusion. The QR-tracking controller dispatches into it on every lock
12
+ * (alongside, or instead of, the synthetic GPS vote — the vote is optional, the
13
+ * `qrDetected` emission is not). Both the QR-tracking demo and the Recorder
14
+ * consume the SAME slice — it is not duplicated per app and does NOT live in the
15
+ * closed-source fusion core (locked maintainer decision, 2026-06-15 interview).
16
+ *
17
+ * Shape (locked decision): a dictionary keyed by the decoded payload (text/URL),
18
+ * value = a per-marker BOUNDED RING BUFFER of detections + a size-lifecycle
19
+ * estimate. Identity is the payload itself (already unique, human-debuggable) —
20
+ * two physically-distinct markers sharing a payload merge by design, and a
21
+ * *moving* marker with one payload accumulates a motion path (also desired).
22
+ * The ring buffer is bounded by {@link QrDetectedState.maxHistory} so a naive
23
+ * consumer cannot leak; `pruneQrDetections` is the explicit custom-cleanup path.
24
+ *
25
+ * Detection-agnostic on purpose (Note 1): the same shape (world pose + label +
26
+ * confidence + timestamp) generalizes to future object detection (YOLO etc.),
27
+ * so the fields are deliberately not QR-specific beyond the payload key.
28
+ *
29
+ * This slice is OPT-IN: apps wire it via `createSlamAppStore({ extraReducers:
30
+ * { qrDetected: qrDetectedReducer } })`. It is not a built-in of the store
31
+ * factory, so framework consumers that never detect anything pay nothing.
32
+ *
33
+ * @see ../ar/qr-tracking-controller.ts — the producer (dispatches `recordQrDetection`).
34
+ * @see ../ar/qr-pose.ts — `Pose` / `QrPoseSolution` (the per-detection geometry).
35
+ */
36
+ /**
37
+ * Default ring-buffer cap per marker. Bounded so an overlay that never prunes
38
+ * still cannot leak; large enough to support a robust median over a sliding
39
+ * window and a short motion path. Override via {@link setQrMaxHistory}.
40
+ */
41
+ const DEFAULT_QR_MAX_HISTORY = 32;
42
+ const initialSize = () => ({
43
+ status: "unknown",
44
+ estimateM: null,
45
+ sampleCount: 0,
46
+ spreadM: 0
47
+ });
48
+ const initialState = {
49
+ maxHistory: 32,
50
+ markers: {}
51
+ };
52
+ /** Trim a detection list to at most `cap`, dropping the oldest. */
53
+ function capDetections(detections, cap) {
54
+ if (cap <= 0) return [];
55
+ return detections.length > cap ? detections.slice(detections.length - cap) : detections.slice();
56
+ }
57
+ const qrDetectedSlice = createSlice({
58
+ name: "qrDetected",
59
+ initialState,
60
+ reducers: {
61
+ /**
62
+ * Append a detection to its marker's ring buffer (creating the marker on
63
+ * first sight), enforcing `maxHistory`. Returns a fresh state object rather
64
+ * than mutating the immer draft: the entry's `Pose` carries readonly
65
+ * `Vector3`/`Quaternion` tuples that immer's `WritableDraft` rejects (same
66
+ * reason `tracking-slice.originReset` returns new state).
67
+ */
68
+ recordQrDetection(state, action) {
69
+ const entry = action.payload;
70
+ const existing = state.markers[entry.text];
71
+ const detections = capDetections(existing ? [...existing.detections, entry] : [entry], state.maxHistory);
72
+ const marker = {
73
+ text: entry.text,
74
+ detections,
75
+ size: existing?.size ?? initialSize()
76
+ };
77
+ return {
78
+ ...state,
79
+ markers: {
80
+ ...state.markers,
81
+ [entry.text]: marker
82
+ }
83
+ };
84
+ },
85
+ /**
86
+ * Set a marker's size estimate (the Note 3 size lifecycle). Creates the
87
+ * marker with an empty history if it has no detections yet (a size can be
88
+ * authored before any detection).
89
+ */
90
+ recordQrSizeEstimate(state, action) {
91
+ const { text, estimate } = action.payload;
92
+ const existing = state.markers[text];
93
+ const marker = {
94
+ text,
95
+ detections: existing ? existing.detections.slice() : [],
96
+ size: estimate
97
+ };
98
+ return {
99
+ ...state,
100
+ markers: {
101
+ ...state.markers,
102
+ [text]: marker
103
+ }
104
+ };
105
+ },
106
+ /**
107
+ * Drop the oldest `count` detections from one marker (explicit custom
108
+ * cleanup, beyond the automatic ring-buffer cap). No-op for an unknown
109
+ * marker or `count <= 0`.
110
+ */
111
+ pruneQrDetections(state, action) {
112
+ const { text, count } = action.payload;
113
+ const existing = state.markers[text];
114
+ if (!existing || count <= 0) return state;
115
+ const marker = {
116
+ ...existing,
117
+ detections: existing.detections.slice(count)
118
+ };
119
+ return {
120
+ ...state,
121
+ markers: {
122
+ ...state.markers,
123
+ [text]: marker
124
+ }
125
+ };
126
+ },
127
+ /** Remove one marker entirely. */
128
+ clearQrMarker(state, action) {
129
+ const { [action.payload.text]: _removed, ...rest } = state.markers;
130
+ return {
131
+ ...state,
132
+ markers: rest
133
+ };
134
+ },
135
+ /** Remove every marker (e.g. on session reset). */
136
+ clearAllQrMarkers(state) {
137
+ return {
138
+ ...state,
139
+ markers: {}
140
+ };
141
+ },
142
+ /**
143
+ * Change the ring-buffer cap. Existing markers are re-trimmed immediately
144
+ * so the invariant "no marker exceeds `maxHistory`" holds at all times.
145
+ */
146
+ setQrMaxHistory(state, action) {
147
+ const next = Math.max(0, Math.floor(action.payload));
148
+ const markers = {};
149
+ for (const [key, marker] of Object.entries(state.markers)) markers[key] = {
150
+ ...marker,
151
+ detections: capDetections(marker.detections, next)
152
+ };
153
+ return {
154
+ ...state,
155
+ maxHistory: next,
156
+ markers
157
+ };
158
+ }
159
+ }
160
+ });
161
+ const { recordQrDetection, recordQrSizeEstimate, pruneQrDetections, clearQrMarker, clearAllQrMarkers, setQrMaxHistory } = qrDetectedSlice.actions;
162
+ const qrDetectedReducer = qrDetectedSlice.reducer;
163
+ function selectQrMarkers(state) {
164
+ return state.qrDetected.markers;
165
+ }
166
+ function selectQrMarker(state, text) {
167
+ return state.qrDetected.markers[text];
168
+ }
169
+ /**
170
+ * The newest detection for a marker (the natural overlay-persistence source —
171
+ * an overlay reads this and keeps the last pose across detection misses rather
172
+ * than unmounting/flickering).
173
+ */
174
+ function selectLatestQrDetection(state, text) {
175
+ const marker = state.qrDetected.markers[text];
176
+ if (!marker || marker.detections.length === 0) return void 0;
177
+ return marker.detections[marker.detections.length - 1];
178
+ }
179
+ function selectQrSize(state, text) {
180
+ return state.qrDetected.markers[text]?.size;
181
+ }
182
+ /**
183
+ * Resolve a marker's measured size for the high-weight GPS vote
184
+ * (framework-wiring-options Part B, Option a). Returns the running-median
185
+ * `estimateM` ONCE the size lifecycle reports `'estimated'`, else `null` ("keep
186
+ * scanning"). This is the bridge an app injects as the QR controller's
187
+ * `resolveSizeM`, so a size-less-but-geo level votes the moment the slice
188
+ * converges — **without** the `ar` controller ever importing the slice (which
189
+ * would close the `ar → state` cycle):
190
+ *
191
+ * ```ts
192
+ * resolveSizeM: (text) => selectResolvedQrSizeM(store.getState(), text),
193
+ * ```
194
+ *
195
+ * Per the Note 3 decision (confirmed) a reliably-estimated *measured* size
196
+ * drives the vote under the *same* gate as an authored size — no extra
197
+ * size-specific bar — because the §7 occupancy + reprojection + outlier-rejection
198
+ * backstops already bound a bad read.
199
+ */
200
+ function selectResolvedQrSizeM(state, text) {
201
+ const size = state.qrDetected.markers[text]?.size;
202
+ return size?.status === "estimated" ? size.estimateM : null;
203
+ }
204
+ /** Per-axis median of a numeric list (returns the lower-middle for even n). */
205
+ function medianOf(values) {
206
+ const sorted = [...values].sort((a, b) => a - b);
207
+ return sorted[Math.floor((sorted.length - 1) / 2)];
208
+ }
209
+ /**
210
+ * Robust per-axis median of the world positions across a marker's detection
211
+ * window — the "running estimate" the bounded buffer enables (Note 3). Outliers
212
+ * in fewer than half the samples cannot move the result outside the inlier
213
+ * range. Skips RAW-only entries that carry no solved pose (D-A); returns `null`
214
+ * for an empty window (or one with no solved poses at all).
215
+ */
216
+ function medianQrPosition(entries) {
217
+ const poses = entries.map((e) => e.qrPoseWorld).filter((p) => p !== void 0);
218
+ if (poses.length === 0) return null;
219
+ const xs = poses.map((p) => p.position[0]);
220
+ const ys = poses.map((p) => p.position[1]);
221
+ const zs = poses.map((p) => p.position[2]);
222
+ return [
223
+ medianOf(xs),
224
+ medianOf(ys),
225
+ medianOf(zs)
226
+ ];
227
+ }
228
+ /**
229
+ * The pose-stability lifecycle for a marker, derived from its raw detection ring
230
+ * buffer (NOT stored separately — the detections are the source of truth, unlike
231
+ * the depth-measured size which is accumulated outside the buffer). Mirrors the
232
+ * size lifecycle's `unknown → measuring → stable` shape so the HUD can show
233
+ * "converging…" vs "stable". See `ar/qr-pose-aggregation.ts`.
234
+ */
235
+ function selectQrPoseStability(state, text, options) {
236
+ const marker = state.qrDetected.markers[text];
237
+ return evaluateQrPoseStability(marker ? marker.detections.map((d) => d.qrPoseWorld).filter((p) => p !== void 0) : [], options);
238
+ }
239
+ /**
240
+ * The robust filtered QR world pose, exposed ONLY once the stability gate is
241
+ * `stable` (else `null` — "keep accumulating"). This is the pose analogue of
242
+ * {@link selectResolvedQrSizeM}: the `resolveStablePose`-style bridge an app
243
+ * injects into the QR controller / demo so the `ar` layer never imports the
244
+ * slice. The high-weight vote and the smooth overlay consume THIS, never the raw
245
+ * latest pose (which stays available via {@link selectLatestQrDetection} for
246
+ * scanning feedback / overlay persistence across misses).
247
+ *
248
+ * ```ts
249
+ * resolveStablePose: (text) => selectStableQrPose(store.getState(), text),
250
+ * ```
251
+ */
252
+ function selectStableQrPose(state, text, options) {
253
+ const stability = selectQrPoseStability(state, text, options);
254
+ return stability.status === "stable" ? stability.pose : null;
255
+ }
256
+ /**
257
+ * Narrow a stored entry to a {@link RawQrObservation} — i.e. one that carries
258
+ * the full raw detector output. During the D-A-2 transition some entries may
259
+ * still be solved-pose-only (no raw fields); those are skipped by the derive
260
+ * selector. After WS-3 every entry is raw and this guard always holds.
261
+ */
262
+ function toRawObservation(entry) {
263
+ if (!entry.corners || !entry.cameraPose || !entry.projectionMatrix || entry.imageWidth === void 0 || entry.imageHeight === void 0) return null;
264
+ return {
265
+ text: entry.text,
266
+ corners: entry.corners,
267
+ cameraPose: entry.cameraPose,
268
+ projectionMatrix: entry.projectionMatrix,
269
+ imageWidth: entry.imageWidth,
270
+ imageHeight: entry.imageHeight,
271
+ timestamp: entry.timestamp
272
+ };
273
+ }
274
+ /**
275
+ * Derive the best-effort solved WORLD pose for a marker from its RAW observation
276
+ * history (decision D-A): accumulate size over the history against the injected
277
+ * `resolveDepthAt` as-of join, then PnP-solve the latest observation. Returns
278
+ * `null` when the marker is unknown, has no raw observations, cannot be sized
279
+ * yet, or PnP rejects the latest quad — the re-test-friendly replacement for the
280
+ * once-stored solved pose. Runs identically live and on replay (only
281
+ * `deps.resolveDepthAt` differs). See `ar/qr-derived-pose.ts`.
282
+ */
283
+ function selectSolvedQrPose(state, text, deps) {
284
+ return deriveSolvedQrPose(text, selectQrRawObservations(state, text), deps);
285
+ }
286
+ /**
287
+ * The marker's stored entries narrowed to {@link RawQrObservation}s (entries
288
+ * carrying the full raw detector output), oldest→newest. Empty for an unknown
289
+ * marker or one with only solved-pose-only (transitional) entries. This is the
290
+ * shared mapping the derive-on-read selectors and the incremental deriver
291
+ * consume — exposed so a stateful consumer (the recorder's debug controller)
292
+ * can hand raw observations to `createIncrementalQrPlacement` without
293
+ * re-implementing the `toRawObservation` guard.
294
+ */
295
+ function selectQrRawObservations(state, text) {
296
+ const marker = state.qrDetected.markers[text];
297
+ if (!marker) return [];
298
+ return marker.detections.map(toRawObservation).filter((o) => o !== null);
299
+ }
300
+ /**
301
+ * Derive the best-effort {@link DerivedQrPlacement} (solved pose AND the metric
302
+ * size that solved it) for a marker from its RAW observation history. The debug
303
+ * viz needs the size to draw the cube, so this returns both in one history pass
304
+ * (decision D-A; see {@link deriveQrPlacement}). `null` under the same
305
+ * conditions as {@link selectSolvedQrPose}. Runs identically live and on replay.
306
+ */
307
+ function selectDerivedQrPlacement(state, text, deps) {
308
+ return deriveQrPlacement(text, selectQrRawObservations(state, text), deps);
309
+ }
310
+ //#endregion
311
+ export { DEFAULT_QR_MAX_HISTORY, clearAllQrMarkers, clearQrMarker, medianQrPosition, pruneQrDetections, qrDetectedReducer, recordQrDetection, recordQrSizeEstimate, selectDerivedQrPlacement, selectLatestQrDetection, selectQrMarker, selectQrMarkers, selectQrPoseStability, selectQrRawObservations, selectQrSize, selectResolvedQrSizeM, selectSolvedQrPose, selectStableQrPose, setQrMaxHistory };
@@ -1,2 +1,2 @@
1
- import { _ as validateRecordingOptions, a as IMAGE_CONSTRAINTS, c as RecordingOptionsInput, d as loadRecordingOptions, f as resetRecordingOptions, g as validateImageOptions, h as validateDepthOptions, i as DepthCaptureOptions, l as STORAGE_KEY, m as validateArCrashIsolationOptions, n as DEFAULT_RECORDING_OPTIONS, o as ImageCaptureOptions, p as saveRecordingOptions, r as DEPTH_CONSTRAINTS, s as RecordingOptions, t as ArCrashIsolationOptions, u as cloneRecordingOptions } from "../recording-options-DJBXuMxV.js";
2
- export { ArCrashIsolationOptions, DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, DepthCaptureOptions, IMAGE_CONSTRAINTS, ImageCaptureOptions, RecordingOptions, RecordingOptionsInput, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateDepthOptions, validateImageOptions, validateRecordingOptions };
1
+ import { C as validateFrameTileDisplayOptions, D as validateRecordingOptions, E as validateQrOptions, O as validateVisualizationOptions, S as validateDepthOptions, T as validateOccupancyOptions, _ as cloneRecordingOptions, a as FRAME_TILE_DISPLAY_CONSTRAINTS, b as saveRecordingOptions, c as ImageCaptureOptions, d as QR_CONSTRAINTS, f as QrCaptureOptions, g as VisualizationOptions, h as STORAGE_KEY, i as DepthCaptureOptions, l as OCCUPANCY_CONSTRAINTS, m as RecordingOptionsInput, n as DEFAULT_RECORDING_OPTIONS, o as FrameTileDisplayOptions, p as RecordingOptions, r as DEPTH_CONSTRAINTS, s as IMAGE_CONSTRAINTS, t as ArCrashIsolationOptions, u as OccupancyOptions, v as loadRecordingOptions, w as validateImageOptions, x as validateArCrashIsolationOptions, y as resetRecordingOptions } from "../recording-options-D93kksJU.js";
2
+ export { ArCrashIsolationOptions, DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, DepthCaptureOptions, FRAME_TILE_DISPLAY_CONSTRAINTS, FrameTileDisplayOptions, IMAGE_CONSTRAINTS, ImageCaptureOptions, OCCUPANCY_CONSTRAINTS, OccupancyOptions, QR_CONSTRAINTS, QrCaptureOptions, RecordingOptions, RecordingOptionsInput, STORAGE_KEY, VisualizationOptions, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateDepthOptions, validateFrameTileDisplayOptions, validateImageOptions, validateOccupancyOptions, validateQrOptions, validateRecordingOptions, validateVisualizationOptions };
@@ -40,6 +40,19 @@ const DEFAULT_RECORDING_OPTIONS = {
40
40
  enableCss3dRenderer: true,
41
41
  enableCameraTextureAcquisition: true,
42
42
  applyChromiumProjectionLayerWorkaround: true
43
+ },
44
+ occupancy: { cellSizeM: .15 },
45
+ frameTileDisplay: { divisor: 2 },
46
+ visualization: {
47
+ frameTiles: true,
48
+ occupancyCubes: true,
49
+ gpsAlignmentMarkers: true,
50
+ compassCubes: true
51
+ },
52
+ qr: {
53
+ enabled: false,
54
+ intervalMs: 125,
55
+ captureSize: 1024
43
56
  }
44
57
  };
45
58
  /** Validation constraints for depth options */
@@ -74,6 +87,54 @@ const IMAGE_CONSTRAINTS = {
74
87
  }
75
88
  };
76
89
  /**
90
+ * Validation constraints for occupancy options.
91
+ *
92
+ * `cellSizeM` is clamped to 1–20 cm. The floor exists because cell count (and
93
+ * therefore the cube `InstancedMesh`, the grid `Map`, and the COLMAP
94
+ * `points3D` row count) scales as 1/cellSize³ — sub-centimetre voxels are both
95
+ * a memory/perf cliff on a phone and below the depth sensor's noise floor.
96
+ * Step is 1 cm (the settings slider operates in cm).
97
+ */
98
+ const OCCUPANCY_CONSTRAINTS = { cellSizeM: {
99
+ min: .01,
100
+ max: .2,
101
+ step: .01
102
+ } };
103
+ /**
104
+ * Validation constraints for the frame-tile display-resolution divisor.
105
+ *
106
+ * Clamped to 1–8 (full down to one-eighth per dimension), mirroring the capture
107
+ * `IMAGE_CONSTRAINTS.resolutionDivisor` range so the settings slider behaves the
108
+ * same. The intended stops are 1, 2, 4, 8 (full / half / quarter / eighth); the
109
+ * divisor is rounded to an integer so the resize target dimensions stay clean.
110
+ */
111
+ const FRAME_TILE_DISPLAY_CONSTRAINTS = { divisor: {
112
+ min: 1,
113
+ max: 8,
114
+ step: 1
115
+ } };
116
+ /**
117
+ * Validation constraints for QR-capture options.
118
+ *
119
+ * `intervalMs` is clamped to 50–1000 ms (20 Hz down to 1 Hz): below ~50 ms the
120
+ * detector cannot keep up and frames just queue; above 1 s tracking feels dead.
121
+ * `captureSize` is clamped to 256–2048 px: under 256 even a near QR loses its
122
+ * modules; over 2048 the blit + decode cost is not worth it on a phone. Both
123
+ * back a settings slider so a corrupt stored value can never break capture.
124
+ */
125
+ const QR_CONSTRAINTS = {
126
+ intervalMs: {
127
+ min: 50,
128
+ max: 1e3,
129
+ step: 25
130
+ },
131
+ captureSize: {
132
+ min: 256,
133
+ max: 2048,
134
+ step: 128
135
+ }
136
+ };
137
+ /**
77
138
  * Validate and normalize AR crash isolation flags.
78
139
  * Missing or invalid values fall back to defaults.
79
140
  */
@@ -89,12 +150,43 @@ function validateArCrashIsolationOptions(options) {
89
150
  };
90
151
  }
91
152
  /**
153
+ * Validate and normalize the live debug-overlay visibility toggles.
154
+ * Each field is boolean-or-default (same policy as the AR-crash-isolation
155
+ * flags): a missing, corrupted, or pre-feature persisted value falls back to
156
+ * the ON default so an overlay is never silently disabled by bad input.
157
+ */
158
+ function validateVisualizationOptions(options) {
159
+ const defaults = DEFAULT_RECORDING_OPTIONS.visualization;
160
+ return {
161
+ frameTiles: typeof options.frameTiles === "boolean" ? options.frameTiles : defaults.frameTiles,
162
+ occupancyCubes: typeof options.occupancyCubes === "boolean" ? options.occupancyCubes : defaults.occupancyCubes,
163
+ gpsAlignmentMarkers: typeof options.gpsAlignmentMarkers === "boolean" ? options.gpsAlignmentMarkers : defaults.gpsAlignmentMarkers,
164
+ compassCubes: typeof options.compassCubes === "boolean" ? options.compassCubes : defaults.compassCubes
165
+ };
166
+ }
167
+ /**
92
168
  * Clamp a value to the specified constraints.
93
169
  */
94
170
  function clamp(value, min, max) {
95
171
  return Math.max(min, Math.min(max, value));
96
172
  }
97
173
  /**
174
+ * Validate and normalize QR-capture options. `enabled` is boolean-or-default
175
+ * (a corrupt/pre-feature value falls back to the OFF default so QR never
176
+ * silently turns ON); `intervalMs`/`captureSize` are clamped to
177
+ * {@link QR_CONSTRAINTS}, with a `Number.isFinite` guard so a stored `NaN`
178
+ * (which is `typeof 'number'` and survives `clamp`) falls back to the default
179
+ * rather than breaking the camera-frame source.
180
+ */
181
+ function validateQrOptions(options) {
182
+ const defaults = DEFAULT_RECORDING_OPTIONS.qr;
183
+ return {
184
+ enabled: typeof options.enabled === "boolean" ? options.enabled : defaults.enabled,
185
+ intervalMs: clamp(typeof options.intervalMs === "number" && Number.isFinite(options.intervalMs) ? options.intervalMs : defaults.intervalMs, QR_CONSTRAINTS.intervalMs.min, QR_CONSTRAINTS.intervalMs.max),
186
+ captureSize: clamp(typeof options.captureSize === "number" && Number.isFinite(options.captureSize) ? options.captureSize : defaults.captureSize, QR_CONSTRAINTS.captureSize.min, QR_CONSTRAINTS.captureSize.max)
187
+ };
188
+ }
189
+ /**
98
190
  * Validate and normalize depth options.
99
191
  * Invalid values are clamped to valid ranges.
100
192
  */
@@ -103,7 +195,7 @@ function validateDepthOptions(options) {
103
195
  return {
104
196
  enabled: typeof options.enabled === "boolean" ? options.enabled : defaults.enabled,
105
197
  intervalMs: clamp(typeof options.intervalMs === "number" ? options.intervalMs : defaults.intervalMs, DEPTH_CONSTRAINTS.intervalMs.min, DEPTH_CONSTRAINTS.intervalMs.max),
106
- gridSize: clamp(typeof options.gridSize === "number" ? options.gridSize : defaults.gridSize, DEPTH_CONSTRAINTS.gridSize.min, DEPTH_CONSTRAINTS.gridSize.max),
198
+ gridSize: clamp(typeof options.gridSize === "number" && Number.isFinite(options.gridSize) ? Math.round(options.gridSize) : defaults.gridSize, DEPTH_CONSTRAINTS.gridSize.min, DEPTH_CONSTRAINTS.gridSize.max),
107
199
  rgb: typeof options.rgb === "boolean" ? options.rgb : defaults.rgb
108
200
  };
109
201
  }
@@ -121,6 +213,30 @@ function validateImageOptions(options) {
121
213
  };
122
214
  }
123
215
  /**
216
+ * Validate and normalize occupancy options.
217
+ * Invalid values are clamped to valid ranges.
218
+ *
219
+ * Note the explicit `Number.isFinite` guard: `OccupancyGrid` throws a
220
+ * `RangeError` on a non-finite cell size, and `clamp(NaN, …)` would otherwise
221
+ * pass `NaN` straight through (it is `typeof 'number'`). Falling back to the
222
+ * default keeps a corrupted stored value from crashing grid construction.
223
+ */
224
+ function validateOccupancyOptions(options) {
225
+ const defaults = DEFAULT_RECORDING_OPTIONS.occupancy;
226
+ return { cellSizeM: clamp(typeof options.cellSizeM === "number" && Number.isFinite(options.cellSizeM) ? options.cellSizeM : defaults.cellSizeM, OCCUPANCY_CONSTRAINTS.cellSizeM.min, OCCUPANCY_CONSTRAINTS.cellSizeM.max) };
227
+ }
228
+ /**
229
+ * Validate and normalize frame-tile display options. `divisor` is clamped to
230
+ * {@link FRAME_TILE_DISPLAY_CONSTRAINTS} and rounded to an integer, with a
231
+ * `Number.isFinite` guard so a stored `NaN` (which is `typeof 'number'` and
232
+ * survives `clamp`) falls back to the default rather than producing a broken
233
+ * resize target.
234
+ */
235
+ function validateFrameTileDisplayOptions(options) {
236
+ const defaults = DEFAULT_RECORDING_OPTIONS.frameTileDisplay;
237
+ return { divisor: clamp(typeof options.divisor === "number" && Number.isFinite(options.divisor) ? Math.round(options.divisor) : defaults.divisor, FRAME_TILE_DISPLAY_CONSTRAINTS.divisor.min, FRAME_TILE_DISPLAY_CONSTRAINTS.divisor.max) };
238
+ }
239
+ /**
124
240
  * Validate and normalize a full RecordingOptions object.
125
241
  * Merges with defaults and clamps invalid values.
126
242
  */
@@ -128,7 +244,11 @@ function validateRecordingOptions(options) {
128
244
  return {
129
245
  depth: validateDepthOptions(options.depth ?? {}),
130
246
  images: validateImageOptions(options.images ?? {}),
131
- arCrashIsolation: validateArCrashIsolationOptions(options.arCrashIsolation ?? {})
247
+ arCrashIsolation: validateArCrashIsolationOptions(options.arCrashIsolation ?? {}),
248
+ occupancy: validateOccupancyOptions(options.occupancy ?? {}),
249
+ frameTileDisplay: validateFrameTileDisplayOptions(options.frameTileDisplay ?? {}),
250
+ visualization: validateVisualizationOptions(options.visualization ?? {}),
251
+ qr: validateQrOptions(options.qr ?? {})
132
252
  };
133
253
  }
134
254
  /**
@@ -187,8 +307,12 @@ function cloneRecordingOptions(options) {
187
307
  return {
188
308
  depth: { ...options.depth },
189
309
  images: { ...options.images },
190
- arCrashIsolation: { ...options.arCrashIsolation }
310
+ arCrashIsolation: { ...options.arCrashIsolation },
311
+ occupancy: { ...options.occupancy },
312
+ frameTileDisplay: { ...options.frameTileDisplay },
313
+ visualization: { ...options.visualization },
314
+ qr: { ...options.qr }
191
315
  };
192
316
  }
193
317
  //#endregion
194
- export { DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, IMAGE_CONSTRAINTS, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateDepthOptions, validateImageOptions, validateRecordingOptions };
318
+ export { DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, FRAME_TILE_DISPLAY_CONSTRAINTS, IMAGE_CONSTRAINTS, OCCUPANCY_CONSTRAINTS, QR_CONSTRAINTS, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateDepthOptions, validateFrameTileDisplayOptions, validateImageOptions, validateOccupancyOptions, validateQrOptions, validateRecordingOptions, validateVisualizationOptions };
@@ -1,3 +1,3 @@
1
- import { L as CombinedRootState } from "../create-slam-app-store-qNXQ-tq5.js";
2
- import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-BL-x-7k0.js";
1
+ import { L as CombinedRootState } from "../create-slam-app-store-Ce2FMKCp.js";
2
+ import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-CUEK82tM.js";
3
3
  export { CombinedRootState, ReplayRecordingOptions, replayRecording };
@@ -1,2 +1,2 @@
1
- import { a as recordWriteFailure, i as recordDepthSample, n as SessionMetadata, o as recordingReducer, r as endSession, s as startSession, t as RecordingState } from "../recording-slice-89O07ebs.js";
1
+ import { a as recordWriteFailure, i as recordDepthSample, n as SessionMetadata, o as recordingReducer, r as endSession, s as startSession, t as RecordingState } from "../recording-slice-B-uCgGoh.js";
2
2
  export { RecordingState, SessionMetadata, endSession, recordDepthSample, recordWriteFailure, recordingReducer, startSession };
@@ -11,8 +11,12 @@ const recordingSlice = createSlice({
11
11
  },
12
12
  reducers: {
13
13
  startSession(state, action) {
14
+ const { scenarioName, contextTag, ...rest } = action.payload;
14
15
  state.isRecording = true;
15
- state.sessionMetadata = action.payload;
16
+ state.sessionMetadata = {
17
+ ...rest,
18
+ contextTag: contextTag ?? scenarioName ?? ""
19
+ };
16
20
  state.actionCount = 0;
17
21
  state.failedWriteCount = 0;
18
22
  state.latestDepthSample = null;
@@ -1,2 +1,2 @@
1
- import { a as ReplayAction, c as computeInterActionDelay, i as ProgressCallback, l as extractActionTimestamp, n as DEFAULT_MAX_DELAY_MS, o as ReplayEngine, r as ErrorCallback, s as ReplayState, t as CompleteCallback } from "../replay-engine-BRqszuQ9.js";
1
+ import { a as ReplayAction, c as computeInterActionDelay, i as ProgressCallback, l as extractActionTimestamp, n as DEFAULT_MAX_DELAY_MS, o as ReplayEngine, r as ErrorCallback, s as ReplayState, t as CompleteCallback } from "../replay-engine-BSVd7Zzb.js";
2
2
  export { CompleteCallback, DEFAULT_MAX_DELAY_MS, ErrorCallback, ProgressCallback, ReplayAction, ReplayEngine, ReplayState, computeInterActionDelay, extractActionTimestamp };
@@ -7,9 +7,12 @@ const MAX_CONSECUTIVE_ERRORS = 10;
7
7
  * Extract an absolute epoch-ms timestamp from a Redux action, or null
8
8
  * if the action type doesn't carry one.
9
9
  *
10
- * IMPORTANT: depthSample uses performance.now() (relative to page load),
11
- * NOT epoch ms. Returning it here would mix clock domains and produce
12
- * garbage delays. It must return null. (Risk R4)
10
+ * IMPORTANT: the high-frequency sensor streams (depthSample, qrDetected) return
11
+ * null here ON PURPOSE they are NOT used to pace replay delays (they anchor to
12
+ * the GPS/image/session timeline by recorded ORDER instead). Their payload
13
+ * `timestamp` IS epoch ms (`performance.timeOrigin + frameTs`, see
14
+ * `ar/depth-sampler.ts`); the QR size as-of join reads those payload timestamps
15
+ * directly, independently of this pacing function. (Risk R4)
13
16
  *
14
17
  * @param action - A Redux action with type and optional payload
15
18
  * @returns Epoch milliseconds, or null if unavailable/unreliable
@@ -37,6 +40,7 @@ function extractActionTimestamp(action) {
37
40
  return null;
38
41
  }
39
42
  case "recording/recordDepthSample": return null;
43
+ case "qrDetected/recordQrDetection": return null;
40
44
  case "recording/endSession": return null;
41
45
  default: return null;
42
46
  }
@@ -1,3 +1,3 @@
1
- import { t as SubscribableStore } from "../subscribe-to-selector-CP3BKSbF.js";
2
- import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-rrkLkFTt.js";
1
+ import { t as SubscribableStore } from "../subscribe-to-selector-Bp3sLx8L.js";
2
+ import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-BZP81hjh.js";
3
3
  export { StoreSubscriberDeps, SubscribableStore, wireStoreSubscribers };
@@ -1,2 +1,2 @@
1
- import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-CP3BKSbF.js";
1
+ import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-Bp3sLx8L.js";
2
2
  export { SubscribableStore, subscribeToSelector };
@@ -1,2 +1,2 @@
1
- import { A as resetTrackingQuality, C as computeResidualConsensus, D as firstAgreementReached, E as degradedCountUpdated, F as snapshotsTrimmed, I as trackingQualityReducer, M as selectRecentAlignments, N as selectTrackingQuality, O as matrixDelta, P as snapshotPushed, S as computeGpsVsFusedDivergence, T as createTrackingQualityListenerMiddleware, _ as TrackingQualityState, b as computeCoverage, c as CompassAgreementResult, d as DEFAULT_TRACKING_QUALITY_OPTIONS, f as GpsAccuracyResult, g as TrackingQualitySliceState, h as TrackingQualityReport, j as selectFirstAgreementObservationIndex, k as reportUpdated, l as ConvergenceResult, m as TrackingQualityOptions, p as ResidualConsensusResult, s as AlignmentSnapshot, u as CoverageResult, v as computeCompassAgreement, w as computeTrackingQualityReport, x as computeGpsAccuracy, y as computeConvergence } from "../create-slam-app-store-qNXQ-tq5.js";
1
+ import { A as resetTrackingQuality, C as computeResidualConsensus, D as firstAgreementReached, E as degradedCountUpdated, F as snapshotsTrimmed, I as trackingQualityReducer, M as selectRecentAlignments, N as selectTrackingQuality, O as matrixDelta, P as snapshotPushed, S as computeGpsVsFusedDivergence, T as createTrackingQualityListenerMiddleware, _ as TrackingQualityState, b as computeCoverage, c as CompassAgreementResult, d as DEFAULT_TRACKING_QUALITY_OPTIONS, f as GpsAccuracyResult, g as TrackingQualitySliceState, h as TrackingQualityReport, j as selectFirstAgreementObservationIndex, k as reportUpdated, l as ConvergenceResult, m as TrackingQualityOptions, p as ResidualConsensusResult, s as AlignmentSnapshot, u as CoverageResult, v as computeCompassAgreement, w as computeTrackingQualityReport, x as computeGpsAccuracy, y as computeConvergence } from "../create-slam-app-store-Ce2FMKCp.js";
2
2
  export { AlignmentSnapshot, CompassAgreementResult, ConvergenceResult, CoverageResult, DEFAULT_TRACKING_QUALITY_OPTIONS, GpsAccuracyResult, ResidualConsensusResult, TrackingQualityOptions, TrackingQualityReport, TrackingQualitySliceState, TrackingQualityState, computeCompassAgreement, computeConvergence, computeCoverage, computeGpsAccuracy, computeGpsVsFusedDivergence, computeResidualConsensus, computeTrackingQualityReport, createTrackingQualityListenerMiddleware, degradedCountUpdated, firstAgreementReached, matrixDelta, reportUpdated, resetTrackingQuality, selectFirstAgreementObservationIndex, selectRecentAlignments, selectTrackingQuality, snapshotPushed, snapshotsTrimmed, trackingQualityReducer };
@@ -1,2 +1,2 @@
1
- import { a as TrackingSliceState, c as poseLost, d as selectLastRestartedPayload, f as selectLastSensorOrientation, g as trackingReducer, h as selectTrackingPhase, i as TrackingPhase, l as poseReceived, m as selectLostFrameCount, n as PoseReceivedPayload, o as clearLastRestartedPayload, p as selectLastValidPose, r as ResetTransformData, s as originReset, t as DeviceOrientation, u as resetTracking } from "../tracking-slice-CdK8c-8P.js";
1
+ import { a as TrackingSliceState, c as poseLost, d as selectLastRestartedPayload, f as selectLastSensorOrientation, g as trackingReducer, h as selectTrackingPhase, i as TrackingPhase, l as poseReceived, m as selectLostFrameCount, n as PoseReceivedPayload, o as clearLastRestartedPayload, p as selectLastValidPose, r as ResetTransformData, s as originReset, t as DeviceOrientation, u as resetTracking } from "../tracking-slice-DJZ9qeGg.js";
2
2
  export { DeviceOrientation, PoseReceivedPayload, ResetTransformData, TrackingPhase, TrackingSliceState, clearLastRestartedPayload, originReset, poseLost, poseReceived, resetTracking, selectLastRestartedPayload, selectLastSensorOrientation, selectLastValidPose, selectLostFrameCount, selectTrackingPhase, trackingReducer };
@@ -2,6 +2,7 @@ import "./state/gps-event-coordinator.js";
2
2
  import "./state/tracking-slice.js";
3
3
  import "./state/recording-options.js";
4
4
  import "./state/recording-slice.js";
5
+ import "./state/qr-detected-slice.js";
5
6
  import "./state/gps-ar-pose-sampler.js";
6
7
  import "./state/app-selectors.js";
7
8
  import { selectTrackingQuality } from "./state/tracking-quality.js";
@@ -1,2 +1,2 @@
1
- import { n as formatFrameFilename, r as formatTimestamp, t as formatActionFilename } from "../file-system-utils-DyAmcuvX.js";
2
- export { formatActionFilename, formatFrameFilename, formatTimestamp };
1
+ import { a as formatTimestamp, i as formatFrameFilename, n as SESSION_IMAGES_DIR, r as formatActionFilename, t as LEGACY_SESSION_IMAGES_DIR } from "../file-system-utils-Bs7lpeVP.js";
2
+ export { LEGACY_SESSION_IMAGES_DIR, SESSION_IMAGES_DIR, formatActionFilename, formatFrameFilename, formatTimestamp };