gps-plus-slam-app-framework 1.0.6 → 1.1.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 (198) hide show
  1. package/LICENSE +4 -4
  2. package/README.md +310 -115
  3. package/dist/accuracy-circles-DEKr0Hoh.d.ts +43 -0
  4. package/dist/{app-selectors-CHt7DLuq.d.ts → app-selectors-CR9OWodf.d.ts} +8 -10
  5. package/dist/ar/camera-blit-capture.d.ts +1 -1
  6. package/dist/ar/capability-checker.d.ts +2 -0
  7. package/dist/ar/capability-checker.js +25 -0
  8. package/dist/ar/capture-failure-tracker.d.ts +1 -1
  9. package/dist/ar/chromium-camera-access-workaround.d.ts +2 -2
  10. package/dist/ar/chromium-camera-access-workaround.js +197 -8
  11. package/dist/ar/depth-sampler.d.ts +2 -2
  12. package/dist/ar/enable-gps-ar.d.ts +2 -0
  13. package/dist/ar/enable-gps-ar.js +137 -0
  14. package/dist/ar/frame-loop.d.ts +2 -0
  15. package/dist/ar/frame-loop.js +63 -0
  16. package/dist/ar/image-capture.d.ts +1 -1
  17. package/dist/ar/image-capture.js +1 -1
  18. package/dist/ar/index.d.ts +16 -12
  19. package/dist/ar/index.js +8 -4
  20. package/dist/ar/replay-scene.d.ts +1 -1
  21. package/dist/ar/scene-node-names.d.ts +1 -1
  22. package/dist/ar/webxr-session.d.ts +4 -4
  23. package/dist/ar/webxr-session.js +173 -93
  24. package/dist/ar/xr-camera-texture.d.ts +1 -1
  25. package/dist/ar/xr-error-handler.d.ts +1 -1
  26. package/dist/ar/xr-frame-loop.d.ts +2 -0
  27. package/dist/ar/xr-frame-loop.js +69 -0
  28. package/dist/ar-world-group-alignment-Dn4rQk_c.d.ts +43 -0
  29. package/dist/capability-checker-BTCmZRL4.d.ts +43 -0
  30. package/dist/chromium-camera-access-workaround-MifIwK9x.d.ts +137 -0
  31. package/dist/core/index.d.ts +2 -2
  32. package/dist/core/index.js +2 -2
  33. package/dist/create-slam-app-store-B76AGaI0.d.ts +371 -0
  34. package/dist/{depth-sampler-BNCtnEoP.d.ts → depth-sampler-CqrkRr2N.d.ts} +1 -1
  35. package/dist/enable-gps-ar-DNbO0zbg.d.ts +95 -0
  36. package/dist/{file-system-DD5TczSO.d.ts → file-system-TIsDfamK.d.ts} +32 -2
  37. package/dist/frame-conversions-D2EYjeNa.d.ts +54 -0
  38. package/dist/frame-loop-BTeRpDm4.d.ts +47 -0
  39. package/dist/frustum-visibility-DGnJqls0.d.ts +58 -0
  40. package/dist/{fused-path-DtqCSqJh.d.ts → fused-path-BdVHmDW9.d.ts} +1 -1
  41. package/dist/geo/h3-proximity.d.ts +1 -1
  42. package/dist/geo/index.d.ts +1 -1
  43. package/dist/{geo-types-Xx-LVcnw.d.ts → geo-types-clgzl8b5.d.ts} +16 -1
  44. package/dist/{gps-GKc6_Cly.d.ts → gps-B7AlMPz5.d.ts} +1 -1
  45. package/dist/gps-anchor-CISLPQIb.d.ts +59 -0
  46. package/dist/{gps-ar-pose-sampler-D_Zso40V.d.ts → gps-ar-pose-sampler-BypXZUqC.d.ts} +1 -1
  47. package/dist/{gps-event-coordinator-fwvmVfku.d.ts → gps-event-coordinator-BKVZbS1M.d.ts} +3 -3
  48. package/dist/gps-event-markers-BlHq3jYV.d.ts +103 -0
  49. package/dist/hit-test-reticle-BGXOxrUh.d.ts +45 -0
  50. package/dist/{image-capture-2pKjYUS6.d.ts → image-capture-BXZUmj7j.d.ts} +1 -1
  51. package/dist/index--ldLph4V.d.ts +2 -0
  52. package/dist/index-22v8MXJX.d.ts +23 -0
  53. package/dist/index-BB2KEWbN2.d.ts +37 -0
  54. package/dist/index.d.ts +64 -52
  55. package/dist/index.js +21 -8
  56. package/dist/{leaflet-map-overlay-CQZcnxbc.d.ts → leaflet-map-overlay-jNt-kcUr.d.ts} +22 -11
  57. package/dist/{logger-B81iwxx0.d.ts → logger-BZ44Dhd3.d.ts} +7 -1
  58. package/dist/map-data-BGIBM5rv.d.ts +49 -0
  59. package/dist/map-overlay-draw-DcGs9UV3.d.ts +42 -0
  60. package/dist/{null-storage-backend-C0TiwwzQ.d.ts → null-storage-backend-BPQJSaMw.d.ts} +2 -2
  61. package/dist/{opfs-storage-backend-Gt3rd76D.d.ts → opfs-storage-backend-u4QKvgvP.d.ts} +2 -2
  62. package/dist/{permission-checker-Cu3nbH1S.d.ts → permission-checker-CfV7INCa.d.ts} +41 -1
  63. package/dist/persistence-middleware-xB5sTllJ.d.ts +55 -0
  64. package/dist/{recording-options-BYUFX8iU.d.ts → recording-options-BSFpBSZH.d.ts} +10 -4
  65. package/dist/{recording-replayer-BFhFs7Mc.d.ts → recording-replayer-CTL2cUE0.d.ts} +2 -2
  66. package/dist/{recording-slice-BiktUIWF.d.ts → recording-slice-Cb888P6d.d.ts} +2 -2
  67. package/dist/{replay-engine-B88Va62x.d.ts → replay-engine-B-5GIZtc.d.ts} +1 -1
  68. package/dist/{replay-scene-DtUm6bsr.d.ts → replay-scene-DkNfldPh.d.ts} +3 -3
  69. package/dist/sensors/gps-error-handler.d.ts +1 -1
  70. package/dist/sensors/gps.d.ts +1 -1
  71. package/dist/sensors/gps.js +2 -2
  72. package/dist/sensors/index.d.ts +3 -3
  73. package/dist/sensors/index.js +1 -1
  74. package/dist/sensors/permission-checker.d.ts +2 -2
  75. package/dist/sensors/permission-checker.js +79 -1
  76. package/dist/session-disposers-M-oashRH.js +57 -0
  77. package/dist/state/app-selectors.d.ts +2 -2
  78. package/dist/state/app-selectors.js +33 -3
  79. package/dist/state/combined-root-state.d.ts +2 -0
  80. package/dist/state/combined-root-state.js +1 -0
  81. package/dist/state/create-slam-app-store.d.ts +1 -1
  82. package/dist/state/create-slam-app-store.js +27 -13
  83. package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
  84. package/dist/state/gps-event-coordinator.d.ts +1 -1
  85. package/dist/state/index.d.ts +17 -16
  86. package/dist/state/index.js +6 -4
  87. package/dist/state/persistence-middleware.d.ts +2 -2
  88. package/dist/state/persistence-middleware.js +43 -9
  89. package/dist/state/recording-options.d.ts +1 -1
  90. package/dist/state/recording-replayer.d.ts +2 -2
  91. package/dist/state/recording-slice.d.ts +1 -1
  92. package/dist/state/replay-engine.d.ts +1 -1
  93. package/dist/state/store-subscribers.d.ts +2 -2
  94. package/dist/state/store-subscribers.js +38 -12
  95. package/dist/state/subscribe-to-selector.d.ts +1 -1
  96. package/dist/state/tracking-quality.d.ts +2 -0
  97. package/dist/state/tracking-quality.js +723 -0
  98. package/dist/state/tracking-slice.d.ts +2 -0
  99. package/dist/state/tracking-slice.js +147 -0
  100. package/dist/state-BkIfpiyA.js +87 -0
  101. package/dist/storage/file-system-utils.d.ts +1 -1
  102. package/dist/storage/file-system.d.ts +2 -2
  103. package/dist/storage/file-system.js +52 -1
  104. package/dist/storage/index.d.ts +8 -8
  105. package/dist/storage/null-storage-backend.d.ts +1 -1
  106. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  107. package/dist/storage/opfs-storage.d.ts +1 -1
  108. package/dist/storage/storage-backend.d.ts +1 -1
  109. package/dist/storage/zip-export.d.ts +1 -1
  110. package/dist/storage/zip-reader.d.ts +1 -1
  111. package/dist/storage/zip-reader.js +8 -4
  112. package/dist/{storage-backend-yVnuWGTr.d.ts → storage-backend-yDSKafAQ.d.ts} +1 -1
  113. package/dist/{store-subscribers-DKKHld49.d.ts → store-subscribers-swlbtg_z.d.ts} +31 -6
  114. package/dist/{subscribe-to-selector-ghDKYtap.d.ts → subscribe-to-selector--KQpgLXG.d.ts} +1 -1
  115. package/dist/test-utils/browser-mocks.d.ts +1 -1
  116. package/dist/test-utils/zip-round-trip-helpers.d.ts +7 -1
  117. package/dist/test-utils/zip-round-trip-helpers.js +2 -1
  118. package/dist/tracking-slice-CDREeoZ_.d.ts +86 -0
  119. package/dist/types/ar-types.d.ts +1 -1
  120. package/dist/types/geo-types.d.ts +2 -2
  121. package/dist/types/index.d.ts +3 -3
  122. package/dist/utils/concurrency.d.ts +1 -1
  123. package/dist/utils/failure-tracker.d.ts +1 -1
  124. package/dist/utils/format-file-size.d.ts +1 -1
  125. package/dist/utils/fused-path.d.ts +1 -1
  126. package/dist/utils/index.d.ts +6 -6
  127. package/dist/utils/list-formatter.d.ts +1 -1
  128. package/dist/utils/logger.d.ts +1 -1
  129. package/dist/utils/logger.js +68 -8
  130. package/dist/visualization/accuracy-circles.d.ts +2 -0
  131. package/dist/visualization/accuracy-circles.js +57 -0
  132. package/dist/visualization/alignment-lerper.d.ts +1 -1
  133. package/dist/visualization/ar-world-group-alignment.d.ts +2 -0
  134. package/dist/visualization/ar-world-group-alignment.js +51 -0
  135. package/dist/visualization/camera-follower.d.ts +1 -1
  136. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  137. package/dist/visualization/frame-conversions.d.ts +2 -0
  138. package/dist/visualization/frame-conversions.js +88 -0
  139. package/dist/visualization/frustum-visibility.d.ts +2 -0
  140. package/dist/visualization/frustum-visibility.js +139 -0
  141. package/dist/visualization/gps-anchor.d.ts +2 -0
  142. package/dist/visualization/gps-anchor.js +217 -0
  143. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  144. package/dist/visualization/gps-event-markers.d.ts +2 -2
  145. package/dist/visualization/gps-event-markers.js +69 -2
  146. package/dist/visualization/hit-test-reticle.d.ts +2 -0
  147. package/dist/visualization/hit-test-reticle.js +81 -0
  148. package/dist/visualization/index.d.ts +19 -12
  149. package/dist/visualization/index.js +9 -2
  150. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  151. package/dist/visualization/leaflet-map-overlay.js +33 -79
  152. package/dist/visualization/lerp-utils.d.ts +1 -1
  153. package/dist/visualization/map-data.d.ts +2 -0
  154. package/dist/visualization/map-data.js +33 -0
  155. package/dist/visualization/map-overlay-draw.d.ts +2 -0
  156. package/dist/visualization/map-overlay-draw.js +107 -0
  157. package/dist/visualization/map-overlay.d.ts +1 -1
  158. package/dist/visualization/three-dispose.d.ts +1 -1
  159. package/dist/visualization/vis-colors.d.ts +1 -1
  160. package/dist/webxr-nue-basis-BbdqhqY6.js +54 -0
  161. package/dist/{webxr-session-COlSrXdL.d.ts → webxr-session-BnArFCew.d.ts} +76 -14
  162. package/dist/xr-frame-loop-BKckC7xC.d.ts +70 -0
  163. package/dist/{zip-reader-KgJEXbca.d.ts → zip-reader-B2lzN8F5.d.ts} +5 -0
  164. package/package.json +16 -16
  165. package/dist/ar/tracking-state.d.ts +0 -2
  166. package/dist/ar/tracking-state.js +0 -164
  167. package/dist/chromium-camera-access-workaround-CU5zSKNr.d.ts +0 -61
  168. package/dist/combined-root-state-B3SvPNjU.d.ts +0 -6
  169. package/dist/create-slam-app-store-DBm7g2Zd.d.ts +0 -85
  170. package/dist/gps-anchored-mesh-manager-CovAFK4g.d.ts +0 -56
  171. package/dist/gps-event-markers-sVyO9utS.d.ts +0 -67
  172. package/dist/persistence-middleware-z-75iisb.d.ts +0 -31
  173. package/dist/tracking-state-B9QfuZCg.d.ts +0 -101
  174. package/dist/visualization/gps-anchored-mesh-manager.d.ts +0 -2
  175. package/dist/visualization/gps-anchored-mesh-manager.js +0 -128
  176. /package/dist/{alignment-lerper-D9BeDwZX.d.ts → alignment-lerper-CCZJf-Xb.d.ts} +0 -0
  177. /package/dist/{ar-types-B-ORgk6Z.d.ts → ar-types-isPsQptb.d.ts} +0 -0
  178. /package/dist/{camera-blit-capture-DHuyrrxr.d.ts → camera-blit-capture-75WWa9Xb.d.ts} +0 -0
  179. /package/dist/{camera-follower-BMqPddxx.d.ts → camera-follower-B-nS1sr6.d.ts} +0 -0
  180. /package/dist/{capture-failure-tracker-2Q6oI2uQ.d.ts → capture-failure-tracker-D4HYJNOj.d.ts} +0 -0
  181. /package/dist/{concurrency-p6gB30fT.d.ts → concurrency-Bsmv53qw.d.ts} +0 -0
  182. /package/dist/{css3d-renderer-manager-rgLMVAvd.d.ts → css3d-renderer-manager-EDED3nvg.d.ts} +0 -0
  183. /package/dist/{failure-tracker-1sQ_Cgyw.d.ts → failure-tracker-D7ELffm0.d.ts} +0 -0
  184. /package/dist/{file-system-utils-BH8FdyIu.d.ts → file-system-utils-BH6uIs2i.d.ts} +0 -0
  185. /package/dist/{format-file-size-onGq0k1Q.d.ts → format-file-size-B7gJb3Md.d.ts} +0 -0
  186. /package/dist/{gps-compass-cubes-jRIJMOuh.d.ts → gps-compass-cubes-Ys6Hf1nc.d.ts} +0 -0
  187. /package/dist/{gps-error-handler-CH1S528z.d.ts → gps-error-handler-BLDhUlw5.d.ts} +0 -0
  188. /package/dist/{h3-proximity-GwesPTFT.d.ts → h3-proximity-DJnghtir.d.ts} +0 -0
  189. /package/dist/{lerp-utils-CZgQlrmu.d.ts → lerp-utils-DNcSmKVX.d.ts} +0 -0
  190. /package/dist/{list-formatter-DKQQVLcl.d.ts → list-formatter-CsqcU4v5.d.ts} +0 -0
  191. /package/dist/{map-overlay-C9ifl_4-.d.ts → map-overlay-yrFsUtGv.d.ts} +0 -0
  192. /package/dist/{opfs-storage-Dlq1I_Gb.d.ts → opfs-storage-LsLY6VZV.d.ts} +0 -0
  193. /package/dist/{scene-node-names-DPsaE2z3.d.ts → scene-node-names-IHW7HR4S.d.ts} +0 -0
  194. /package/dist/{three-dispose-qt_r5wR1.d.ts → three-dispose-WTBAfaWU.d.ts} +0 -0
  195. /package/dist/{vis-colors-BKkdTIKb.d.ts → vis-colors-Djnyg_UH.d.ts} +0 -0
  196. /package/dist/{xr-camera-texture-BbukNE79.d.ts → xr-camera-texture-BU5YvKAM.d.ts} +0 -0
  197. /package/dist/{xr-error-handler-DTHfmvrl.d.ts → xr-error-handler-DcUFshNF.d.ts} +0 -0
  198. /package/dist/{zip-export-BjrQPv0X.d.ts → zip-export-C0_ErAqT.d.ts} +0 -0
