@glassly/engine 0.1.0-dev.99 → 0.2.8-dev.445

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 (501) hide show
  1. package/README.md +28 -0
  2. package/build/engine.d.ts +32 -10
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +24 -16
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/dev.d.ts +9 -0
  7. package/build/facades/dev.d.ts.map +1 -1
  8. package/build/facades/dev.js +9 -0
  9. package/build/facades/dev.js.map +1 -1
  10. package/build/facades/display.d.ts +10 -3
  11. package/build/facades/display.d.ts.map +1 -1
  12. package/build/facades/display.js +22 -8
  13. package/build/facades/display.js.map +1 -1
  14. package/build/facades/displayMirror.d.ts +11 -0
  15. package/build/facades/displayMirror.d.ts.map +1 -1
  16. package/build/facades/displayMirror.js +22 -0
  17. package/build/facades/displayMirror.js.map +1 -1
  18. package/build/facades/glasses.d.ts +31 -1
  19. package/build/facades/glasses.d.ts.map +1 -1
  20. package/build/facades/glasses.js +51 -0
  21. package/build/facades/glasses.js.map +1 -1
  22. package/build/facades/memory.d.ts +0 -6
  23. package/build/facades/memory.d.ts.map +1 -1
  24. package/build/facades/memory.js +1 -9
  25. package/build/facades/memory.js.map +1 -1
  26. package/build/facades/miniapps.d.ts +3 -1
  27. package/build/facades/miniapps.d.ts.map +1 -1
  28. package/build/facades/miniapps.js +3 -1
  29. package/build/facades/miniapps.js.map +1 -1
  30. package/build/facades/pairing.d.ts.map +1 -1
  31. package/build/facades/pairing.js +5 -4
  32. package/build/facades/pairing.js.map +1 -1
  33. package/build/facades/permissions.d.ts +2 -0
  34. package/build/facades/permissions.d.ts.map +1 -1
  35. package/build/facades/permissions.js +48 -0
  36. package/build/facades/permissions.js.map +1 -1
  37. package/build/facades/phoneNotifications.d.ts +20 -9
  38. package/build/facades/phoneNotifications.d.ts.map +1 -1
  39. package/build/facades/phoneNotifications.js +33 -26
  40. package/build/facades/phoneNotifications.js.map +1 -1
  41. package/build/facades/settings.d.ts.map +1 -1
  42. package/build/facades/settings.js +1 -2
  43. package/build/facades/settings.js.map +1 -1
  44. package/build/generated/releaseMetadata.js +4 -4
  45. package/build/generated/releaseMetadata.js.map +1 -1
  46. package/build/index.d.ts +6 -4
  47. package/build/index.d.ts.map +1 -1
  48. package/build/index.js +5 -4
  49. package/build/index.js.map +1 -1
  50. package/build/react/MentraLiveOtaFlow.js +2 -2
  51. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  52. package/build/react/useMentraLiveOta.d.ts.map +1 -1
  53. package/build/react/useMentraLiveOta.js +8 -7
  54. package/build/react/useMentraLiveOta.js.map +1 -1
  55. package/build/runtime/config.d.ts +1 -1
  56. package/build/runtime/config.d.ts.map +1 -1
  57. package/build/runtime/config.js.map +1 -1
  58. package/build/services/AppRegistry.d.ts +4 -1
  59. package/build/services/AppRegistry.d.ts.map +1 -1
  60. package/build/services/AppRegistry.js +21 -1
  61. package/build/services/AppRegistry.js.map +1 -1
  62. package/build/services/BlobStore.d.ts +2 -2
  63. package/build/services/BlobStore.js +3 -3
  64. package/build/services/BlobStore.js.map +1 -1
  65. package/build/services/CloudClientService.d.ts +7 -3
  66. package/build/services/CloudClientService.d.ts.map +1 -1
  67. package/build/services/CloudClientService.js +34 -9
  68. package/build/services/CloudClientService.js.map +1 -1
  69. package/build/services/DevServerBridge.d.ts +1 -18
  70. package/build/services/DevServerBridge.d.ts.map +1 -1
  71. package/build/services/DevServerBridge.js +9 -8
  72. package/build/services/DevServerBridge.js.map +1 -1
  73. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  74. package/build/services/DeviceEventRouter.js +58 -71
  75. package/build/services/DeviceEventRouter.js.map +1 -1
  76. package/build/services/DisplayDiagnostics.d.ts +5 -0
  77. package/build/services/DisplayDiagnostics.d.ts.map +1 -0
  78. package/build/services/DisplayDiagnostics.js +105 -0
  79. package/build/services/DisplayDiagnostics.js.map +1 -0
  80. package/build/services/DisplayProcessor.d.ts +6 -7
  81. package/build/services/DisplayProcessor.d.ts.map +1 -1
  82. package/build/services/DisplayProcessor.js +17 -45
  83. package/build/services/DisplayProcessor.js.map +1 -1
  84. package/build/services/GestureDotsService.d.ts +10 -4
  85. package/build/services/GestureDotsService.d.ts.map +1 -1
  86. package/build/services/GestureDotsService.js +70 -16
  87. package/build/services/GestureDotsService.js.map +1 -1
  88. package/build/services/GlassesKeyboardInput.d.ts +77 -0
  89. package/build/services/GlassesKeyboardInput.d.ts.map +1 -0
  90. package/build/services/GlassesKeyboardInput.js +261 -0
  91. package/build/services/GlassesKeyboardInput.js.map +1 -0
  92. package/build/services/GlassesSensorService.d.ts +47 -0
  93. package/build/services/GlassesSensorService.d.ts.map +1 -0
  94. package/build/services/GlassesSensorService.js +242 -0
  95. package/build/services/GlassesSensorService.js.map +1 -0
  96. package/build/services/GlassesTouchInput.d.ts +14 -0
  97. package/build/services/GlassesTouchInput.d.ts.map +1 -0
  98. package/build/services/GlassesTouchInput.js +107 -0
  99. package/build/services/GlassesTouchInput.js.map +1 -0
  100. package/build/services/HeadingService.d.ts +0 -6
  101. package/build/services/HeadingService.d.ts.map +1 -1
  102. package/build/services/HeadingService.js +0 -27
  103. package/build/services/HeadingService.js.map +1 -1
  104. package/build/services/HotspotShutdown.d.ts.map +1 -1
  105. package/build/services/HotspotShutdown.js +2 -1
  106. package/build/services/HotspotShutdown.js.map +1 -1
  107. package/build/services/JSCrashloopReportService.js +2 -2
  108. package/build/services/JSCrashloopReportService.js.map +1 -1
  109. package/build/services/JSRouter.d.ts +6 -4
  110. package/build/services/JSRouter.d.ts.map +1 -1
  111. package/build/services/JSRouter.js +15 -13
  112. package/build/services/JSRouter.js.map +1 -1
  113. package/build/services/LocalDisplayManager.d.ts +35 -7
  114. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  115. package/build/services/LocalDisplayManager.js +105 -27
  116. package/build/services/LocalDisplayManager.js.map +1 -1
  117. package/build/services/LocalMiniappDataMigration.d.ts +1 -1
  118. package/build/services/LocalMiniappDataMigration.js +1 -1
  119. package/build/services/LocalMiniappDataMigration.js.map +1 -1
  120. package/build/services/LocalMiniappRuntime.d.ts +64 -11
  121. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  122. package/build/services/LocalMiniappRuntime.js +441 -35
  123. package/build/services/LocalMiniappRuntime.js.map +1 -1
  124. package/build/services/LocalSttFallbackCoordinator.d.ts +3 -0
  125. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  126. package/build/services/LocalSttFallbackCoordinator.js +27 -8
  127. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  128. package/build/services/MemoryStore.d.ts +8 -33
  129. package/build/services/MemoryStore.d.ts.map +1 -1
  130. package/build/services/MemoryStore.js +30 -56
  131. package/build/services/MemoryStore.js.map +1 -1
  132. package/build/services/MiniappEngine.d.ts +3 -3
  133. package/build/services/MiniappEngine.js +7 -7
  134. package/build/services/MiniappEngine.js.map +1 -1
  135. package/build/services/MiniappLauncher.d.ts +8 -0
  136. package/build/services/MiniappLauncher.d.ts.map +1 -1
  137. package/build/services/MiniappLauncher.js +19 -1
  138. package/build/services/MiniappLauncher.js.map +1 -1
  139. package/build/services/MirrorService.d.ts +24 -0
  140. package/build/services/MirrorService.d.ts.map +1 -0
  141. package/build/services/MirrorService.js +132 -0
  142. package/build/services/MirrorService.js.map +1 -0
  143. package/build/services/NativeNavigationService.d.ts +2 -3
  144. package/build/services/NativeNavigationService.d.ts.map +1 -1
  145. package/build/services/NativeNavigationService.js +24 -21
  146. package/build/services/NativeNavigationService.js.map +1 -1
  147. package/build/services/NavigationHandlers.d.ts.map +1 -1
  148. package/build/services/NavigationHandlers.js +10 -5
  149. package/build/services/NavigationHandlers.js.map +1 -1
  150. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  151. package/build/services/OtaInstallCoordinator.js +24 -23
  152. package/build/services/OtaInstallCoordinator.js.map +1 -1
  153. package/build/services/PairedGlassesLatch.d.ts +7 -0
  154. package/build/services/PairedGlassesLatch.d.ts.map +1 -0
  155. package/build/services/PairedGlassesLatch.js +38 -0
  156. package/build/services/PairedGlassesLatch.js.map +1 -0
  157. package/build/services/PhoneActivity.d.ts +23 -0
  158. package/build/services/PhoneActivity.d.ts.map +1 -0
  159. package/build/services/PhoneActivity.js +111 -0
  160. package/build/services/PhoneActivity.js.map +1 -0
  161. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -1
  162. package/build/services/PhoneCameraFovCoordinator.js +4 -3
  163. package/build/services/PhoneCameraFovCoordinator.js.map +1 -1
  164. package/build/services/PhoneNotificationsSync.d.ts +40 -6
  165. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  166. package/build/services/PhoneNotificationsSync.js +160 -84
  167. package/build/services/PhoneNotificationsSync.js.map +1 -1
  168. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  169. package/build/services/PhonePhotoCoordinator.js +6 -5
  170. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  171. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  172. package/build/services/PhoneStreamCoordinator.js +8 -7
  173. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  174. package/build/services/STTModelManager.d.ts +3 -0
  175. package/build/services/STTModelManager.d.ts.map +1 -1
  176. package/build/services/STTModelManager.js +15 -1
  177. package/build/services/STTModelManager.js.map +1 -1
  178. package/build/services/SceneRenderer.d.ts +43 -2
  179. package/build/services/SceneRenderer.d.ts.map +1 -1
  180. package/build/services/SceneRenderer.js +123 -8
  181. package/build/services/SceneRenderer.js.map +1 -1
  182. package/build/services/StreamLifecycleController.d.ts.map +1 -1
  183. package/build/services/StreamLifecycleController.js +6 -14
  184. package/build/services/StreamLifecycleController.js.map +1 -1
  185. package/build/services/TTSModelManager.js +1 -1
  186. package/build/services/TTSModelManager.js.map +1 -1
  187. package/build/services/UIRouter.d.ts +2 -2
  188. package/build/services/UIRouter.js +5 -5
  189. package/build/services/UIRouter.js.map +1 -1
  190. package/build/services/asg/galleryPathMigration.js +1 -1
  191. package/build/services/asg/galleryPathMigration.js.map +1 -1
  192. package/build/services/asg/gallerySyncService.js +2 -2
  193. package/build/services/asg/gallerySyncService.js.map +1 -1
  194. package/build/services/asg/localStorageService.d.ts.map +1 -1
  195. package/build/services/asg/localStorageService.js +3 -3
  196. package/build/services/asg/localStorageService.js.map +1 -1
  197. package/build/services/debug/DashboardOpenTrace.d.ts +32 -0
  198. package/build/services/debug/DashboardOpenTrace.d.ts.map +1 -0
  199. package/build/services/debug/DashboardOpenTrace.js +128 -0
  200. package/build/services/debug/DashboardOpenTrace.js.map +1 -0
  201. package/build/services/debug/dashboardOpenTimeline.d.ts +77 -0
  202. package/build/services/debug/dashboardOpenTimeline.d.ts.map +1 -0
  203. package/build/services/debug/dashboardOpenTimeline.js +195 -0
  204. package/build/services/debug/dashboardOpenTimeline.js.map +1 -0
  205. package/build/services/effectiveCapabilities.d.ts.map +1 -1
  206. package/build/services/effectiveCapabilities.js +25 -13
  207. package/build/services/effectiveCapabilities.js.map +1 -1
  208. package/build/services/gestureClaims.d.ts +34 -15
  209. package/build/services/gestureClaims.d.ts.map +1 -1
  210. package/build/services/gestureClaims.js +42 -21
  211. package/build/services/gestureClaims.js.map +1 -1
  212. package/build/services/manifestFields.d.ts +9 -0
  213. package/build/services/manifestFields.d.ts.map +1 -0
  214. package/build/services/manifestFields.js +26 -0
  215. package/build/services/manifestFields.js.map +1 -0
  216. package/build/services/memoryStoreInstance.d.ts +0 -10
  217. package/build/services/memoryStoreInstance.d.ts.map +1 -1
  218. package/build/services/memoryStoreInstance.js +2 -31
  219. package/build/services/memoryStoreInstance.js.map +1 -1
  220. package/build/services/mirrorSnapshot.d.ts +16 -0
  221. package/build/services/mirrorSnapshot.d.ts.map +1 -0
  222. package/build/services/mirrorSnapshot.js +102 -0
  223. package/build/services/mirrorSnapshot.js.map +1 -0
  224. package/build/services/nativeNavigationGate.d.ts +8 -0
  225. package/build/services/nativeNavigationGate.d.ts.map +1 -0
  226. package/build/services/nativeNavigationGate.js +12 -0
  227. package/build/services/nativeNavigationGate.js.map +1 -0
  228. package/build/services/notificationAppCatalog.d.ts +7 -0
  229. package/build/services/notificationAppCatalog.d.ts.map +1 -0
  230. package/build/services/notificationAppCatalog.js +16 -0
  231. package/build/services/notificationAppCatalog.js.map +1 -0
  232. package/build/services/notifications/notificationPolicy.d.ts +55 -0
  233. package/build/services/notifications/notificationPolicy.d.ts.map +1 -0
  234. package/build/services/notifications/notificationPolicy.js +41 -0
  235. package/build/services/notifications/notificationPolicy.js.map +1 -0
  236. package/build/services/pages/OverlayPages.d.ts +26 -0
  237. package/build/services/pages/OverlayPages.d.ts.map +1 -0
  238. package/build/services/pages/OverlayPages.js +80 -0
  239. package/build/services/pages/OverlayPages.js.map +1 -0
  240. package/build/services/pages/PageHost.d.ts +32 -0
  241. package/build/services/pages/PageHost.d.ts.map +1 -0
  242. package/build/services/pages/PageHost.js +99 -0
  243. package/build/services/pages/PageHost.js.map +1 -0
  244. package/build/services/pages/overlayApps.d.ts +25 -0
  245. package/build/services/pages/overlayApps.d.ts.map +1 -0
  246. package/build/services/pages/overlayApps.js +66 -0
  247. package/build/services/pages/overlayApps.js.map +1 -0
  248. package/build/services/pages/pageEligibility.d.ts +9 -0
  249. package/build/services/pages/pageEligibility.d.ts.map +1 -0
  250. package/build/services/pages/pageEligibility.js +27 -0
  251. package/build/services/pages/pageEligibility.js.map +1 -0
  252. package/build/services/settingsBridge.d.ts +58 -0
  253. package/build/services/settingsBridge.d.ts.map +1 -0
  254. package/build/services/settingsBridge.js +90 -0
  255. package/build/services/settingsBridge.js.map +1 -0
  256. package/build/services/speakerProfileStoreInstance.js +1 -1
  257. package/build/services/speakerProfileStoreInstance.js.map +1 -1
  258. package/build/services/uiShim.d.ts +1 -1
  259. package/build/services/uiShim.js +4 -4
  260. package/build/services/uiShim.js.map +1 -1
  261. package/build/stores/appVisibility.d.ts +28 -0
  262. package/build/stores/appVisibility.d.ts.map +1 -1
  263. package/build/stores/appVisibility.js +34 -0
  264. package/build/stores/appVisibility.js.map +1 -1
  265. package/build/stores/apps.d.ts +13 -0
  266. package/build/stores/apps.d.ts.map +1 -1
  267. package/build/stores/apps.js +66 -12
  268. package/build/stores/apps.js.map +1 -1
  269. package/build/stores/bluetoothSettingKeys.js +1 -1
  270. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  271. package/build/stores/display.d.ts +3 -3
  272. package/build/stores/display.d.ts.map +1 -1
  273. package/build/stores/display.js +16 -4
  274. package/build/stores/display.js.map +1 -1
  275. package/build/stores/glasses.d.ts.map +1 -1
  276. package/build/stores/glasses.js +14 -1
  277. package/build/stores/glasses.js.map +1 -1
  278. package/build/stores/settings.d.ts +1 -0
  279. package/build/stores/settings.d.ts.map +1 -1
  280. package/build/stores/settings.js +170 -44
  281. package/build/stores/settings.js.map +1 -1
  282. package/build/types/applet.d.ts +14 -1
  283. package/build/types/applet.d.ts.map +1 -1
  284. package/build/types/applet.js.map +1 -1
  285. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
  286. package/build/types/capabilities/even-realities-g2.js +5 -0
  287. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  288. package/build/types/capabilities/simulated-glasses.d.ts +1 -1
  289. package/build/types/capabilities/simulated-glasses.d.ts.map +1 -1
  290. package/build/types/capabilities/simulated-glasses.js +7 -4
  291. package/build/types/capabilities/simulated-glasses.js.map +1 -1
  292. package/build/types/hardware.d.ts +20 -1
  293. package/build/types/hardware.d.ts.map +1 -1
  294. package/build/types/hardware.js +2 -0
  295. package/build/types/hardware.js.map +1 -1
  296. package/build/types/index.d.ts +1 -0
  297. package/build/types/index.d.ts.map +1 -1
  298. package/build/types/index.js +1 -0
  299. package/build/types/index.js.map +1 -1
  300. package/build/utils/debouncedPatch.d.ts +3 -1
  301. package/build/utils/debouncedPatch.d.ts.map +1 -1
  302. package/build/utils/debouncedPatch.js +6 -3
  303. package/build/utils/debouncedPatch.js.map +1 -1
  304. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  305. package/build/utils/devMiniappLaunch.js +4 -3
  306. package/build/utils/devMiniappLaunch.js.map +1 -1
  307. package/build/utils/display/measurer/TextMeasurer.d.ts +2 -159
  308. package/build/utils/display/measurer/TextMeasurer.d.ts.map +1 -1
  309. package/build/utils/display/measurer/TextMeasurer.js +2 -272
  310. package/build/utils/display/measurer/TextMeasurer.js.map +1 -1
  311. package/build/utils/display/measurer/script-detection.d.ts +1 -52
  312. package/build/utils/display/measurer/script-detection.d.ts.map +1 -1
  313. package/build/utils/display/measurer/script-detection.js +2 -183
  314. package/build/utils/display/measurer/script-detection.js.map +1 -1
  315. package/build/utils/display/pixelArt.d.ts +3 -0
  316. package/build/utils/display/pixelArt.d.ts.map +1 -0
  317. package/build/utils/display/pixelArt.js +3 -0
  318. package/build/utils/display/pixelArt.js.map +1 -0
  319. package/build/utils/display/profiles/g1.d.ts +1 -32
  320. package/build/utils/display/profiles/g1.d.ts.map +1 -1
  321. package/build/utils/display/profiles/g1.js +2 -212
  322. package/build/utils/display/profiles/g1.js.map +1 -1
  323. package/build/utils/display/profiles/g2.d.ts +1 -12
  324. package/build/utils/display/profiles/g2.d.ts.map +1 -1
  325. package/build/utils/display/profiles/g2.js +2 -33
  326. package/build/utils/display/profiles/g2.js.map +1 -1
  327. package/build/utils/display/profiles/nex.d.ts +1 -9
  328. package/build/utils/display/profiles/nex.d.ts.map +1 -1
  329. package/build/utils/display/profiles/nex.js +2 -149
  330. package/build/utils/display/profiles/nex.js.map +1 -1
  331. package/build/utils/display/profiles/types.d.ts +1 -102
  332. package/build/utils/display/profiles/types.d.ts.map +1 -1
  333. package/build/utils/display/profiles/types.js.map +1 -1
  334. package/build/utils/display/profiles/z100.d.ts +1 -32
  335. package/build/utils/display/profiles/z100.d.ts.map +1 -1
  336. package/build/utils/display/profiles/z100.js +2 -181
  337. package/build/utils/display/profiles/z100.js.map +1 -1
  338. package/build/utils/display/scene/degrade.d.ts.map +1 -1
  339. package/build/utils/display/scene/degrade.js +4 -1
  340. package/build/utils/display/scene/degrade.js.map +1 -1
  341. package/build/utils/display/scene/differ.d.ts +13 -1
  342. package/build/utils/display/scene/differ.d.ts.map +1 -1
  343. package/build/utils/display/scene/differ.js +22 -0
  344. package/build/utils/display/scene/differ.js.map +1 -1
  345. package/build/utils/display/scene/index.d.ts +1 -1
  346. package/build/utils/display/scene/index.d.ts.map +1 -1
  347. package/build/utils/display/scene/index.js.map +1 -1
  348. package/build/utils/display/scene/process.d.ts.map +1 -1
  349. package/build/utils/display/scene/process.js +183 -11
  350. package/build/utils/display/scene/process.js.map +1 -1
  351. package/build/utils/display/scene/store.d.ts +1 -1
  352. package/build/utils/display/scene/store.d.ts.map +1 -1
  353. package/build/utils/display/scene/store.js +0 -0
  354. package/build/utils/display/scene/store.js.map +1 -1
  355. package/build/utils/display/scene/svgPath.d.ts +9 -0
  356. package/build/utils/display/scene/svgPath.d.ts.map +1 -0
  357. package/build/utils/display/scene/svgPath.js +167 -0
  358. package/build/utils/display/scene/svgPath.js.map +1 -0
  359. package/build/utils/display/scene/types.d.ts +2 -142
  360. package/build/utils/display/scene/types.d.ts.map +1 -1
  361. package/build/utils/display/scene/types.js +0 -0
  362. package/build/utils/display/scene/types.js.map +1 -1
  363. package/build/utils/display/wrapper/TextWrapper.d.ts +1 -101
  364. package/build/utils/display/wrapper/TextWrapper.d.ts.map +1 -1
  365. package/build/utils/display/wrapper/TextWrapper.js +2 -470
  366. package/build/utils/display/wrapper/TextWrapper.js.map +1 -1
  367. package/build/utils/display/wrapper/types.d.ts +2 -74
  368. package/build/utils/display/wrapper/types.d.ts.map +1 -1
  369. package/build/utils/display/wrapper/types.js +2 -10
  370. package/build/utils/display/wrapper/types.js.map +1 -1
  371. package/build/utils/hardware/hardware.js +1 -1
  372. package/build/utils/hardware/hardware.js.map +1 -1
  373. package/build/utils/timers/timers.d.ts.map +1 -1
  374. package/build/utils/timers/timers.js +26 -4
  375. package/build/utils/timers/timers.js.map +1 -1
  376. package/package.json +8 -8
  377. package/src/engine.ts +24 -15
  378. package/src/facades/dev.ts +10 -0
  379. package/src/facades/display.ts +21 -8
  380. package/src/facades/displayMirror.ts +29 -0
  381. package/src/facades/glasses.ts +61 -1
  382. package/src/facades/memory.ts +1 -12
  383. package/src/facades/miniapps.ts +4 -1
  384. package/src/facades/pairing.ts +8 -6
  385. package/src/facades/permissions.ts +46 -0
  386. package/src/facades/phoneNotifications.ts +45 -26
  387. package/src/facades/settings.ts +1 -2
  388. package/src/generated/releaseMetadata.ts +4 -4
  389. package/src/index.ts +6 -9
  390. package/src/react/MentraLiveOtaFlow.tsx +2 -2
  391. package/src/react/useMentraLiveOta.ts +14 -8
  392. package/src/runtime/config.ts +1 -1
  393. package/src/services/AppRegistry.ts +43 -7
  394. package/src/services/BlobStore.ts +3 -3
  395. package/src/services/CloudClientService.ts +37 -10
  396. package/src/services/DevServerBridge.ts +12 -10
  397. package/src/services/DeviceEventRouter.ts +73 -74
  398. package/src/services/DisplayDiagnostics.ts +107 -0
  399. package/src/services/DisplayProcessor.ts +20 -55
  400. package/src/services/GestureDotsService.ts +77 -16
  401. package/src/services/GlassesKeyboardInput.ts +306 -0
  402. package/src/services/GlassesSensorService.ts +233 -0
  403. package/src/services/GlassesTouchInput.ts +128 -0
  404. package/src/services/HeadingService.ts +0 -24
  405. package/src/services/HotspotShutdown.ts +3 -1
  406. package/src/services/JSCrashloopReportService.ts +2 -2
  407. package/src/services/JSRouter.ts +29 -14
  408. package/src/services/LocalDisplayManager.ts +112 -26
  409. package/src/services/LocalMiniappDataMigration.ts +1 -1
  410. package/src/services/LocalMiniappRuntime.ts +500 -43
  411. package/src/services/LocalSttFallbackCoordinator.ts +28 -7
  412. package/src/services/MemoryStore.ts +32 -80
  413. package/src/services/MiniappEngine.ts +7 -7
  414. package/src/services/MiniappLauncher.ts +25 -1
  415. package/src/services/MirrorService.ts +139 -0
  416. package/src/services/NativeNavigationService.ts +29 -25
  417. package/src/services/NavigationHandlers.ts +10 -5
  418. package/src/services/OtaInstallCoordinator.ts +36 -34
  419. package/src/services/PairedGlassesLatch.ts +39 -0
  420. package/src/services/PhoneActivity.ts +116 -0
  421. package/src/services/PhoneCameraFovCoordinator.ts +6 -4
  422. package/src/services/PhoneNotificationsSync.ts +185 -103
  423. package/src/services/PhonePhotoCoordinator.ts +9 -10
  424. package/src/services/PhoneStreamCoordinator.ts +13 -11
  425. package/src/services/STTModelManager.ts +15 -1
  426. package/src/services/SceneRenderer.ts +148 -10
  427. package/src/services/StreamLifecycleController.ts +9 -7
  428. package/src/services/TTSModelManager.ts +1 -1
  429. package/src/services/UIRouter.ts +5 -5
  430. package/src/services/asg/galleryPathMigration.ts +1 -1
  431. package/src/services/asg/gallerySyncService.ts +2 -2
  432. package/src/services/asg/localStorageService.ts +3 -3
  433. package/src/services/debug/DashboardOpenTrace.ts +153 -0
  434. package/src/services/debug/dashboardOpenTimeline.ts +237 -0
  435. package/src/services/effectiveCapabilities.ts +32 -13
  436. package/src/services/gestureClaims.ts +72 -23
  437. package/src/services/manifestFields.ts +23 -0
  438. package/src/services/memoryStoreInstance.ts +2 -34
  439. package/src/services/mirrorSnapshot.ts +116 -0
  440. package/src/services/nativeNavigationGate.ts +14 -0
  441. package/src/services/notificationAppCatalog.ts +21 -0
  442. package/src/services/notifications/notificationPolicy.ts +87 -0
  443. package/src/services/pages/OverlayPages.ts +86 -0
  444. package/src/services/pages/PageHost.ts +116 -0
  445. package/src/services/pages/overlayApps.ts +72 -0
  446. package/src/services/pages/pageEligibility.ts +30 -0
  447. package/src/services/settingsBridge.ts +111 -0
  448. package/src/services/speakerProfileStoreInstance.ts +1 -1
  449. package/src/services/uiShim.ts +4 -4
  450. package/src/stores/appVisibility.ts +45 -0
  451. package/src/stores/apps.ts +110 -32
  452. package/src/stores/bluetoothSettingKeys.ts +1 -1
  453. package/src/stores/display.ts +18 -7
  454. package/src/stores/glasses.ts +15 -1
  455. package/src/stores/settings.ts +175 -51
  456. package/src/types/applet.ts +14 -1
  457. package/src/types/capabilities/even-realities-g2.ts +6 -0
  458. package/src/types/capabilities/simulated-glasses.ts +8 -4
  459. package/src/types/hardware.ts +34 -1
  460. package/src/types/index.ts +1 -0
  461. package/src/utils/debouncedPatch.ts +8 -4
  462. package/src/utils/devMiniappLaunch.ts +5 -3
  463. package/src/utils/display/measurer/TextMeasurer.ts +3 -346
  464. package/src/utils/display/measurer/script-detection.ts +10 -218
  465. package/src/utils/display/pixelArt.ts +3 -0
  466. package/src/utils/display/profiles/g1.ts +2 -236
  467. package/src/utils/display/profiles/g2.ts +2 -39
  468. package/src/utils/display/profiles/nex.ts +2 -166
  469. package/src/utils/display/profiles/types.ts +9 -132
  470. package/src/utils/display/profiles/z100.ts +2 -198
  471. package/src/utils/display/scene/degrade.ts +4 -1
  472. package/src/utils/display/scene/differ.ts +25 -1
  473. package/src/utils/display/scene/index.ts +5 -0
  474. package/src/utils/display/scene/process.ts +196 -13
  475. package/src/utils/display/scene/store.ts +0 -0
  476. package/src/utils/display/scene/svgPath.ts +163 -0
  477. package/src/utils/display/scene/types.ts +0 -0
  478. package/src/utils/display/wrapper/TextWrapper.ts +2 -546
  479. package/src/utils/display/wrapper/types.ts +3 -102
  480. package/src/utils/hardware/hardware.ts +1 -1
  481. package/src/utils/timers/timers.ts +32 -4
  482. package/build/services/CustomDashboardModel.d.ts +0 -48
  483. package/build/services/CustomDashboardModel.d.ts.map +0 -1
  484. package/build/services/CustomDashboardModel.js +0 -73
  485. package/build/services/CustomDashboardModel.js.map +0 -1
  486. package/build/services/CustomDashboardService.d.ts +0 -63
  487. package/build/services/CustomDashboardService.d.ts.map +0 -1
  488. package/build/services/CustomDashboardService.js +0 -576
  489. package/build/services/CustomDashboardService.js.map +0 -1
  490. package/build/services/DashboardCoordinator.d.ts +0 -26
  491. package/build/services/DashboardCoordinator.d.ts.map +0 -1
  492. package/build/services/DashboardCoordinator.js +0 -125
  493. package/build/services/DashboardCoordinator.js.map +0 -1
  494. package/build/services/iosNotificationAppCatalog.d.ts +0 -13
  495. package/build/services/iosNotificationAppCatalog.d.ts.map +0 -1
  496. package/build/services/iosNotificationAppCatalog.js +0 -28
  497. package/build/services/iosNotificationAppCatalog.js.map +0 -1
  498. package/src/services/CustomDashboardModel.ts +0 -83
  499. package/src/services/CustomDashboardService.ts +0 -598
  500. package/src/services/DashboardCoordinator.ts +0 -121
  501. package/src/services/iosNotificationAppCatalog.ts +0 -39
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Stage timeline of one dashboard open, for the dashboard open timing debug
3
+ * setting. The clock starts when the opening gesture reaches the phone (or
4
+ * when native received it, if the event carries that time); every stage is
5
+ * stamped as ms after that. No I/O: DashboardOpenTrace.ts feeds it.
6
+ */
7
+ /** In order. `js` exists only when the gesture carried a native receipt time; `settled` needs CFW rev 27+. */
8
+ export const DASHBOARD_OPEN_STAGES = ["js", "showView", "slot", "frame", "presented", "settled"];
9
+ /** The stages every open must pass; the first one missing is where it stalled. */
10
+ const REQUIRED_STAGES = ["showView", "slot", "frame", "presented"];
11
+ const STAGE_LABELS = {
12
+ js: "js",
13
+ showView: "showView",
14
+ slot: "slot",
15
+ frame: "frame",
16
+ presented: "shown",
17
+ settled: "settled",
18
+ };
19
+ /** A gesture not answered with showView by then did not open the dashboard; a later stage missing by then stalled. */
20
+ export const OPEN_WINDOW_MS = 15_000;
21
+ /** How long to wait for the settle report once the frame was presented. */
22
+ export const SETTLE_GRACE_MS = 2_000;
23
+ /** A native receipt time further back than this is on some other clock. */
24
+ const MAX_NATIVE_LAG_MS = 60_000;
25
+ export class DashboardOpenTimeline {
26
+ deps;
27
+ active = null;
28
+ constructor(deps) {
29
+ this.deps = deps;
30
+ }
31
+ get tracing() {
32
+ return this.active !== null;
33
+ }
34
+ /** The seq of the open's first overlay frame, once sent. */
35
+ get frameSeq() {
36
+ return this.active?.frameSeq ?? null;
37
+ }
38
+ /**
39
+ * An opening gesture reached the phone. While an earlier one still waits
40
+ * for showView, the same gesture again only counts as a repeat (the clock
41
+ * keeps the first); a different gesture restarts the clock. Ignored once
42
+ * the open is under way.
43
+ */
44
+ trigger(trigger, context, nativeAt) {
45
+ const active = this.active;
46
+ if (active) {
47
+ if (active.trace.stages.showView !== undefined)
48
+ return;
49
+ if (active.trace.trigger === trigger) {
50
+ active.trace.repeats++;
51
+ return;
52
+ }
53
+ this.cancel();
54
+ }
55
+ const now = this.deps.now();
56
+ const lag = nativeAt === undefined ? -1 : now - nativeAt;
57
+ const native = nativeAt !== undefined && lag >= 0 && lag <= MAX_NATIVE_LAG_MS;
58
+ this.active = {
59
+ trace: { trigger, repeats: 0, stages: native ? { js: lag } : {}, appUpMs: null, ...context },
60
+ startedAt: native ? nativeAt : now,
61
+ frameSeq: null,
62
+ timer: this.deps.setTimeout(() => this.expire(), OPEN_WINDOW_MS),
63
+ };
64
+ }
65
+ /** An overlay app asked for the dashboard view; `appConnectedAt` is when it connected, if known. */
66
+ showViewRequested(appConnectedAt) {
67
+ const active = this.active;
68
+ if (!active || active.trace.stages.showView !== undefined)
69
+ return;
70
+ active.trace.appUpMs = appConnectedAt === null ? null : active.startedAt - appConnectedAt;
71
+ this.stamp("showView");
72
+ }
73
+ slotGranted() {
74
+ if (this.active?.trace.stages.showView !== undefined)
75
+ this.stamp("slot");
76
+ }
77
+ /** The first overlay frame after the grant left for the glasses. */
78
+ frameSent(seq) {
79
+ const active = this.active;
80
+ if (!active || active.trace.stages.slot === undefined || active.frameSeq !== null)
81
+ return;
82
+ active.frameSeq = seq;
83
+ this.stamp("frame");
84
+ }
85
+ /**
86
+ * The glasses reported a frame. A later frame presented folds ours in; ours
87
+ * not presented is noted, and the window still waits for a later one.
88
+ */
89
+ framePresented(seq, presented, reason) {
90
+ const active = this.active;
91
+ if (!active || active.frameSeq === null || seq < active.frameSeq)
92
+ return;
93
+ if (active.trace.stages.presented !== undefined)
94
+ return;
95
+ if (!presented) {
96
+ if (seq === active.frameSeq)
97
+ active.trace.reason = reason ?? "not presented";
98
+ return;
99
+ }
100
+ this.stamp("presented");
101
+ this.deps.clearTimeout(active.timer);
102
+ active.timer = this.deps.setTimeout(() => this.finish(), SETTLE_GRACE_MS);
103
+ }
104
+ /** The glasses finished the animations of frame `seq` (and every frame before it). */
105
+ frameSettled(seq) {
106
+ const active = this.active;
107
+ if (!active || active.frameSeq === null || seq < active.frameSeq)
108
+ return;
109
+ this.stamp("presented");
110
+ this.stamp("settled");
111
+ this.finish();
112
+ }
113
+ cancel() {
114
+ if (!this.active)
115
+ return;
116
+ this.deps.clearTimeout(this.active.timer);
117
+ this.active = null;
118
+ }
119
+ stamp(stage) {
120
+ const active = this.active;
121
+ if (!active || active.trace.stages[stage] !== undefined)
122
+ return;
123
+ active.trace.stages[stage] = this.deps.now() - active.startedAt;
124
+ }
125
+ expire() {
126
+ const active = this.active;
127
+ if (!active)
128
+ return;
129
+ if (active.trace.stages.showView === undefined) {
130
+ this.active = null;
131
+ return;
132
+ }
133
+ this.finish();
134
+ }
135
+ finish() {
136
+ const active = this.active;
137
+ if (!active)
138
+ return;
139
+ this.deps.clearTimeout(active.timer);
140
+ this.active = null;
141
+ const { trace } = active;
142
+ const stalledAt = REQUIRED_STAGES.find((stage) => trace.stages[stage] === undefined);
143
+ if (stalledAt)
144
+ trace.stalledAt = stalledAt;
145
+ else
146
+ delete trace.reason;
147
+ this.deps.onFinished(trace);
148
+ }
149
+ }
150
+ /** Notification title and body: the headline, the stage timeline, then the context that separates cold opens from warm ones. */
151
+ export function formatDashboardOpenTrace(trace) {
152
+ const shown = trace.stages.presented;
153
+ const title = trace.stalledAt === undefined && shown !== undefined
154
+ ? `Dashboard opened in ${formatMs(shown)} ms`
155
+ : `Dashboard open stalled before ${STAGE_LABELS[trace.stalledAt ?? "presented"]}`;
156
+ const gesture = trace.repeats > 0 ? `${trace.trigger} ×${trace.repeats + 1}` : trace.trigger;
157
+ const timeline = [`${gesture} 0`];
158
+ for (const stage of DASHBOARD_OPEN_STAGES) {
159
+ const at = trace.stages[stage];
160
+ if (at !== undefined)
161
+ timeline.push(`${STAGE_LABELS[stage]} +${formatMs(at)}`);
162
+ }
163
+ const context = [
164
+ trace.sinceLastOpenMs === null
165
+ ? "first open this session"
166
+ : `last open ${formatDuration(trace.sinceLastOpenMs)} ago`,
167
+ ];
168
+ if (trace.appUpMs !== null) {
169
+ context.push(trace.appUpMs < 0
170
+ ? `dashboard app connected ${formatDuration(-trace.appUpMs)} after the gesture`
171
+ : `dashboard app up ${formatDuration(trace.appUpMs)}`);
172
+ }
173
+ if (trace.glassesUpMs !== null)
174
+ context.push(`glasses up ${formatDuration(trace.glassesUpMs)}`);
175
+ const lines = [timeline.join(" · "), context.join(" · ")];
176
+ if (trace.reason)
177
+ lines.push(`glasses: ${trace.reason}`);
178
+ return { title, body: lines.join("\n") };
179
+ }
180
+ /** 5210 → "5,210". Hand-rolled so it doesn't depend on the runtime's Intl. */
181
+ function formatMs(ms) {
182
+ return String(Math.round(ms)).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
183
+ }
184
+ function formatDuration(ms) {
185
+ if (ms < 1_000)
186
+ return `${Math.round(ms)} ms`;
187
+ if (ms < 60_000)
188
+ return `${Math.round(ms / 1_000)} s`;
189
+ if (ms < 3_600_000)
190
+ return `${Math.round(ms / 60_000)} min`;
191
+ const hours = Math.floor(ms / 3_600_000);
192
+ const minutes = Math.round((ms % 3_600_000) / 60_000);
193
+ return minutes > 0 ? `${hours} h ${minutes} min` : `${hours} h`;
194
+ }
195
+ //# sourceMappingURL=dashboardOpenTimeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboardOpenTimeline.js","sourceRoot":"","sources":["../../../src/services/debug/dashboardOpenTimeline.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Stage timeline of one dashboard open, for the dashboard open timing debug\n * setting. The clock starts when the opening gesture reaches the phone (or\n * when native received it, if the event carries that time); every stage is\n * stamped as ms after that. No I/O: DashboardOpenTrace.ts feeds it.\n */\n\nexport type DashboardOpenTrigger = \"head-up\" | \"double-tap\"\n\n/** In order. `js` exists only when the gesture carried a native receipt time; `settled` needs CFW rev 27+. */\nexport const DASHBOARD_OPEN_STAGES = [\"js\", \"showView\", \"slot\", \"frame\", \"presented\", \"settled\"] as const\nexport type DashboardOpenStage = (typeof DASHBOARD_OPEN_STAGES)[number]\n\n/** The stages every open must pass; the first one missing is where it stalled. */\nconst REQUIRED_STAGES: readonly DashboardOpenStage[] = [\"showView\", \"slot\", \"frame\", \"presented\"]\n\nconst STAGE_LABELS: Record<DashboardOpenStage, string> = {\n js: \"js\",\n showView: \"showView\",\n slot: \"slot\",\n frame: \"frame\",\n presented: \"shown\",\n settled: \"settled\",\n}\n\nexport interface DashboardOpenTrace {\n trigger: DashboardOpenTrigger\n /** Further triggers of the same kind before the dashboard asked for the view. */\n repeats: number\n stages: Partial<Record<DashboardOpenStage, number>>\n /** The first required stage that never arrived; absent once the glasses presented the frame. */\n stalledAt?: DashboardOpenStage\n /** What the glasses said when they reported the frame not presented. */\n reason?: string\n /** Since the overlay slot was last granted; null when it hasn't been this session. */\n sinceLastOpenMs: number | null\n /** How long the requesting overlay app had been connected at the gesture; negative when it connected after. */\n appUpMs: number | null\n /** How long the glasses had been connected at the gesture. */\n glassesUpMs: number | null\n}\n\nexport interface DashboardOpenTimelineDeps {\n now(): number\n setTimeout: (callback: () => void, ms: number) => number\n clearTimeout: (handle: number) => void\n /** A finished open. A gesture the dashboard never answered with showView is dropped silently. */\n onFinished(trace: DashboardOpenTrace): void\n}\n\n/** A gesture not answered with showView by then did not open the dashboard; a later stage missing by then stalled. */\nexport const OPEN_WINDOW_MS = 15_000\n/** How long to wait for the settle report once the frame was presented. */\nexport const SETTLE_GRACE_MS = 2_000\n/** A native receipt time further back than this is on some other clock. */\nconst MAX_NATIVE_LAG_MS = 60_000\n\ninterface ActiveOpen {\n trace: DashboardOpenTrace\n startedAt: number\n frameSeq: number | null\n timer: number\n}\n\nexport class DashboardOpenTimeline {\n private active: ActiveOpen | null = null\n\n constructor(private readonly deps: DashboardOpenTimelineDeps) {}\n\n get tracing(): boolean {\n return this.active !== null\n }\n\n /** The seq of the open's first overlay frame, once sent. */\n get frameSeq(): number | null {\n return this.active?.frameSeq ?? null\n }\n\n /**\n * An opening gesture reached the phone. While an earlier one still waits\n * for showView, the same gesture again only counts as a repeat (the clock\n * keeps the first); a different gesture restarts the clock. Ignored once\n * the open is under way.\n */\n trigger(\n trigger: DashboardOpenTrigger,\n context: {sinceLastOpenMs: number | null; glassesUpMs: number | null},\n nativeAt?: number,\n ): void {\n const active = this.active\n if (active) {\n if (active.trace.stages.showView !== undefined) return\n if (active.trace.trigger === trigger) {\n active.trace.repeats++\n return\n }\n this.cancel()\n }\n const now = this.deps.now()\n const lag = nativeAt === undefined ? -1 : now - nativeAt\n const native = nativeAt !== undefined && lag >= 0 && lag <= MAX_NATIVE_LAG_MS\n this.active = {\n trace: {trigger, repeats: 0, stages: native ? {js: lag} : {}, appUpMs: null, ...context},\n startedAt: native ? nativeAt : now,\n frameSeq: null,\n timer: this.deps.setTimeout(() => this.expire(), OPEN_WINDOW_MS),\n }\n }\n\n /** An overlay app asked for the dashboard view; `appConnectedAt` is when it connected, if known. */\n showViewRequested(appConnectedAt: number | null): void {\n const active = this.active\n if (!active || active.trace.stages.showView !== undefined) return\n active.trace.appUpMs = appConnectedAt === null ? null : active.startedAt - appConnectedAt\n this.stamp(\"showView\")\n }\n\n slotGranted(): void {\n if (this.active?.trace.stages.showView !== undefined) this.stamp(\"slot\")\n }\n\n /** The first overlay frame after the grant left for the glasses. */\n frameSent(seq: number): void {\n const active = this.active\n if (!active || active.trace.stages.slot === undefined || active.frameSeq !== null) return\n active.frameSeq = seq\n this.stamp(\"frame\")\n }\n\n /**\n * The glasses reported a frame. A later frame presented folds ours in; ours\n * not presented is noted, and the window still waits for a later one.\n */\n framePresented(seq: number, presented: boolean, reason?: string): void {\n const active = this.active\n if (!active || active.frameSeq === null || seq < active.frameSeq) return\n if (active.trace.stages.presented !== undefined) return\n if (!presented) {\n if (seq === active.frameSeq) active.trace.reason = reason ?? \"not presented\"\n return\n }\n this.stamp(\"presented\")\n this.deps.clearTimeout(active.timer)\n active.timer = this.deps.setTimeout(() => this.finish(), SETTLE_GRACE_MS)\n }\n\n /** The glasses finished the animations of frame `seq` (and every frame before it). */\n frameSettled(seq: number): void {\n const active = this.active\n if (!active || active.frameSeq === null || seq < active.frameSeq) return\n this.stamp(\"presented\")\n this.stamp(\"settled\")\n this.finish()\n }\n\n cancel(): void {\n if (!this.active) return\n this.deps.clearTimeout(this.active.timer)\n this.active = null\n }\n\n private stamp(stage: DashboardOpenStage): void {\n const active = this.active\n if (!active || active.trace.stages[stage] !== undefined) return\n active.trace.stages[stage] = this.deps.now() - active.startedAt\n }\n\n private expire(): void {\n const active = this.active\n if (!active) return\n if (active.trace.stages.showView === undefined) {\n this.active = null\n return\n }\n this.finish()\n }\n\n private finish(): void {\n const active = this.active\n if (!active) return\n this.deps.clearTimeout(active.timer)\n this.active = null\n const {trace} = active\n const stalledAt = REQUIRED_STAGES.find((stage) => trace.stages[stage] === undefined)\n if (stalledAt) trace.stalledAt = stalledAt\n else delete trace.reason\n this.deps.onFinished(trace)\n }\n}\n\n/** Notification title and body: the headline, the stage timeline, then the context that separates cold opens from warm ones. */\nexport function formatDashboardOpenTrace(trace: DashboardOpenTrace): {title: string; body: string} {\n const shown = trace.stages.presented\n const title =\n trace.stalledAt === undefined && shown !== undefined\n ? `Dashboard opened in ${formatMs(shown)} ms`\n : `Dashboard open stalled before ${STAGE_LABELS[trace.stalledAt ?? \"presented\"]}`\n\n const gesture = trace.repeats > 0 ? `${trace.trigger} ×${trace.repeats + 1}` : trace.trigger\n const timeline = [`${gesture} 0`]\n for (const stage of DASHBOARD_OPEN_STAGES) {\n const at = trace.stages[stage]\n if (at !== undefined) timeline.push(`${STAGE_LABELS[stage]} +${formatMs(at)}`)\n }\n\n const context = [\n trace.sinceLastOpenMs === null\n ? \"first open this session\"\n : `last open ${formatDuration(trace.sinceLastOpenMs)} ago`,\n ]\n if (trace.appUpMs !== null) {\n context.push(\n trace.appUpMs < 0\n ? `dashboard app connected ${formatDuration(-trace.appUpMs)} after the gesture`\n : `dashboard app up ${formatDuration(trace.appUpMs)}`,\n )\n }\n if (trace.glassesUpMs !== null) context.push(`glasses up ${formatDuration(trace.glassesUpMs)}`)\n\n const lines = [timeline.join(\" · \"), context.join(\" · \")]\n if (trace.reason) lines.push(`glasses: ${trace.reason}`)\n return {title, body: lines.join(\"\\n\")}\n}\n\n/** 5210 → \"5,210\". Hand-rolled so it doesn't depend on the runtime's Intl. */\nfunction formatMs(ms: number): string {\n return String(Math.round(ms)).replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\")\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1_000) return `${Math.round(ms)} ms`\n if (ms < 60_000) return `${Math.round(ms / 1_000)} s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)} min`\n const hours = Math.floor(ms / 3_600_000)\n const minutes = Math.round((ms % 3_600_000) / 60_000)\n return minutes > 0 ? `${hours} h ${minutes} min` : `${hours} h`\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"effectiveCapabilities.d.ts","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"AAWA,OAAO,EAAC,WAAW,EAAwB,KAAK,YAAY,EAAC,MAAM,UAAU,CAAA;AAM7E,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAI3D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAkBzE"}
1
+ {"version":3,"file":"effectiveCapabilities.d.ts","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"AAcA,OAAO,EAAqB,WAAW,EAAwB,KAAK,YAAY,EAAC,MAAM,UAAU,CAAA;AAOjG,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAI3D;AAMD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CA6BzE"}
@@ -6,36 +6,48 @@
6
6
  * Native publishes the canvas into the glasses status (0 = none). Because
7
7
  * that path is phone-side rasterization, the stock firmware's container
8
8
  * pools (6 text / 4 image) don't bound a CFW frame either — element caps are
9
- * lifted alongside the canvas, bounded only to keep frames sane.
9
+ * lifted alongside the canvas, bounded only to keep frames sane. Firmware
10
+ * that draws vector shapes keeps 128 scene slots, so shape-heavy frames
11
+ * (a game board, a chart) get a larger shared text/shape budget.
10
12
  */
11
13
  import { useGlassesStore } from "../stores/glasses";
12
- import { DeviceTypes, getModelCapabilities } from "../types";
13
- import { isGlasslyCfwFirmwareVersion } from "./CustomDashboardModel";
14
+ import { SETTINGS, useSettingsStore } from "../stores/settings";
15
+ import { ALL_DISPLAY_SHAPES, DeviceTypes, getModelCapabilities } from "../types";
16
+ import { resolveNativeNavigation } from "./nativeNavigationGate";
14
17
  const CFW_MAX_TEXT_ELEMENTS = 24;
15
18
  const CFW_MAX_IMAGE_ELEMENTS = 16;
19
+ const CFW_VECTOR_MAX_ELEMENTS = 120;
16
20
  export function runtimeSceneCanvas() {
17
21
  const { sceneCanvasWidth, sceneCanvasHeight } = useGlassesStore.getState();
18
22
  if (!sceneCanvasWidth || !sceneCanvasHeight || sceneCanvasWidth <= 0 || sceneCanvasHeight <= 0)
19
23
  return null;
20
24
  return { width: sceneCanvasWidth, height: sceneCanvasHeight };
21
25
  }
26
+ function forceNativeNavigationOnCfw() {
27
+ return useSettingsStore.getState().getSetting(SETTINGS.cfw_native_navigation.key) === true;
28
+ }
22
29
  export function getEffectiveCapabilities(model) {
23
30
  const base = getModelCapabilities(model);
24
- const canvas = runtimeSceneCanvas();
25
- // The firmware marker lifts the caps even when no canvas has been published:
26
- // CFW classification rides the version string, and a G2 running Glassly CFW
27
- // always rasterizes phone-side regardless of whether native reported dims.
28
- const cfw = model === DeviceTypes.G2 && isGlasslyCfwFirmwareVersion(useGlassesStore.getState().firmwareVersion);
29
- if (!base.display || (!canvas && !cfw))
31
+ // Only G2 CFW publishes a canvas; never let a stale value reshape another model.
32
+ const canvas = model === DeviceTypes.G2 ? runtimeSceneCanvas() : null;
33
+ if (!base.display || !canvas)
30
34
  return base;
35
+ // Glassly CFW supports the complete vector contract, including SVG and rotation.
36
+ // Upstream g2flash publishes a raster canvas with sceneVector=false.
37
+ const vector = useGlassesStore.getState().sceneVector === true;
31
38
  return {
32
39
  ...base,
40
+ hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),
33
41
  display: {
34
42
  ...base.display,
35
- width: canvas?.width ?? base.display.width,
36
- height: canvas?.height ?? base.display.height,
37
- maxTextElements: Math.max(base.display.maxTextElements ?? 0, CFW_MAX_TEXT_ELEMENTS),
38
- maxImageElements: Math.max(base.display.maxImageElements ?? 0, CFW_MAX_IMAGE_ELEMENTS),
43
+ width: canvas.width,
44
+ height: canvas.height,
45
+ maxTextElements: Math.max(base.display.maxTextElements ?? 0, vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_TEXT_ELEMENTS),
46
+ // Vector firmware draws an image as one cached slot, the same as a shape.
47
+ maxImageElements: Math.max(base.display.maxImageElements ?? 0, vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_IMAGE_ELEMENTS),
48
+ ...(vector
49
+ ? { shapes: [...ALL_DISPLAY_SHAPES, "svg"], animation: true, rotation: true, depth: true }
50
+ : {}),
39
51
  },
40
52
  };
41
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"effectiveCapabilities.js","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,WAAW,EAAE,oBAAoB,EAAoB,MAAM,UAAU,CAAA;AAC7E,OAAO,EAAC,2BAA2B,EAAC,MAAM,wBAAwB,CAAA;AAElE,MAAM,qBAAqB,GAAG,EAAE,CAAA;AAChC,MAAM,sBAAsB,GAAG,EAAE,CAAA;AAOjC,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAC,gBAAgB,EAAE,iBAAiB,EAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IACxE,IAAI,CAAC,gBAAgB,IAAI,CAAC,iBAAiB,IAAI,gBAAgB,IAAI,CAAC,IAAI,iBAAiB,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC3G,OAAO,EAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,iBAAiB,EAAC,CAAA;AAC7D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAkB;IACzD,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAA;IACxC,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAA;IACnC,6EAA6E;IAC7E,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,GAAG,GAAG,KAAK,KAAK,WAAW,CAAC,EAAE,IAAI,2BAA2B,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,eAAe,CAAC,CAAA;IAC/G,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACnD,OAAO;QACL,GAAG,IAAI;QACP,OAAO,EAAE;YACP,GAAG,IAAI,CAAC,OAAO;YACf,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK;YAC1C,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAC7C,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,CAAC,EAAE,qBAAqB,CAAC;YACnF,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,CAAC,EAAE,sBAAsB,CAAC;SACvF;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * Effective device capabilities — the static model profile plus what the\n * connected firmware reports at runtime. The runtime override is the scene\n * canvas: Glassly CFW on the G2 rasterizes scenes phone-side straight into\n * the 640×480 panel, so the drawable canvas grows past stock's 576×288.\n * Native publishes the canvas into the glasses status (0 = none). Because\n * that path is phone-side rasterization, the stock firmware's container\n * pools (6 text / 4 image) don't bound a CFW frame either — element caps are\n * lifted alongside the canvas, bounded only to keep frames sane.\n */\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {DeviceTypes, getModelCapabilities, type Capabilities} from \"../types\"\nimport {isGlasslyCfwFirmwareVersion} from \"./CustomDashboardModel\"\n\nconst CFW_MAX_TEXT_ELEMENTS = 24\nconst CFW_MAX_IMAGE_ELEMENTS = 16\n\nexport interface SceneCanvasSize {\n width: number\n height: number\n}\n\nexport function runtimeSceneCanvas(): SceneCanvasSize | null {\n const {sceneCanvasWidth, sceneCanvasHeight} = useGlassesStore.getState()\n if (!sceneCanvasWidth || !sceneCanvasHeight || sceneCanvasWidth <= 0 || sceneCanvasHeight <= 0) return null\n return {width: sceneCanvasWidth, height: sceneCanvasHeight}\n}\n\nexport function getEffectiveCapabilities(model: DeviceTypes): Capabilities {\n const base = getModelCapabilities(model)\n const canvas = runtimeSceneCanvas()\n // The firmware marker lifts the caps even when no canvas has been published:\n // CFW classification rides the version string, and a G2 running Glassly CFW\n // always rasterizes phone-side regardless of whether native reported dims.\n const cfw = model === DeviceTypes.G2 && isGlasslyCfwFirmwareVersion(useGlassesStore.getState().firmwareVersion)\n if (!base.display || (!canvas && !cfw)) return base\n return {\n ...base,\n display: {\n ...base.display,\n width: canvas?.width ?? base.display.width,\n height: canvas?.height ?? base.display.height,\n maxTextElements: Math.max(base.display.maxTextElements ?? 0, CFW_MAX_TEXT_ELEMENTS),\n maxImageElements: Math.max(base.display.maxImageElements ?? 0, CFW_MAX_IMAGE_ELEMENTS),\n },\n }\n}\n"]}
1
+ {"version":3,"file":"effectiveCapabilities.js","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAC,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAoB,MAAM,UAAU,CAAA;AACjG,OAAO,EAAC,uBAAuB,EAAC,MAAM,wBAAwB,CAAA;AAE9D,MAAM,qBAAqB,GAAG,EAAE,CAAA;AAChC,MAAM,sBAAsB,GAAG,EAAE,CAAA;AACjC,MAAM,uBAAuB,GAAG,GAAG,CAAA;AAOnC,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAC,gBAAgB,EAAE,iBAAiB,EAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IACxE,IAAI,CAAC,gBAAgB,IAAI,CAAC,iBAAiB,IAAI,gBAAgB,IAAI,CAAC,IAAI,iBAAiB,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC3G,OAAO,EAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,iBAAiB,EAAC,CAAA;AAC7D,CAAC;AAED,SAAS,0BAA0B;IACjC,OAAO,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAA;AAC5F,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAkB;IACzD,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAA;IACxC,iFAAiF;IACjF,MAAM,MAAM,GAAG,KAAK,KAAK,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IACrE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACzC,iFAAiF;IACjF,qEAAqE;IACrE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,WAAW,KAAK,IAAI,CAAA;IAC9D,OAAO;QACL,GAAG,IAAI;QACP,mBAAmB,EAAE,uBAAuB,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC;QAC1G,OAAO,EAAE;YACP,GAAG,IAAI,CAAC,OAAO;YACf,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,eAAe,EAAE,IAAI,CAAC,GAAG,CACvB,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,CAAC,EACjC,MAAM,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,qBAAqB,CACzD;YACD,0EAA0E;YAC1E,gBAAgB,EAAE,IAAI,CAAC,GAAG,CACxB,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,CAAC,EAClC,MAAM,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,sBAAsB,CAC1D;YACD,GAAG,CAAC,MAAM;gBACR,CAAC,CAAC,EAAC,MAAM,EAAE,CAAC,GAAG,kBAAkB,EAAE,KAAc,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAC;gBACjG,CAAC,CAAC,EAAE,CAAC;SACR;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * Effective device capabilities — the static model profile plus what the\n * connected firmware reports at runtime. The runtime override is the scene\n * canvas: Glassly CFW on the G2 rasterizes scenes phone-side straight into\n * the 640×480 panel, so the drawable canvas grows past stock's 576×288.\n * Native publishes the canvas into the glasses status (0 = none). Because\n * that path is phone-side rasterization, the stock firmware's container\n * pools (6 text / 4 image) don't bound a CFW frame either — element caps are\n * lifted alongside the canvas, bounded only to keep frames sane. Firmware\n * that draws vector shapes keeps 128 scene slots, so shape-heavy frames\n * (a game board, a chart) get a larger shared text/shape budget.\n */\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {ALL_DISPLAY_SHAPES, DeviceTypes, getModelCapabilities, type Capabilities} from \"../types\"\nimport {resolveNativeNavigation} from \"./nativeNavigationGate\"\n\nconst CFW_MAX_TEXT_ELEMENTS = 24\nconst CFW_MAX_IMAGE_ELEMENTS = 16\nconst CFW_VECTOR_MAX_ELEMENTS = 120\n\nexport interface SceneCanvasSize {\n width: number\n height: number\n}\n\nexport function runtimeSceneCanvas(): SceneCanvasSize | null {\n const {sceneCanvasWidth, sceneCanvasHeight} = useGlassesStore.getState()\n if (!sceneCanvasWidth || !sceneCanvasHeight || sceneCanvasWidth <= 0 || sceneCanvasHeight <= 0) return null\n return {width: sceneCanvasWidth, height: sceneCanvasHeight}\n}\n\nfunction forceNativeNavigationOnCfw(): boolean {\n return useSettingsStore.getState().getSetting(SETTINGS.cfw_native_navigation.key) === true\n}\n\nexport function getEffectiveCapabilities(model: DeviceTypes): Capabilities {\n const base = getModelCapabilities(model)\n // Only G2 CFW publishes a canvas; never let a stale value reshape another model.\n const canvas = model === DeviceTypes.G2 ? runtimeSceneCanvas() : null\n if (!base.display || !canvas) return base\n // Glassly CFW supports the complete vector contract, including SVG and rotation.\n // Upstream g2flash publishes a raster canvas with sceneVector=false.\n const vector = useGlassesStore.getState().sceneVector === true\n return {\n ...base,\n hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),\n display: {\n ...base.display,\n width: canvas.width,\n height: canvas.height,\n maxTextElements: Math.max(\n base.display.maxTextElements ?? 0,\n vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_TEXT_ELEMENTS,\n ),\n // Vector firmware draws an image as one cached slot, the same as a shape.\n maxImageElements: Math.max(\n base.display.maxImageElements ?? 0,\n vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_IMAGE_ELEMENTS,\n ),\n ...(vector\n ? {shapes: [...ALL_DISPLAY_SHAPES, \"svg\" as const], animation: true, rotation: true, depth: true}\n : {}),\n },\n }\n}\n"]}
@@ -1,18 +1,37 @@
1
1
  /**
2
- * Gesture-claim arbitration (CLAIM_GESTURES), split out of
3
- * LocalMiniappRuntime so it is unit-testable — the runtime module itself
4
- * pulls a native import surface bun can't parse.
2
+ * Gesture registrations in claim order. Exactly one miniapp receives each
3
+ * gesture: the last registration containing it wins. Named scopes let an
4
+ * app temporarily claim gestures without overwriting its persistent shortcuts.
5
+ * Releasing a scope restores the preceding registration; stopping removes all
6
+ * of the app's scopes. This module has no native dependencies.
5
7
  */
