@glassly/engine 0.1.0-dev.98 → 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
@@ -19,36 +19,49 @@
19
19
  import BluetoothSdk from "@glassly/bluetooth-sdk/internal";
20
20
  import { shallow } from "zustand/shallow";
21
21
  import localMiniappRuntime from "./LocalMiniappRuntime";
22
- import localDisplayManager from "./LocalDisplayManager";
22
+ import glassesKeyboardInput from "./GlassesKeyboardInput";
23
+ import glassesSensorService from "./GlassesSensorService";
23
24
  import localSttFallbackCoordinator from "./LocalSttFallbackCoordinator";
24
- import { MiniappExitGesture } from "./MiniappExitGesture";
25
- import nativeNavigationService from "./NativeNavigationService";
26
25
  import speakerLabelResolver from "./SpeakerLabelResolver";
27
26
  import { phonePhotoCoordinator } from "./PhonePhotoCoordinator";
28
27
  import { phoneStreamCoordinator } from "./PhoneStreamCoordinator";
29
28
  import { isGlassesConnected } from "./GlassesReadiness";
30
29
  import { useGlassesStore } from "../stores/glasses";
31
- import { SETTINGS, useSettingsStore } from "../stores/settings";
30
+ import { useSettingsStore } from "../stores/settings";
32
31
  import { useAppStatusStore } from "../stores/apps";
33
32
  import { retirePendingSelectionOnPromotion } from "./PairingIdentity";
34
33
  import GlobalEventEmitter from "../utils/GlobalEventEmitter";
35
- import { BgTimer } from "../utils/timers";
36
34
  import { asgCameraApi } from "./asg/asgCameraApi";
37
- import dashboardCoordinator from "./DashboardCoordinator";
38
- import gestureDotsService from "./GestureDotsService";
35
+ import { dispatchTouchEvent, resetTouchInput } from "./GlassesTouchInput";
36
+ import dashboardOpenTrace from "./debug/DashboardOpenTrace";
39
37
  let subs = [];
40
- const miniappExitGesture = new MiniappExitGesture({
41
- getState: () => useAppStatusStore.getState(),
42
- getVisiblePackageName: () => localDisplayManager.getVisibleMiniappPackageName(),
43
- beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),
44
- finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),
45
- cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),
46
- setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),
47
- clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),
48
- });
49
38
  export function startDeviceEventRouter() {
50
39
  if (subs.length)
51
40
  return;
41
+ subs.push(BluetoothSdk.addListener("light_sensor", ({ type: _type, ...reading }) => {
42
+ localMiniappRuntime.forwardEvent("light_sensor", reading);
43
+ }),
44
+ // Stock firmware emits these only inside its nav UI; custom firmware whenever the
45
+ // raw compass runs. Either way they feed the one session.compass stream.
46
+ BluetoothSdk.addListener("compass_heading", ({ type: _type, heading, ...reading }) => {
47
+ localMiniappRuntime.forwardEvent("glasses_compass", { degrees: heading, ...reading });
48
+ }), BluetoothSdk.addListener("compass_calibration", ({ type: _type, ...reading }) => {
49
+ localMiniappRuntime.forwardEvent("compass_calibration", reading);
50
+ }), {
51
+ remove: useGlassesStore.subscribe((s) => s.connection, (connection) => {
52
+ if (isGlassesConnected(connection))
53
+ void glassesSensorService.resync();
54
+ else
55
+ glassesSensorService.disconnected();
56
+ }),
57
+ }, {
58
+ // CFW classification lands after device-ready; compass demand recorded before
59
+ // that only reaches the firmware once the scene canvas is published.
60
+ remove: useGlassesStore.subscribe((s) => s.sceneCanvasWidth, (width) => {
61
+ if ((width ?? 0) > 0)
62
+ void glassesSensorService.resync();
63
+ }),
64
+ });
52
65
  // --- device state → engine stores ---
53
66
  // Standalone WiFi status → glasses store.
54
67
  subs.push(BluetoothSdk.addListener("wifi_status_change", (event) => {
@@ -69,16 +82,22 @@ export function startDeviceEventRouter() {
69
82
  };
70
83
  }, (wifi) => localMiniappRuntime.forwardEvent("glasses_wifi", wifi), { equalityFn: shallow }),
71
84
  });
72
- // Incremental battery status → glasses store + local miniapps.
85
+ // Battery streams follow the store, including initial native hydration and
86
+ // onGlassesStatus patches. G2 can report its level without battery_status;
87
+ // forwarding only that legacy event leaves a running dashboard ring-only.
88
+ subs.push({
89
+ remove: useGlassesStore.subscribe((s) => ({ level: s.batteryLevel, charging: s.charging }), (battery) => {
90
+ localMiniappRuntime.forwardEvent("glasses_battery_update", {
91
+ type: "glasses_battery_update",
92
+ ...battery,
93
+ timestamp: Date.now(),
94
+ });
95
+ }, { equalityFn: shallow }),
96
+ });
97
+ // Legacy standalone events feed the same store subscription.
73
98
  subs.push(BluetoothSdk.addListener("battery_status", (event) => {
74
99
  const state = useGlassesStore.getState();
75
100
  state.setBatteryInfo(event.level, event.charging, state.caseBatteryLevel, state.caseCharging);
76
- localMiniappRuntime.forwardEvent("glasses_battery_update", {
77
- type: "glasses_battery_update",
78
- level: event.level,
79
- charging: event.charging,
80
- timestamp: event.timestamp ?? Date.now(),
81
- });
82
101
  }));
83
102
  // Hotspot status → glasses store + event bus. engine's own gallerySyncService listens
84
103
  // on the bus for these, so without this bridge engine's gallery sync is dead.
