gps-plus-slam-app-framework 1.0.0 → 1.0.7

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 (216) hide show
  1. package/LICENSE +4 -4
  2. package/README.md +249 -108
  3. package/dist/accuracy-circles-CsxE_7x1.d.ts +43 -0
  4. package/dist/{app-selectors-DyMzxCEC.d.ts → app-selectors-BeU0_ab8.d.ts} +14 -15
  5. package/dist/ar/camera-blit-capture.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-sampler.d.ts +2 -2
  9. package/dist/ar/depth-sampler.js +1 -1
  10. package/dist/ar/frame-loop.d.ts +2 -0
  11. package/dist/ar/frame-loop.js +40 -0
  12. package/dist/ar/image-capture.d.ts +1 -1
  13. package/dist/ar/image-capture.js +1 -1
  14. package/dist/ar/index.d.ts +12 -12
  15. package/dist/ar/index.js +3 -3
  16. package/dist/ar/replay-scene.d.ts +1 -1
  17. package/dist/ar/scene-node-names.d.ts +1 -1
  18. package/dist/ar/webxr-session.d.ts +4 -4
  19. package/dist/ar/webxr-session.js +151 -56
  20. package/dist/ar/xr-camera-texture.d.ts +1 -1
  21. package/dist/ar/xr-error-handler.d.ts +1 -1
  22. package/dist/core/index.d.ts +2 -0
  23. package/dist/core/index.js +2 -0
  24. package/dist/create-slam-app-store-CNsZ3rcX.d.ts +371 -0
  25. package/dist/{depth-sampler-BNCtnEoP.d.ts → depth-sampler-xg8vna6c.d.ts} +1 -1
  26. package/dist/file-system-DTApEv_Q.d.ts +114 -0
  27. package/dist/frame-loop-QJIxLin0.d.ts +42 -0
  28. package/dist/frustum-visibility-BHnFrSKQ.d.ts +58 -0
  29. package/dist/{fused-path-DtqCSqJh.d.ts → fused-path-CB4DwWOG.d.ts} +1 -1
  30. package/dist/geo/h3-proximity.d.ts +2 -0
  31. package/dist/{ref-points/h3-ref-point.js → geo/h3-proximity.js} +16 -12
  32. package/dist/geo/index.d.ts +2 -0
  33. package/dist/geo/index.js +2 -0
  34. package/dist/{geo-types-Xx-LVcnw.d.ts → geo-types-CTuF23gG.d.ts} +16 -1
  35. package/dist/{gps-Ddzr2KlX.d.ts → gps-DXoQb9Kf.d.ts} +1 -1
  36. package/dist/gps-anchor-CnWY61XK.d.ts +48 -0
  37. package/dist/gps-anchor-DjC_3MoT.js +257 -0
  38. package/dist/gps-ar-pose-sampler-DsM1Kb2N.d.ts +43 -0
  39. package/dist/{recording-coordinator-DzflP07_.d.ts → gps-event-coordinator-DBx9I1YX.d.ts} +7 -6
  40. package/dist/gps-event-markers-BGr-iLvi.d.ts +103 -0
  41. package/dist/{h3-ref-point-CFhayNSZ.d.ts → h3-proximity-BhfgNbdW.d.ts} +16 -12
  42. package/dist/{image-capture-2pKjYUS6.d.ts → image-capture-DZLxHDN9.d.ts} +1 -1
  43. package/dist/index-gzWJMVpo.d.ts +2 -0
  44. package/dist/index-pOjUCKEP.d.ts +24 -0
  45. package/dist/index.d.ts +59 -56
  46. package/dist/index.js +22 -21
  47. package/dist/{leaflet-map-overlay-CvfQXTye.d.ts → leaflet-map-overlay-Cew7XAoO.d.ts} +32 -17
  48. package/dist/licensing/index.d.ts +1 -1
  49. package/dist/licensing/index.js +1 -1
  50. package/dist/{logger-B81iwxx0.d.ts → logger-C9vugiQb.d.ts} +7 -1
  51. package/dist/map-data-sih4g64v.d.ts +49 -0
  52. package/dist/map-overlay-draw-B3R1c9Xp.d.ts +42 -0
  53. package/dist/{null-storage-backend-CsWvQwN8.d.ts → null-storage-backend-BKOS7JpL.d.ts} +4 -2
  54. package/dist/{opfs-storage-ChOzF_Wp.d.ts → opfs-storage-DOPkzgeT.d.ts} +54 -51
  55. package/dist/opfs-storage-backend-Cj_7PWoq.d.ts +13 -0
  56. package/dist/{permission-checker-jO2ziCWC.d.ts → permission-checker-DPJl_RU6.d.ts} +41 -1
  57. package/dist/persistence-middleware-BkHIqvaA.d.ts +55 -0
  58. package/dist/{recording-replayer-oOzPITrd.d.ts → recording-replayer-CmCIIZ02.d.ts} +2 -2
  59. package/dist/recording-slice-d6OtCrza.d.ts +28 -0
  60. package/dist/{replay-engine-DuX316ae.d.ts → replay-engine-BGBRQyXs.d.ts} +4 -1
  61. package/dist/{replay-scene-DtUm6bsr.d.ts → replay-scene-DpAGcAKY.d.ts} +3 -3
  62. package/dist/sensors/gps-error-handler.d.ts +1 -1
  63. package/dist/sensors/gps.d.ts +1 -1
  64. package/dist/sensors/gps.js +2 -2
  65. package/dist/sensors/index.d.ts +3 -3
  66. package/dist/sensors/index.js +1 -1
  67. package/dist/sensors/permission-checker.d.ts +2 -2
  68. package/dist/sensors/permission-checker.js +79 -1
  69. package/dist/state/app-selectors.d.ts +2 -2
  70. package/dist/state/app-selectors.js +33 -3
  71. package/dist/state/combined-root-state.d.ts +2 -0
  72. package/dist/state/combined-root-state.js +1 -0
  73. package/dist/state/create-slam-app-store.d.ts +2 -0
  74. package/dist/state/create-slam-app-store.js +75 -0
  75. package/dist/state/gps-ar-pose-sampler.d.ts +4 -0
  76. package/dist/state/gps-ar-pose-sampler.js +19 -0
  77. package/dist/state/{recording-coordinator.d.ts → gps-event-coordinator.d.ts} +1 -1
  78. package/dist/state/{recording-coordinator.js → gps-event-coordinator.js} +3 -4
  79. package/dist/state/index.d.ts +17 -17
  80. package/dist/state/index.js +10 -8
  81. package/dist/state/persistence-middleware.d.ts +2 -2
  82. package/dist/state/persistence-middleware.js +39 -11
  83. package/dist/state/recording-options.d.ts +1 -1
  84. package/dist/state/recording-replayer.d.ts +3 -2
  85. package/dist/state/recording-replayer.js +2 -2
  86. package/dist/state/recording-slice.d.ts +2 -0
  87. package/dist/state/{recorder-slice.js → recording-slice.js} +7 -15
  88. package/dist/state/replay-engine.d.ts +1 -1
  89. package/dist/state/replay-engine.js +3 -3
  90. package/dist/state/store-subscribers.d.ts +2 -2
  91. package/dist/state/store-subscribers.js +38 -20
  92. package/dist/state/subscribe-to-selector.d.ts +1 -1
  93. package/dist/state/tracking-quality.d.ts +2 -0
  94. package/dist/state/tracking-quality.js +723 -0
  95. package/dist/state/tracking-slice.d.ts +2 -0
  96. package/dist/state/tracking-slice.js +147 -0
  97. package/dist/storage/file-system-utils.d.ts +1 -1
  98. package/dist/storage/file-system.d.ts +2 -2
  99. package/dist/storage/file-system.js +154 -94
  100. package/dist/storage/index.d.ts +9 -11
  101. package/dist/storage/index.js +5 -7
  102. package/dist/storage/null-storage-backend.d.ts +1 -1
  103. package/dist/storage/null-storage-backend.js +7 -0
  104. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  105. package/dist/storage/opfs-storage-backend.js +7 -1
  106. package/dist/storage/opfs-storage.d.ts +2 -2
  107. package/dist/storage/opfs-storage.js +69 -85
  108. package/dist/storage/storage-backend.d.ts +2 -2
  109. package/dist/storage/zip-export.d.ts +2 -2
  110. package/dist/storage/zip-export.js +55 -62
  111. package/dist/storage/zip-reader.d.ts +2 -2
  112. package/dist/storage/zip-reader.js +32 -5
  113. package/dist/storage-backend-CT0Il_AB.d.ts +28 -0
  114. package/dist/{store-subscribers-CuoIzB5T.d.ts → store-subscribers-Dce_CoJF.d.ts} +31 -7
  115. package/dist/{subscribe-to-selector-UTZ0KkAc.d.ts → subscribe-to-selector-DIHm3BRZ.d.ts} +1 -1
  116. package/dist/test-utils/browser-mocks.d.ts +1 -1
  117. package/dist/test-utils/zip-round-trip-helpers.d.ts +7 -1
  118. package/dist/test-utils/zip-round-trip-helpers.js +6 -5
  119. package/dist/tracking-slice-utCNND8t.d.ts +86 -0
  120. package/dist/types/ar-types.d.ts +1 -1
  121. package/dist/types/geo-types.d.ts +2 -2
  122. package/dist/types/index.d.ts +3 -3
  123. package/dist/utils/concurrency.d.ts +1 -1
  124. package/dist/utils/failure-tracker.d.ts +1 -1
  125. package/dist/utils/format-file-size.d.ts +1 -1
  126. package/dist/utils/fused-path.d.ts +1 -1
  127. package/dist/utils/index.d.ts +6 -6
  128. package/dist/utils/list-formatter.d.ts +1 -1
  129. package/dist/utils/logger.d.ts +1 -1
  130. package/dist/utils/logger.js +68 -8
  131. package/dist/visualization/accuracy-circles.d.ts +2 -0
  132. package/dist/visualization/accuracy-circles.js +57 -0
  133. package/dist/visualization/alignment-lerper.d.ts +1 -1
  134. package/dist/visualization/camera-follower.d.ts +1 -1
  135. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  136. package/dist/visualization/frustum-visibility.d.ts +2 -0
  137. package/dist/visualization/frustum-visibility.js +139 -0
  138. package/dist/visualization/gps-anchor.d.ts +2 -0
  139. package/dist/visualization/gps-anchor.js +2 -0
  140. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  141. package/dist/visualization/gps-event-markers.d.ts +2 -2
  142. package/dist/visualization/gps-event-markers.js +69 -2
  143. package/dist/visualization/index.d.ts +17 -12
  144. package/dist/visualization/index.js +6 -2
  145. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  146. package/dist/visualization/leaflet-map-overlay.js +56 -98
  147. package/dist/visualization/lerp-utils.d.ts +1 -1
  148. package/dist/visualization/map-data.d.ts +2 -0
  149. package/dist/visualization/map-data.js +33 -0
  150. package/dist/visualization/map-overlay-draw.d.ts +2 -0
  151. package/dist/visualization/map-overlay-draw.js +107 -0
  152. package/dist/visualization/map-overlay.d.ts +1 -1
  153. package/dist/visualization/three-dispose.d.ts +1 -1
  154. package/dist/visualization/vis-colors.d.ts +1 -1
  155. package/dist/{webxr-session-COlSrXdL.d.ts → webxr-session-DB5cIZnN.d.ts} +53 -12
  156. package/dist/{zip-export-Rtpi75JJ.d.ts → zip-export-QO4iLIi7.d.ts} +58 -7
  157. package/dist/{zip-reader-D7pkpX8K.d.ts → zip-reader-D1105idz.d.ts} +38 -1
  158. package/package.json +29 -18
  159. package/dist/ar/tracking-state.d.ts +0 -2
  160. package/dist/ar/tracking-state.js +0 -164
  161. package/dist/community-license-key-ChZLCJyO.d.ts +0 -21
  162. package/dist/file-system-BsQgYCU3.d.ts +0 -128
  163. package/dist/gps-event-markers-DpEy2qRd.d.ts +0 -67
  164. package/dist/licensing/community-license-key.d.ts +0 -2
  165. package/dist/licensing/community-license-key.js +0 -21
  166. package/dist/opfs-storage-backend-CVcmfJHN.d.ts +0 -11
  167. package/dist/persistence-middleware-DrptY02i.d.ts +0 -31
  168. package/dist/recorder-slice-fcwAXrsh.d.ts +0 -35
  169. package/dist/ref-point-importer-B_wn5ABk.d.ts +0 -62
  170. package/dist/ref-point-loader-Pt1smgHS.d.ts +0 -117
  171. package/dist/ref-points/h3-ref-point.d.ts +0 -2
  172. package/dist/ref-points/index.d.ts +0 -2
  173. package/dist/ref-points/index.js +0 -2
  174. package/dist/ref-points-slice-tC-Wd6XJ.d.ts +0 -48
  175. package/dist/reference-points-CFWy8_wj.d.ts +0 -68
  176. package/dist/routing-slice-BlHTM8eh.d.ts +0 -23
  177. package/dist/state/recorder-slice.d.ts +0 -2
  178. package/dist/state/ref-points-slice.d.ts +0 -2
  179. package/dist/state/ref-points-slice.js +0 -44
  180. package/dist/state/routing-slice.d.ts +0 -2
  181. package/dist/state/routing-slice.js +0 -13
  182. package/dist/state/store.d.ts +0 -9
  183. package/dist/state/store.js +0 -71
  184. package/dist/storage/ref-point-importer.d.ts +0 -2
  185. package/dist/storage/ref-point-importer.js +0 -163
  186. package/dist/storage/ref-point-loader.d.ts +0 -2
  187. package/dist/storage/ref-point-loader.js +0 -248
  188. package/dist/storage/ref-point-recovery.d.ts +0 -28
  189. package/dist/storage/ref-point-recovery.js +0 -159
  190. package/dist/storage-backend-BcEItnFr.d.ts +0 -20
  191. package/dist/store-C7PKOj8S.d.ts +0 -96
  192. package/dist/tracking-state-B9QfuZCg.d.ts +0 -101
  193. package/dist/visualization/reference-points.d.ts +0 -2
  194. package/dist/visualization/reference-points.js +0 -160
  195. /package/dist/{alignment-lerper-D9BeDwZX.d.ts → alignment-lerper-L1RbWHQ4.d.ts} +0 -0
  196. /package/dist/{ar-types-B-ORgk6Z.d.ts → ar-types-Ck8LEq8k.d.ts} +0 -0
  197. /package/dist/{camera-blit-capture-DHuyrrxr.d.ts → camera-blit-capture-C1V9I1Z6.d.ts} +0 -0
  198. /package/dist/{camera-follower-BMqPddxx.d.ts → camera-follower-1k8mnw5J.d.ts} +0 -0
  199. /package/dist/{capture-failure-tracker-2Q6oI2uQ.d.ts → capture-failure-tracker-JKb1ST8E.d.ts} +0 -0
  200. /package/dist/{chromium-camera-access-workaround-CU5zSKNr.d.ts → chromium-camera-access-workaround-NvChXsi8.d.ts} +0 -0
  201. /package/dist/{concurrency-p6gB30fT.d.ts → concurrency-CMw8pSaG.d.ts} +0 -0
  202. /package/dist/{css3d-renderer-manager-rgLMVAvd.d.ts → css3d-renderer-manager-CkjCb2HW.d.ts} +0 -0
  203. /package/dist/{failure-tracker-1sQ_Cgyw.d.ts → failure-tracker-DnHXOIRa.d.ts} +0 -0
  204. /package/dist/{file-system-utils-5cWO8Xyv.d.ts → file-system-utils-CtKiiw27.d.ts} +0 -0
  205. /package/dist/{format-file-size-onGq0k1Q.d.ts → format-file-size-BTS5H62-.d.ts} +0 -0
  206. /package/dist/{gps-compass-cubes-C-6JEnpR.d.ts → gps-compass-cubes-CsLtFv4B.d.ts} +0 -0
  207. /package/dist/{gps-error-handler-VPuNOuf8.d.ts → gps-error-handler-Dg3ziRVY.d.ts} +0 -0
  208. /package/dist/{lerp-utils-87NWjUOD.d.ts → lerp-utils-B8MM8zCR.d.ts} +0 -0
  209. /package/dist/{list-formatter-DKQQVLcl.d.ts → list-formatter-CNqi-E2j.d.ts} +0 -0
  210. /package/dist/{map-overlay-DuiZuCTx.d.ts → map-overlay-DAmreyA3.d.ts} +0 -0
  211. /package/dist/{recording-options-BYUFX8iU.d.ts → recording-options-Dq6IsKAv.d.ts} +0 -0
  212. /package/dist/{scene-node-names-DPsaE2z3.d.ts → scene-node-names-6LBjPmwz.d.ts} +0 -0
  213. /package/dist/{three-dispose-CCBHjE_a.d.ts → three-dispose-C2drkp6f.d.ts} +0 -0
  214. /package/dist/{vis-colors-Qi2vlRGt.d.ts → vis-colors-CHQhjal3.d.ts} +0 -0
  215. /package/dist/{xr-camera-texture-BbukNE79.d.ts → xr-camera-texture-BU5YvKAM.d.ts} +0 -0
  216. /package/dist/{xr-error-handler-DTHfmvrl.d.ts → xr-error-handler-CDY5QxGb.d.ts} +0 -0