@@ -1,7 +1,7 @@
1
- import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-B81iwxx0.js";
2
- import { n as computeFusedPath, r as fusedGpsFromOdom, t as FusedPathInput } from "../fused-path-DtqCSqJh.js";
3
- import { n as FailureTrackerConfig, r as createFailureTracker, t as FailureTracker } from "../failure-tracker-1sQ_Cgyw.js";
4
- import { t as mapWithConcurrencyLimit } from "../concurrency-p6gB30fT.js";
5
- import { t as formatFileSize } from "../format-file-size-onGq0k1Q.js";
6
- import { t as listFormatter } from "../list-formatter-DKQQVLcl.js";
1
+ import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-BZ44Dhd3.js";
2
+ import { n as computeFusedPath, r as fusedGpsFromOdom, t as FusedPathInput } from "../fused-path-BdVHmDW9.js";
3
+ import { n as FailureTrackerConfig, r as createFailureTracker, t as FailureTracker } from "../failure-tracker-D7ELffm0.js";
4
+ import { t as mapWithConcurrencyLimit } from "../concurrency-Bsmv53qw.js";
5
+ import { t as formatFileSize } from "../format-file-size-B7gJb3Md.js";
6
+ import { t as listFormatter } from "../list-formatter-CsqcU4v5.js";
7
7
  export { type FailureTracker, type FailureTrackerConfig, type FusedPathInput, type LogEntry, LogLevel, type Logger, clearLogBuffer, computeFusedPath, createFailureTracker, createLogger, formatFileSize, fusedGpsFromOdom, getGlobalLogLevel, getLogBuffer, listFormatter, mapWithConcurrencyLimit, setGlobalLogLevel, subscribeToLogs };