@@ -153,51 +172,16 @@ export function startDeviceEventRouter() {
153
172
  subs.push(BluetoothSdk.addListener("button_press", (event) => {
154
173
  localMiniappRuntime.forwardEvent("button_press", event);
155
174
  }));
156
- subs.push(BluetoothSdk.addListener("touch_event", (event) => {
157
- // While the custom dashboard is visible it owns its focus/navigation
158
- // gestures; do not leak those into a miniapp running underneath it.
159
- if (dashboardCoordinator.handleGesture(event.gestureName))
160
- return;
161
- // Gesture-dots debug indicator (any device, gated by its setting). It
162
- // only paints while the screen is otherwise empty; the gesture still
163
- // flows to miniapps below.
164
- gestureDotsService.noteGesture(event.gestureName);
165
- localMiniappRuntime.forwardEvent("touch_event", event);
166
- // Double-tap policy, moved out of the native G2 handlers: exit a visible
167
- // unclaimed miniapp; otherwise open the dashboard or toggle head-up.
168
- // Native only reports the gesture now.
169
- if (event.gestureName === "double_tap") {
170
- // A foreground miniapp that claimed double_tap (CLAIM_GESTURES) is
171
- // using it as an in-app gesture (e.g. the terminal's back-to-menu) —
172
- // the event was already forwarded above; skip the system default.
173
- if (localMiniappRuntime.foregroundClaimsGesture("double_tap"))
174
- return;
175
- // An unclaimed double tap exits only the miniapp whose frame is visibly
176
- // on the glasses. The controller shows the bounded OS notice first,
177
- // then uses the normal store stop path (and closes its phone overlay).
178
- // Checked before the dashboard toggle: while an app owns the screen an
179
- // unclaimed double-tap always means "leave the app" — the (custom or
180
- // native) dashboard is for the empty screen, and head-up still opens
181
- // the custom dashboard mid-app.
182
- if (miniappExitGesture.tryExitVisibleMiniapp())
183
- return;
184
- // Eligible G2 custom firmware replaces the native dashboard.
185
- if (dashboardCoordinator.toggleCustom())
186
- return;
187
- const useNativeDashboard = useSettingsStore.getState().getSetting(SETTINGS.use_native_dashboard.key) !== false;
188
- if (useNativeDashboard) {
189
- // The modal nav UI owns the screen; showDashboard would fight it.
190
- if (!nativeNavigationService.isActive) {
191
- void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {
192
- console.error("DeviceEventRouter: showDashboard failed:", err);
193
- });
194
- }
195
- }
196
- else {
197
- dashboardCoordinator.toggleLegacy();
198
- }
199
- }
200
- }));
175
+ subs.push(BluetoothSdk.addListener("touch_event", dispatchTouchEvent));
176
+ // Physical keyboard bridged through G2 Glassly CFW: raw HID transitions →
177
+ // layout-translated keyboard_event stream (with synthesized auto-repeat).
178
+ subs.push(BluetoothSdk.addListener("keyboard_key", (event) => glassesKeyboardInput.handle(event)));
179
+ subs.push({
180
+ remove: useGlassesStore.subscribe((s) => s.keyboardConnected === true, (connected) => {
181
+ if (!connected)
182
+ glassesKeyboardInput.reset();
183
+ }),
184
+ });
201
185
  // G2 IMU accelerometer — payload already matches the miniapp AccelData shape.
202
186
  subs.push(BluetoothSdk.addListener("accel_event", (event) => {
203
187
  localMiniappRuntime.forwardEvent("accel_event", {
@@ -208,10 +192,13 @@ export function startDeviceEventRouter() {
208
192
  });
209
193
  }));
210
194
  // Head position — translate native {up:boolean} → SDK {position:"up"|"down"}.
195
+ // The dashboard miniapp opens on head-up from this stream (ADR-0006).
211
196
  subs.push(BluetoothSdk.addListener("head_up", (event) => {
212
- dashboardCoordinator.setHeadUp(event.up);
197
+ if (event.up)
198
+ dashboardOpenTrace.trigger("head-up");
213
199
  localMiniappRuntime.forwardEvent("head_up", { position: event.up ? "up" : "down", timestamp: Date.now() });
214
200
  }));
201
+ subs.push({ remove: dashboardOpenTrace.start() });
215
202
  // On-device STT transcripts → local miniapps only while the local engine is
216
203
  // active (cloud fallback or an explicit forceLocal subscription). The runtime
217
204
  // filters mixed local/cloud delivery per miniapp to avoid double-delivery.
@@ -240,7 +227,7 @@ export function startDeviceEventRouter() {
240
227
  useAppStatusStore.getState().stop(packageName);
241
228
  }
242
229
  else {
243
- useAppStatusStore.getState().start(app, { skipNavigation: true });
230
+ useAppStatusStore.getState().start(app, { skipNavigation: true, launchSource: "glasses" });
244
231
  }
245
232
  }));
246
233
  }
@@ -248,6 +235,6 @@ export function stopDeviceEventRouter() {
248
235
  for (const sub of subs)
249
236
  sub.remove();
250
237
  subs = [];
251
- miniappExitGesture.reset();
238
+ resetTouchInput();
252
239
  }