@@ -0,0 +1,75 @@
1
+ import { trackingReducer } from "./tracking-slice.js";
2
+ import { recordWriteFailure, recordingReducer } from "./recording-slice.js";
3
+ import { createTrackingQualityListenerMiddleware, trackingQualityReducer } from "./tracking-quality.js";
4
+ import { createPersistenceMiddleware, slicePrefixOf } from "./persistence-middleware.js";
5
+ import { arElementsReducer, gpsDataReducer, gpsElementsReducer, sanitizeForDevTools, setZeroPos, validateLicenseKey } from "gps-plus-slam-js";
6
+ import { configureStore } from "@reduxjs/toolkit";
7
+ import { COMMUNITY_LICENSE_KEY } from "gps-plus-slam-js/community-license-key";
8
+ //#region ../src/state/create-slam-app-store.ts
9
+ /**
10
+ * `createSlamAppStore` — composable Redux store factory for AR+GPS apps.
11
+ *
12
+ * Introduced in Iter 1 of the AppFramework/RecorderApp boundary migration.
13
+ * Wires the three library reducers (`gpsData`, `gpsElements`, `arElements`),
14
+ * the framework's recording lifecycle slice, and the persistence middleware.
15
+ *
16
+ * Recorder-only state (routing screen, ref-points, scenario) is plugged in
17
+ * by the consumer via `extraReducers` / `extraMiddleware`. The factory itself
18
+ * never references those concepts so apps that don't need them (POI viewers,
19
+ * navigation arrows, …) compose freely.
20
+ *
21
+ * The legacy `createRecorderStore` from `store.ts` is built on top of this
22
+ * factory and will move out of the framework in Iter 1D.
23
+ *
24
+ * @see docs/2026-05-03-appframework-vs-recorderapp-boundary-analysis.md — Iter 1
25
+ */
26
+ /**
27
+ * Slice prefixes the framework always persists, derived from the actual
28
+ * library / framework action creators (never hand-typed). A rename of the
29
+ * `gpsData` or `recording` slice therefore propagates here automatically
30
+ * instead of silently dropping that slice's actions from recordings.
31
+ */
32
+ const BUILTIN_PERSISTED_PREFIXES = [slicePrefixOf(setZeroPos.type), slicePrefixOf(recordWriteFailure.type)];
33
+ /**
34
+ * Build a Redux store wired with library + recording slices, persistence
35
+ * middleware, and any caller-supplied extras. See module docstring for the
36
+ * design rationale.
37
+ */
38
+ function createSlamAppStore(options) {
39
+ const { storageBackend, extraReducers, extraMiddleware, persistedExtraPrefixes, onWriteFailure, enableDevChecks = true, licenseKey = COMMUNITY_LICENSE_KEY, trackingQualityOptions } = options;
40
+ validateLicenseKey(licenseKey);
41
+ const reducer = {
42
+ gpsData: gpsDataReducer,
43
+ gpsElements: gpsElementsReducer,
44
+ arElements: arElementsReducer,
45
+ recording: recordingReducer,
46
+ tracking: trackingReducer,
47
+ trackingQuality: trackingQualityReducer,
48
+ ...extraReducers ?? {}
49
+ };
50
+ const trackingQualityMiddleware = createTrackingQualityListenerMiddleware(trackingQualityOptions);
51
+ const store = configureStore({
52
+ reducer,
53
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware({
54
+ serializableCheck: enableDevChecks,
55
+ immutableCheck: enableDevChecks
56
+ }).prepend(trackingQualityMiddleware).concat(createPersistenceMiddleware({
57
+ storageBackend,
58
+ onWriteFailure,
59
+ persistedPrefixes: [...BUILTIN_PERSISTED_PREFIXES, ...persistedExtraPrefixes ?? []]
60
+ }), ...extraMiddleware ?? []),
61
+ devTools: {
62
+ actionSanitizer: sanitizeForDevTools,
63
+ stateSanitizer: sanitizeForDevTools
64
+ }
65
+ });
66
+ return {
67
+ getState: () => store.getState(),
68
+ dispatch: store.dispatch,
69
+ subscribe: (listener) => store.subscribe(listener),
70
+ writeFrame: (blob, index) => storageBackend.writeFrame(blob, index),
71
+ writeSessionMetadata: (metadata) => storageBackend.writeSessionMetadata(metadata)
72
+ };
73
+ }
74
+ //#endregion
75
+ export { createSlamAppStore };
@@ -0,0 +1,4 @@
1
+ import { t as ARPose } from "../ar-types-Ck8LEq8k.js";
2
+ import { o as extractOdomPosition, s as extractOdomRotation } from "../gps-event-coordinator-DBx9I1YX.js";
3
+ import { i as captureGpsAnchorSample, n as GpsAnchorSample, r as GpsAnchorSampleGpsPoint, t as CaptureGpsAnchorSampleOptions } from "../gps-ar-pose-sampler-DsM1Kb2N.js";
4
+ export { ARPose, CaptureGpsAnchorSampleOptions, GpsAnchorSample, GpsAnchorSampleGpsPoint, captureGpsAnchorSample, extractOdomPosition, extractOdomRotation };
@@ -0,0 +1,19 @@
1
+ import { extractOdomPosition, extractOdomRotation } from "./gps-event-coordinator.js";
2
+ //#region ../src/state/gps-ar-pose-sampler.ts
3
+ /**
4
+ * Capture a GPS-AR pose snapshot. Extracts `odomPosition`/`odomRotation`
5
+ * from the supplied `arPose`. The supplied `gpsPoint` and optional
6
+ * `fusedGpsPoint` are stored verbatim.
7
+ */
8
+ function captureGpsAnchorSample(arPose, gpsPoint, options = {}) {
9
+ return {
10
+ gpsPoint,
11
+ fusedGpsPoint: options.fusedGpsPoint,
12
+ arPose,
13
+ odomPosition: extractOdomPosition(arPose),
14
+ odomRotation: extractOdomRotation(arPose),
15
+ timestamp: options.timestamp ?? Date.now()
16
+ };
17
+ }
18
+ //#endregion
19
+ export { captureGpsAnchorSample, extractOdomPosition, extractOdomRotation };
@@ -1,2 +1,2 @@
1
- import { a as eulerToQuaternion, c as getLastDeviceOrientation, i as createGpsPositionHandler, l as resetCoordinatorState, n as buildRawGpsPoint, o as extractOdomPosition, r as buildRecordGpsEventPayload, s as extractOdomRotation, t as RecordingCoordinatorConfig, u as updateDeviceOrientation } from "../recording-coordinator-DzflP07_.js";
1
+ import { a as eulerToQuaternion, c as getLastDeviceOrientation, i as createGpsPositionHandler, l as resetCoordinatorState, n as buildRawGpsPoint, o as extractOdomPosition, r as buildRecordGpsEventPayload, s as extractOdomRotation, t as RecordingCoordinatorConfig, u as updateDeviceOrientation } from "../gps-event-coordinator-DBx9I1YX.js";
2
2
  export { RecordingCoordinatorConfig, buildRawGpsPoint, buildRecordGpsEventPayload, createGpsPositionHandler, eulerToQuaternion, extractOdomPosition, extractOdomRotation, getLastDeviceOrientation, resetCoordinatorState, updateDeviceOrientation };