@@ -1,2 +1,2 @@
1
- import { t as listFormatter } from "../list-formatter-DKQQVLcl.js";
1
+ import { t as listFormatter } from "../list-formatter-CsqcU4v5.js";
2
2
  export { listFormatter };
@@ -1,2 +1,2 @@
1
- import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-B81iwxx0.js";
1
+ import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-BZ44Dhd3.js";
2
2
  export { LogEntry, LogLevel, Logger, clearLogBuffer, createLogger, getGlobalLogLevel, getLogBuffer, setGlobalLogLevel, subscribeToLogs };
@@ -12,7 +12,13 @@ import * as Sentry from "@sentry/browser";
12
12
  *
13
13
  * Sentry Integration:
14
14
  * - All log levels add Sentry breadcrumbs for debugging context
15
- * - log.error() with Error objects automatically reports to Sentry
15
+ * - log.warn() reports a standalone Sentry Issue (captureMessage)
16
+ * - log.error() reports a standalone Sentry Issue: captureException for Error
17
+ * arguments, or a captureMessage fallback for string-only errors
18
+ * - warn/error Issues are grouped by a normalized message template
19
+ * (['log', level, tag, template]) so dynamic values (frame indices, sizes,
20
+ * filenames) collapse into one Issue per message kind without merging
21
+ * genuinely different messages that share a tag
16
22
  */