253
240
  //# sourceMappingURL=DeviceEventRouter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceEventRouter.js","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Device-event router — engine-owned. Subscribes to the inbound native BLE/device\n * events and routes them into the engine runtime: device stores, the process event\n * bus, the photo/stream coordinators, and local miniapps (via forwardEvent).\n *\n * Why this exists: engine owns the stores, coordinators, miniapp runtime, and facades,\n * but it never *subscribed to the device* for most events — the host MantleManager was\n * still the event router the whole runtime secretly depended on. So a bare OEM that\n * imported engine + called engine.start() got a connected runtime with almost no device\n * data flowing in (no miniapp input, dead gallery sync, starved coordinators). This\n * service moves those inbound bridges into engine so ANY host gets them.\n *\n * Scope: the engine legs only. The Cloud V1 relays that used to live beside them in\n * MantleManager (touch→cloud, the cloud-SDK stream/photo legs, etc.) were deleted with\n * Cloud V1 app end-of-life.\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport {shallow} from \"zustand/shallow\"\n\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport {MiniappExitGesture} from \"./MiniappExitGesture\"\nimport nativeNavigationService from \"./NativeNavigationService\"\nimport speakerLabelResolver from \"./SpeakerLabelResolver\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {BgTimer} from \"../utils/timers\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\nimport dashboardCoordinator from \"./DashboardCoordinator\"\nimport gestureDotsService from \"./GestureDotsService\"\n\nlet subs: Array<{remove: () => void}> = []\n\nconst miniappExitGesture = new MiniappExitGesture({\n getState: () => useAppStatusStore.getState(),\n getVisiblePackageName: () => localDisplayManager.getVisibleMiniappPackageName(),\n beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),\n finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),\n cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),\n setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),\n clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),\n})\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n // --- device state → engine stores ---\n\n // Standalone WiFi status → glasses store.\n subs.push(\n BluetoothSdk.addListener(\"wifi_status_change\", (event) => {\n const {type: _type, ...wifi} = event\n useGlassesStore.getState().setGlassesInfo({wifi})\n }),\n )\n\n // Forward glasses Wi-Fi to miniapps (session.glasses.onWifi) from the STORE — the\n // single source of truth — so every path converges here: wifi_status_change,\n // onGlassesStatus, and BLE disconnect. Effective connectivity requires the glasses\n // to be connected AND on Wi-Fi, so a disconnect correctly flips `connected` false.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => {\n const connected = isGlassesConnected(s.connection) && s.wifi.state === \"connected\"\n return {\n connected,\n ssid: s.wifi.state === \"connected\" ? s.wifi.ssid : undefined,\n localIp: s.wifi.state === \"connected\" ? s.wifi.localIp : undefined,\n }\n },\n (wifi) => localMiniappRuntime.forwardEvent(\"glasses_wifi\", wifi),\n {equalityFn: shallow},\n ),\n })\n\n // Incremental battery status → glasses store + local miniapps.\n subs.push(\n BluetoothSdk.addListener(\"battery_status\", (event) => {\n const state = useGlassesStore.getState()\n state.setBatteryInfo(event.level, event.charging, state.caseBatteryLevel, state.caseCharging)\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n level: event.level,\n charging: event.charging,\n timestamp: event.timestamp ?? Date.now(),\n })\n }),\n )\n\n // Hotspot status → glasses store + event bus. engine's own gallerySyncService listens\n // on the bus for these, so without this bridge engine's gallery sync is dead.\n subs.push(\n BluetoothSdk.addListener(\"hotspot_status_change\", (event) => {\n const enabled = event.state === \"enabled\"\n const ssid = enabled ? event.ssid : \"\"\n const password = enabled ? event.password : \"\"\n const localIp = enabled ? event.localIp : \"\"\n useGlassesStore.getState().setHotspotInfo(enabled, ssid, password, localIp)\n // Always retarget the ASG camera client: on disable (or an event with no\n // IP) fall back to the localhost default so later calls fail fast instead\n // of hitting the previous network's stale glasses IP.\n asgCameraApi.setServer(localIp || \"localhost\", 8089)\n GlobalEventEmitter.emit(\"hotspot_status_change\", {enabled, ssid, password, local_ip: localIp})\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"hotspot_error\", (event) => {\n GlobalEventEmitter.emit(\"hotspot_error\", {error_message: event.errorMessage, timestamp: event.timestamp})\n }),\n )\n\n // Glasses gallery content counts → event bus (consumed by gallerySyncService).\n subs.push(\n BluetoothSdk.addListener(\"gallery_status\", (event) => {\n GlobalEventEmitter.emit(\"gallery_status\", {\n photos: event.photos,\n videos: event.videos,\n total: event.total,\n has_content: event.hasContent,\n camera_busy: event.cameraBusy,\n })\n }),\n )\n\n // Hardware-originated setting changes (user changes a setting ON the glasses) → store.\n // The inbound complement to GlassesSettingsSync (which only pushes store→device).\n subs.push(\n BluetoothSdk.addListener(\"save_setting\", async (event) => {\n const settings = useSettingsStore.getState()\n // Damp the relay: native re-echoes some settings unconditionally (e.g.\n // the identity block at every handleDeviceReady) — a same-value echo\n // must not become a store write (persistence churn + change-push noise).\n if (settings.getSetting(event.key) !== event.value) {\n await settings.setSetting(event.key, event.value)\n }\n // Two-phase identity: a promoted default retires the pending selection\n // marker. The rule lives with PairingIdentity (a no-op for other keys).\n await retirePendingSelectionOnPromotion(event.key, event.value)\n }),\n )\n\n // --- coordinators (owns()-gated) ---\n\n // Phone-owned photo errors settle the in-flight long-poll fast (vs. timeout).\n // Non-owned photo responses used to forward to the Cloud V1 photo pipeline via\n // restComms; that relay was removed with Cloud V1 app end-of-life, so they drop.\n // Wire v2 sends a short 4-hex BLE requestId; the coordinator maps it back to\n // the cloud requestId (resolveCloudRequestId) before ownership checks.\n subs.push(\n BluetoothSdk.addListener(\"photo_response\", (event) => {\n const cloudRequestId = event.requestId ? phonePhotoCoordinator.resolveCloudRequestId(event.requestId) : \"\"\n if (cloudRequestId && phonePhotoCoordinator.owns(cloudRequestId)) {\n if (event.state === \"error\") {\n phonePhotoCoordinator.handlePhotoError(\n event.requestId ?? cloudRequestId,\n event.errorCode ?? \"GLASSES_ERROR\",\n event.errorMessage ?? \"Glasses reported an error\",\n )\n }\n }\n }),\n )\n\n // Phone-owned stream status / keep-alive → the stream coordinator. The Cloud V1 relay\n // for non-owned streams was removed with Cloud V1 app end-of-life.\n subs.push(\n BluetoothSdk.addListener(\"stream_status\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleGlassesStatus(event)\n }\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"keep_alive_ack\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleKeepAliveAck(event)\n }\n }),\n )\n\n // --- device input → local miniapps (forwardEvent is subscriber-gated; a no-op when\n // no miniapp listens). The v1 SocketComms cloud legs for these were removed with\n // the Cloud V1 ripout. ---\n\n subs.push(\n BluetoothSdk.addListener(\"button_press\", (event) => {\n localMiniappRuntime.forwardEvent(\"button_press\", event)\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"touch_event\", (event) => {\n // While the custom dashboard is visible it owns its focus/navigation\n // gestures; do not leak those into a miniapp running underneath it.\n if (dashboardCoordinator.handleGesture(event.gestureName)) return\n\n // Gesture-dots debug indicator (any device, gated by its setting). It\n // only paints while the screen is otherwise empty; the gesture still\n // flows to miniapps below.\n gestureDotsService.noteGesture(event.gestureName)\n\n localMiniappRuntime.forwardEvent(\"touch_event\", event)\n // Double-tap policy, moved out of the native G2 handlers: exit a visible\n // unclaimed miniapp; otherwise open the dashboard or toggle head-up.\n // Native only reports the gesture now.\n if (event.gestureName === \"double_tap\") {\n // A foreground miniapp that claimed double_tap (CLAIM_GESTURES) is\n // using it as an in-app gesture (e.g. the terminal's back-to-menu) —\n // the event was already forwarded above; skip the system default.\n if (localMiniappRuntime.foregroundClaimsGesture(\"double_tap\")) return\n\n // An unclaimed double tap exits only the miniapp whose frame is visibly\n // on the glasses. The controller shows the bounded OS notice first,\n // then uses the normal store stop path (and closes its phone overlay).\n // Checked before the dashboard toggle: while an app owns the screen an\n // unclaimed double-tap always means \"leave the app\" — the (custom or\n // native) dashboard is for the empty screen, and head-up still opens\n // the custom dashboard mid-app.\n if (miniappExitGesture.tryExitVisibleMiniapp()) return\n\n // Eligible G2 custom firmware replaces the native dashboard.\n if (dashboardCoordinator.toggleCustom()) return\n\n const useNativeDashboard = useSettingsStore.getState().getSetting(SETTINGS.use_native_dashboard.key) !== false\n if (useNativeDashboard) {\n // The modal nav UI owns the screen; showDashboard would fight it.\n if (!nativeNavigationService.isActive) {\n void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {\n console.error(\"DeviceEventRouter: showDashboard failed:\", err)\n })\n }\n } else {\n dashboardCoordinator.toggleLegacy()\n }\n }\n }),\n )\n // G2 IMU accelerometer — payload already matches the miniapp AccelData shape.\n subs.push(\n BluetoothSdk.addListener(\"accel_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"accel_event\", {\n x: event.x,\n y: event.y,\n z: event.z,\n timestamp: typeof event.timestamp === \"number\" ? event.timestamp : Date.now(),\n })\n }),\n )\n // Head position — translate native {up:boolean} → SDK {position:\"up\"|\"down\"}.\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n dashboardCoordinator.setHeadUp(event.up)\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n // On-device STT transcripts → local miniapps only while the local engine is\n // active (cloud fallback or an explicit forceLocal subscription). The runtime\n // filters mixed local/cloud delivery per miniapp to avoid double-delivery.\n // forwardEvent is subscriber-gated — a no-op when no miniapp listens.\n // (Was MantleManager.handle_local_transcription; its offline-captions display branch\n // went away with the pseudo captions renderer.)\n subs.push(\n BluetoothSdk.addListener(\"local_transcription\", (event) => {\n if (!localSttFallbackCoordinator.isActive()) return\n const lang = event.transcribeLanguage ?? localSttFallbackCoordinator.getActiveLanguage() ?? \"en-US\"\n // Native stamps resolvedSpeakerId when the voice matched the enrolled\n // profile; surface that as `isUser` (the native join has no\n // confident-other signal, so unmatched stays unannotated).\n localMiniappRuntime.forwardEvent(\n `transcription:${lang}`,\n speakerLabelResolver.annotateLocalTranscript(event),\n \"local\",\n )\n }),\n )\n\n // --- glasses swipe-menu app launcher (G2) ---\n subs.push(\n BluetoothSdk.addListener(\"miniapp_selected\", (event) => {\n const packageName = event.packageName as string\n if (!packageName) return\n const app = useAppStatusStore.getState().apps.find((a) => a.packageName === packageName)\n if (!app) return\n // Toggle: stop if running, else start.\n if (app.running) {\n useAppStatusStore.getState().stop(packageName)\n } else {\n useAppStatusStore.getState().start(app, {skipNavigation: true})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n miniappExitGesture.reset()\n}\n"]}
