@glassly/engine 0.1.0-dev.1 → 0.1.0-dev.100

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 (343) hide show
  1. package/README.md +57 -19
  2. package/build/bluetooth-sdk/debug.d.ts +2 -0
  3. package/build/bluetooth-sdk/debug.d.ts.map +1 -0
  4. package/build/bluetooth-sdk/debug.js +2 -0
  5. package/build/bluetooth-sdk/debug.js.map +1 -0
  6. package/build/bluetooth-sdk/index.d.ts +3 -0
  7. package/build/bluetooth-sdk/index.d.ts.map +1 -0
  8. package/build/bluetooth-sdk/index.js +3 -0
  9. package/build/bluetooth-sdk/index.js.map +1 -0
  10. package/build/bluetooth-sdk/ota-transport.d.ts +2 -0
  11. package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
  12. package/build/bluetooth-sdk/ota-transport.js +2 -0
  13. package/build/bluetooth-sdk/ota-transport.js.map +1 -0
  14. package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
  15. package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
  16. package/build/bluetooth-sdk/photo-receiver.js +3 -0
  17. package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
  18. package/build/bluetooth-sdk/react.d.ts +2 -0
  19. package/build/bluetooth-sdk/react.d.ts.map +1 -0
  20. package/build/bluetooth-sdk/react.js +2 -0
  21. package/build/bluetooth-sdk/react.js.map +1 -0
  22. package/build/bluetooth-sdk/types.d.ts +2 -0
  23. package/build/bluetooth-sdk/types.d.ts.map +1 -0
  24. package/build/bluetooth-sdk/types.js +2 -0
  25. package/build/bluetooth-sdk/types.js.map +1 -0
  26. package/build/engine.d.ts +30 -18
  27. package/build/engine.d.ts.map +1 -1
  28. package/build/engine.js +12 -3
  29. package/build/engine.js.map +1 -1
  30. package/build/facades/dev.d.ts +1 -1
  31. package/build/facades/display.d.ts +11 -4
  32. package/build/facades/display.d.ts.map +1 -1
  33. package/build/facades/display.js +12 -2
  34. package/build/facades/display.js.map +1 -1
  35. package/build/facades/displayMirror.d.ts +2 -6
  36. package/build/facades/displayMirror.d.ts.map +1 -1
  37. package/build/facades/displayMirror.js +7 -14
  38. package/build/facades/displayMirror.js.map +1 -1
  39. package/build/facades/glasses.d.ts +6 -13
  40. package/build/facades/glasses.d.ts.map +1 -1
  41. package/build/facades/glasses.js +7 -3
  42. package/build/facades/glasses.js.map +1 -1
  43. package/build/facades/ota.d.ts +21 -21
  44. package/build/facades/ota.d.ts.map +1 -1
  45. package/build/facades/ota.js +26 -10
  46. package/build/facades/ota.js.map +1 -1
  47. package/build/facades/pairing.d.ts +11 -5
  48. package/build/facades/pairing.d.ts.map +1 -1
  49. package/build/facades/pairing.js +93 -17
  50. package/build/facades/pairing.js.map +1 -1
  51. package/build/facades/phoneNotifications.d.ts +12 -2
  52. package/build/facades/phoneNotifications.d.ts.map +1 -1
  53. package/build/facades/phoneNotifications.js +30 -10
  54. package/build/facades/phoneNotifications.js.map +1 -1
  55. package/build/facades/speakers.d.ts +1 -1
  56. package/build/facades/speakers.d.ts.map +1 -1
  57. package/build/facades/speakers.js.map +1 -1
  58. package/build/facades/speech.d.ts +7 -6
  59. package/build/facades/speech.d.ts.map +1 -1
  60. package/build/facades/speech.js +1 -1
  61. package/build/facades/speech.js.map +1 -1
  62. package/build/generated/releaseMetadata.d.ts +12 -0
  63. package/build/generated/releaseMetadata.d.ts.map +1 -0
  64. package/build/generated/releaseMetadata.js +10 -0
  65. package/build/generated/releaseMetadata.js.map +1 -0
  66. package/build/index.d.ts +9 -6
  67. package/build/index.d.ts.map +1 -1
  68. package/build/index.js +7 -5
  69. package/build/index.js.map +1 -1
  70. package/build/react/MentraLiveOtaFlow.d.ts +44 -0
  71. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  72. package/build/react/MentraLiveOtaFlow.js +322 -0
  73. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  74. package/build/react/index.d.ts +3 -0
  75. package/build/react/index.d.ts.map +1 -0
  76. package/build/react/index.js +3 -0
  77. package/build/react/index.js.map +1 -0
  78. package/build/react/useEngineSnapshot.d.ts +2 -0
  79. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  80. package/build/react/useEngineSnapshot.js +14 -0
  81. package/build/react/useEngineSnapshot.js.map +1 -0
  82. package/build/react/useMentraLiveOta.d.ts +84 -0
  83. package/build/react/useMentraLiveOta.d.ts.map +1 -0
  84. package/build/react/useMentraLiveOta.js +627 -0
  85. package/build/react/useMentraLiveOta.js.map +1 -0
  86. package/build/services/AppRegistry.d.ts +8 -0
  87. package/build/services/AppRegistry.d.ts.map +1 -1
  88. package/build/services/AppRegistry.js +15 -0
  89. package/build/services/AppRegistry.js.map +1 -1
  90. package/build/services/BlobStore.d.ts +2 -0
  91. package/build/services/BlobStore.d.ts.map +1 -1
  92. package/build/services/BlobStore.js +63 -10
  93. package/build/services/BlobStore.js.map +1 -1
  94. package/build/services/CustomDashboardModel.d.ts +48 -0
  95. package/build/services/CustomDashboardModel.d.ts.map +1 -0
  96. package/build/services/CustomDashboardModel.js +73 -0
  97. package/build/services/CustomDashboardModel.js.map +1 -0
  98. package/build/services/CustomDashboardService.d.ts +55 -0
  99. package/build/services/CustomDashboardService.d.ts.map +1 -0
  100. package/build/services/CustomDashboardService.js +492 -0
  101. package/build/services/CustomDashboardService.js.map +1 -0
  102. package/build/services/DashboardCoordinator.d.ts +26 -0
  103. package/build/services/DashboardCoordinator.d.ts.map +1 -0
  104. package/build/services/DashboardCoordinator.js +125 -0
  105. package/build/services/DashboardCoordinator.js.map +1 -0
  106. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  107. package/build/services/DeviceEventRouter.js +45 -6
  108. package/build/services/DeviceEventRouter.js.map +1 -1
  109. package/build/services/DisplayProcessor.d.ts +3 -3
  110. package/build/services/DisplayProcessor.js +3 -3
  111. package/build/services/DisplayProcessor.js.map +1 -1
  112. package/build/services/GestureDotsService.d.ts +40 -0
  113. package/build/services/GestureDotsService.d.ts.map +1 -0
  114. package/build/services/GestureDotsService.js +167 -0
  115. package/build/services/GestureDotsService.js.map +1 -0
  116. package/build/services/GlassesStatusProjection.d.ts +15 -1
  117. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  118. package/build/services/GlassesStatusProjection.js +18 -11
  119. package/build/services/GlassesStatusProjection.js.map +1 -1
  120. package/build/services/HotspotOtaTransport.d.ts.map +1 -1
  121. package/build/services/HotspotOtaTransport.js +8 -7
  122. package/build/services/HotspotOtaTransport.js.map +1 -1
  123. package/build/services/LocalDisplayManager.d.ts +55 -3
  124. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  125. package/build/services/LocalDisplayManager.js +196 -16
  126. package/build/services/LocalDisplayManager.js.map +1 -1
  127. package/build/services/LocalMiniappRuntime.d.ts +13 -0
  128. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  129. package/build/services/LocalMiniappRuntime.js +32 -0
  130. package/build/services/LocalMiniappRuntime.js.map +1 -1
  131. package/build/services/MiniappExitGesture.d.ts +33 -0
  132. package/build/services/MiniappExitGesture.d.ts.map +1 -0
  133. package/build/services/MiniappExitGesture.js +54 -0
  134. package/build/services/MiniappExitGesture.js.map +1 -0
  135. package/build/services/OtaAutoChain.d.ts +41 -0
  136. package/build/services/OtaAutoChain.d.ts.map +1 -0
  137. package/build/services/OtaAutoChain.js +91 -0
  138. package/build/services/OtaAutoChain.js.map +1 -0
  139. package/build/services/OtaErrorMapping.d.ts +18 -0
  140. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  141. package/build/services/OtaErrorMapping.js +63 -0
  142. package/build/services/OtaErrorMapping.js.map +1 -0
  143. package/build/services/OtaInstallCoordinator.d.ts +26 -3
  144. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  145. package/build/services/OtaInstallCoordinator.js +38 -13
  146. package/build/services/OtaInstallCoordinator.js.map +1 -1
  147. package/build/services/OtaService.d.ts.map +1 -1
  148. package/build/services/OtaService.js +1 -1
  149. package/build/services/OtaService.js.map +1 -1
  150. package/build/services/OtaUpdateCheckService.d.ts +4 -1
  151. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  152. package/build/services/OtaUpdateCheckService.js +28 -16
  153. package/build/services/OtaUpdateCheckService.js.map +1 -1
  154. package/build/services/PhoneNotificationsSync.d.ts +13 -0
  155. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  156. package/build/services/PhoneNotificationsSync.js +101 -9
  157. package/build/services/PhoneNotificationsSync.js.map +1 -1
  158. package/build/services/PhonePhotoCoordinator.js +1 -1
  159. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  160. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  161. package/build/services/PhoneStreamCoordinator.js +9 -6
  162. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  163. package/build/services/SceneRenderer.d.ts.map +1 -1
  164. package/build/services/SceneRenderer.js +5 -0
  165. package/build/services/SceneRenderer.js.map +1 -1
  166. package/build/services/StreamLifecycleController.d.ts +4 -0
  167. package/build/services/StreamLifecycleController.d.ts.map +1 -1
  168. package/build/services/StreamLifecycleController.js +4 -0
  169. package/build/services/StreamLifecycleController.js.map +1 -1
  170. package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
  171. package/build/services/asg/localNetworkTransport.js +13 -12
  172. package/build/services/asg/localNetworkTransport.js.map +1 -1
  173. package/build/services/effectiveCapabilities.d.ts +1 -1
  174. package/build/services/effectiveCapabilities.d.ts.map +1 -1
  175. package/build/services/effectiveCapabilities.js +26 -7
  176. package/build/services/effectiveCapabilities.js.map +1 -1
  177. package/build/services/gestureClaims.d.ts +18 -0
  178. package/build/services/gestureClaims.d.ts.map +1 -0
  179. package/build/services/gestureClaims.js +24 -0
  180. package/build/services/gestureClaims.js.map +1 -0
  181. package/build/services/gestureDotGlyphs.d.ts +8 -0
  182. package/build/services/gestureDotGlyphs.d.ts.map +1 -0
  183. package/build/services/gestureDotGlyphs.js +13 -0
  184. package/build/services/gestureDotGlyphs.js.map +1 -0
  185. package/build/services/glassesClockSync.d.ts.map +1 -1
  186. package/build/services/glassesClockSync.js +5 -1
  187. package/build/services/glassesClockSync.js.map +1 -1
  188. package/build/services/iosNotificationAppCatalog.d.ts +13 -0
  189. package/build/services/iosNotificationAppCatalog.d.ts.map +1 -0
  190. package/build/services/iosNotificationAppCatalog.js +28 -0
  191. package/build/services/iosNotificationAppCatalog.js.map +1 -0
  192. package/build/services/otaDisplayState.d.ts +1 -1
  193. package/build/services/otaDisplayState.d.ts.map +1 -1
  194. package/build/services/otaDisplayState.js.map +1 -1
  195. package/build/services/otaInstallPolicy.d.ts +5 -1
  196. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  197. package/build/services/otaInstallPolicy.js +4 -0
  198. package/build/services/otaInstallPolicy.js.map +1 -1
  199. package/build/services/otaLegacyMapping.d.ts +1 -1
  200. package/build/services/otaLegacyMapping.d.ts.map +1 -1
  201. package/build/services/otaLegacyMapping.js.map +1 -1
  202. package/build/services/otaManifestPolicy.d.ts +14 -0
  203. package/build/services/otaManifestPolicy.d.ts.map +1 -0
  204. package/build/services/otaManifestPolicy.js +20 -0
  205. package/build/services/otaManifestPolicy.js.map +1 -0
  206. package/build/services/otaManifestUrl.d.ts +2 -1
  207. package/build/services/otaManifestUrl.d.ts.map +1 -1
  208. package/build/services/otaManifestUrl.js +24 -32
  209. package/build/services/otaManifestUrl.js.map +1 -1
  210. package/build/services/otaReleaseVersion.d.ts +10 -0
  211. package/build/services/otaReleaseVersion.d.ts.map +1 -0
  212. package/build/services/otaReleaseVersion.js +14 -0
  213. package/build/services/otaReleaseVersion.js.map +1 -0
  214. package/build/stores/appVisibility.d.ts +3 -0
  215. package/build/stores/appVisibility.d.ts.map +1 -0
  216. package/build/stores/appVisibility.js +3 -0
  217. package/build/stores/appVisibility.js.map +1 -0
  218. package/build/stores/apps.d.ts.map +1 -1
  219. package/build/stores/apps.js +7 -1
  220. package/build/stores/apps.js.map +1 -1
  221. package/build/stores/core.d.ts +3 -3
  222. package/build/stores/core.d.ts.map +1 -1
  223. package/build/stores/core.js.map +1 -1
  224. package/build/stores/display.d.ts +4 -4
  225. package/build/stores/display.d.ts.map +1 -1
  226. package/build/stores/display.js +12 -36
  227. package/build/stores/display.js.map +1 -1
  228. package/build/stores/glasses.d.ts +10 -4
  229. package/build/stores/glasses.d.ts.map +1 -1
  230. package/build/stores/glasses.js +2 -0
  231. package/build/stores/glasses.js.map +1 -1
  232. package/build/stores/settings.d.ts.map +1 -1
  233. package/build/stores/settings.js +140 -5
  234. package/build/stores/settings.js.map +1 -1
  235. package/build/types/applet.d.ts +3 -1
  236. package/build/types/applet.d.ts.map +1 -1
  237. package/build/types/applet.js +0 -3
  238. package/build/types/applet.js.map +1 -1
  239. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
  240. package/build/types/capabilities/even-realities-g2.js +2 -2
  241. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  242. package/build/types/enums.d.ts +1 -5
  243. package/build/types/enums.d.ts.map +1 -1
  244. package/build/types/enums.js +1 -8
  245. package/build/types/enums.js.map +1 -1
  246. package/build/types/hardware.d.ts +1 -1
  247. package/build/types/hardware.d.ts.map +1 -1
  248. package/build/types/hardware.js +1 -0
  249. package/build/types/hardware.js.map +1 -1
  250. package/build/types/index.d.ts +3 -7
  251. package/build/types/index.d.ts.map +1 -1
  252. package/build/types/index.js +3 -7
  253. package/build/types/index.js.map +1 -1
  254. package/build/utils/display/composer/ColumnComposer.d.ts +1 -1
  255. package/build/utils/display/composer/ColumnComposer.js +1 -1
  256. package/build/utils/display/composer/ColumnComposer.js.map +1 -1
  257. package/build/utils/display/scene/process.d.ts.map +1 -1
  258. package/build/utils/display/scene/process.js +4 -3
  259. package/build/utils/display/scene/process.js.map +1 -1
  260. package/build/utils/display/scene/types.d.ts +6 -2
  261. package/build/utils/display/scene/types.d.ts.map +1 -1
  262. package/build/utils/display/scene/types.js.map +1 -1
  263. package/package.json +69 -35
  264. package/src/bluetooth-sdk/debug.ts +1 -0
  265. package/src/bluetooth-sdk/index.ts +2 -0
  266. package/src/bluetooth-sdk/ota-transport.ts +1 -0
  267. package/src/bluetooth-sdk/photo-receiver.ts +2 -0
  268. package/src/bluetooth-sdk/react.ts +1 -0
  269. package/src/bluetooth-sdk/types.ts +1 -0
  270. package/src/engine.ts +12 -3
  271. package/src/facades/display.ts +12 -2
  272. package/src/facades/displayMirror.ts +7 -15
  273. package/src/facades/glasses.ts +8 -4
  274. package/src/facades/ota.ts +36 -11
  275. package/src/facades/pairing.ts +103 -23
  276. package/src/facades/phoneNotifications.ts +32 -10
  277. package/src/facades/speakers.ts +1 -1
  278. package/src/facades/speech.ts +21 -7
  279. package/src/generated/releaseMetadata.ts +20 -0
  280. package/src/index.ts +31 -5
  281. package/src/react/MentraLiveOtaFlow.tsx +754 -0
  282. package/src/react/index.ts +21 -0
  283. package/src/react/useEngineSnapshot.ts +16 -0
  284. package/src/react/useMentraLiveOta.ts +793 -0
  285. package/src/services/AppRegistry.ts +16 -0
  286. package/src/services/BlobStore.ts +57 -10
  287. package/src/services/CustomDashboardModel.ts +83 -0
  288. package/src/services/CustomDashboardService.ts +516 -0
  289. package/src/services/DashboardCoordinator.ts +121 -0
  290. package/src/services/DeviceEventRouter.ts +48 -8
  291. package/src/services/DisplayProcessor.ts +3 -3
  292. package/src/services/GestureDotsService.ts +164 -0
  293. package/src/services/GlassesStatusProjection.ts +20 -11
  294. package/src/services/HotspotOtaTransport.ts +8 -7
  295. package/src/services/LocalDisplayManager.ts +198 -18
  296. package/src/services/LocalMiniappRuntime.ts +37 -0
  297. package/src/services/MiniappExitGesture.ts +76 -0
  298. package/src/services/OtaAutoChain.ts +130 -0
  299. package/src/services/OtaErrorMapping.ts +80 -0
  300. package/src/services/OtaInstallCoordinator.ts +40 -13
  301. package/src/services/OtaService.ts +1 -2
  302. package/src/services/OtaUpdateCheckService.ts +33 -19
  303. package/src/services/PhoneNotificationsSync.ts +125 -9
  304. package/src/services/PhonePhotoCoordinator.ts +1 -1
  305. package/src/services/PhoneStreamCoordinator.ts +10 -6
  306. package/src/services/SceneRenderer.ts +4 -0
  307. package/src/services/StreamLifecycleController.ts +4 -0
  308. package/src/services/asg/localNetworkTransport.ts +20 -19
  309. package/src/services/effectiveCapabilities.ts +27 -7
  310. package/src/services/gestureClaims.ts +28 -0
  311. package/src/services/gestureDotGlyphs.ts +15 -0
  312. package/src/services/glassesClockSync.ts +5 -1
  313. package/src/services/iosNotificationAppCatalog.ts +39 -0
  314. package/src/services/otaDisplayState.ts +1 -1
  315. package/src/services/otaInstallPolicy.ts +5 -1
  316. package/src/services/otaLegacyMapping.ts +1 -1
  317. package/src/services/otaManifestPolicy.ts +43 -0
  318. package/src/services/otaManifestUrl.ts +27 -36
  319. package/src/services/otaReleaseVersion.ts +22 -0
  320. package/src/stores/appVisibility.ts +3 -0
  321. package/src/stores/apps.ts +7 -1
  322. package/src/stores/core.ts +4 -4
  323. package/src/stores/display.ts +16 -48
  324. package/src/stores/glasses.ts +15 -6
  325. package/src/stores/settings.ts +140 -5
  326. package/src/types/applet.ts +3 -1
  327. package/src/types/capabilities/even-realities-g2.ts +3 -2
  328. package/src/types/enums.ts +1 -9
  329. package/src/types/hardware.ts +2 -1
  330. package/src/types/index.ts +3 -7
  331. package/src/utils/display/composer/ColumnComposer.ts +1 -1
  332. package/src/utils/display/scene/process.ts +4 -3
  333. package/src/utils/display/scene/types.ts +0 -0
  334. package/build/devtools.d.ts +0 -11
  335. package/build/devtools.d.ts.map +0 -1
  336. package/build/devtools.js +0 -13
  337. package/build/devtools.js.map +0 -1
  338. package/build/internal.d.ts +0 -67
  339. package/build/internal.d.ts.map +0 -1
  340. package/build/internal.js +0 -90
  341. package/build/internal.js.map +0 -1
  342. package/src/devtools.ts +0 -13
  343. package/src/internal.ts +0 -155