6
- /**
7
- * True iff the miniapp in front of the user has claimed `gesture`.
8
- *
9
- * "In front" spans both screens. Most claiming apps are glasses-side: the
10
- * terminal runs as a background miniapp with the phone pocketed, so gating on
11
- * the phone's Compositor foreground alone left its double_tap claim inert and
12
- * the system dashboard kept opening mid-session. Glasses ownership is the
13
- * primary signal; the phone foreground is still honored for apps driven from
14
- * the handset. A claimer that owns neither screen is background and never
15
- * hijacks a system gesture.
16
- */
17
- export declare function claimsGesture(claims: ReadonlyMap<string, ReadonlySet<string>>, gesture: string, glassesOwner: string | null, phoneForeground: string | null): boolean;
8
+ interface GestureRegistration {
9
+ packageName: string;
10
+ scope: string;
11
+ gestures: ReadonlySet<string>;
12
+ }
13
+ export interface GestureClaimState {
14
+ registrations: readonly GestureRegistration[];
15
+ }
16
+ export interface GestureClaimChange {
17
+ packageName: string;
18
+ gesture: string;
19
+ held: boolean;
20
+ }
21
+ export declare function emptyGestureClaims(): GestureClaimState;
22
+ /** The single receiver of this gesture, regardless of how many scopes registered it. */
23
+ export declare function gestureClaimant(state: GestureClaimState, gesture: string): string | null;
24
+ /** Registration and current ownership differ when another app claimed more recently. */
25
+ export declare function hasGestureClaim(state: GestureClaimState, packageName: string, gesture: string): boolean;
26
+ /** Replace one scope's set. A nonempty registration always becomes the newest claim. */
27
+ export declare function setGestureClaims(state: GestureClaimState, packageName: string, gestures: readonly string[], scope?: string): {
28
+ state: GestureClaimState;
29
+ changes: GestureClaimChange[];
30
+ };
31
+ /** Stop removes every scope without notifying the stopped app. */
32
+ export declare function dropGestureClaims(state: GestureClaimState, packageName: string): {
33
+ state: GestureClaimState;
34
+ changes: GestureClaimChange[];
35
+ };
36
+ export {};
18
37
  //# sourceMappingURL=gestureClaims.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gestureClaims.d.ts","sourceRoot":"","sources":["../../src/services/gestureClaims.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,EAChD,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,GAC7B,OAAO,CAKT"}