1
+ {"version":3,"file":"DeviceEventRouter.js","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Device-event router — engine-owned. Subscribes to the inbound native BLE/device\n * events and routes them into the engine runtime: device stores, the process event\n * bus, the photo/stream coordinators, and local miniapps (via forwardEvent).\n *\n * Why this exists: engine owns the stores, coordinators, miniapp runtime, and facades,\n * but it never *subscribed to the device* for most events — the host MantleManager was\n * still the event router the whole runtime secretly depended on. So a bare OEM that\n * imported engine + called engine.start() got a connected runtime with almost no device\n * data flowing in (no miniapp input, dead gallery sync, starved coordinators). This\n * service moves those inbound bridges into engine so ANY host gets them.\n *\n * Scope: the engine legs only. The Cloud V1 relays that used to live beside them in\n * MantleManager (touch→cloud, the cloud-SDK stream/photo legs, etc.) were deleted with\n * Cloud V1 app end-of-life.\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport {shallow} from \"zustand/shallow\"\n\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport glassesKeyboardInput from \"./GlassesKeyboardInput\"\nimport glassesSensorService from \"./GlassesSensorService\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport speakerLabelResolver from \"./SpeakerLabelResolver\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\nimport {dispatchTouchEvent, resetTouchInput} from \"./GlassesTouchInput\"\nimport dashboardOpenTrace from \"./debug/DashboardOpenTrace\"\n\nlet subs: Array<{remove: () => void}> = []\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n subs.push(\n BluetoothSdk.addListener(\"light_sensor\", ({type: _type, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"light_sensor\", reading)\n }),\n // Stock firmware emits these only inside its nav UI; custom firmware whenever the\n // raw compass runs. Either way they feed the one session.compass stream.\n BluetoothSdk.addListener(\"compass_heading\", ({type: _type, heading, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"glasses_compass\", {degrees: heading, ...reading})\n }),\n BluetoothSdk.addListener(\"compass_calibration\", ({type: _type, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"compass_calibration\", reading)\n }),\n {\n remove: useGlassesStore.subscribe(\n (s) => s.connection,\n (connection) => {\n if (isGlassesConnected(connection)) void glassesSensorService.resync()\n else glassesSensorService.disconnected()\n },\n ),\n },\n {\n // CFW classification lands after device-ready; compass demand recorded before\n // that only reaches the firmware once the scene canvas is published.\n remove: useGlassesStore.subscribe(\n (s) => s.sceneCanvasWidth,\n (width) => {\n if ((width ?? 0) > 0) void glassesSensorService.resync()\n },\n ),\n },\n )\n\n // --- device state → engine stores ---\n\n // Standalone WiFi status → glasses store.\n subs.push(\n BluetoothSdk.addListener(\"wifi_status_change\", (event) => {\n const {type: _type, ...wifi} = event\n useGlassesStore.getState().setGlassesInfo({wifi})\n }),\n )\n\n // Forward glasses Wi-Fi to miniapps (session.glasses.onWifi) from the STORE — the\n // single source of truth — so every path converges here: wifi_status_change,\n // onGlassesStatus, and BLE disconnect. Effective connectivity requires the glasses\n // to be connected AND on Wi-Fi, so a disconnect correctly flips `connected` false.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => {\n const connected = isGlassesConnected(s.connection) && s.wifi.state === \"connected\"\n return {\n connected,\n ssid: s.wifi.state === \"connected\" ? s.wifi.ssid : undefined,\n localIp: s.wifi.state === \"connected\" ? s.wifi.localIp : undefined,\n }\n },\n (wifi) => localMiniappRuntime.forwardEvent(\"glasses_wifi\", wifi),\n {equalityFn: shallow},\n ),\n })\n\n // Battery streams follow the store, including initial native hydration and\n // onGlassesStatus patches. G2 can report its level without battery_status;\n // forwarding only that legacy event leaves a running dashboard ring-only.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => ({level: s.batteryLevel, charging: s.charging}),\n (battery) => {\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n ...battery,\n timestamp: Date.now(),\n })\n },\n {equalityFn: shallow},\n ),\n })\n\n // Legacy standalone events feed the same store subscription.\n subs.push(\n BluetoothSdk.addListener(\"battery_status\", (event) => {\n const state = useGlassesStore.getState()\n state.setBatteryInfo(event.level, event.charging, state.caseBatteryLevel, state.caseCharging)\n }),\n )\n\n // Hotspot status → glasses store + event bus. engine's own gallerySyncService listens\n // on the bus for these, so without this bridge engine's gallery sync is dead.\n subs.push(\n BluetoothSdk.addListener(\"hotspot_status_change\", (event) => {\n const enabled = event.state === \"enabled\"\n const ssid = enabled ? event.ssid : \"\"\n const password = enabled ? event.password : \"\"\n const localIp = enabled ? event.localIp : \"\"\n useGlassesStore.getState().setHotspotInfo(enabled, ssid, password, localIp)\n // Always retarget the ASG camera client: on disable (or an event with no\n // IP) fall back to the localhost default so later calls fail fast instead\n // of hitting the previous network's stale glasses IP.\n asgCameraApi.setServer(localIp || \"localhost\", 8089)\n GlobalEventEmitter.emit(\"hotspot_status_change\", {enabled, ssid, password, local_ip: localIp})\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"hotspot_error\", (event) => {\n GlobalEventEmitter.emit(\"hotspot_error\", {error_message: event.errorMessage, timestamp: event.timestamp})\n }),\n )\n\n // Glasses gallery content counts → event bus (consumed by gallerySyncService).\n subs.push(\n BluetoothSdk.addListener(\"gallery_status\", (event) => {\n GlobalEventEmitter.emit(\"gallery_status\", {\n photos: event.photos,\n videos: event.videos,\n total: event.total,\n has_content: event.hasContent,\n camera_busy: event.cameraBusy,\n })\n }),\n )\n\n // Hardware-originated setting changes (user changes a setting ON the glasses) → store.\n // The inbound complement to GlassesSettingsSync (which only pushes store→device).\n subs.push(\n BluetoothSdk.addListener(\"save_setting\", async (event) => {\n const settings = useSettingsStore.getState()\n // Damp the relay: native re-echoes some settings unconditionally (e.g.\n // the identity block at every handleDeviceReady) — a same-value echo\n // must not become a store write (persistence churn + change-push noise).\n if (settings.getSetting(event.key) !== event.value) {\n await settings.setSetting(event.key, event.value)\n }\n // Two-phase identity: a promoted default retires the pending selection\n // marker. The rule lives with PairingIdentity (a no-op for other keys).\n await retirePendingSelectionOnPromotion(event.key, event.value)\n }),\n )\n\n // --- coordinators (owns()-gated) ---\n\n // Phone-owned photo errors settle the in-flight long-poll fast (vs. timeout).\n // Non-owned photo responses used to forward to the Cloud V1 photo pipeline via\n // restComms; that relay was removed with Cloud V1 app end-of-life, so they drop.\n // Wire v2 sends a short 4-hex BLE requestId; the coordinator maps it back to\n // the cloud requestId (resolveCloudRequestId) before ownership checks.\n subs.push(\n BluetoothSdk.addListener(\"photo_response\", (event) => {\n const cloudRequestId = event.requestId ? phonePhotoCoordinator.resolveCloudRequestId(event.requestId) : \"\"\n if (cloudRequestId && phonePhotoCoordinator.owns(cloudRequestId)) {\n if (event.state === \"error\") {\n phonePhotoCoordinator.handlePhotoError(\n event.requestId ?? cloudRequestId,\n event.errorCode ?? \"GLASSES_ERROR\",\n event.errorMessage ?? \"Glasses reported an error\",\n )\n }\n }\n }),\n )\n\n // Phone-owned stream status / keep-alive → the stream coordinator. The Cloud V1 relay\n // for non-owned streams was removed with Cloud V1 app end-of-life.\n subs.push(\n BluetoothSdk.addListener(\"stream_status\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleGlassesStatus(event)\n }\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"keep_alive_ack\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleKeepAliveAck(event)\n }\n }),\n )\n\n // --- device input → local miniapps (forwardEvent is subscriber-gated; a no-op when\n // no miniapp listens). The v1 SocketComms cloud legs for these were removed with\n // the Cloud V1 ripout. ---\n\n subs.push(\n BluetoothSdk.addListener(\"button_press\", (event) => {\n localMiniappRuntime.forwardEvent(\"button_press\", event)\n }),\n )\n subs.push(BluetoothSdk.addListener(\"touch_event\", dispatchTouchEvent))\n // Physical keyboard bridged through G2 Glassly CFW: raw HID transitions →\n // layout-translated keyboard_event stream (with synthesized auto-repeat).\n subs.push(BluetoothSdk.addListener(\"keyboard_key\", (event) => glassesKeyboardInput.handle(event)))\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => s.keyboardConnected === true,\n (connected) => {\n if (!connected) glassesKeyboardInput.reset()\n },\n ),\n })\n // G2 IMU accelerometer — payload already matches the miniapp AccelData shape.\n subs.push(\n BluetoothSdk.addListener(\"accel_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"accel_event\", {\n x: event.x,\n y: event.y,\n z: event.z,\n timestamp: typeof event.timestamp === \"number\" ? event.timestamp : Date.now(),\n })\n }),\n )\n // Head position — translate native {up:boolean} → SDK {position:\"up\"|\"down\"}.\n // The dashboard miniapp opens on head-up from this stream (ADR-0006).\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n if (event.up) dashboardOpenTrace.trigger(\"head-up\")\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n subs.push({remove: dashboardOpenTrace.start()})\n // On-device STT transcripts → local miniapps only while the local engine is\n // active (cloud fallback or an explicit forceLocal subscription). The runtime\n // filters mixed local/cloud delivery per miniapp to avoid double-delivery.\n // forwardEvent is subscriber-gated — a no-op when no miniapp listens.\n // (Was MantleManager.handle_local_transcription; its offline-captions display branch\n // went away with the pseudo captions renderer.)\n subs.push(\n BluetoothSdk.addListener(\"local_transcription\", (event) => {\n if (!localSttFallbackCoordinator.isActive()) return\n const lang = event.transcribeLanguage ?? localSttFallbackCoordinator.getActiveLanguage() ?? \"en-US\"\n // Native stamps resolvedSpeakerId when the voice matched the enrolled\n // profile; surface that as `isUser` (the native join has no\n // confident-other signal, so unmatched stays unannotated).\n localMiniappRuntime.forwardEvent(\n `transcription:${lang}`,\n speakerLabelResolver.annotateLocalTranscript(event),\n \"local\",\n )\n }),\n )\n\n // --- glasses swipe-menu app launcher (G2) ---\n subs.push(\n BluetoothSdk.addListener(\"miniapp_selected\", (event) => {\n const packageName = event.packageName as string\n if (!packageName) return\n const app = useAppStatusStore.getState().apps.find((a) => a.packageName === packageName)\n if (!app) return\n // Toggle: stop if running, else start.\n if (app.running) {\n useAppStatusStore.getState().stop(packageName)\n } else {\n useAppStatusStore.getState().start(app, {skipNavigation: true, launchSource: \"glasses\"})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n resetTouchInput()\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { DisplayDiagnostic } from "@glassly/miniapp";
2
+ export declare function collectDisplayDiagnostics(entries: readonly {
3
+ message: string;
4
+ }[], now?: number): DisplayDiagnostic[];
5
+ //# sourceMappingURL=DisplayDiagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DisplayDiagnostics.d.ts","sourceRoot":"","sources":["../../src/services/DisplayDiagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAA;AAkEvD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,SAAS;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,EAAE,EACrC,GAAG,SAAa,GACf,iBAAiB,EAAE,CAqCrB"}
@@ -0,0 +1,105 @@
1
+ // Explicit allowlist: the diagnostics API must never expose ordinary console
2
+ // messages, packet bytes, or arbitrary application content to another miniapp.
3
+ const fields = new Set([
4
+ "v",
5
+ "run",
6
+ "id",
7
+ "t",
8
+ "type",
9
+ "app",
10
+ "seq",
11
+ "page",
12
+ "pageGeneration",
13
+ "qL",
14
+ "qR",
15
+ "payloadQueue",
16
+ "pendingFrame",
17
+ "cfw",
18
+ "mic",
19
+ "background",
20
+ "fpsLimit",
21
+ "windowMs",
22
+ "txBytes",
23
+ "txPackets",
24
+ "txBytesL",
25
+ "txBytesR",
26
+ "framesIn",
27
+ "framesEncoded",
28
+ "framesAcked",
29
+ "coalesced",
30
+ "errors",
31
+ "timeouts",
32
+ "nacks",
33
+ "resync",
34
+ "vectorFrames",
35
+ "rasterFrames",
36
+ "noop",
37
+ "fragments",
38
+ "elements",
39
+ "animatedElements",
40
+ "payloadBytes",
41
+ "uploadBytes",
42
+ "encodeErrors",
43
+ "stockTextWrites",
44
+ "stockTextBytes",
45
+ "imuSamples",
46
+ "session",
47
+ "fragment",
48
+ "fragments",
49
+ "mode",
50
+ "bytes",
51
+ "totalMs",
52
+ "result",
53
+ "written",
54
+ "attempt",
55
+ "stage",
56
+ "ok",
57
+ "mtuL",
58
+ "mtuR",
59
+ "encodeMs",
60
+ "vectorFallback",
61
+ ]);
62
+ const timing = /^(creditWaitMs[LR]|writeQueueMs|transferMs|submitMs|writeMs|ackMs|resumeMs|frameMs|encodeMs|frameQueueMs)(Avg|Max|Total)$/;
63
+ export function collectDisplayDiagnostics(entries, now = Date.now()) {
64
+ const records = [];
65
+ const seen = new Set();
66
+ for (let i = entries.length - 1; i >= 0 && records.length < 180; i--) {
67
+ const message = entries[i].message;
68
+ const offset = message.indexOf("G2PERF ");
69
+ if (offset < 0 || message.length > 8192)
70
+ continue;
71
+ try {
72
+ const raw = JSON.parse(message.slice(offset + 7));
73
+ if (!raw ||
74
+ raw.v !== 1 ||
75
+ typeof raw.run !== "string" ||
76
+ raw.run.length > 64 ||
77
+ !Number.isSafeInteger(raw.id) ||
78
+ raw.id < 1 ||
79
+ !Number.isSafeInteger(raw.t) ||
80
+ raw.t < now - 180_000 ||
81
+ raw.t > now + 5000 ||
82
+ !["sample", "stall", "session"].includes(raw.type))
83
+ continue;
84
+ const key = `${raw.run}:${raw.id}`;
85
+ if (seen.has(key))
86
+ continue;
87
+ const record = {};
88
+ for (const [name, value] of Object.entries(raw)) {
89
+ if (!fields.has(name) && !timing.test(name))
90
+ continue;
91
+ if ((typeof value === "number" && Number.isFinite(value)) || typeof value === "boolean")
92
+ record[name] = value;
93
+ else if (typeof value === "string")
94
+ record[name] = value.slice(0, 160);
95
+ }
96
+ records.push(record);
97
+ seen.add(key);
98
+ }
99
+ catch {
100
+ /* Ignore unrelated or incomplete console lines. */
101
+ }
102
+ }
103
+ return records.reverse();
104
+ }
105
+ //# sourceMappingURL=DisplayDiagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DisplayDiagnostics.js","sourceRoot":"","sources":["../../src/services/DisplayDiagnostics.ts"],"names":[],"mappings":"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","sourcesContent":["import type {DisplayDiagnostic} from \"@glassly/miniapp\"\n\n// Explicit allowlist: the diagnostics API must never expose ordinary console\n// messages, packet bytes, or arbitrary application content to another miniapp.\nconst fields = new Set([\n \"v\",\n \"run\",\n \"id\",\n \"t\",\n \"type\",\n \"app\",\n \"seq\",\n \"page\",\n \"pageGeneration\",\n \"qL\",\n \"qR\",\n \"payloadQueue\",\n \"pendingFrame\",\n \"cfw\",\n \"mic\",\n \"background\",\n \"fpsLimit\",\n \"windowMs\",\n \"txBytes\",\n \"txPackets\",\n \"txBytesL\",\n \"txBytesR\",\n \"framesIn\",\n \"framesEncoded\",\n \"framesAcked\",\n \"coalesced\",\n \"errors\",\n \"timeouts\",\n \"nacks\",\n \"resync\",\n \"vectorFrames\",\n \"rasterFrames\",\n \"noop\",\n \"fragments\",\n \"elements\",\n \"animatedElements\",\n \"payloadBytes\",\n \"uploadBytes\",\n \"encodeErrors\",\n \"stockTextWrites\",\n \"stockTextBytes\",\n \"imuSamples\",\n \"session\",\n \"fragment\",\n \"fragments\",\n \"mode\",\n \"bytes\",\n \"totalMs\",\n \"result\",\n \"written\",\n \"attempt\",\n \"stage\",\n \"ok\",\n \"mtuL\",\n \"mtuR\",\n \"encodeMs\",\n \"vectorFallback\",\n])\nconst timing =\n /^(creditWaitMs[LR]|writeQueueMs|transferMs|submitMs|writeMs|ackMs|resumeMs|frameMs|encodeMs|frameQueueMs)(Avg|Max|Total)$/\n\nexport function collectDisplayDiagnostics(\n entries: readonly {message: string}[],\n now = Date.now(),\n): DisplayDiagnostic[] {\n const records: DisplayDiagnostic[] = []\n const seen = new Set<string>()\n for (let i = entries.length - 1; i >= 0 && records.length < 180; i--) {\n const message = entries[i].message\n const offset = message.indexOf(\"G2PERF \")\n if (offset < 0 || message.length > 8192) continue\n try {\n const raw = JSON.parse(message.slice(offset + 7))\n if (\n !raw ||\n raw.v !== 1 ||\n typeof raw.run !== \"string\" ||\n raw.run.length > 64 ||\n !Number.isSafeInteger(raw.id) ||\n raw.id < 1 ||\n !Number.isSafeInteger(raw.t) ||\n raw.t < now - 180_000 ||\n raw.t > now + 5000 ||\n ![\"sample\", \"stall\", \"session\"].includes(raw.type)\n )\n continue\n const key = `${raw.run}:${raw.id}`\n if (seen.has(key)) continue\n const record: Record<string, string | number | boolean> = {}\n for (const [name, value] of Object.entries(raw)) {\n if (!fields.has(name) && !timing.test(name)) continue\n if ((typeof value === \"number\" && Number.isFinite(value)) || typeof value === \"boolean\") record[name] = value\n else if (typeof value === \"string\") record[name] = value.slice(0, 160)\n }\n records.push(record as DisplayDiagnostic)\n seen.add(key)\n } catch {\n /* Ignore unrelated or incomplete console lines. */\n }\n }\n return records.reverse()\n}\n"]}
@@ -13,10 +13,7 @@
13
13
  * @see notes/miniapp-display-api-redesign.md for the current display design