@@ -1,7 +1,6 @@
1
1
  import { createLogger } from "../utils/logger.js";
2
- import { recordGpsEvent, setZeroPos } from "./store.js";
3
- import { eulerToQuaternion, getOdometryPositions, getZeroReference } from "gps-plus-slam-js";
4
- //#region ../src/state/recording-coordinator.ts
2
+ import { eulerToQuaternion, getOdometryPositions, getZeroReference, recordGpsEvent, setZeroPos } from "gps-plus-slam-js";
3
+ //#region ../src/state/gps-event-coordinator.ts
5
4
  const log = createLogger("RecordingCoordinator");
6
5
  /** Counter for generating unique GPS point IDs */
7
6
  let gpsEventCounter = 0;
@@ -122,7 +121,7 @@ function buildRecordGpsEventPayload(gpsPosition, arPose, deviceOrientation) {
122
121
  function createGpsPositionHandler(config) {
123
122
  const { store, getArPose } = config;
124
123
  return (position) => {
125
- if (!store.getState().recorder.isRecording) return;
124
+ if (!store.getState().recording.isRecording) return;
126
125
  const arPose = getArPose();
127
126
  if (!arPose) {
128
127
  log.warn("GPS arrived but no AR pose available");
@@ -1,17 +1,17 @@
1
- import { i as DepthSample, r as DepthPoint } from "../ar-types-B-ORgk6Z.js";
2
- 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, n as DEFAULT_RECORDING_OPTIONS, o as ImageCaptureOptions, p as saveRecordingOptions, r as DEPTH_CONSTRAINTS, s as RecordingOptions, u as cloneRecordingOptions } from "../recording-options-BYUFX8iU.js";
3
- import { n as SessionMetadata } from "../opfs-storage-ChOzF_Wp.js";
4
- import { t as StorageBackend } from "../storage-backend-BcEItnFr.js";
5
- import { a as recordWriteFailure, c as startSession, i as recordDepthSample, n as SessionMetadata$1, o as recorderReducer, r as endSession, s as setCurrentScenarioName, t as RecorderState } from "../recorder-slice-fcwAXrsh.js";
6
- import { a as resetRefPointsState, i as refPointsReducer, n as clearSessionRefPointUsage, o as selectCachedKnownRefPoints, r as incrementRefPointUsage, s as setImportedRefPoints, t as RefPointsState } from "../ref-points-slice-tC-Wd6XJ.js";
7
- import { i as routingReducer, n as RoutingState, r as navigateTo, t as AppScreen } from "../routing-slice-BlHTM8eh.js";
8
- import { n as RefPointMark } from "../ref-point-loader-Pt1smgHS.js";
9
- import { _ as setZeroPos, a as MarkReferencePointPayload, c as RecordGpsEventPayload, d as RootState, f as add2dImage, g as recordGpsEvent, h as markReferencePoint, i as LatLong, l as RecorderStore, m as createRecorderStore, n as CombinedRootState, o as RawDeviceOrientation, p as calcRelativeCoordsInMeters, r as GpsPoint, s as RawGpsPoint, t as AppDispatch, u as RecorderStoreOptions } from "../store-C7PKOj8S.js";
10
- import { a as eulerToQuaternion, c as getLastDeviceOrientation, i as createGpsPositionHandler, l as resetCoordinatorState, n as buildRawGpsPoint, o as extractOdomPosition, r as buildRecordGpsEventPayload, s as extractOdomRotation, t as RecordingCoordinatorConfig, u as updateDeviceOrientation } from "../recording-coordinator-DzflP07_.js";
11
- import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-oOzPITrd.js";
12
- import { n as createPersistenceMiddleware, t as PersistenceMiddlewareOptions } from "../persistence-middleware-DrptY02i.js";
13
- 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-DuX316ae.js";
14
- import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-UTZ0KkAc.js";
15
- import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-CuoIzB5T.js";
16
- import { a as selectReferencePoints, i as selectOdometryRotations, n as selectGpsPositions, o as selectZeroReference, r as selectOdometryPositions, t as selectAlignmentMatrix } from "../app-selectors-DyMzxCEC.js";
17
- export { type AppDispatch, type AppScreen, type CombinedRootState, type CompleteCallback, DEFAULT_MAX_DELAY_MS, DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, type DepthCaptureOptions, type DepthPoint, type DepthSample, type ErrorCallback, type GpsPoint, IMAGE_CONSTRAINTS, type ImageCaptureOptions, type LatLong, type MarkReferencePointPayload, type SessionMetadata as OpfsSessionMetadata, type PersistenceMiddlewareOptions, type ProgressCallback, type RawDeviceOrientation, type RawGpsPoint, type RecordGpsEventPayload, type RecorderState, type RecorderStore, type RecorderStoreOptions, type RecordingCoordinatorConfig, type RecordingOptions, type RecordingOptionsInput, type RefPointMark, type RefPointsState, type ReplayAction, ReplayEngine, type ReplayRecordingOptions, type ReplayState, type RootState, type RoutingState, STORAGE_KEY, type SessionMetadata$1 as SessionMetadata, type StorageBackend, type StoreSubscriberDeps, type SubscribableStore, add2dImage, buildRawGpsPoint, buildRecordGpsEventPayload, calcRelativeCoordsInMeters, clearSessionRefPointUsage, cloneRecordingOptions, computeInterActionDelay, createGpsPositionHandler, createPersistenceMiddleware, createRecorderStore, endSession, eulerToQuaternion, extractActionTimestamp, extractOdomPosition, extractOdomRotation, getLastDeviceOrientation, incrementRefPointUsage, loadRecordingOptions, markReferencePoint, navigateTo, recordDepthSample, recordGpsEvent, recordWriteFailure, recorderReducer, refPointsReducer, replayRecording, resetCoordinatorState, resetRecordingOptions, resetRefPointsState, routingReducer, saveRecordingOptions, selectAlignmentMatrix, selectCachedKnownRefPoints, selectGpsPositions, selectOdometryPositions, selectOdometryRotations, selectReferencePoints, selectZeroReference, setCurrentScenarioName, setImportedRefPoints, setZeroPos, startSession, subscribeToSelector, updateDeviceOrientation, validateDepthOptions, validateImageOptions, validateRecordingOptions, wireStoreSubscribers };
1
+ import { i as DepthSample, r as DepthPoint } from "../ar-types-Ck8LEq8k.js";
2
+ 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-utCNND8t.js";
3
+ 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, n as DEFAULT_RECORDING_OPTIONS, o as ImageCaptureOptions, p as saveRecordingOptions, r as DEPTH_CONSTRAINTS, u as cloneRecordingOptions } from "../recording-options-Dq6IsKAv.js";
4
+ import { a as recordWriteFailure, i as recordDepthSample, n as SessionMetadata$1, o as recordingReducer, r as endSession, s as startSession, t as RecordingState } from "../recording-slice-d6OtCrza.js";
5
+ import { n as SessionMetadata } from "../opfs-storage-DOPkzgeT.js";
6
+ import { n as StorageBackend } from "../storage-backend-CT0Il_AB.js";
7
+ 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, a as SlamAppStoreOptions, b as computeCoverage, d as DEFAULT_TRACKING_QUALITY_OPTIONS, g as TrackingQualitySliceState, h as TrackingQualityReport, i as SlamAppStore, j as selectFirstAgreementObservationIndex, k as reportUpdated, m as TrackingQualityOptions, n as SlamAppMiddleware, o as createSlamAppStore, r as SlamAppRootState, s as AlignmentSnapshot, t as SlamAppCombinedState, v as computeCompassAgreement, w as computeTrackingQualityReport, x as computeGpsAccuracy, y as computeConvergence } from "../create-slam-app-store-CNsZ3rcX.js";
8
+ import { a as eulerToQuaternion, c as getLastDeviceOrientation, i as createGpsPositionHandler, l as resetCoordinatorState, n as buildRawGpsPoint, o as extractOdomPosition, r as buildRecordGpsEventPayload, s as extractOdomRotation, t as RecordingCoordinatorConfig, u as updateDeviceOrientation } from "../gps-event-coordinator-DBx9I1YX.js";
9
+ import { i as captureGpsAnchorSample, n as GpsAnchorSample, r as GpsAnchorSampleGpsPoint, t as CaptureGpsAnchorSampleOptions } from "../gps-ar-pose-sampler-DsM1Kb2N.js";
10
+ import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-CmCIIZ02.js";
11
+ import { n as createPersistenceMiddleware, r as slicePrefixOf, t as PersistenceMiddlewareOptions } from "../persistence-middleware-BkHIqvaA.js";
12
+ 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-BGBRQyXs.js";
13
+ import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-DIHm3BRZ.js";
14
+ import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-Dce_CoJF.js";
15
+ import { a as selectOdometryRotations, i as selectOdometryPositions, n as selectFrameTilesInWebXR, o as selectZeroReference, r as selectGpsPositions, t as selectAlignmentMatrix } from "../app-selectors-BeU0_ab8.js";
16
+ import { Add2dImagePayload, GpsPoint, LatLong, RawDeviceOrientation, RawGpsPoint, RecordGpsEventPayload, add2dImage, calcRelativeCoordsInMeters, recordGpsEvent, setZeroPos } from "gps-plus-slam-js";
17
+ export { type Add2dImagePayload, type AlignmentSnapshot, type CaptureGpsAnchorSampleOptions, type CompleteCallback, DEFAULT_MAX_DELAY_MS, DEFAULT_RECORDING_OPTIONS, DEFAULT_TRACKING_QUALITY_OPTIONS, DEPTH_CONSTRAINTS, type DepthCaptureOptions, type DepthPoint, type DepthSample, type DeviceOrientation, type ErrorCallback, type GpsAnchorSample, type GpsAnchorSampleGpsPoint, type GpsPoint, IMAGE_CONSTRAINTS, type ImageCaptureOptions, type LatLong, type SessionMetadata as OpfsSessionMetadata, type PersistenceMiddlewareOptions, type PoseReceivedPayload, type ProgressCallback, type RawDeviceOrientation, type RawGpsPoint, type RecordGpsEventPayload, type RecordingCoordinatorConfig, type RecordingOptionsInput, type RecordingState, type ReplayAction, ReplayEngine, type ReplayRecordingOptions, type ReplayState, type ResetTransformData, STORAGE_KEY, type SessionMetadata$1 as SessionMetadata, type SlamAppCombinedState, type SlamAppMiddleware, type SlamAppRootState, type SlamAppStore, type SlamAppStoreOptions, type StorageBackend, type StoreSubscriberDeps, type SubscribableStore, type TrackingPhase, type TrackingQualityOptions, type TrackingQualityReport, type TrackingQualitySliceState, type TrackingQualityState, type TrackingSliceState, add2dImage, buildRawGpsPoint, buildRecordGpsEventPayload, calcRelativeCoordsInMeters, captureGpsAnchorSample, clearLastRestartedPayload, cloneRecordingOptions, computeCompassAgreement, computeConvergence, computeCoverage, computeGpsAccuracy, computeGpsVsFusedDivergence, computeInterActionDelay, 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, selectRecentAlignments, selectTrackingPhase, selectTrackingQuality, selectZeroReference, setZeroPos, slicePrefixOf, snapshotPushed, snapshotsTrimmed, startSession, subscribeToSelector, trackingQualityReducer, trackingReducer, updateDeviceOrientation, validateDepthOptions, validateImageOptions, validateRecordingOptions, wireStoreSubscribers };
@@ -1,13 +1,15 @@
1
- import { endSession, recordDepthSample, recordWriteFailure, recorderReducer, setCurrentScenarioName, startSession } from "./recorder-slice.js";
2
- import { clearSessionRefPointUsage, incrementRefPointUsage, refPointsReducer, resetRefPointsState, selectCachedKnownRefPoints, setImportedRefPoints } from "./ref-points-slice.js";
3
- import { navigateTo, routingReducer } from "./routing-slice.js";
4
- import { createPersistenceMiddleware } from "./persistence-middleware.js";
5
- import { add2dImage, calcRelativeCoordsInMeters, createRecorderStore, markReferencePoint, recordGpsEvent, setZeroPos } from "./store.js";
6
- import { buildRawGpsPoint, buildRecordGpsEventPayload, createGpsPositionHandler, eulerToQuaternion, extractOdomPosition, extractOdomRotation, getLastDeviceOrientation, resetCoordinatorState, updateDeviceOrientation } from "./recording-coordinator.js";
1
+ import { buildRawGpsPoint, buildRecordGpsEventPayload, createGpsPositionHandler, eulerToQuaternion, extractOdomPosition, extractOdomRotation, getLastDeviceOrientation, resetCoordinatorState, updateDeviceOrientation } from "./gps-event-coordinator.js";
2
+ import { clearLastRestartedPayload, originReset, poseLost, poseReceived, resetTracking, selectLastRestartedPayload, selectLastSensorOrientation, selectLastValidPose, selectLostFrameCount, selectTrackingPhase, trackingReducer } from "./tracking-slice.js";
7
3
  import { DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, IMAGE_CONSTRAINTS, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateDepthOptions, validateImageOptions, validateRecordingOptions } from "./recording-options.js";
4
+ import { endSession, recordDepthSample, recordWriteFailure, recordingReducer, startSession } from "./recording-slice.js";
5
+ import { captureGpsAnchorSample } from "./gps-ar-pose-sampler.js";
6
+ import { selectAlignmentMatrix, selectFrameTilesInWebXR, selectGpsPositions, selectOdometryPositions, selectOdometryRotations, selectZeroReference } from "./app-selectors.js";
7
+ 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";
8
+ import { createPersistenceMiddleware, slicePrefixOf } from "./persistence-middleware.js";
9
+ import { createSlamAppStore } from "./create-slam-app-store.js";
8
10
  import { replayRecording } from "./recording-replayer.js";
9
11
  import { DEFAULT_MAX_DELAY_MS, ReplayEngine, computeInterActionDelay, extractActionTimestamp } from "./replay-engine.js";
10
12
  import { subscribeToSelector } from "./subscribe-to-selector.js";
11
- import { selectAlignmentMatrix, selectGpsPositions, selectOdometryPositions, selectOdometryRotations, selectReferencePoints, selectZeroReference } from "./app-selectors.js";
12
13
  import { wireStoreSubscribers } from "./store-subscribers.js";
13
- export { DEFAULT_MAX_DELAY_MS, DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, IMAGE_CONSTRAINTS, ReplayEngine, STORAGE_KEY, add2dImage, buildRawGpsPoint, buildRecordGpsEventPayload, calcRelativeCoordsInMeters, clearSessionRefPointUsage, cloneRecordingOptions, computeInterActionDelay, createGpsPositionHandler, createPersistenceMiddleware, createRecorderStore, endSession, eulerToQuaternion, extractActionTimestamp, extractOdomPosition, extractOdomRotation, getLastDeviceOrientation, incrementRefPointUsage, loadRecordingOptions, markReferencePoint, navigateTo, recordDepthSample, recordGpsEvent, recordWriteFailure, recorderReducer, refPointsReducer, replayRecording, resetCoordinatorState, resetRecordingOptions, resetRefPointsState, routingReducer, saveRecordingOptions, selectAlignmentMatrix, selectCachedKnownRefPoints, selectGpsPositions, selectOdometryPositions, selectOdometryRotations, selectReferencePoints, selectZeroReference, setCurrentScenarioName, setImportedRefPoints, setZeroPos, startSession, subscribeToSelector, updateDeviceOrientation, validateDepthOptions, validateImageOptions, validateRecordingOptions, wireStoreSubscribers };
14
+ import { add2dImage, calcRelativeCoordsInMeters, recordGpsEvent, setZeroPos } from "gps-plus-slam-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, 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, selectRecentAlignments, selectTrackingPhase, selectTrackingQuality, selectZeroReference, setZeroPos, slicePrefixOf, snapshotPushed, snapshotsTrimmed, startSession, subscribeToSelector, trackingQualityReducer, trackingReducer, updateDeviceOrientation, validateDepthOptions, validateImageOptions, validateRecordingOptions, wireStoreSubscribers };
@@ -1,2 +1,2 @@
1
- import { n as createPersistenceMiddleware, t as PersistenceMiddlewareOptions } from "../persistence-middleware-DrptY02i.js";
2
- export { PersistenceMiddlewareOptions, createPersistenceMiddleware };
1
+ import { n as createPersistenceMiddleware, r as slicePrefixOf, t as PersistenceMiddlewareOptions } from "../persistence-middleware-BkHIqvaA.js";
2
+ export { PersistenceMiddlewareOptions, createPersistenceMiddleware, slicePrefixOf };
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from "../utils/logger.js";
2
- import { recordWriteFailure } from "./recorder-slice.js";
2
+ import { recordWriteFailure } from "./recording-slice.js";
3
3
  //#region ../src/state/persistence-middleware.ts
4
4
  const log = createLogger("PersistenceMiddleware");
5
5
  /** Maximum number of concurrent OPFS write operations. */
@@ -27,31 +27,59 @@ var WriteQueue = class {
27
27
  }
28
28
  };
