@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,306 @@
1
+ import type {KeyboardKeyEvent} from "@glassly/bluetooth-sdk"
2
+
3
+ import localMiniappRuntime from "./LocalMiniappRuntime"
4
+ import {BgTimer} from "../utils/timers"
5
+
6
+ /**
7
+ * Keyboard event as miniapps receive it on the `keyboard_event` stream. The
8
+ * shape mirrors the DOM: `key` is the logical key (layout applied, US only for
9
+ * now), `code` the physical key, `text` the printable character the press
10
+ * produces (down/repeat only), `repeat` a synthesized auto-repeat of a held key.
11
+ */
12
+ export interface KeyboardEventPayload {
13
+ phase: "down" | "up"
14
+ key: string
15
+ code: string
16
+ modifiers: {shift: boolean; ctrl: boolean; alt: boolean; meta: boolean}
17
+ text?: string
18
+ repeat: boolean
19
+ timestamp: number
20
+ }
21
+
22
+ interface Timers {
23
+ setTimeout: (callback: () => void, delayMs: number) => number
24
+ clearTimeout: (id: number) => void
25
+ }
26
+
27
+ export interface KeyboardInputOptions {
28
+ forward: (payload: KeyboardEventPayload) => void
29
+ timers?: Timers
30
+ now?: () => number
31
+ /** Typematic delay before the first repeat and the interval between repeats. */
32
+ repeatDelayMs?: number
33
+ repeatIntervalMs?: number
34
+ }
35
+
36
+ const REPEAT_DELAY_MS = 400
37
+ const REPEAT_INTERVAL_MS = 50
38
+
39
+ /** [unshifted, shifted] for US layout punctuation on the keyboard page. */
40
+ const PUNCTUATION: Record<number, [string, string, string]> = {
41
+ 45: ["-", "_", "Minus"],
42
+ 46: ["=", "+", "Equal"],
43
+ 47: ["[", "{", "BracketLeft"],
44
+ 48: ["]", "}", "BracketRight"],
45
+ 49: ["\\", "|", "Backslash"],
46
+ 50: ["#", "~", "IntlHash"],
47
+ 51: [";", ":", "Semicolon"],
48
+ 52: ["'", '"', "Quote"],
49
+ 53: ["`", "~", "Backquote"],
50
+ 54: [",", "<", "Comma"],
51
+ 55: [".", ">", "Period"],
52
+ 56: ["/", "?", "Slash"],
53
+ 100: ["\\", "|", "IntlBackslash"],
54
+ }
55
+
56
+ const SHIFTED_DIGITS = "!@#$%^&*()"
57
+ const DIGITS = "1234567890"
58
+
59
+ const NAMED: Record<number, [string, string]> = {
60
+ 40: ["Enter", "Enter"],
61
+ 41: ["Escape", "Escape"],
62
+ 42: ["Backspace", "Backspace"],
63
+ 43: ["Tab", "Tab"],
64
+ 57: ["CapsLock", "CapsLock"],
65
+ 70: ["PrintScreen", "PrintScreen"],
66
+ 71: ["ScrollLock", "ScrollLock"],
67
+ 72: ["Pause", "Pause"],
68
+ 73: ["Insert", "Insert"],
69
+ 74: ["Home", "Home"],
70
+ 75: ["PageUp", "PageUp"],
71
+ 76: ["Delete", "Delete"],
72
+ 77: ["End", "End"],
73
+ 78: ["PageDown", "PageDown"],
74
+ 79: ["ArrowRight", "ArrowRight"],
75
+ 80: ["ArrowLeft", "ArrowLeft"],
76
+ 81: ["ArrowDown", "ArrowDown"],
77
+ 82: ["ArrowUp", "ArrowUp"],
78
+ 83: ["NumLock", "NumLock"],
79
+ 88: ["Enter", "NumpadEnter"],
80
+ 101: ["ContextMenu", "ContextMenu"],
81
+ 102: ["Power", "Power"],
82
+ 224: ["Control", "ControlLeft"],
83
+ 225: ["Shift", "ShiftLeft"],
84
+ 226: ["Alt", "AltLeft"],
85
+ 227: ["Meta", "MetaLeft"],
86
+ 228: ["Control", "ControlRight"],
87
+ 229: ["Shift", "ShiftRight"],
88
+ 230: ["Alt", "AltRight"],
89
+ 231: ["Meta", "MetaRight"],
90
+ }
91
+
92
+ const NUMPAD: Record<number, [string, string]> = {
93
+ 84: ["/", "NumpadDivide"],
94
+ 85: ["*", "NumpadMultiply"],
95
+ 86: ["-", "NumpadSubtract"],
96
+ 87: ["+", "NumpadAdd"],
97
+ 99: [".", "NumpadDecimal"],
98
+ 103: ["=", "NumpadEqual"],
99
+ }
100
+
101
+ const CONSUMER: Record<number, string> = {
102
+ 0xe9: "AudioVolumeUp",
103
+ 0xea: "AudioVolumeDown",
104
+ 0xe2: "AudioVolumeMute",
105
+ 0xcd: "MediaPlayPause",
106
+ 0xb0: "MediaPlay",
107
+ 0xb1: "MediaPause",
108
+ 0xb5: "MediaTrackNext",
109
+ 0xb6: "MediaTrackPrevious",
110
+ 0xb7: "MediaStop",
111
+ 0x6f: "BrightnessUp",
112
+ 0x70: "BrightnessDown",
113
+ 0x221: "BrowserSearch",
114
+ 0x223: "BrowserHome",
115
+ 0x224: "BrowserBack",
116
+ 0x225: "BrowserForward",
117
+ 0x18a: "LaunchMail",
118
+ 0x192: "LaunchApplication2",
119
+ 0x194: "LaunchApplication1",
120
+ }
121
+
122
+ interface Translated {
123
+ key: string
124
+ code: string
125
+ text?: string
126
+ }
127
+
128
+ /**
129
+ * Map one HID usage to DOM-style key/code/text under the US layout. Exported
130
+ * for tests; `shift` is the effective shift (Shift XOR CapsLock for letters).
131
+ */
132
+ export function translateUsage(page: number, usage: number, shift: boolean, capsLock: boolean): Translated {
133
+ if (page === 7) {
134
+ if (usage >= 4 && usage <= 29) {
135
+ const lower = String.fromCharCode(97 + usage - 4)
136
+ const upper = shift !== capsLock
137
+ const key = upper ? lower.toUpperCase() : lower
138
+ return {key, code: `Key${lower.toUpperCase()}`, text: key}
139
+ }
140
+ if (usage >= 30 && usage <= 39) {
141
+ const index = usage - 30
142
+ const key = shift ? SHIFTED_DIGITS[index]! : DIGITS[index]!
143
+ return {key, code: `Digit${DIGITS[index]}`, text: key}
144
+ }
145
+ if (usage === 44) return {key: " ", code: "Space", text: " "}
146
+ const punctuation = PUNCTUATION[usage]
147
+ if (punctuation) {
148
+ const key = shift ? punctuation[1] : punctuation[0]
149
+ return {key, code: punctuation[2], text: key}
150
+ }
151
+ if (usage >= 58 && usage <= 69) return {key: `F${usage - 57}`, code: `F${usage - 57}`}
152
+ if (usage >= 104 && usage <= 115) return {key: `F${usage - 91}`, code: `F${usage - 91}`}
153
+ if (usage >= 89 && usage <= 97) {
154
+ const digit = String(usage - 88)
155
+ return {key: digit, code: `Numpad${digit}`, text: digit}
156
+ }
157
+ if (usage === 98) return {key: "0", code: "Numpad0", text: "0"}
158
+ const numpad = NUMPAD[usage]
159
+ if (numpad) return {key: numpad[0], code: numpad[1], text: numpad[0]}
160
+ const named = NAMED[usage]
161
+ if (named) return {key: named[0], code: named[1]}
162
+ return {key: "Unidentified", code: `Usage${usage.toString(16).padStart(2, "0")}`}
163
+ }
164
+ if (page === 12) {
165
+ const name = CONSUMER[usage]
166
+ return name ? {key: name, code: name} : {key: "Unidentified", code: `Consumer${usage.toString(16)}`}
167
+ }
168
+ return {key: "Unidentified", code: `Page${page.toString(16)}Usage${usage.toString(16)}`}
169
+ }
170
+
171
+ const MODIFIER_USAGES = new Set([224, 225, 226, 227, 228, 229, 230, 231])
172
+
173
+ /**
174
+ * Turns the native bridge's raw HID transitions into layout-translated
175
+ * keyboard events and synthesizes typematic auto-repeat, which BLE HID
176
+ * keyboards never send (the report just stays asserted). Modifier state is
177
+ * derived from the same stream, so a lost release (the bridge emits an `up`
178
+ * for every gap) always clears it.
179
+ */
180
+ export class GlassesKeyboardInput {
181
+ private readonly forward: (payload: KeyboardEventPayload) => void
182
+ private readonly timers: Timers
183
+ private readonly now: () => number
184
+ private readonly repeatDelayMs: number
185
+ private readonly repeatIntervalMs: number
186
+ private held = new Map<string, {page: number; usage: number}>()
187
+ private modifiers = {shift: false, ctrl: false, alt: false, meta: false}
188
+ private capsLock = false
189
+ private repeatTimer: number | null = null
190
+ private repeating: {page: number; usage: number} | null = null
191
+
192
+ constructor(options: KeyboardInputOptions) {
193
+ this.forward = options.forward
194
+ this.timers = options.timers ?? {
195
+ setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),
196
+ clearTimeout: (id) => BgTimer.clearTimeout(id),
197
+ }
198
+ this.now = options.now ?? (() => Date.now())
199
+ this.repeatDelayMs = options.repeatDelayMs ?? REPEAT_DELAY_MS
200
+ this.repeatIntervalMs = options.repeatIntervalMs ?? REPEAT_INTERVAL_MS
201
+ }
202
+
203
+ /** One raw transition from the `keyboard_key` native event. */
204
+ handle(event: KeyboardKeyEvent): void {
205
+ const id = `${event.page}:${event.usage}`
206
+ if (event.down) {
207
+ if (this.held.has(id)) return // duplicate report; the firmware already de-duplicates
208
+ this.held.set(id, {page: event.page, usage: event.usage})
209
+ this.applyModifier(event.page, event.usage, true)
210
+ if (event.page === 7 && event.usage === 57) this.capsLock = !this.capsLock
211
+ this.emit("down", event.page, event.usage, false, event.timestamp)
212
+ if (!(event.page === 7 && MODIFIER_USAGES.has(event.usage))) this.startRepeat(event.page, event.usage)
213
+ } else {
214
+ if (!this.held.delete(id)) return
215
+ this.applyModifier(event.page, event.usage, false)
216
+ if (this.repeating?.page === event.page && this.repeating.usage === event.usage) this.stopRepeat()
217
+ this.emit("up", event.page, event.usage, false, event.timestamp)
218
+ }
219
+ }
220
+
221
+ /** Link lost or keyboard dropped: forget everything without emitting. */
222
+ reset(): void {
223
+ this.stopRepeat()
224
+ this.held.clear()
225
+ this.modifiers = {shift: false, ctrl: false, alt: false, meta: false}
226
+ this.capsLock = false
227
+ }
228
+
229
+ private applyModifier(page: number, usage: number, down: boolean): void {
230
+ if (page !== 7) return
231
+ switch (usage) {
232
+ case 224:
233
+ case 228:
234
+ this.modifiers.ctrl = down || this.otherHeld(usage === 224 ? 228 : 224)
235
+ break
236
+ case 225:
237
+ case 229:
238
+ this.modifiers.shift = down || this.otherHeld(usage === 225 ? 229 : 225)
239
+ break
240
+ case 226:
241
+ case 230:
242
+ this.modifiers.alt = down || this.otherHeld(usage === 226 ? 230 : 226)
243
+ break
244
+ case 227:
245
+ case 231:
246
+ this.modifiers.meta = down || this.otherHeld(usage === 227 ? 231 : 227)
247
+ break
248
+ }
249
+ }
250
+
251
+ private otherHeld(usage: number): boolean {
252
+ return this.held.has(`7:${usage}`)
253
+ }
254
+
255
+ private emit(phase: "down" | "up", page: number, usage: number, repeat: boolean, timestamp: number): void {
256
+ const translated = translateUsage(page, usage, this.modifiers.shift, this.capsLock)
257
+ const payload: KeyboardEventPayload = {
258
+ phase,
259
+ key: translated.key,
260
+ code: translated.code,
261
+ modifiers: {...this.modifiers},
262
+ repeat,
263
+ timestamp,
264
+ }
265
+ // Text only for plain presses: Ctrl/Alt/Meta chords are commands, not typing.
266
+ if (
267
+ phase === "down" &&
268
+ translated.text !== undefined &&
269
+ !this.modifiers.ctrl &&
270
+ !this.modifiers.alt &&
271
+ !this.modifiers.meta
272
+ ) {
273
+ payload.text = translated.text
274
+ }
275
+ this.forward(payload)
276
+ }
277
+
278
+ private startRepeat(page: number, usage: number): void {
279
+ this.stopRepeat()
280
+ this.repeating = {page, usage}
281
+ this.repeatTimer = this.timers.setTimeout(() => this.repeatTick(), this.repeatDelayMs)
282
+ }
283
+
284
+ private repeatTick(): void {
285
+ const key = this.repeating
286
+ if (!key || !this.held.has(`${key.page}:${key.usage}`)) {
287
+ this.stopRepeat()
288
+ return
289
+ }
290
+ this.emit("down", key.page, key.usage, true, this.now())
291
+ this.repeatTimer = this.timers.setTimeout(() => this.repeatTick(), this.repeatIntervalMs)
292
+ }
293
+
294
+ private stopRepeat(): void {
295
+ if (this.repeatTimer !== null) this.timers.clearTimeout(this.repeatTimer)
296
+ this.repeatTimer = null
297
+ this.repeating = null
298
+ }
299
+ }
300
+
301
+ /** Engine singleton: fans translated events out to subscribed miniapps. */
302
+ const glassesKeyboardInput = new GlassesKeyboardInput({
303
+ forward: (payload) => localMiniappRuntime.forwardEvent("keyboard_event", payload),
304
+ })
305
+
306
+ export default glassesKeyboardInput
@@ -0,0 +1,233 @@
1
+ import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
2
+ import {MiniappErrorCode} from "@glassly/miniapp/protocol"
3
+ import {BgTimer} from "../utils/timers"
4
+
5
+ type Sensor = "light" | "compass"
6
+ type Operation = "query" | "start" | "stop"
7
+ type Options = Record<string, number>
8
+ type Control = (sensor: Sensor, operation: Operation, options: Options) => Promise<boolean>
9
+ type Owner = {app: string; options: Options}
10
+
11
+ const RANGES: Record<Sensor, Record<string, [number, number]>> = {
12
+ light: {intervalMs: [100, 5000], minDelta: [0, 65535], heartbeatMs: [0, 65535]},
13
+ compass: {intervalMs: [50, 2000], minChangeDegrees: [0, 359]},
14
+ }
15
+ const COMPASS_DEFAULTS: Options = {intervalMs: 1000, minChangeDegrees: 5}
16
+ const COMPASS_RETRY_MS = [2000, 4000, 8000]
17
+
18
+ function fail(code: MiniappErrorCode, message: string): never {
19
+ throw Object.assign(new Error(message), {code})
20
+ }
21
+
22
+ function sameOptions(a: Options | null, b: Options | null): boolean {
23
+ if (!a || !b) return a === b
24
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)])
25
+ for (const key of keys) if (a[key] !== b[key]) return false
26
+ return true
27
+ }
28
+
29
+ /**
30
+ * Light: explicit, miniapp-scoped ownership. A subscriber alone never changes auto brightness.
31
+ * Compass: demand-driven. It runs while any miniapp is subscribed; `start(options)` only
32
+ * records that miniapp's override and everyone is served the smallest interval and
33
+ * threshold requested. Native navigation temporarily borrows both; its stock configuration wins.
34
+ */
35
+ export class GlassesSensorService {
36
+ private lightOwner: Owner | null = null
37
+ private compassSubscribers = new Set<string>()
38
+ private compassOverrides = new Map<string, Options>()
39
+ /** Options the firmware currently runs; null while stopped. */
40
+ private compassApplied: Options | null = null
41
+ private compassRetry: number | null = null
42
+ private compassRetryAttempt = 0
43
+ private pending: Promise<unknown> = Promise.resolve()
44
+ private navigationActive = false
45
+ private generation = 0
46
+
47
+ constructor(private readonly control: Control) {}
48
+
49
+ private serial<T>(work: () => Promise<T>): Promise<T> {
50
+ const next = this.pending.then(work)
51
+ this.pending = next.catch(() => {})
52
+ return next
53
+ }
54
+
55
+ private validate(payload: Record<string, unknown>): {sensor: Sensor; operation: Operation; options: Options} {
56
+ const sensor = payload.sensor
57
+ const operation = payload.operation
58
+ if (sensor !== "light" && sensor !== "compass") fail(MiniappErrorCode.INVALID_ARGUMENT, "Unknown sensor")
59
+ if (operation !== "start" && operation !== "stop" && !(sensor === "light" && operation === "query")) {
60
+ fail(MiniappErrorCode.INVALID_ARGUMENT, "Unknown sensor operation")
61
+ }
62
+ const raw = payload.options ?? {}
63
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
64
+ fail(MiniappErrorCode.INVALID_ARGUMENT, "Invalid sensor options")
65
+ const ranges = RANGES[sensor]
66
+ const options: Options = {}
67
+ for (const [key, value] of Object.entries(raw)) {
68
+ const range = Object.prototype.hasOwnProperty.call(ranges, key) ? ranges[key] : undefined
69
+ if (!range || typeof value !== "number" || !Number.isInteger(value) || value < range[0] || value > range[1]) {
70
+ fail(MiniappErrorCode.INVALID_ARGUMENT, `Invalid ${key}`)
71
+ }
72
+ options[key] = value
73
+ }
74
+ return {sensor, operation, options}
75
+ }
76
+
77
+ request(app: string, payload: Record<string, unknown>): Promise<void> {
78
+ const generation = this.generation
79
+ return this.serial(async () => {
80
+ if (generation !== this.generation) fail(MiniappErrorCode.NOT_CONNECTED, "Glasses disconnected")
81
+ const {sensor, operation, options} = this.validate(payload)
82
+ if (sensor === "compass") {
83
+ if (operation === "start") this.compassOverrides.set(app, options)
84
+ else this.compassOverrides.delete(app)
85
+ if (this.navigationActive) return
86
+ await this.applyCompass(generation, true)
87
+ return
88
+ }
89
+ const owner = this.lightOwner
90
+ if (operation !== "query" && owner && owner.app !== app) {
91
+ fail(MiniappErrorCode.INVALID_ARGUMENT, "This sensor is controlled by another miniapp")
92
+ }
93
+ if (operation === "stop" && !owner) return
94
+ if (this.navigationActive) {
95
+ if (operation === "stop") {
96
+ this.lightOwner = null
97
+ return
98
+ }
99
+ fail(MiniappErrorCode.INVALID_ARGUMENT, "Native navigation currently owns the glasses sensors")
100
+ }
101
+ // Keep cleanup ownership even after an ambiguous transport failure: the firmware
102
+ // may have applied the command before its ACK was lost.
103
+ if (operation === "start") this.lightOwner = {app, options}
104
+ if (!(await this.control("light", operation, options))) this.commandFailed()
105
+ if (generation !== this.generation) fail(MiniappErrorCode.NOT_CONNECTED, "Glasses disconnected")
106
+ if (operation === "stop") this.lightOwner = null
107
+ })
108
+ }
109
+
110
+ private commandFailed(): never {
111
+ fail(
112
+ MiniappErrorCode.INTERNAL,
113
+ "Sensor command failed: connect G2 custom firmware and keep its miniapp display active",
114
+ )
115
+ }
116
+
117
+ /** Miniapps currently subscribed to the glasses compass stream. Replaces the previous set. */
118
+ setCompassSubscribers(apps: Iterable<string>): Promise<void> {
119
+ const generation = this.generation
120
+ return this.serial(async () => {
121
+ this.compassSubscribers = new Set(apps)
122
+ if (generation !== this.generation || this.navigationActive) return
123
+ await this.applyCompass(generation, false)
124
+ })
125
+ }
126
+
127
+ private compassDemand(): Options | null {
128
+ if (this.compassSubscribers.size === 0 && this.compassOverrides.size === 0) return null
129
+ const requests = [...this.compassOverrides.values()].map((o) => ({...COMPASS_DEFAULTS, ...o}))
130
+ for (const app of this.compassSubscribers) if (!this.compassOverrides.has(app)) requests.push(COMPASS_DEFAULTS)
131
+ const demand: Options = {}
132
+ for (const key of Object.keys(COMPASS_DEFAULTS)) demand[key] = Math.min(...requests.map((r) => r[key]))
133
+ return demand
134
+ }
135
+
136
+ /**
137
+ * Bring the firmware in line with current demand. Explicit requests surface failures;
138
+ * subscription-driven applies stay silent (stock firmware, no CFW carrier yet) and retry
139
+ * a few times so a compass subscribed before firmware classification still starts.
140
+ */
141
+ private async applyCompass(generation: number, explicit: boolean): Promise<void> {
142
+ this.clearCompassRetry()
143
+ const demand = this.compassDemand()
144
+ if (sameOptions(demand, this.compassApplied)) return
145
+ if (!demand) {
146
+ const ok = await this.control("compass", "stop", {})
147
+ if (generation === this.generation) this.compassApplied = null
148
+ if (!ok && explicit) this.commandFailed()
149
+ return
150
+ }
151
+ const ok = await this.control("compass", "start", demand)
152
+ if (generation !== this.generation) {
153
+ if (explicit) fail(MiniappErrorCode.NOT_CONNECTED, "Glasses disconnected")
154
+ return
155
+ }
156
+ if (ok) {
157
+ this.compassApplied = demand
158
+ this.compassRetryAttempt = 0
159
+ return
160
+ }
161
+ if (explicit) this.commandFailed()
162
+ const delay = COMPASS_RETRY_MS[this.compassRetryAttempt]
163
+ if (delay === undefined) return
164
+ this.compassRetryAttempt++
165
+ this.compassRetry = BgTimer.setTimeout(() => {
166
+ this.compassRetry = null
167
+ void this.resync()
168
+ }, delay)
169
+ }
170
+
171
+ private clearCompassRetry(): void {
172
+ if (this.compassRetry != null) BgTimer.clearTimeout(this.compassRetry)
173
+ this.compassRetry = null
174
+ }
175
+
176
+ /** Re-apply compass demand after the glasses connect or their firmware is classified. */
177
+ resync(): Promise<void> {
178
+ const generation = this.generation
179
+ return this.serial(async () => {
180
+ if (generation !== this.generation || this.navigationActive) return
181
+ await this.applyCompass(generation, false).catch(() => {})
182
+ })
183
+ }
184
+
185
+ release(app: string): Promise<void> {
186
+ const generation = this.generation
187
+ return this.serial(async () => {
188
+ if (generation !== this.generation) return
189
+ const hadSubscription = this.compassSubscribers.delete(app)
190
+ const hadOverride = this.compassOverrides.delete(app)
191
+ const hadCompass = hadSubscription || hadOverride
192
+ if (this.lightOwner?.app === app) {
193
+ if (!this.navigationActive) await this.control("light", "stop", {}).catch(() => false)
194
+ this.lightOwner = null
195
+ }
196
+ if (hadCompass && !this.navigationActive) await this.applyCompass(generation, false).catch(() => {})
197
+ })
198
+ }
199
+
200
+ setNavigationActive(active: boolean): Promise<void> {
201
+ const generation = this.generation
202
+ return this.serial(async () => {
203
+ if (generation !== this.generation) return
204
+ if (this.navigationActive === active) return
205
+ this.navigationActive = active
206
+ this.clearCompassRetry()
207
+ if (active) {
208
+ if (this.lightOwner) await this.control("light", "stop", {}).catch(() => false)
209
+ if (this.compassApplied) await this.control("compass", "stop", {}).catch(() => false)
210
+ this.compassApplied = null
211
+ return
212
+ }
213
+ if (this.lightOwner) await this.control("light", "start", this.lightOwner.options).catch(() => false)
214
+ this.compassRetryAttempt = 0
215
+ await this.applyCompass(generation, false).catch(() => {})
216
+ })
217
+ }
218
+
219
+ /** Never replay requests from an old miniapp connection onto a new pair of glasses. */
220
+ disconnected(): void {
221
+ this.generation++
222
+ this.clearCompassRetry()
223
+ this.compassRetryAttempt = 0
224
+ this.lightOwner = null
225
+ this.compassApplied = null
226
+ this.navigationActive = false
227
+ }
228
+ }
229
+
230
+ const glassesSensorService = new GlassesSensorService((sensor, operation, options) =>
231
+ BluetoothSdk.glassesSensorControl(sensor, operation, options),
232
+ )
233
+ export default glassesSensorService
@@ -0,0 +1,128 @@
1
+ import {useDisplayStore} from "../stores/display"
2
+ import {DeviceModels, type TouchEvent} from "@glassly/bluetooth-sdk"
3
+ import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
4
+
5
+ import {useAppStatusStore} from "../stores/apps"
6
+ import {useGlassesStore} from "../stores/glasses"
7
+ import {BgTimer} from "../utils/timers"
8
+ import dashboardOpenTrace from "./debug/DashboardOpenTrace"
9
+ import gestureDotsService from "./GestureDotsService"
10
+ import localDisplayManager from "./LocalDisplayManager"
11
+ import localMiniappRuntime from "./LocalMiniappRuntime"
12
+ import {MiniappExitGesture} from "./MiniappExitGesture"
13
+ import nativeNavigationService from "./NativeNavigationService"
14
+ import {isOverlayApp} from "./pages/overlayApps"
15
+
16
+ const miniappExitGesture = new MiniappExitGesture({
17
+ getState: () => useAppStatusStore.getState(),
18
+ getVisiblePackageName: () => localDisplayManager.getVisibleMiniappPackageName(),
19
+ beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),
20
+ finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),
21
+ cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),
22
+ setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),
23
+ clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),
24
+ })
25
+
26
+ // Raw CFW ring reports include every scroll step. Rate-limit scroll actions
27
+ // separately from press/release recognition, using native receipt timestamps
28
+ // so a JS backlog does not collapse otherwise separate gestures.
29
+ const RING_SCROLL_INTERVAL_MS = 100
30
+ let lastRingScrollAt: number | null = null
31
+
32
+ function shouldForwardScroll(event: TouchEvent): boolean {
33
+ if (
34
+ event.deviceModel !== DeviceModels.G2 ||
35
+ event.source !== 2 ||
36
+ (event.gestureName !== "swipe_up" && event.gestureName !== "swipe_down")
37
+ )
38
+ return true
39
+ const elapsed = lastRingScrollAt === null ? null : event.timestamp - lastRingScrollAt
40
+ if (elapsed !== null && elapsed >= 0 && elapsed < RING_SCROLL_INTERVAL_MS) return false
41
+ lastRingScrollAt = event.timestamp
42
+ return true
43
+ }
44
+
45
+ /**
46
+ * Route both real glasses input and phone mirror controls through the same
47
+ * policy. A claimed gesture goes to the app holding the claim, alone; an
48
+ * unclaimed one goes to every subscriber and then the engine's default. The
49
+ * one rule ahead of the claims: an unclaimed double tap on a miniapp whose
50
+ * frame is visibly on the glasses leaves that app, whoever else claimed the
51
+ * gesture — the dashboard's claim is for the empty screen.
52
+ */
53
+ export function dispatchTouchEvent(event: TouchEvent): void {
54
+ const gesture = event.gestureName
55
+ const claimant = localMiniappRuntime.gestureClaimant(gesture)
56
+
57
+ if (gesture === "double_tap") {
58
+ // A visible main miniapp opts out of OS exit by registering double tap (the
59
+ // terminal's back-to-menu). The latest claimant still receives it. Otherwise
60
+ // the tap means "leave the app". The controller shows the bounded OS
61
+ // notice first, then uses the normal
62
+ // store stop path (and closes its phone overlay).
63
+ const visible = localDisplayManager.getVisibleMiniappPackageName()
64
+ if (
65
+ visible !== null &&
66
+ !localMiniappRuntime.hasGestureClaim(visible, gesture) &&
67
+ miniappExitGesture.tryExitVisibleMiniapp()
68
+ ) {
69
+ gestureDotsService.noteGesture(gesture, true)
70
+ return
71
+ }
72
+ }
73
+
74
+ if (claimant !== null) {
75
+ // An overlay app claims the double tap on an empty screen to open the dashboard.
76
+ if (gesture === "double_tap" && isOverlayApp(claimant)) dashboardOpenTrace.trigger("double-tap", event.timestamp)
77
+ // The press may have started before this app claimed its release edge.
78
+ gestureDotsService.noteGesture(gesture, true)
79
+ if (!shouldForwardScroll(event)) return
80
+ localMiniappRuntime.forwardEvent("touch_event", event, undefined, claimant)
81
+ return
82
+ }
83
+
84
+ // Gesture-dots debug indicator (any device, gated by its setting). It
85
+ // only paints while the screen is otherwise empty. Process even suppressed
86
+ // scroll repeats here so their completion still clears a held dot.
87
+ gestureDotsService.noteGesture(gesture)
88
+
89
+ if (!shouldForwardScroll(event)) return
90
+ localMiniappRuntime.forwardEvent("touch_event", event)
91
+
92
+ // Nobody claimed a double tap: the glasses' own dashboard. On eligible
93
+ // Glassly CFW glasses the dashboard miniapp claims it while it runs
94
+ // (ADR-0006); stock firmware keeps its native dashboard (ADR-0002). The
95
+ // modal nav UI owns the screen; showDashboard would fight it.
96
+ if (gesture === "double_tap" && !nativeNavigationService.isActive) {
97
+ useDisplayStore.getState().setDisplayEvent({layout: {layoutType: "unavailable", reason: "firmware-dashboard"}})
98
+ void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {
99
+ console.error("DeviceEventRouter: showDashboard failed:", err)
100
+ })
101
+ }
102
+ }
103
+
104
+ export function resetTouchInput(): void {
105
+ miniappExitGesture.reset()
106
+ lastRingScrollAt = null
107
+ }
108
+
109
+ /** Phone mirror input uses the same dashboard/miniapp policy as real glasses. */
110
+ export function sendGesture(
111
+ gestureName:
112
+ | "single_tap"
113
+ | "tap_start"
114
+ | "double_tap"
115
+ | "tap_end"
116
+ | "tap_then_hold"
117
+ | "touch_down"
118
+ | "swipe_up"
119
+ | "swipe_down",
120
+ ): void {
121
+ const model = useGlassesStore.getState().deviceModel
122
+ dispatchTouchEvent({
123
+ type: "touch_event",
124
+ gestureName,
125
+ deviceModel: Object.values(DeviceModels).find((candidate) => candidate === model) ?? DeviceModels.Simulated,
126
+ timestamp: Date.now(),
127
+ })
128
+ }