1
+ {"version":3,"file":"gestureClaims.d.ts","sourceRoot":"","sources":["../../src/services/gestureClaims.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,UAAU,mBAAmB;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,SAAS,mBAAmB,EAAE,CAAA;CAC9C;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,CAAA;CACd;AAED,wBAAgB,kBAAkB,IAAI,iBAAiB,CAEtD;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMxF;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvG;AAeD,wFAAwF;AACxF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,iBAAiB,EACxB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,KAAK,SAAY,GAChB;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,kBAAkB,EAAE,CAAA;CAAC,CAO3D;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,iBAAiB,EACxB,WAAW,EAAE,MAAM,GAClB;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,kBAAkB,EAAE,CAAA;CAAC,CAG3D"}
@@ -1,24 +1,45 @@
1
- /**
2
- * Gesture-claim arbitration (CLAIM_GESTURES), split out of
3
- * LocalMiniappRuntime so it is unit-testable — the runtime module itself
4
- * pulls a native import surface bun can't parse.
5
- */
6
- /**
7
- * True iff the miniapp in front of the user has claimed `gesture`.
8
- *
9
- * "In front" spans both screens. Most claiming apps are glasses-side: the
10
- * terminal runs as a background miniapp with the phone pocketed, so gating on
11
- * the phone's Compositor foreground alone left its double_tap claim inert and
12
- * the system dashboard kept opening mid-session. Glasses ownership is the
13
- * primary signal; the phone foreground is still honored for apps driven from
14
- * the handset. A claimer that owns neither screen is background and never
15
- * hijacks a system gesture.
16
- */
17
- export function claimsGesture(claims, gesture, glassesOwner, phoneForeground) {
18
- for (const pkg of [glassesOwner, phoneForeground]) {
19
- if (pkg && claims.get(pkg)?.has(gesture) === true)
20
- return true;
1
+ export function emptyGestureClaims() {
2
+ return { registrations: [] };
3
+ }
4
+ /** The single receiver of this gesture, regardless of how many scopes registered it. */
5
+ export function gestureClaimant(state, gesture) {
6
+ for (let i = state.registrations.length - 1; i >= 0; i--) {
7
+ const registration = state.registrations[i];
8
+ if (registration.gestures.has(gesture))
9
+ return registration.packageName;
10
+ }
11
+ return null;
12
+ }
13
+ /** Registration and current ownership differ when another app claimed more recently. */
14
+ export function hasGestureClaim(state, packageName, gesture) {
15
+ return state.registrations.some((entry) => entry.packageName === packageName && entry.gestures.has(gesture));
16
+ }
17
+ function changesBetween(before, after) {
18
+ const gestures = new Set([...before.registrations, ...after.registrations].flatMap((entry) => [...entry.gestures]));
19
+ const changes = [];
20
+ for (const gesture of gestures) {
21
+ const was = gestureClaimant(before, gesture);
22
+ const now = gestureClaimant(after, gesture);
23
+ if (was === now)
24
+ continue;
25
+ if (was !== null)
26
+ changes.push({ packageName: was, gesture, held: false });
27
+ if (now !== null)
28
+ changes.push({ packageName: now, gesture, held: true });
21
29
  }
22
- return false;
30
+ return changes;
31
+ }
32
+ /** Replace one scope's set. A nonempty registration always becomes the newest claim. */
33
+ export function setGestureClaims(state, packageName, gestures, scope = "default") {
34
+ const registrations = state.registrations.filter((entry) => entry.packageName !== packageName || entry.scope !== scope);
35
+ if (gestures.length)
36
+ registrations.push({ packageName, scope, gestures: new Set(gestures) });
37
+ const after = { registrations };
38
+ return { state: after, changes: changesBetween(state, after) };
39
+ }
40
+ /** Stop removes every scope without notifying the stopped app. */
41
+ export function dropGestureClaims(state, packageName) {
42
+ const after = { registrations: state.registrations.filter((entry) => entry.packageName !== packageName) };
43
+ return { state: after, changes: changesBetween(state, after).filter((change) => change.packageName !== packageName) };
23
44
  }
24
45
  //# sourceMappingURL=gestureClaims.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gestureClaims.js","sourceRoot":"","sources":["../../src/services/gestureClaims.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAgD,EAChD,OAAe,EACf,YAA2B,EAC3B,eAA8B;IAE9B,KAAK,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,EAAE,CAAC;QAClD,IAAI,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;IAChE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC","sourcesContent":["/**\n * Gesture-claim arbitration (CLAIM_GESTURES), split out of\n * LocalMiniappRuntime so it is unit-testable — the runtime module itself\n * pulls a native import surface bun can't parse.\n */\n\n/**\n * True iff the miniapp in front of the user has claimed `gesture`.\n *\n * \"In front\" spans both screens. Most claiming apps are glasses-side: the\n * terminal runs as a background miniapp with the phone pocketed, so gating on\n * the phone's Compositor foreground alone left its double_tap claim inert and\n * the system dashboard kept opening mid-session. Glasses ownership is the\n * primary signal; the phone foreground is still honored for apps driven from\n * the handset. A claimer that owns neither screen is background and never\n * hijacks a system gesture.\n */\nexport function claimsGesture(\n claims: ReadonlyMap<string, ReadonlySet<string>>,\n gesture: string,\n glassesOwner: string | null,\n phoneForeground: string | null,\n): boolean {\n for (const pkg of [glassesOwner, phoneForeground]) {\n if (pkg && claims.get(pkg)?.has(gesture) === true) return true\n }\n return false\n}\n"]}
1
+ {"version":3,"file":"gestureClaims.js","sourceRoot":"","sources":["../../src/services/gestureClaims.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Gesture registrations in claim order. Exactly one miniapp receives each\n * gesture: the last registration containing it wins. Named scopes let an\n * app temporarily claim gestures without overwriting its persistent shortcuts.\n * Releasing a scope restores the preceding registration; stopping removes all\n * of the app's scopes. This module has no native dependencies.\n */\ninterface GestureRegistration {\n packageName: string\n scope: string\n gestures: ReadonlySet<string>\n}\n\nexport interface GestureClaimState {\n registrations: readonly GestureRegistration[]\n}\n\nexport interface GestureClaimChange {\n packageName: string\n gesture: string\n held: boolean\n}\n\nexport function emptyGestureClaims(): GestureClaimState {\n return {registrations: []}\n}\n\n/** The single receiver of this gesture, regardless of how many scopes registered it. */\nexport function gestureClaimant(state: GestureClaimState, gesture: string): string | null {\n for (let i = state.registrations.length - 1; i >= 0; i--) {\n const registration = state.registrations[i]\n if (registration.gestures.has(gesture)) return registration.packageName\n }\n return null\n}\n\n/** Registration and current ownership differ when another app claimed more recently. */\nexport function hasGestureClaim(state: GestureClaimState, packageName: string, gesture: string): boolean {\n return state.registrations.some((entry) => entry.packageName === packageName && entry.gestures.has(gesture))\n}\n\nfunction changesBetween(before: GestureClaimState, after: GestureClaimState): GestureClaimChange[] {\n const gestures = new Set([...before.registrations, ...after.registrations].flatMap((entry) => [...entry.gestures]))\n const changes: GestureClaimChange[] = []\n for (const gesture of gestures) {\n const was = gestureClaimant(before, gesture)\n const now = gestureClaimant(after, gesture)\n if (was === now) continue\n if (was !== null) changes.push({packageName: was, gesture, held: false})\n if (now !== null) changes.push({packageName: now, gesture, held: true})\n }\n return changes\n}\n\n/** Replace one scope's set. A nonempty registration always becomes the newest claim. */\nexport function setGestureClaims(\n state: GestureClaimState,\n packageName: string,\n gestures: readonly string[],\n scope = \"default\",\n): {state: GestureClaimState; changes: GestureClaimChange[]} {\n const registrations = state.registrations.filter(\n (entry) => entry.packageName !== packageName || entry.scope !== scope,\n )\n if (gestures.length) registrations.push({packageName, scope, gestures: new Set(gestures)})\n const after = {registrations}\n return {state: after, changes: changesBetween(state, after)}\n}\n\n/** Stop removes every scope without notifying the stopped app. */\nexport function dropGestureClaims(\n state: GestureClaimState,\n packageName: string,\n): {state: GestureClaimState; changes: GestureClaimChange[]} {\n const after = {registrations: state.registrations.filter((entry) => entry.packageName !== packageName)}\n return {state: after, changes: changesBetween(state, after).filter((change) => change.packageName !== packageName)}\n}\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Pure normalizers for the free-text manifest fields (miniapp.json) that the
3
+ * installed-app projection carries onto ClientApp.
4
+ */
5
+ /** A non-empty manifest string, trimmed; anything else is absent. */
6
+ export declare function normalizeManifestText(raw: unknown): string | undefined;
7
+ /** Manifest tags: lowercase slugs, de-duplicated, non-strings dropped. */
8
+ export declare function normalizeManifestTags(raw: unknown): string[];
9
+ //# sourceMappingURL=manifestFields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifestFields.d.ts","sourceRoot":"","sources":["../../src/services/manifestFields.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qEAAqE;AACrE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAItE;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CAS5D"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Pure normalizers for the free-text manifest fields (miniapp.json) that the
3
+ * installed-app projection carries onto ClientApp.
4
+ */
5
+ /** A non-empty manifest string, trimmed; anything else is absent. */
6
+ export function normalizeManifestText(raw) {
7
+ if (typeof raw !== "string")
8
+ return undefined;
9
+ const trimmed = raw.trim();
10
+ return trimmed.length > 0 ? trimmed : undefined;
11
+ }
12
+ /** Manifest tags: lowercase slugs, de-duplicated, non-strings dropped. */
13
+ export function normalizeManifestTags(raw) {
14
+ if (!Array.isArray(raw))
15
+ return [];
16
+ const out = [];
17
+ for (const tag of raw) {
18
+ if (typeof tag !== "string")
19
+ continue;
20
+ const slug = tag.trim().toLowerCase();
21
+ if (slug.length > 0 && !out.includes(slug))
22
+ out.push(slug);
23
+ }
24
+ return out;
25
+ }
26
+ //# sourceMappingURL=manifestFields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifestFields.js","sourceRoot":"","sources":["../../src/services/manifestFields.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAC1B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;AACjD,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAClC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAQ;QACrC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QACrC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC5D,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["/**\n * Pure normalizers for the free-text manifest fields (miniapp.json) that the\n * installed-app projection carries onto ClientApp.\n */\n\n/** A non-empty manifest string, trimmed; anything else is absent. */\nexport function normalizeManifestText(raw: unknown): string | undefined {\n if (typeof raw !== \"string\") return undefined\n const trimmed = raw.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n/** Manifest tags: lowercase slugs, de-duplicated, non-strings dropped. */\nexport function normalizeManifestTags(raw: unknown): string[] {\n if (!Array.isArray(raw)) return []\n const out: string[] = []\n for (const tag of raw) {\n if (typeof tag !== \"string\") continue\n const slug = tag.trim().toLowerCase()\n if (slug.length > 0 && !out.includes(slug)) out.push(slug)\n }\n return out\n}\n"]}
@@ -5,15 +5,5 @@
5
5
  * dependency-free for tests.
6
6
  */
7
7
  import { MemoryStore } from "./MemoryStore";
8
- /** Whether the extended (Plus/Pro) retention window is active right now. */
9
- export declare function isMemoryRetentionExtended(): boolean;
10
- /**
11
- * Flip the visible retention window. Called by the host app when the
12
- * subscription state resolves or changes. Downgrading freezes reads to the
13
- * standard window but deletes nothing — the older timeline stays stored and
14
- * reappears on re-upgrade (MemoryStore prunes at the extended bound for
15
- * every tier).
16
- */
17
- export declare function setMemoryRetentionExtended(extended: boolean): void;
18
8
  export declare const memoryStore: MemoryStore;
19
9
  //# sourceMappingURL=memoryStoreInstance.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memoryStoreInstance.d.ts","sourceRoot":"","sources":["../../src/services/memoryStoreInstance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAqB,WAAW,EAAqB,MAAM,eAAe,CAAA;AAejF,4EAA4E;AAC5E,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAIlE;AAED,eAAO,MAAM,WAAW,aAetB,CAAA"}
1
+ {"version":3,"file":"memoryStoreInstance.d.ts","sourceRoot":"","sources":["../../src/services/memoryStoreInstance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AAEzC,eAAO,MAAM,WAAW,aActB,CAAA"}
@@ -7,38 +7,9 @@
7
7
  import { AppState } from "react-native";
8
8
  import { storage as mmkvStorage } from "../utils/storage/storage";
9
9
  import { cloudClientService } from "./CloudClientService";
10
- import { EXTENDED_RETENTION, MemoryStore, STANDARD_RETENTION } from "./MemoryStore";
11
- /**
12
- * Persisted "Plus/Pro memory retention" flag. Cached in MMKV so a paying user
13
- * who relaunches offline keeps their full read window; only a DEFINITIVE
14
- * subscription answer may change it (the RevenueCatSetup effect passes
15
- * nothing on unknown/null customer info).
16
- */
17
- const RETENTION_EXTENDED_KEY = "memory_retention_extended";
18
- let retentionExtended = (() => {
19
- const result = mmkvStorage.load(RETENTION_EXTENDED_KEY);
20
- return result.is_ok() && result.value === true;
21
- })();
22
- /** Whether the extended (Plus/Pro) retention window is active right now. */
23
- export function isMemoryRetentionExtended() {
24
- return retentionExtended;
25
- }
26
- /**
27
- * Flip the visible retention window. Called by the host app when the
28
- * subscription state resolves or changes. Downgrading freezes reads to the
29
- * standard window but deletes nothing — the older timeline stays stored and
30
- * reappears on re-upgrade (MemoryStore prunes at the extended bound for
31
- * every tier).
32
- */
33
- export function setMemoryRetentionExtended(extended) {
34
- if (retentionExtended === extended)
35
- return;
36
- retentionExtended = extended;
37
- mmkvStorage.save(RETENTION_EXTENDED_KEY, extended);
38
- }
10
+ import { MemoryStore } from "./MemoryStore";
39
11
  export const memoryStore = new MemoryStore({
40
- getUserId: () => cloudClientService.resolveGlasslyUserId(),
41
- getRetention: () => (retentionExtended ? EXTENDED_RETENTION : STANDARD_RETENTION),
12
+ getUserId: () => cloudClientService.resolveUserId(),
42
13
  backend: {
43
14
  get(key) {
44
15
  const result = mmkvStorage.load(key);
@@ -1 +1 @@
1
- {"version":3,"file":"memoryStoreInstance.js","sourceRoot":"","sources":["../../src/services/memoryStoreInstance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AAErC,OAAO,EAAC,OAAO,IAAI,WAAW,EAAC,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAC,kBAAkB,EAAE,WAAW,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAA;AAEjF;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG,2BAA2B,CAAA;AAE1D,IAAI,iBAAiB,GAAG,CAAC,GAAY,EAAE;IACrC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAU,sBAAsB,CAAC,CAAA;IAChE,OAAO,MAAM,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAA;AAChD,CAAC,CAAC,EAAE,CAAA;AAEJ,4EAA4E;AAC5E,MAAM,UAAU,yBAAyB;IACvC,OAAO,iBAAiB,CAAA;AAC1B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAiB;IAC1D,IAAI,iBAAiB,KAAK,QAAQ;QAAE,OAAM;IAC1C,iBAAiB,GAAG,QAAQ,CAAA;IAC5B,WAAW,CAAC,IAAI,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAA;AACpD,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC;IACzC,SAAS,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,oBAAoB,EAAE;IAC1D,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,kBAAkB,CAAC;IACjF,OAAO,EAAE;QACP,GAAG,CAAC,GAAW;YACb,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAU,GAAG,CAAC,CAAA;YAC7C,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;QAC7C,CAAC;QACD,GAAG,CAAC,GAAW,EAAE,KAAc;YAC7B,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC9B,CAAC;QACD,MAAM,CAAC,GAAW;YAChB,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC;KACF;CACF,CAAC,CAAA;AAEF,+EAA+E;AAC/E,6EAA6E;AAC7E,mFAAmF;AACnF,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;IAC5C,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAM;IAC9B,KAAK,WAAW,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACrC,OAAO,CAAC,IAAI,CAAC,0CAA0C,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACpG,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["/**\n * The engine's singleton MemoryStore — the shared user-scoped memory timeline\n * behind LocalMiniappRuntime's MEMORY_* bridge handlers and engine.memory.\n * Lives in its own module (not MemoryStore.ts) so the store class stays\n * dependency-free for tests.\n */\n\nimport {AppState} from \"react-native\"\n\nimport {storage as mmkvStorage} from \"../utils/storage/storage\"\nimport {cloudClientService} from \"./CloudClientService\"\nimport {EXTENDED_RETENTION, MemoryStore, STANDARD_RETENTION} from \"./MemoryStore\"\n\n/**\n * Persisted \"Plus/Pro memory retention\" flag. Cached in MMKV so a paying user\n * who relaunches offline keeps their full read window; only a DEFINITIVE\n * subscription answer may change it (the RevenueCatSetup effect passes\n * nothing on unknown/null customer info).\n */\nconst RETENTION_EXTENDED_KEY = \"memory_retention_extended\"\n\nlet retentionExtended = ((): boolean => {\n const result = mmkvStorage.load<boolean>(RETENTION_EXTENDED_KEY)\n return result.is_ok() && result.value === true\n})()\n\n/** Whether the extended (Plus/Pro) retention window is active right now. */\nexport function isMemoryRetentionExtended(): boolean {\n return retentionExtended\n}\n\n/**\n * Flip the visible retention window. Called by the host app when the\n * subscription state resolves or changes. Downgrading freezes reads to the\n * standard window but deletes nothing — the older timeline stays stored and\n * reappears on re-upgrade (MemoryStore prunes at the extended bound for\n * every tier).\n */\nexport function setMemoryRetentionExtended(extended: boolean): void {\n if (retentionExtended === extended) return\n retentionExtended = extended\n mmkvStorage.save(RETENTION_EXTENDED_KEY, extended)\n}\n\nexport const memoryStore = new MemoryStore({\n getUserId: () => cloudClientService.resolveGlasslyUserId(),\n getRetention: () => (retentionExtended ? EXTENDED_RETENTION : STANDARD_RETENTION),\n backend: {\n get(key: string): unknown | null {\n const result = mmkvStorage.load<unknown>(key)\n return result.is_ok() ? result.value : null\n },\n set(key: string, value: unknown): void {\n mmkvStorage.save(key, value)\n },\n remove(key: string): void {\n mmkvStorage.remove(key)\n },\n },\n})\n\n// Transcript writes are debounced (see MemoryStore.schedulePersistLog), so the\n// tail of a conversation can be in-memory only when the user leaves the app.\n// Losing it to a background kill would be silent data loss — flush on the way out.\nAppState.addEventListener(\"change\", (state) => {\n if (state === \"active\") return\n void memoryStore.flush().catch((err) => {\n console.warn(\"MemoryStore: flush on background failed:\", err instanceof Error ? err.message : err)\n })\n})\n"]}
1
+ {"version":3,"file":"memoryStoreInstance.js","sourceRoot":"","sources":["../../src/services/memoryStoreInstance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AAErC,OAAO,EAAC,OAAO,IAAI,WAAW,EAAC,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AAEzC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC;IACzC,SAAS,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,aAAa,EAAE;IACnD,OAAO,EAAE;QACP,GAAG,CAAC,GAAW;YACb,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAU,GAAG,CAAC,CAAA;YAC7C,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;QAC7C,CAAC;QACD,GAAG,CAAC,GAAW,EAAE,KAAc;YAC7B,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC9B,CAAC;QACD,MAAM,CAAC,GAAW;YAChB,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC;KACF;CACF,CAAC,CAAA;AAEF,+EAA+E;AAC/E,6EAA6E;AAC7E,mFAAmF;AACnF,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;IAC5C,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAM;IAC9B,KAAK,WAAW,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACrC,OAAO,CAAC,IAAI,CAAC,0CAA0C,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACpG,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["/**\n * The engine's singleton MemoryStore — the shared user-scoped memory timeline\n * behind LocalMiniappRuntime's MEMORY_* bridge handlers and engine.memory.\n * Lives in its own module (not MemoryStore.ts) so the store class stays\n * dependency-free for tests.\n */\n\nimport {AppState} from \"react-native\"\n\nimport {storage as mmkvStorage} from \"../utils/storage/storage\"\nimport {cloudClientService} from \"./CloudClientService\"\nimport {MemoryStore} from \"./MemoryStore\"\n\nexport const memoryStore = new MemoryStore({\n getUserId: () => cloudClientService.resolveUserId(),\n backend: {\n get(key: string): unknown | null {\n const result = mmkvStorage.load<unknown>(key)\n return result.is_ok() ? result.value : null\n },\n set(key: string, value: unknown): void {\n mmkvStorage.save(key, value)\n },\n remove(key: string): void {\n mmkvStorage.remove(key)\n },\n },\n})\n\n// Transcript writes are debounced (see MemoryStore.schedulePersistLog), so the\n// tail of a conversation can be in-memory only when the user leaves the app.\n// Losing it to a background kill would be silent data loss — flush on the way out.\nAppState.addEventListener(\"change\", (state) => {\n if (state === \"active\") return\n void memoryStore.flush().catch((err) => {\n console.warn(\"MemoryStore: flush on background failed:\", err instanceof Error ? err.message : err)\n })\n})\n"]}