17
23
  /**
18
24
  * Log levels in order of verbosity (lower = more verbose)
@@ -180,20 +186,74 @@ function addSentryBreadcrumb(level, tag, args) {
180
186
  });
181
187
  }
182
188
  /**
189
+ * Normalize a log message body into a stable "template" for Sentry grouping.
190
+ *
191
+ * Replaces the dynamic tokens that typically vary between otherwise-identical
192
+ * log lines (numbers, UUIDs, quoted strings) with placeholders. This makes the
193
+ * fingerprint depend on the *kind* of message rather than its concrete values,
194
+ * so that:
195
+ * - the same message with different dynamic values collapses into one Issue
196
+ * (e.g. `frame 12` / `frame 87` -> `frame {n}`), and
197
+ * - two genuinely different messages under the same tag stay as separate
198
+ * Issues (because their templates differ).
199
+ *
200
+ * Order matters: UUIDs are replaced before bare numbers, otherwise the number
201
+ * rule would shred the digit groups inside a UUID first.
202
+ */
203
+ function toFingerprintTemplate(body) {
204
+ return body.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "{uuid}").replace(/-?\d+(?:\.\d+)?(?:e[+-]?\d+)?/gi, "{n}").replace(/"[^"]*"/g, "\"{str}\"").replace(/'[^']*'/g, "'{str}'");
205
+ }
206
+ /**
207
+ * Capture a log line as a standalone Sentry Issue (message event).
208
+ *
209
+ * Shared by `reportWarningToSentry` and the string-only branch of
210
+ * `reportErrorsToSentry`. The fingerprint is
211
+ * `['log', level, tag, <normalized template>]` so that dynamic values in the
212
+ * message do not fragment one logical problem into many Issues, while distinct
213
+ * messages from the same tag remain distinct Issues (see
214
+ * {@link toFingerprintTemplate}).
215
+ */
216
+ function captureLogMessage(level, tag, args) {
217
+ const body = args.map((arg) => serializeArg(arg)).join(" ");
218
+ const message = `[${tag}] ${body}`;
219
+ Sentry.captureMessage(message, {
220
+ level,
221
+ fingerprint: [
222
+ "log",
223
+ level,
224
+ tag,
225
+ toFingerprintTemplate(body)
226
+ ]
227
+ });
228
+ }
229
+ /**
183
230
  * Report a warning to Sentry as a standalone message.
184
231
  * Called for log.warn() so warnings are independently visible in the
185
232
  * Sentry dashboard, not only as breadcrumbs attached to later exceptions.
186
233
  */
187
234
  function reportWarningToSentry(tag, args) {
188
- const message = `[${tag}] ${args.map((arg) => serializeArg(arg)).join(" ")}`;
189
- Sentry.captureMessage(message, "warning");
235
+ captureLogMessage("warning", tag, args);
190
236
  }
191
237
  /**
192
- * Report Error objects to Sentry.
193
- * Only called for log.error() - other levels just add breadcrumbs.
238
+ * Report log.error() arguments to Sentry as standalone Issues.
239
+ *
240
+ * - For every argument that is an `Error`, calls `captureException` so the
241
+ * Issue carries a full stack trace.
242
+ * - If NO argument is an `Error` (a string-only `log.error(...)`), falls back
243
+ * to a message event so the error still surfaces as an Issue instead of only
244
+ * a breadcrumb/Log.
245
+ *
246
+ * The fallback is mutually exclusive with `captureException` to avoid duplicate
247
+ * Issues when an Error is present, and groups via the same normalized template
248
+ * fingerprint as warnings (see {@link captureLogMessage}).
194
249
  */