29
29
  /**
30
+ * Extract the slice prefix from a namespaced Redux action type.
31
+ *
32
+ * `'gpsData/setZeroPos'` → `'gpsData'`. Returns the whole string when there
33
+ * is no slash (e.g. `'@@INIT'`). This is the single point that turns a
34
+ * slice-owned action type into the value the persistence whitelist matches
35
+ * on, so call sites can derive prefixes from real action creators instead of
36
+ * re-typing literals.
37
+ */
38
+ function slicePrefixOf(actionType) {
39
+ const slashIndex = actionType.indexOf("/");
40
+ return slashIndex === -1 ? actionType : actionType.slice(0, slashIndex);
41
+ }
42
+ /** Read `state.recording.isRecording`, defaulting to false for any shape. */
43
+ function readIsRecording(state) {
44
+ return state?.recording?.isRecording ?? false;
45
+ }
46
+ /**
47
+ * Decide whether the just-reduced action belongs to a session whose actions
48
+ * must be persisted. True while recording, and also for the `endSession`
49
+ * action that just flipped `isRecording` to false (Issue 5) so the session's
50
+ * final action is still captured.
51
+ */
52
+ function isInPersistableSession(wasRecording, isRecording, actionType) {
53
+ return isRecording || wasRecording && actionType === "recording/endSession";
54
+ }
55
+ /**
30
56
  * Create a Redux middleware that persists qualifying actions to storage
31
57
  * during active recording sessions.
32
58
  *
33
59
  * Persistence rules:
34
- * - Only persists when `state.recorder.isRecording` is true (checked AFTER
60
+ * - Only persists when `state.recording.isRecording` is true (checked AFTER
35
61
  * the action is reduced, so `startSession` itself is included).
36
- * - Persists `gpsData/*` and `recorder/*` actions.
37
- * - Excludes `recorder/recordWriteFailure` to prevent recursive persistence.
38
- * - Excludes `routing/*`, `refPoints/*`, and any other non-recording actions.
62
+ * - Persists actions whose slice prefix is listed in `persistedPrefixes`.
63
+ * - Excludes `recording/recordWriteFailure` to prevent recursive persistence.
64
+ * - Excludes `routing/*` and any other non-whitelisted actions.
39
65
  * - Uses 1-based indexing for action files (000001.json, 000002.json, …).
40
66
  * - Each middleware instance maintains its own action index (Bug 10 fix).
41
67
  */