@@ -20,7 +20,9 @@ import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
20
20
  import {shallow} from "zustand/shallow"
21
21
 
22
22
  import localMiniappRuntime from "./LocalMiniappRuntime"
23
+ import localDisplayManager from "./LocalDisplayManager"
23
24
  import localSttFallbackCoordinator from "./LocalSttFallbackCoordinator"
25
+ import {MiniappExitGesture} from "./MiniappExitGesture"
24
26
  import nativeNavigationService from "./NativeNavigationService"
25
27
  import speakerLabelResolver from "./SpeakerLabelResolver"
26
28
  import {phonePhotoCoordinator} from "./PhonePhotoCoordinator"
@@ -31,10 +33,23 @@ import {SETTINGS, useSettingsStore} from "../stores/settings"
31
33
  import {useAppStatusStore} from "../stores/apps"
32
34
  import {retirePendingSelectionOnPromotion} from "./PairingIdentity"
33
35
  import GlobalEventEmitter from "../utils/GlobalEventEmitter"
36
+ import {BgTimer} from "../utils/timers"
34
37
  import {asgCameraApi} from "./asg/asgCameraApi"
38
+ import dashboardCoordinator from "./DashboardCoordinator"
39
+ import gestureDotsService from "./GestureDotsService"
35
40
 