14
14
  */
15
15
  import { type DisplayProfile, type WrapOptions, type BreakMode } from "../utils/display";
16
- /**
17
- * Supported device models for display processing
18
- */
19
- export type DeviceModel = "g1" | "g2" | "z100" | "nex" | "mach1" | "mentra-live" | "simulated" | "unknown";
16
+ import { DeviceTypes } from "../types/enums";
20
17
  /**
21
18
  * Display event types that we process
22
19
  */
@@ -90,7 +87,7 @@ export interface DisplayProcessorOptions {
90
87
  * const processor = DisplayProcessor.getInstance()
91
88
  *
92
89
  * // When glasses connect
93
- * processor.setDeviceModel("Even Realities G1")
90
+ * processor.setDeviceModel(DeviceTypes.G1)
94
91
  *
95
92
  * // Process display events
96
93
  * const processed = processor.processDisplayEvent(rawEvent)
@@ -136,7 +133,7 @@ export declare class DisplayProcessor {
136
133
  /**
137
134
  * Get the current device model
138
135
  */
139
- getDeviceModel(): DeviceModel;
136
+ getDeviceModel(): DeviceTypes;
140
137
  /**
141
138
  * Get the current display profile
142
139
  */
@@ -188,6 +185,8 @@ export declare class DisplayProcessor {
188
185
  * @returns Width in pixels
189
186
  */
190
187
  measureText(text: string): number;
188
+ /** Rendered advance of one character on the current device's font, in canvas px. */
189
+ measureChar(char: string): number;
191
190
  /**
192
191
  * Check if text fits on a single line
193
192
  *
@@ -206,7 +205,7 @@ export declare class DisplayProcessor {
206
205
  /**
207
206
  * Get detailed text measurement
208
207
  */
209
- measureTextDetailed(text: string): import("../utils/display").TextMeasurement;
208
+ measureTextDetailed(text: string): import("@glassly/miniapp/display").TextMeasurement;
210
209
  /**
211
210
  * Set the break mode for text wrapping
212
211
  *
@@ -1 +1 @@
1
- {"version":3,"file":"DisplayProcessor.d.ts","sourceRoot":"","sources":["../../src/services/DisplayProcessor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DisplayProcessor.d.ts","sourceRoot":"","sources":["../../src/services/DisplayProcessor.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@ import { createDisplayToolkit, G1_PROFILE, G2_PROFILE, Z100_PROFILE, NEX_PROFILE
16
16
  import { ISLAND_SETTINGS_KEYS } from "../runtime/config";
17
17
  import { useGlassesStore } from "../stores/glasses";
18
18
  import { useSettingsStore } from "../stores/settings";
19
+ import { DeviceTypes } from "../types/enums";
19
20
  import { isGlassesConnected } from "./GlassesReadiness";
20
21
  /**
21
22
  * Get current placeholder values from device state
@@ -73,45 +74,12 @@ function isBreakMode(value) {
73
74
  * Map device model names to display profiles
74
75
  */
75
76
  const DEVICE_PROFILES = {
76
- "g1": G1_PROFILE,
77
- "g2": G2_PROFILE,
78
- "z100": Z100_PROFILE,
79
- "nex": NEX_PROFILE,
80
- "mach1": Z100_PROFILE, // Mach1 uses same hardware as Vuzix Z100
81
- "mentra-live": G1_PROFILE, // Mentra Live has no display, uses G1 as fallback
82
- "simulated": G1_PROFILE, // Simulated uses G1 profile
83
- "unknown": G1_PROFILE, // Default to G1
77
+ [DeviceTypes.G1]: G1_PROFILE,
78
+ [DeviceTypes.G2]: G2_PROFILE,
79
+ [DeviceTypes.Z100]: Z100_PROFILE,
80
+ [DeviceTypes.NEX]: NEX_PROFILE,
81
+ [DeviceTypes.MACH1]: Z100_PROFILE, // Mach1 uses same hardware as Vuzix Z100
84
82
  };
85
- /**
86
- * Normalize various model name strings to our DeviceModel type
87
- */
88
- function normalizeModelName(modelName) {
89
- if (!modelName)
90
- return "unknown";
91
- const lower = modelName.toLowerCase();
92
- if (lower.includes("g2") || lower.includes("even realities g2")) {
93
- return "g2";
94
- }
95
- if (lower.includes("g1") || lower.includes("even realities")) {
96
- return "g1";
97
- }
98
- if (lower.includes("z100") || lower.includes("vuzix")) {
99
- return "z100";
100
- }
101
- if (lower.includes("nex") || lower.includes("glassly display")) {
102
- return "nex";
103
- }
104
- if (lower.includes("mach1") || lower.includes("mach 1")) {
105
- return "mach1";
106
- }
107
- if (lower.includes("glassly live") || lower.includes("mentra-live")) {
108
- return "mentra-live";
109
- }
110
- if (lower.includes("simulated") || lower.includes("simulator")) {
111
- return "simulated";
112
- }
113
- return "unknown";
114
- }
115
83
  // =============================================================================
116
84
  // DisplayProcessor Class
117
85
  // =============================================================================
@@ -123,7 +91,7 @@ function normalizeModelName(modelName) {
123
91
  * const processor = DisplayProcessor.getInstance()
124
92
  *
125
93
  * // When glasses connect
126
- * processor.setDeviceModel("Even Realities G1")
94
+ * processor.setDeviceModel(DeviceTypes.G1)
127
95
  *
128
96
  * // Process display events
129
97
  * const processed = processor.processDisplayEvent(rawEvent)
@@ -135,7 +103,7 @@ export class DisplayProcessor {
135
103
  wrapper;
136
104
  composer;
137
105
  profile;
138
- deviceModel = "unknown";
106
+ deviceModel = DeviceTypes.NONE;
139
107
  options;
140
108
  runtimeAttached = false;
141
109
  unsubscribeGlassesStatus = null;
@@ -212,17 +180,17 @@ export class DisplayProcessor {
212
180
  * @param modelName - The model name from the connected glasses (e.g., "Even Realities G1")
213
181
  */
214
182
  setDeviceModel(modelName) {
215
- const normalizedModel = normalizeModelName(modelName);
216
- if (normalizedModel === this.deviceModel) {
183
+ const model = Object.values(DeviceTypes).find((deviceType) => deviceType === modelName) ?? DeviceTypes.NONE;
184
+ if (model === this.deviceModel) {
217
185
  return; // No change needed
218
186
  }
219
- this.deviceModel = normalizedModel;
220
- const newProfile = DEVICE_PROFILES[normalizedModel];
187
+ this.deviceModel = model;
188
+ const newProfile = DEVICE_PROFILES[model] ?? G1_PROFILE;
221
189
  if (newProfile !== this.profile) {
222
190
  this.updateProfile(newProfile);
223
191
  }
224
192
  if (this.options.debug) {
225
- console.log(`[DisplayProcessor] Device model set to: ${normalizedModel} (from: ${modelName})`);
193
+ console.log(`[DisplayProcessor] Device model set to: ${model} (from: ${modelName})`);
226
194
  }
227
195
  }
228
196
  /**
@@ -496,6 +464,10 @@ export class DisplayProcessor {
496
464
  measureText(text) {
497
465
  return this.measurer.measureText(text);
498
466
  }
467
+ /** Rendered advance of one character on the current device's font, in canvas px. */
468
+ measureChar(char) {
469
+ return this.measurer.measureChar(char);
470
+ }
499
471
  /**
500
472
  * Check if text fits on a single line
501
473
  *