42
68
  function createPersistenceMiddleware(options) {
43
- const { storageBackend, onWriteFailure } = options;
69
+ const { storageBackend, onWriteFailure, persistedPrefixes } = options;
70
+ const normalizedPrefixes = persistedPrefixes.map((prefix) => prefix.endsWith("/") ? prefix : `${prefix}/`);
71
+ const excludedActionType = recordWriteFailure.type;
44
72
  let actionIndex = 0;
45
73
  const writeQueue = new WriteQueue();
46
74
  const middleware = (store) => (next) => (action) => {
47
75
  const typedAction = action;
48
76
  const actionType = typeof typedAction.type === "string" ? typedAction.type : null;
49
- const wasRecording = store.getState().recorder?.isRecording ?? false;
77
+ const wasRecording = readIsRecording(store.getState());
50
78
  const result = next(action);
51
79
  if (!actionType) return result;
52
- if (actionType === "recorder/startSession") actionIndex = 0;
53
- if (!((store.getState().recorder?.isRecording ?? false) || wasRecording && actionType === "recorder/endSession")) return result;
54
- if (!(actionType.startsWith("gpsData/") || actionType.startsWith("recorder/") && actionType !== "recorder/recordWriteFailure")) return result;
80
+ if (actionType === "recording/startSession") actionIndex = 0;
81
+ if (!isInPersistableSession(wasRecording, readIsRecording(store.getState()), actionType)) return result;
82
+ if (!(actionType !== excludedActionType && normalizedPrefixes.some((prefix) => actionType.startsWith(prefix)))) return result;
55
83
  const index = ++actionIndex;
56
84
  writeQueue.enqueue(async () => {
57
85
  try {
@@ -68,4 +96,4 @@ function createPersistenceMiddleware(options) {
68
96
  return middleware;
69
97
  }
70
98
  //#endregion
71
- export { createPersistenceMiddleware };
99
+ export { createPersistenceMiddleware, slicePrefixOf };
@@ -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-BYUFX8iU.js";
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-Dq6IsKAv.js";
2
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,2 +1,3 @@
1
- import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-oOzPITrd.js";
2
- export { ReplayRecordingOptions, replayRecording };
1
+ import { L as CombinedRootState } from "../create-slam-app-store-CNsZ3rcX.js";
2
+ import { n as replayRecording, t as ReplayRecordingOptions } from "../recording-replayer-CmCIIZ02.js";
3
+ export { CombinedRootState, ReplayRecordingOptions, replayRecording };
@@ -1,6 +1,6 @@
1
- import { createRecorderStore } from "./store.js";
2
1
  import { loadActionsFromZip } from "../storage/zip-reader.js";
3
2
  import { NullStorageBackend } from "../storage/null-storage-backend.js";
3
+ import { createSlamAppStore } from "./create-slam-app-store.js";
4
4
  //#region ../src/state/recording-replayer.ts
5
5
  /**
6
6
  * Recording Replayer
@@ -29,7 +29,7 @@ import { NullStorageBackend } from "../storage/null-storage-backend.js";
29
29
  * @throws If the zip cannot be parsed or contains invalid data
30
30
  */
31
31
  async function replayRecording(zipData, options) {
32
- const store = createRecorderStore({ storageBackend: new NullStorageBackend() });
32
+ const store = createSlamAppStore({ storageBackend: new NullStorageBackend() });
33
33
  let actions = (await loadActionsFromZip(zipData)).map((e) => e.action);
34
34
  if (options?.migrateActions) actions = options.migrateActions(actions);
35
35
  for (const action of actions) store.dispatch(action);
@@ -0,0 +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-d6OtCrza.js";
2
+ export { RecordingState, SessionMetadata, endSession, recordDepthSample, recordWriteFailure, recordingReducer, startSession };
@@ -1,13 +1,12 @@
1
1
  import { createSlice } from "@reduxjs/toolkit";
2
- //#region ../src/state/recorder-slice.ts
3
- const recorderSlice = createSlice({
4
- name: "recorder",
2
+ //#region ../src/state/recording-slice.ts
3
+ const recordingSlice = createSlice({
4
+ name: "recording",
5
5
  initialState: {
6
6
  isRecording: false,
7
7
  sessionMetadata: null,
8
8
  actionCount: 0,
9
- failedWriteCount: 0,
10
- currentScenarioName: ""
9
+ failedWriteCount: 0
11
10
  },
12
11
  reducers: {
13
12
  startSession(state, action) {
@@ -31,17 +30,10 @@ const recorderSlice = createSlice({
31
30
  */
32
31
  recordWriteFailure(state, _action) {
33
32
  state.failedWriteCount += 1;
34
- },
35
- /**
36
- * Set the currently selected scenario name.
37
- * Replaces the closure variable threading through folder-manager deps.
38
- */
39
- setCurrentScenarioName(state, action) {
40
- state.currentScenarioName = action.payload;
41
33
  }
42
34
  }
43
35
  });
44
- const { startSession, endSession, recordDepthSample, recordWriteFailure, setCurrentScenarioName } = recorderSlice.actions;
45
- const recorderReducer = recorderSlice.reducer;
36
+ const { startSession, endSession, recordDepthSample, recordWriteFailure } = recordingSlice.actions;
37
+ const recordingReducer = recordingSlice.reducer;
46
38
  //#endregion
47
- export { endSession, recordDepthSample, recordWriteFailure, recorderReducer, setCurrentScenarioName, startSession };
39
+ export { endSession, recordDepthSample, recordWriteFailure, recordingReducer, startSession };
@@ -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-DuX316ae.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-BGBRQyXs.js";
2
2
  export { CompleteCallback, DEFAULT_MAX_DELAY_MS, ErrorCallback, ProgressCallback, ReplayAction, ReplayEngine, ReplayState, computeInterActionDelay, extractActionTimestamp };
@@ -25,7 +25,7 @@ function extractActionTimestamp(action) {
25
25
  if (gpsPoint && typeof gpsPoint.timestamp === "number") return gpsPoint.timestamp;
26
26
  return null;
27
27
  }
28
- case "recorder/startSession":
28
+ case "recording/startSession":
29
29
  if (typeof payload.startTime === "number") return payload.startTime;
30
30
  return null;
31
31
  case "gpsData/markReferencePoint": {
@@ -36,8 +36,8 @@ function extractActionTimestamp(action) {
36
36
  if (gpsPoint && typeof gpsPoint.timestamp === "number") return gpsPoint.timestamp;
37
37
  return null;
38
38
  }
39
- case "recorder/recordDepthSample": return null;
40
- case "recorder/endSession": return null;
39
+ case "recording/recordDepthSample": return null;
40
+ case "recording/endSession": return null;
41
41
  default: return null;
42
42
  }
43
43
  }
@@ -1,3 +1,3 @@
1
- import { t as SubscribableStore } from "../subscribe-to-selector-UTZ0KkAc.js";
2
- import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-CuoIzB5T.js";
1
+ import { t as SubscribableStore } from "../subscribe-to-selector-DIHm3BRZ.js";
2
+ import { n as wireStoreSubscribers, t as StoreSubscriberDeps } from "../store-subscribers-Dce_CoJF.js";
3
3
  export { StoreSubscriberDeps, SubscribableStore, wireStoreSubscribers };
@@ -1,6 +1,6 @@
1
- import { fusedGpsFromOdom } from "../utils/fused-path.js";
1
+ import { selectAlignmentMatrix, selectGpsPositions, selectOdometryPositions, selectOdometryRotations, selectZeroReference } from "./app-selectors.js";
2
+ import { buildMapData } from "../visualization/map-data.js";
2
3
  import { subscribeToSelector } from "./subscribe-to-selector.js";
3
- import { selectAlignmentMatrix, selectGpsPositions, selectOdometryPositions, selectOdometryRotations, selectReferencePoints, selectZeroReference } from "./app-selectors.js";
4
4
  import { calcGpsCoords } from "gps-plus-slam-js";
5
5
  import { mat4, vec3 } from "gl-matrix";
6
6
  //#region ../src/state/store-subscribers.ts
@@ -27,6 +27,30 @@ function wireStoreSubscribers(store, deps) {
27
27
  const _odomVec = vec3.create();
28
28
  const _alignedVec = vec3.create();
29
29
  const unsubs = [];
30
+ const alignmentSnapshotGps = [];
31
+ /**
32
+ * Rebuild the full map snapshot from the current store state and hand it to
33
+ * the overlay's `render`. Called whenever the alignment matrix or the GPS
34
+ * positions change — the change-gated subscriptions below provide the
35
+ * throttling, so no extra debouncing is needed. The fused path is always
36
+ * recomputed from the latest matrix inside `buildMapData` (D2).
37
+ */
38
+ function rebuildMap() {
39
+ if (!deps.mapOverlay?.render) return;
40
+ const state = store.getState();
41
+ const rawGpsPath = selectGpsPositions(state).map((p) => ({
42
+ lat: p.latitude,
43
+ lng: p.longitude,
44
+ accuracy: p.latLongAccuracy
45
+ }));
46
+ deps.mapOverlay.render(buildMapData({
47
+ rawGpsPath,
48
+ odometryPositions: selectOdometryPositions(state),
49
+ alignmentMatrix: selectAlignmentMatrix(state),
50
+ zeroRef: selectZeroReference(state),
51
+ alignmentSnapshots: alignmentSnapshotGps
52
+ }));
53
+ }
30
54
  unsubs.push(subscribeToSelector(store, selectAlignmentMatrix, (matrix) => {
31
55
  if (!matrix) return;
32
56
  deps.applyAlignmentMatrix(matrix);
@@ -45,11 +69,15 @@ function wireStoreSubscribers(store, deps) {
45
69
  deps.gpsEventVisualizer.addAlignmentSnapshot(snapshotPos);
46
70
  deps.onAlignmentSnapshot?.(snapshotPos);
47
71
  const zeroRef = selectZeroReference(state);
48
- if (zeroRef && deps.mapOverlay?.addAlignmentSnapshot) {
72
+ if (zeroRef) {
49
73
  const gps = calcGpsCoords(zeroRef, _alignedVec);
50
- deps.mapOverlay.addAlignmentSnapshot(gps.lat, gps.lon);
74
+ alignmentSnapshotGps.push({
75
+ lat: gps.lat,
76
+ lng: gps.lon
77
+ });
51
78
  }
52
79
  }
80
+ rebuildMap();
53
81
  }));
54
82
  unsubs.push(subscribeToSelector(store, selectGpsPositions, (gpsPositions, prevPositions) => {
55
83
  const prevCount = prevPositions?.length ?? 0;
@@ -62,16 +90,13 @@ function wireStoreSubscribers(store, deps) {
62
90
  const gpsPoint = gpsPositions[i];
63
91
  const odomPos = odomPositions[i];
64
92
  if (gpsPoint && odomPos) {
65
- deps.gpsEventVisualizer.addGpsEvent(gpsPoint.coordinates, odomPos);
93
+ const accuracy = deps.showAccuracySpheres ? {
94
+ horizontal: gpsPoint.latLongAccuracy,
95
+ vertical: gpsPoint.altitudeAccuracy
96
+ } : void 0;
97
+ deps.gpsEventVisualizer.addGpsEvent(gpsPoint.coordinates, odomPos, accuracy);
66
98
  deps.onNewGpsPosition?.(gpsPoint.coordinates);
67
99
  deps.onNewGpsLatLng?.(gpsPoint.latitude, gpsPoint.longitude);
68
- deps.mapOverlay?.addRawGpsPoint?.(gpsPoint.latitude, gpsPoint.longitude);
69
- const alignmentMatrix = selectAlignmentMatrix(state);
70
- const zero = selectZeroReference(state);
71
- if (alignmentMatrix && zero && deps.mapOverlay?.addFusedPoint) {
72
- const fusedGps = fusedGpsFromOdom(alignmentMatrix, odomPos, zero);
73
- deps.mapOverlay.addFusedPoint(fusedGps.lat, fusedGps.lon);
74
- }
75
100
  const odomRot = odomRotations[i];
76
101
  if (odomRot) deps.onNewOdomPose?.(odomPos, odomRot);
77
102
  }
@@ -80,14 +105,7 @@ function wireStoreSubscribers(store, deps) {
80
105
  const lastGpsPoint = gpsPositions[gpsPositions.length - 1];
81
106
  deps.mapOverlay.setGpsPosition(lastGpsPoint.latitude, lastGpsPoint.longitude);
82
107
  }
83
- }));
84
- unsubs.push(subscribeToSelector(store, selectReferencePoints, (refPoints, prevPoints) => {
85
- const prevCount = prevPoints?.length ?? 0;
86
- if (!deps.mapOverlay?.addRefPoint || refPoints.length <= prevCount) return;
87
- for (let i = prevCount; i < refPoints.length; i++) {
88
- const rp = refPoints[i];
89
- deps.mapOverlay.addRefPoint(rp.gpsPoint.latitude, rp.gpsPoint.longitude, rp.id);
90
- }
108
+ rebuildMap();
91
109
  }));
92
110
  return () => {
93
111
  for (const unsub of unsubs) unsub();
@@ -1,2 +1,2 @@
1
- import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-UTZ0KkAc.js";
1
+ import { n as subscribeToSelector, t as SubscribableStore } from "../subscribe-to-selector-DIHm3BRZ.js";
2
2
  export { SubscribableStore, subscribeToSelector };
@@ -0,0 +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-CNsZ3rcX.js";
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 };