36
41
  let subs: Array<{remove: () => void}> = []
37
42
 
43
+ const miniappExitGesture = new MiniappExitGesture({
44
+ getState: () => useAppStatusStore.getState(),
45
+ getVisiblePackageName: () => localDisplayManager.getVisibleMiniappPackageName(),
46
+ beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),
47
+ finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),
48
+ cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),
49
+ setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),
50
+ clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),
51
+ })
52
+
38
53
  export function startDeviceEventRouter(): void {
39
54
  if (subs.length) return
40
55
 
@@ -183,13 +198,38 @@ export function startDeviceEventRouter(): void {
183
198
  )
184
199
  subs.push(
185
200
  BluetoothSdk.addListener("touch_event", (event) => {
201
+ // While the custom dashboard is visible it owns its focus/navigation
202
+ // gestures; do not leak those into a miniapp running underneath it.
203
+ if (dashboardCoordinator.handleGesture(event.gestureName)) return
204
+
205
+ // Gesture-dots debug indicator (any device, gated by its setting). It
206
+ // only paints while the screen is otherwise empty; the gesture still
207
+ // flows to miniapps below.
208
+ gestureDotsService.noteGesture(event.gestureName)
209
+
186
210
  localMiniappRuntime.forwardEvent("touch_event", event)
187
- // Double-tap policy, moved out of the native G2 handlers: open the native
188
- // dashboard when the setting is on, else toggle head-up. Native only
189
- // reports the gesture now.
211
+ // Double-tap policy, moved out of the native G2 handlers: exit a visible
212
+ // unclaimed miniapp; otherwise open the dashboard or toggle head-up.
213
+ // Native only reports the gesture now.
190
214
  if (event.gestureName === "double_tap") {
191
- const useNativeDashboard =
192
- useSettingsStore.getState().getSetting(SETTINGS.use_native_dashboard.key) !== false
215
+ // A foreground miniapp that claimed double_tap (CLAIM_GESTURES) is
216
+ // using it as an in-app gesture (e.g. the terminal's back-to-menu) —
217
+ // the event was already forwarded above; skip the system default.
218
+ if (localMiniappRuntime.foregroundClaimsGesture("double_tap")) return
219
+
220
+ // An unclaimed double tap exits only the miniapp whose frame is visibly
221
+ // on the glasses. The controller shows the bounded OS notice first,
222
+ // then uses the normal store stop path (and closes its phone overlay).
223
+ // Checked before the dashboard toggle: while an app owns the screen an
224
+ // unclaimed double-tap always means "leave the app" — the (custom or
225
+ // native) dashboard is for the empty screen, and head-up still opens
226
+ // the custom dashboard mid-app.
227
+ if (miniappExitGesture.tryExitVisibleMiniapp()) return
228
+
229
+ // Eligible G2 custom firmware replaces the native dashboard.
230
+ if (dashboardCoordinator.toggleCustom()) return
231
+
232
+ const useNativeDashboard = useSettingsStore.getState().getSetting(SETTINGS.use_native_dashboard.key) !== false
193
233
  if (useNativeDashboard) {
194
234
  // The modal nav UI owns the screen; showDashboard would fight it.
195
235
  if (!nativeNavigationService.isActive) {
@@ -198,9 +238,7 @@ export function startDeviceEventRouter(): void {
198
238
  })
199
239
  }
200
240
  } else {
201
- void Promise.resolve(BluetoothSdk.toggleHeadUp()).catch((err) => {
202
- console.error("DeviceEventRouter: toggleHeadUp failed:", err)
203
- })
241
+ dashboardCoordinator.toggleLegacy()
204
242
  }
205
243
  }
206
244
  }),
@@ -219,6 +257,7 @@ export function startDeviceEventRouter(): void {
219
257
  // Head position — translate native {up:boolean} → SDK {position:"up"|"down"}.
220
258
  subs.push(
221
259
  BluetoothSdk.addListener("head_up", (event) => {
260
+ dashboardCoordinator.setHeadUp(event.up)
222
261
  localMiniappRuntime.forwardEvent("head_up", {position: event.up ? "up" : "down", timestamp: Date.now()})
223
262
  }),
224
263
  )
@@ -263,4 +302,5 @@ export function startDeviceEventRouter(): void {
263
302
  export function stopDeviceEventRouter(): void {
264
303
  for (const sub of subs) sub.remove()
265
304
  subs = []
305
+ miniappExitGesture.reset()
266
306
  }
@@ -2,15 +2,15 @@
2
2
  * DisplayProcessor
3
3
  *
4
4
  * Processes display events with pixel-accurate text wrapping using the same
5
- * display-utils library as the cloud. This ensures the mobile preview matches
6
- * exactly what is shown on the glasses.
5
+ * Engine display utilities. This ensures the mobile preview matches exactly
6
+ * what is shown on the glasses.
7
7
  *
8
8
  * Key responsibilities:
9
9
  * 1. Intercept display events before sending to native SGC
10
10
  * 2. Wrap text using the correct device profile (G1, etc.)
11
11
  * 3. Ensure GlassesDisplayMirror shows exactly what glasses will show
12
12
  *
13
- * @see cloud/issues/026-mobile-display-processor for design docs
13
+ * @see notes/miniapp-display-api-redesign.md for the current display design
14
14
  */
15
15
 
16
16
  import {
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Gesture dots — a debug indicator that flashes a small glyph top-right on the
3
+ * glasses when a touch gesture arrives, while nothing else is on the display:
4
+ * a filled circle for a single tap, an arrow for a swipe (cleared after a
5
+ * short pulse), a bullseye while a CFW tap-then-hold is held. Extracted from
6
+ * the CFW dashboard service; it renders through
7
+ * the ordinary scene pipeline, so it works on any positioning device (stock
8
+ * G2 included), and is gated by the gesture_dots debug setting.
9
+ *
10
+ * The glyphs are pre-baked images (not text): the stock firmware's builtin
11
+ * font has no ●/↑/↓, and a single image element with a stable id and box maps
12
+ * to ONE re-used firmware image container — flash and idle swap its bytes
13
+ * (data-only update, no page rebuild) instead of creating/removing containers.
14
+ */
15
+ import {SETTINGS, useSettingsStore} from "../stores/settings"
16
+ import type {SceneElementInput} from "../utils/display/scene"
17
+ import {BgTimer} from "../utils/timers"
18
+ import {GESTURE_DOT_GLYPHS, GESTURE_DOT_GLYPH_SIZE, type GestureDotGlyph} from "./gestureDotGlyphs"
19
+ import localDisplayManager from "./LocalDisplayManager"
20
+ import sceneRenderer from "./SceneRenderer"
21
+
22
+ const DOTS_APP_ID = "system.gesture-dots"
23
+ /** How long a gesture stays on the indicator. */
24
+ const GESTURE_FLASH_MS = 700
25
+ const MARGIN_X = 16
26
+ const MARGIN_TOP = 10
27
+
28
+ class GestureDotsService {
29
+ private started = false
30
+ private flash: GestureDotGlyph | null = null
31
+ private flashTimer: number | null = null
32
+ /** Finger currently on the touch bar (tap_start..tap_end; the CFW forwards holds as these edges). */
33
+ private held = false
34
+ /**
35
+ * The CFW's tap_then_hold fired during this press (discrete recognition
36
+ * event; the tap_end that follows on release is what clears it).
37
+ */
38
+ private holdRecognized = false
39
+ /** Whether the indicator scene currently has anything on it (needs a clear when the flash ends). */
40
+ private visible = false
41
+ private unsubs: Array<() => void> = []
42
+
43
+ start(): void {
44
+ if (this.started) return
45
+ this.started = true
46
+ this.unsubs.push(
47
+ // The indicator owns the screen only while it is empty: drop our
48
+ // retained state when something else takes over, and clear any
49
+ // leftover flash when the display empties again.
50
+ localDisplayManager.onActiveDisplayChange((active) => {
51
+ if (active) this.visible = false
52
+ else this.render()
53
+ }),
54
+ )
55
+ }
56
+
57
+ stop(): void {
58
+ for (const unsub of this.unsubs) unsub()
59
+ this.unsubs = []
60
+ if (this.flashTimer !== null) BgTimer.clearTimeout(this.flashTimer)
61
+ this.flashTimer = null
62
+ this.flash = null
63
+ this.held = false
64
+ this.holdRecognized = false
65
+ this.visible = false
66
+ this.started = false
67
+ sceneRenderer.clearApp(DOTS_APP_ID)
68
+ }
69
+
70
+ private enabled(): boolean {
71
+ return useSettingsStore.getState().getSetting(SETTINGS.gesture_dots.key) === true
72
+ }
73
+
74
+ /**
75
+ * Flash a gesture on the indicator. Other gesture names are ignored. The
76
+ * device-event router calls this after the CFW dashboard had its chance to
77
+ * consume the gesture, so dashboard navigation does not flash.
78
+ */
79
+ noteGesture(gestureName: string): void {
80
+ if (!this.started || !this.enabled()) return
81
+ // Tap edges show a steady circle for as long as the finger is down (on
82
+ // CFW these arrive for holds: ET_LONG/ET_REL forwarded as
83
+ // tap_start/tap_end), with no timer — tap_end clears it.
84
+ if (gestureName === "tap_start" || gestureName === "tap_end") {
85
+ const held = gestureName === "tap_start"
86
+ if (this.held === held) return
87
+ this.held = held
88
+ if (!held) this.holdRecognized = false
89
+ this.render()
90
+ return
91
+ }
92
+ // CFW tap-then-hold (the stock Menu gesture): swap the steady dot for the
93
+ // bullseye until release. Recognition arrives mid-press, but set held too
94
+ // in case the tap_start edge was missed — the release edge still clears.
95
+ if (gestureName === "tap_then_hold") {
96
+ this.held = true
97
+ this.holdRecognized = true
98
+ this.render()
99
+ return
100
+ }
101
+ const flash: GestureDotGlyph | null =
102
+ gestureName === "single_tap"
103
+ ? "dot"
104
+ : gestureName === "swipe_up"
105
+ ? "up"
106
+ : gestureName === "swipe_down"
107
+ ? "down"
108
+ : null
109
+ if (!flash) return
110
+ this.flash = flash
111
+ if (this.flashTimer !== null) BgTimer.clearTimeout(this.flashTimer)
112
+ this.flashTimer = BgTimer.setTimeout(() => {
113
+ this.flashTimer = null
114
+ this.flash = null
115
+ this.render()
116
+ }, GESTURE_FLASH_MS)
117
+ this.render()
118
+ }
119
+
120
+ /**
121
+ * Paint or clear the indicator. Nothing is drawn while anything else is on
122
+ * the glasses (miniapp frame or system screen) or when no gesture was just
123
+ * received. The top-right anchor resolves against the device's real canvas,
124
+ * so no size math here.
125
+ *
126
+ * Between flashes the element STAYS in the scene with the blank glyph:
127
+ * removing it would tear down the firmware image container and rebuild the
128
+ * page on every flash (a rebuild storm on stock firmware). Only disabling
129
+ * the setting removes the element outright.
130
+ */
131
+ private render(): void {
132
+ if (!this.started) return
133
+ if (localDisplayManager.hasActiveDisplay()) return
134
+ const shown = this.flash ?? (this.held ? (this.holdRecognized ? "hold" : "dot") : null)
135
+ if (!this.enabled()) {
136
+ if (this.visible) {
137
+ this.visible = false
138
+ sceneRenderer.emitScene(DOTS_APP_ID, "main", [])
139
+ }
140
+ return
141
+ }
142
+ if (!shown && !this.visible) return
143
+ const elements: SceneElementInput[] = [
144
+ {
145
+ type: "image",
146
+ id: "gesture-dot",
147
+ box: {
148
+ x: MARGIN_X,
149
+ y: MARGIN_TOP,
150
+ w: GESTURE_DOT_GLYPH_SIZE,
151
+ h: GESTURE_DOT_GLYPH_SIZE,
152
+ anchor: "top-right",
153
+ },
154
+ data: GESTURE_DOT_GLYPHS[shown ?? "blank"],
155
+ },
156
+ ]
157
+ this.visible = true
158
+ sceneRenderer.emitScene(DOTS_APP_ID, "main", elements)
159
+ }
160
+ }
161
+
162
+ const gestureDotsService = new GestureDotsService()
163
+ export default gestureDotsService
164
+ export {GestureDotsService}
@@ -11,22 +11,26 @@
11
11
  *
12
12
  * Started by `engine.start()`. Idempotent.
13
13
  */
14
- import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
14
+ import BluetoothSdk, {type PublicGlassesStatus} from "@glassly/bluetooth-sdk"
15
15
  import {useCoreStore} from "../stores/core"
16
16
  import {useGlassesStore} from "../stores/glasses"
17
- import localMiniappRuntime from "./LocalMiniappRuntime"
18
17
 
19
18
  let unsubs: Array<() => void> = []
20
19
  let projectionRunId = 0
20
+ let glassesStatusForwarder: ((status: Partial<PublicGlassesStatus>) => void) | null = null
21
+ let hydrationPromise: Promise<void> | null = null
21
22
 
22
- export function startGlassesStatusProjection(): void {
23
- if (unsubs.length) return
23
+ export function startGlassesStatusProjection(
24
+ forwarder?: (status: Partial<PublicGlassesStatus>) => void,
25
+ ): Promise<void> {
26
+ if (forwarder) glassesStatusForwarder = forwarder
27
+ if (unsubs.length) return hydrationPromise ?? Promise.resolve()
24
28
 
25
29
  const runId = ++projectionRunId
26
30
  let bluetoothEventSeen = false
27
31
  let glassesEventSeen = false
28
32
 
29
- void BluetoothSdk.getBluetoothStatus()
33
+ const bluetoothHydration = BluetoothSdk.getBluetoothStatus()
30
34
  .then((status) => {
31
35
  if (runId !== projectionRunId || bluetoothEventSeen) return
32
36
  useCoreStore.getState().setCoreInfo(status)
@@ -35,7 +39,7 @@ export function startGlassesStatusProjection(): void {
35
39
  console.warn("GlassesStatusProjection: getBluetoothStatus failed", error)
36
40
  })
37
41
 
38
- void BluetoothSdk.getGlassesStatus()
42
+ const glassesHydration = BluetoothSdk.getGlassesStatus()
39
43
  .then((status) => {
40
44
  if (runId !== projectionRunId || glassesEventSeen) return
41
45
  useGlassesStore.getState().setGlassesInfo(status)
@@ -46,28 +50,33 @@ export function startGlassesStatusProjection(): void {
46
50
 
47
51
  // Bluetooth-adapter status -> core store.
48
52
  unsubs.push(
49
- BluetoothSdk.onBluetoothStatus((changed) => {
53
+ BluetoothSdk.subscribeBluetoothStatus((changed) => {
50
54
  bluetoothEventSeen = true
51
55
  useCoreStore.getState().setCoreInfo(changed)
52
56
  }),
53
57
  )
54
58
 
55
- // Glasses status -> glasses store (+ forward to local miniapps; clear any stale
56
- // OTA-available flag on disconnect).
59
+ // Glasses status -> glasses store (+ optional full-runtime forwarding to local
60
+ // miniapps; clear any stale OTA-available flag on disconnect).
57
61
  unsubs.push(
58
- BluetoothSdk.onGlassesStatus((changed) => {
62
+ BluetoothSdk.subscribeGlassesStatus((changed) => {
59
63
  glassesEventSeen = true
60
64
  useGlassesStore.getState().setGlassesInfo(changed)
61
- localMiniappRuntime.forwardEvent("glasses_connection_state", changed)
65
+ glassesStatusForwarder?.(changed)
62
66
  if (changed.connection?.state === "disconnected") {
63
67
  useGlassesStore.getState().setOtaUpdateAvailable(null)
64
68
  }
65
69
  }),
66
70
  )
71
+
72
+ hydrationPromise = Promise.allSettled([bluetoothHydration, glassesHydration]).then(() => undefined)
73
+ return hydrationPromise
67
74
  }
68
75
 
69
76
  export function stopGlassesStatusProjection(): void {
70
77
  projectionRunId++
71
78
  unsubs.forEach((unsub) => unsub())
72
79
  unsubs = []
80
+ glassesStatusForwarder = null
81
+ hydrationPromise = null
73
82
  }
@@ -1,4 +1,5 @@
1
- import BluetoothSdk, {isEnabledHotspotStatus, OtaServer} from "@glassly/bluetooth-sdk/internal"
1
+ import BluetoothSdk, {isEnabledHotspotStatus} from "@glassly/bluetooth-sdk"
2
+ import {otaServer} from "@glassly/bluetooth-sdk/ota-transport"
2
3
  // One coordinator selects the native Android or iOS staging implementation at runtime.
3
4
  // eslint-disable-next-line react-native/split-platform-components
4
5
  import {PermissionsAndroid, Platform} from "react-native"
@@ -83,7 +84,7 @@ class HotspotOtaTransport {
83
84
  let localAddress = scopedAddress
84
85
  if (Platform.OS === "ios") {
85
86
  try {
86
- localAddress = await OtaServer.waitForWifiAddress(hotspot.localIp, 15_000)
87
+ localAddress = await otaServer.waitForWifiAddress(hotspot.localIp, 15_000)
87
88
  } catch (error) {
88
89
  throw new HotspotOtaTransportError(
89
90
  "hotspot_join_failed",
@@ -94,10 +95,10 @@ class HotspotOtaTransport {
94
95
  const artifactPaths = Object.fromEntries(this.prepared.map((artifact) => [artifact.sha256, artifact.filePath]))
95
96
  // Start exactly one native listener to learn its selected port, then atomically replace
96
97
  // the placeholder with the immutable rewritten manifest before ota_start is sent.
97
- const server = await OtaServer.startOtaServer("{}", artifactPaths, localAddress)
98
+ const server = await otaServer.start("{}", artifactPaths, localAddress)
98
99
  this.serverStarted = true
99
100
  const manifest = rewriteManifestForLocalServer(checkResult.manifestBody, this.prepared, server.baseUrl)
100
- const published = await OtaServer.startOtaServer(manifest, artifactPaths, server.host)
101
+ const published = await otaServer.start(manifest, artifactPaths, server.host)
101
102
  if (published.manifestUrl !== server.manifestUrl) {
102
103
  throw new Error("Local OTA server endpoint changed while publishing the manifest")
103
104
  }
@@ -141,11 +142,11 @@ class HotspotOtaTransport {
141
142
  destination: string,
142
143
  onProgress?: (bytesWritten: number, contentLength: number) => void,
143
144
  ): Promise<{statusCode: number}> => {
144
- const subscription = OtaServer.addListener("artifactDownloadProgress", (event) => {
145
+ const subscription = otaServer.onArtifactDownloadProgress((event) => {
145
146
  if (event.destination === destination) onProgress?.(event.bytesWritten, event.contentLength)
146
147
  })
147
148
  try {
148
- return await OtaServer.downloadArtifact(entry.url, destination)
149
+ return await otaServer.downloadArtifact(entry.url, destination)
149
150
  } finally {
150
151
  subscription.remove()
151
152
  }
@@ -164,7 +165,7 @@ class HotspotOtaTransport {
164
165
  console.warn("[OTA_PROGRESS] glasses hotspot shutdown was not confirmed after bounded retries")
165
166
  }
166
167
  }
167
- if (this.serverStarted) await OtaServer.stopOtaServer().catch(() => {})
168
+ if (this.serverStarted) await otaServer.stop().catch(() => {})
168
169
  if (this.localNetworkConnected) await localNetworkTransport.disconnect().catch(() => {})
169
170
  await cleanupArtifacts().catch(() => {})
170
171
  this.prepared = []