195
- function reportErrorsToSentry(args) {
196
- for (const arg of args) if (arg instanceof Error) Sentry.captureException(arg);
250
+ function reportErrorsToSentry(tag, args) {
251
+ let capturedError = false;
252
+ for (const arg of args) if (arg instanceof Error) {
253
+ Sentry.captureException(arg);
254
+ capturedError = true;
255
+ }
256
+ if (!capturedError) captureLogMessage("error", tag, args);
197
257
  }
198
258
  function createLogger(tag) {
199
259
  const prefix = `[${tag}]`;
@@ -217,7 +277,7 @@ function createLogger(tag) {
217
277
  error: (...args) => {
218
278
  addToBuffer(3, tag, args);
219
279
  addSentryBreadcrumb(3, tag, args);
220
- reportErrorsToSentry(args);
280
+ reportErrorsToSentry(tag, args);
221
281
  if (globalLogLevel <= 3) console.error(prefix, ...args);
222
282
  }
223
283
  };
@@ -0,0 +1,2 @@
1
+ import { a as addAccuracyCircles, i as AccuracyCircleSample, n as ACCURACY_CIRCLE_STROKE_OPACITY, r as ACCURACY_CIRCLE_WEIGHT, t as ACCURACY_CIRCLE_FILL_OPACITY } from "../accuracy-circles-DEKr0Hoh.js";
2
+ export { ACCURACY_CIRCLE_FILL_OPACITY, ACCURACY_CIRCLE_STROKE_OPACITY, ACCURACY_CIRCLE_WEIGHT, AccuracyCircleSample, addAccuracyCircles };
@@ -0,0 +1,57 @@
1
+ import L from "leaflet";
2
+ //#region ../src/visualization/accuracy-circles.ts
3
+ /**
4
+ * Shared helper for drawing per-event GPS accuracy circles on a Leaflet map.
5
+ *
6
+ * Lives in the app-framework (D4 of the unified-map plan,
7
+ * gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-unified-trajectory-map-phase3-plan.md)
8
+ * so BOTH the framework's shared map-overlay-draw module AND the recorder
9
+ * app's preview/summary maps can reuse it. The recorder's
10
+ * `ui/accuracy-circles.ts` now re-exports from here.
11
+ *
12
+ * Future style changes only need to happen in one place.
13
+ */
14
+ /**
15
+ * Style for per-event GPS accuracy circles. Radius comes from the GPS event's
16
+ * horizontal accuracy in meters; larger circles mean lower-quality fixes.
17
+ * Filled and stroked with a highly transparent variant of the path color so
18
+ * overlapping circles remain legible without obscuring the basemap or the
19
+ * polyline drawn on top.
20
+ */
21
+ const ACCURACY_CIRCLE_FILL_OPACITY = .12;
22
+ const ACCURACY_CIRCLE_STROKE_OPACITY = .5;
23
+ const ACCURACY_CIRCLE_WEIGHT = 1;
24
+ /**
25
+ * Draw one transparent circle per sample whose `accuracy` is a finite
26
+ * positive number. Samples without a usable accuracy value are skipped so
27
+ * pre-accuracy recordings still render their polyline.
28
+ *
29
+ * Circles are added to `map` immediately. The caller is responsible for
30
+ * draw-order: invoke this BEFORE adding the polyline so the line stays
31
+ * visually on top of the circles.
32
+ *
33
+ * @param map - The Leaflet map to add circles to.
34
+ * @param samples - GPS samples to consider; non-positive / non-finite
35
+ * `accuracy` values are silently skipped.
36
+ * @param color - CSS color string used for both stroke and fill.
37
+ * @returns The created `L.Circle` instances, in the order they were added.
38
+ * Callers that track layers for cleanup can append these to their list.
39
+ */
40
+ function addAccuracyCircles(map, samples, color) {
41
+ const circles = [];
42
+ for (const sample of samples) {
43
+ if (typeof sample.accuracy !== "number" || !Number.isFinite(sample.accuracy) || sample.accuracy <= 0) continue;
44
+ const circle = L.circle([sample.lat, sample.lng], {
45
+ radius: sample.accuracy,
46
+ color,
47
+ weight: 1,
48
+ opacity: ACCURACY_CIRCLE_STROKE_OPACITY,
49
+ fillColor: color,
50
+ fillOpacity: ACCURACY_CIRCLE_FILL_OPACITY
51
+ }).addTo(map);
52
+ circles.push(circle);
53
+ }
54
+ return circles;
55
+ }
56
+ //#endregion
57
+ export { ACCURACY_CIRCLE_FILL_OPACITY, ACCURACY_CIRCLE_STROKE_OPACITY, ACCURACY_CIRCLE_WEIGHT, addAccuracyCircles };
@@ -1,2 +1,2 @@
1
- import { n as createAlignmentLerper, t as AlignmentLerper } from "../alignment-lerper-D9BeDwZX.js";
1
+ import { n as createAlignmentLerper, t as AlignmentLerper } from "../alignment-lerper-CCZJf-Xb.js";
2
2
  export { AlignmentLerper, createAlignmentLerper };
@@ -0,0 +1,2 @@
1
+ import { n as ArWorldGroupAlignmentOptions, r as enableArWorldGroupAlignment, t as ArWorldGroupAlignmentHandle } from "../ar-world-group-alignment-Dn4rQk_c.js";
2
+ export { ArWorldGroupAlignmentHandle, ArWorldGroupAlignmentOptions, enableArWorldGroupAlignment };
@@ -0,0 +1,51 @@
1
+ import { registerFrameUpdate } from "../ar/frame-loop.js";
2
+ import { t as registerSessionDisposer } from "../session-disposers-M-oashRH.js";
3
+ import { createAlignmentLerper } from "./alignment-lerper.js";
4
+ import { selectAlignmentMatrix } from "../state/app-selectors.js";
5
+ import { subscribeToSelector } from "../state/subscribe-to-selector.js";
6
+ //#region ../src/visualization/ar-world-group-alignment.ts
7
+ /**
8
+ * Begin applying the store's (lerped) alignment to `arWorldGroup`. Returns a
9
+ * handle whose `dispose()` removes both the store subscription and the
10
+ * per-frame update.
11
+ *
12
+ * Disposal is tied to the AR session lifecycle: the binding registers itself
13
+ * with the session-disposer registry that `resetWebXRState()` flushes on
14
+ * teardown, so a caller can `enableArWorldGroupAlignment(...)` once and never
15
+ * hold the handle — the binding cannot outlive the session (which is what two
16
+ * apps independently leaked: the store subscription survives a teardown because
17
+ * `clearFrameUpdates` only drops the per-frame tick). Because `initAR()` throws
18
+ * while a prior session is live, every restart passes through that flush. The
19
+ * handle is still returned for callers that want to stop alignment mid-session,
20
+ * and is idempotent + self-deregistering so a manual `dispose()` and the
21
+ * teardown flush never double-run. See
22
+ * gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-06-08-arworldgroup-alignment-session-scoped-disposal.md.
23
+ *
24
+ * Double-drive is still the caller's concern: the recorder owns its own lerper
25
+ * and must NOT also call this (it would double-lerp the same group). It does not
26
+ * call this helper, so flushing the registry on teardown never affects it.
27
+ */
28
+ function enableArWorldGroupAlignment(options) {
29
+ const lerper = createAlignmentLerper(options.arWorldGroup, options.lerpRate);
30
+ const current = selectAlignmentMatrix(options.store.getState());
31
+ if (current !== null && current !== void 0) lerper.setTarget(current);
32
+ const unsubscribe = subscribeToSelector(options.store, selectAlignmentMatrix, (matrix) => {
33
+ if (matrix !== null && matrix !== void 0) lerper.setTarget(matrix);
34
+ });
35
+ const unregister = registerFrameUpdate((dt) => {
36
+ lerper.update(dt);
37
+ });
38
+ let disposed = false;
39
+ const handle = { dispose() {
40
+ if (disposed) return;
41
+ disposed = true;
42
+ unsubscribe();
43
+ unregister();
44
+ lerper.dispose();
45
+ deregisterSessionDisposer();
46
+ } };
47
+ const deregisterSessionDisposer = registerSessionDisposer(() => handle.dispose());
48
+ return handle;
49
+ }
50
+ //#endregion
51
+ export { enableArWorldGroupAlignment };
@@ -1,2 +1,2 @@
1
- import { n as createCameraFollower, t as CameraFollower } from "../camera-follower-BMqPddxx.js";
1
+ import { n as createCameraFollower, t as CameraFollower } from "../camera-follower-B-nS1sr6.js";
2
2
  export { CameraFollower, createCameraFollower };
@@ -1,2 +1,2 @@
1
- import { n as createCss3dRendererManager, t as Css3dRendererManager } from "../css3d-renderer-manager-rgLMVAvd.js";
1
+ import { n as createCss3dRendererManager, t as Css3dRendererManager } from "../css3d-renderer-manager-EDED3nvg.js";
2
2
  export { Css3dRendererManager, createCss3dRendererManager };
@@ -0,0 +1,2 @@
1
+ import { n as worldNueToGps, t as nueToArLocal } from "../frame-conversions-D2EYjeNa.js";
2
+ export { nueToArLocal, worldNueToGps };
@@ -0,0 +1,88 @@
1
+ import { calcGpsCoords } from "../core/index.js";
2
+ import * as THREE from "three";
3
+ //#region ../src/visualization/frame-conversions.ts
4
+ /**
5
+ * `frame-conversions` — small, pure coordinate-frame helpers for the AR scene
6
+ * graph. Currently a single function, `nueToArLocal`, that converts a
7
+ * GPS-world NUE point into the AR-odometry local frame of `arWorldGroup`.
8
+ *
9
+ * Background: the scene root is GPS-world NUE; `arWorldGroup.matrix` IS the
10
+ * alignment matrix, which maps **AR-odometry NUE → GPS-world NUE**. So a
11
+ * direct child of `arWorldGroup` whose WORLD position must equal a GPS-world
12
+ * point `nue` has to store `alignment⁻¹ · nue` as its LOCAL position —
13
+ * writing raw `nue` double-applies the alignment. Getting this wrong was the
14
+ * alignment-frame bug
15
+ * (gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-gps-anchor-alignment-frame-bug.md);
16
+ * this helper centralises the conversion behind one tested, well-named seam.
17
+ *
18
+ * Design (see the plan
19
+ * gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-nue-to-ar-local-helper-plan.md):
20
+ * - **Takes the explicit alignment matrix array**, NOT `Object3D.worldToLocal`.
21
+ * `worldToLocal` would read the live, mid-lerp `matrixWorld` and break
22
+ * replay determinism; the explicit target matrix keeps the result a pure
23
+ * function of its inputs. Full rationale in the review doc
24
+ * (gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-worldtolocal-frame-helper-review.md).
25
+ * - **Positions only.** No pose/quaternion variant exists until a caller
26
+ * needs one (YAGNI). No caller currently sets a GPS-world rotation.
27
+ */
28
+ const scratchMatrix = new THREE.Matrix4();
29
+ /**
30
+ * Convert a GPS-world NUE point into the AR-local position to write into a
31
+ * direct child of `arWorldGroup`, given the current alignment matrix.
32
+ *
33
+ * Computes `alignment⁻¹ · nue`. When the result is later composed back by the
34
+ * group's matrix (the same `alignment`), the child's WORLD position equals
35
+ * `nue` exactly.
36
+ *
37
+ * @param alignment - The alignment matrix as a 16-element **column-major**
38
+ * array (AR-odometry NUE → GPS-world NUE), e.g. from `getAlignmentMatrix()`
39
+ * or `THREE.Matrix4.toArray()`. Not mutated.
40
+ * @param nue - The GPS-world NUE point `[north, up, east]` in metres. Not
41
+ * mutated.
42
+ * @param out - Optional target vector to write into and return (pass a reused
43
+ * scratch to avoid allocation on hot paths). Defaults to a fresh
44
+ * `THREE.Vector3`.
45
+ * @returns `out`, set to the AR-local position.
46
+ */
47
+ function nueToArLocal(alignment, nue, out = new THREE.Vector3()) {
48
+ scratchMatrix.fromArray(alignment).invert();
49
+ return out.set(nue[0], nue[1], nue[2]).applyMatrix4(scratchMatrix);
50
+ }
51
+ /**
52
+ * Convert an object's **GPS-world NUE world position** back into a GPS
53
+ * coordinate, for the GPS-anchor bootstrap that medians where an object was
54
+ * actually placed (mirrors the C# `DetermineAndStoreGpsWorldPose`, which
55
+ * medians the object's world pose and converts via `zero.CalcGpsCoordsOf`).
56
+ *
57
+ * Precondition: `worldNue` must already be in GPS-world NUE — i.e. the object
58
+ * lives under an `arWorldGroup` whose `.matrix` carries the alignment (see
59
+ * `enableArWorldGroupAlignment`), so `object3D.getWorldPosition()` is the
60
+ * GPS-world position. Sampling a pure-VIO (identity-`arWorldGroup`) world
61
+ * position would yield a wrong GPS coordinate.
62
+ *
63
+ * Altitude round-trip: `calcGpsCoords` returns only `lat`/`lon` (it derives
64
+ * them from the North/East axes and ignores Up). The Up axis (`worldNue.y`,
65
+ * NUE index 1) is carried through verbatim as `altitude`, so a later
66
+ * `calcRelativeCoordsInMeters(zero, result, result.altitude, 0)` reproduces the
67
+ * same Up value — keeping the steady-state vertical target consistent with
68
+ * where the object was sampled.
69
+ *
70
+ * @param worldNue - The object's world position in GPS-world NUE metres
71
+ * (`x = North`, `y = Up`, `z = East`). A `THREE.Vector3` satisfies this.
72
+ * @param zero - The GPS zero reference (origin for the conversion).
73
+ * @returns The GPS coordinate of `worldNue`, with `altitude = worldNue.y`.
74
+ */
75
+ function worldNueToGps(worldNue, zero) {
76
+ const gps = calcGpsCoords(zero, [
77
+ worldNue.x,
78
+ worldNue.y,
79
+ worldNue.z
80
+ ]);
81
+ return {
82
+ lat: gps.lat,
83
+ lon: gps.lon,
84
+ altitude: worldNue.y
85
+ };
86
+ }
87
+ //#endregion
88
+ export { nueToArLocal, worldNueToGps };
@@ -0,0 +1,2 @@
1
+ import { i as isSphereInCameraFrustum, n as isObjectInCameraFrustum, r as isPointInCameraFrustum, t as buildCameraFrustum } from "../frustum-visibility-DGnJqls0.js";
2
+ export { buildCameraFrustum, isObjectInCameraFrustum, isPointInCameraFrustum, isSphereInCameraFrustum };
@@ -0,0 +1,139 @@
1
+ import * as THREE from "three";
2
+ //#region ../src/visualization/frustum-visibility.ts
3
+ /**
4
+ * Frustum-visibility predicates used by GPS-anchored components and any other
5
+ * caller that needs to know whether a world-space point/sphere/object is
6
+ * currently visible to a camera. Three.js's `Frustum` has the primitives we
7
+ * need; this module wraps them so:
8
+ *
9
+ * - All three predicates share a module-level scratch `Frustum`/`Matrix4`
10
+ * so a single frame's worth of checks pays the matrix multiply once.
11
+ * - Each predicate accepts an optional `frustum` parameter for callers that
12
+ * already have one built (e.g. via `buildCameraFrustum`) and want to
13
+ * keep doing many checks against it without rebuilding.
14
+ *
15
+ * The Three.js convention here is that `Frustum.containsPoint` is strict
16
+ * (point must be inside all six planes) while `intersectsSphere`/
17
+ * `intersectsObject` accept overlap with any plane, which is the right
18
+ * notion for "is this object at least partially on screen".
19
+ */
20
+ const scratchMatrix = new THREE.Matrix4();
21
+ const scratchFrustum = new THREE.Frustum();
22
+ /**
23
+ * Build (or refresh) a Three.js `Frustum` from `camera`'s current world
24
+ * transform and projection matrix. If `out` is provided, the frustum is
25
+ * written into it and returned; otherwise the module-level scratch frustum
26
+ * is used. Callers MUST treat the returned frustum as immutable until they
27
+ * next call this function or use one of the predicates without an injected
28
+ * frustum (both reuse the same scratch).
29
+ */
30
+ function buildCameraFrustum(camera, out) {
31
+ scratchMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
32
+ const target = out ?? scratchFrustum;
33
+ target.setFromProjectionMatrix(scratchMatrix);
34
+ return target;
35
+ }
36
+ /**
37
+ * Strictly check whether a single world-space point lies inside all six
38
+ * frustum planes. Useful for HUD/follow logic where the caller already has
39
+ * the target world position and wants the cheapest possible check.
40
+ */
41
+ function isPointInCameraFrustum(camera, point, frustum) {
42
+ return (frustum ?? buildCameraFrustum(camera)).containsPoint(point);
43
+ }
44
+ /**
45
+ * Check whether a world-space bounding sphere intersects the camera frustum
46
+ * (returns true even for partial overlap). Useful for callers that maintain
47
+ * a precomputed bounding sphere (LOD systems, instanced meshes, future
48
+ * `ArPowerSaver`).
49
+ */
50
+ function isSphereInCameraFrustum(camera, sphere, frustum) {
51
+ return (frustum ?? buildCameraFrustum(camera)).intersectsSphere(sphere);
52
+ }
53
+ const objectScratchSphere = new THREE.Sphere();
54
+ const objectScratchBox = new THREE.Box3();
55
+ const objectScratchVec = new THREE.Vector3();
56
+ /**
57
+ * Check whether an `Object3D` is currently (at least partially) inside the
58
+ * camera frustum. The world bounding volume is resolved in tiers, cheapest
59
+ * first, so each common Three.js object type is gated by where it actually
60
+ * draws:
61
+ *
62
+ * 1. **Sprites** (`isSprite`) are screen-space billboards with no meaningful
63
+ * world geometry, so their world-space origin is tested as a point.
64
+ * 2. **Objects carrying an object-level `boundingSphere`** (`InstancedMesh`,
65
+ * `SkinnedMesh`, or any caller that pre-populates one) use that sphere,
66
+ * which reflects the instance spread / posed extent — unlike the base
67
+ * geometry sphere. It is computed lazily via `computeBoundingSphere()`
68
+ * when missing.
69
+ * 3. **Single renderables carrying a `geometry`** (`Mesh`, `Points`, `Line`,
70
+ * …) use the geometry's local bounding sphere × `matrixWorld`.
71
+ * 4. **Containers with children** (`Group`, `LOD`, …) use the world-space
72
+ * union of their descendants' bounding boxes (`Box3.setFromObject`),
73
+ * converted to a sphere.
74
+ * 5. **Truly empty / geometry-less objects** have no bounding volume and are
75
+ * treated as visible (`true`) — the conservative default for visibility
76
+ * gating.
77
+ *
78
+ * This deliberately avoids `Frustum.intersectsObject`, which unconditionally
79
+ * dereferences `object.geometry.boundingSphere` and therefore throws a
80
+ * `TypeError` for geometry-less objects such as `Group`s.
81
+ *
82
+ * Callers should ensure `object.updateMatrixWorld()` (with descendants, for
83
+ * containers: `updateMatrixWorld(true)`) has already run this frame (it
84
+ * normally has, via `renderer.render`).
85
+ */
86
+ function isObjectInCameraFrustum(camera, object, frustum) {
87
+ const f = frustum ?? buildCameraFrustum(camera);
88
+ if (object.isSprite) {
89
+ object.getWorldPosition(objectScratchVec);
90
+ return f.containsPoint(objectScratchVec);
91
+ }
92
+ if (!resolveObjectWorldSphere(object, objectScratchSphere)) return true;
93
+ return f.intersectsSphere(objectScratchSphere);
94
+ }
95
+ /**
96
+ * Resolve the world-space bounding sphere for `object` into `out`, returning
97
+ * `true` when a bounding volume was found and `false` for objects that have
98
+ * none (e.g. an empty `Group`). Resolution order mirrors the tiers documented
99
+ * on {@link isObjectInCameraFrustum}: object-level `boundingSphere`
100
+ * (`InstancedMesh`/`SkinnedMesh`), then geometry bounding sphere, then the
101
+ * world-space union of descendant bounding boxes.
102
+ */
103
+ function resolveObjectWorldSphere(object, out) {
104
+ if (tryObjectLevelSphere(object, out)) return true;
105
+ const geometry = object.geometry;
106
+ if (geometry) {
107
+ if (!geometry.boundingSphere) geometry.computeBoundingSphere();
108
+ if (geometry.boundingSphere) {
109
+ out.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld);
110
+ return true;
111
+ }
112
+ }
113
+ if (object.children.length > 0) {
114
+ objectScratchBox.setFromObject(object);
115
+ if (!objectScratchBox.isEmpty()) {
116
+ objectScratchBox.getBoundingSphere(out);
117
+ return true;
118
+ }
119
+ }
120
+ return false;
121
+ }
122
+ /**
123
+ * Tier 2 of {@link resolveObjectWorldSphere}: use an object-level
124
+ * `boundingSphere` when the object is an `InstancedMesh`/`SkinnedMesh` (whose
125
+ * sphere reflects instance spread / posed extent) or already carries a
126
+ * populated sphere. Computes it lazily via `computeBoundingSphere()` when
127
+ * missing. Writes the world-space sphere into `out` and returns `true` on
128
+ * success, `false` when no object-level sphere applies.
129
+ */
130
+ function tryObjectLevelSphere(object, out) {
131
+ const withSphere = object;
132
+ if (!(withSphere.isInstancedMesh || withSphere.isSkinnedMesh || withSphere.boundingSphere)) return false;
133
+ if (!withSphere.boundingSphere && typeof withSphere.computeBoundingSphere === "function") withSphere.computeBoundingSphere();
134
+ if (!withSphere.boundingSphere) return false;
135
+ out.copy(withSphere.boundingSphere).applyMatrix4(object.matrixWorld);
136
+ return true;
137
+ }
138
+ //#endregion
139
+ export { buildCameraFrustum, isObjectInCameraFrustum, isPointInCameraFrustum, isSphereInCameraFrustum };
@@ -0,0 +1,2 @@
1
+ import { a as GpsAnchorSamplePoint, i as GpsAnchorPhase, n as GpsAnchorMode, o as createGpsAnchor, r as GpsAnchorOptions, t as GpsAnchor } from "../gps-anchor-CISLPQIb.js";
2
+ export { GpsAnchor, GpsAnchorMode, GpsAnchorOptions, GpsAnchorPhase, GpsAnchorSamplePoint, createGpsAnchor };