@glassly/engine 0.4.0 → 0.4.1-beta.1138

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 (230) hide show
  1. package/build/engine.d.ts +3 -1
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/engine.js +6 -4
  4. package/build/engine.js.map +1 -1
  5. package/build/facades/display.d.ts +1 -1
  6. package/build/facades/display.d.ts.map +1 -1
  7. package/build/facades/display.js +1 -1
  8. package/build/facades/display.js.map +1 -1
  9. package/build/facades/glasses.js +1 -1
  10. package/build/facades/glasses.js.map +1 -1
  11. package/build/facades/permissions.d.ts.map +1 -1
  12. package/build/facades/permissions.js +0 -2
  13. package/build/facades/permissions.js.map +1 -1
  14. package/build/facades/phoneNotifications.d.ts +7 -0
  15. package/build/facades/phoneNotifications.d.ts.map +1 -1
  16. package/build/facades/phoneNotifications.js +6 -0
  17. package/build/facades/phoneNotifications.js.map +1 -1
  18. package/build/facades/providerKeys.d.ts +1 -1
  19. package/build/facades/providerKeys.js +1 -1
  20. package/build/facades/providerKeys.js.map +1 -1
  21. package/build/facades/reports.d.ts +15 -0
  22. package/build/facades/reports.d.ts.map +1 -1
  23. package/build/facades/reports.js +37 -0
  24. package/build/facades/reports.js.map +1 -1
  25. package/build/facades/settings.js +1 -1
  26. package/build/facades/settings.js.map +1 -1
  27. package/build/generated/releaseMetadata.js +4 -4
  28. package/build/generated/releaseMetadata.js.map +1 -1
  29. package/build/index.d.ts +2 -1
  30. package/build/index.d.ts.map +1 -1
  31. package/build/index.js +1 -1
  32. package/build/index.js.map +1 -1
  33. package/build/react/useMentraLiveOta.d.ts.map +1 -1
  34. package/build/react/useMentraLiveOta.js.map +1 -1
  35. package/build/runtime/config.d.ts.map +1 -1
  36. package/build/runtime/config.js.map +1 -1
  37. package/build/services/AiTierResolution.d.ts +1 -1
  38. package/build/services/AiTierResolution.d.ts.map +1 -1
  39. package/build/services/AiTierResolution.js.map +1 -1
  40. package/build/services/AppRegistry.d.ts +12 -1
  41. package/build/services/AppRegistry.d.ts.map +1 -1
  42. package/build/services/AppRegistry.js +33 -0
  43. package/build/services/AppRegistry.js.map +1 -1
  44. package/build/services/AutomaticReportResult.d.ts.map +1 -1
  45. package/build/services/AutomaticReportResult.js.map +1 -1
  46. package/build/services/CloudClientService.d.ts +7 -5
  47. package/build/services/CloudClientService.d.ts.map +1 -1
  48. package/build/services/CloudClientService.js +17 -14
  49. package/build/services/CloudClientService.js.map +1 -1
  50. package/build/services/DisplayProcessor.d.ts.map +1 -1
  51. package/build/services/DisplayProcessor.js +2 -1
  52. package/build/services/DisplayProcessor.js.map +1 -1
  53. package/build/services/GestureDotsService.js +1 -1
  54. package/build/services/GestureDotsService.js.map +1 -1
  55. package/build/services/HotspotOtaTransport.d.ts.map +1 -1
  56. package/build/services/HotspotOtaTransport.js +0 -1
  57. package/build/services/HotspotOtaTransport.js.map +1 -1
  58. package/build/services/JSRouter.js +2 -2
  59. package/build/services/JSRouter.js.map +1 -1
  60. package/build/services/LocalLlmModelManager.d.ts.map +1 -1
  61. package/build/services/LocalLlmModelManager.js +4 -3
  62. package/build/services/LocalLlmModelManager.js.map +1 -1
  63. package/build/services/LocalLlmProvider.d.ts.map +1 -1
  64. package/build/services/LocalLlmProvider.js +5 -6
  65. package/build/services/LocalLlmProvider.js.map +1 -1
  66. package/build/services/LocalMiniappRuntime.d.ts +10 -3
  67. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  68. package/build/services/LocalMiniappRuntime.js +142 -30
  69. package/build/services/LocalMiniappRuntime.js.map +1 -1
  70. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  71. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  72. package/build/services/NativeNavigationService.d.ts.map +1 -1
  73. package/build/services/NativeNavigationService.js.map +1 -1
  74. package/build/services/NavigationHandlers.d.ts +1 -1
  75. package/build/services/NavigationHandlers.js +2 -2
  76. package/build/services/NavigationHandlers.js.map +1 -1
  77. package/build/services/NavigationService.d.ts +2 -2
  78. package/build/services/NavigationService.d.ts.map +1 -1
  79. package/build/services/NavigationService.js +11 -11
  80. package/build/services/NavigationService.js.map +1 -1
  81. package/build/services/PairingIdentity.d.ts.map +1 -1
  82. package/build/services/PairingIdentity.js.map +1 -1
  83. package/build/services/PhoneCalendarService.d.ts.map +1 -1
  84. package/build/services/PhoneCalendarService.js.map +1 -1
  85. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  86. package/build/services/PhoneNotificationsSync.js +2 -1
  87. package/build/services/PhoneNotificationsSync.js.map +1 -1
  88. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  89. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  90. package/build/services/ProviderKeyValidation.d.ts +1 -1
  91. package/build/services/ProviderKeyValidation.js +1 -1
  92. package/build/services/ProviderKeyValidation.js.map +1 -1
  93. package/build/services/STTModelManager.d.ts.map +1 -1
  94. package/build/services/STTModelManager.js +1 -6
  95. package/build/services/STTModelManager.js.map +1 -1
  96. package/build/services/SupportProfileSync.d.ts.map +1 -1
  97. package/build/services/SupportProfileSync.js +3 -0
  98. package/build/services/SupportProfileSync.js.map +1 -1
  99. package/build/services/TranscriptionRouting.d.ts +3 -0
  100. package/build/services/TranscriptionRouting.d.ts.map +1 -1
  101. package/build/services/TranscriptionRouting.js +14 -0
  102. package/build/services/TranscriptionRouting.js.map +1 -1
  103. package/build/services/UIRouter.d.ts.map +1 -1
  104. package/build/services/UIRouter.js.map +1 -1
  105. package/build/services/controllers.d.ts.map +1 -1
  106. package/build/services/controllers.js +1 -0
  107. package/build/services/controllers.js.map +1 -1
  108. package/build/services/effectiveCapabilities.js +3 -2
  109. package/build/services/effectiveCapabilities.js.map +1 -1
  110. package/build/services/gestureDotGlyphs.d.ts.map +1 -1
  111. package/build/services/gestureDotGlyphs.js.map +1 -1
  112. package/build/services/manifestActions.d.ts.map +1 -1
  113. package/build/services/manifestActions.js.map +1 -1
  114. package/build/services/neuralBand/MetaAuth.d.ts +1 -0
  115. package/build/services/neuralBand/MetaAuth.d.ts.map +1 -1
  116. package/build/services/neuralBand/MetaAuth.js +36 -8
  117. package/build/services/neuralBand/MetaAuth.js.map +1 -1
  118. package/build/services/notificationAppPayload.d.ts +14 -0
  119. package/build/services/notificationAppPayload.d.ts.map +1 -0
  120. package/build/services/notificationAppPayload.js +15 -0
  121. package/build/services/notificationAppPayload.js.map +1 -0
  122. package/build/services/notifications/miniappNotifications.d.ts +31 -0
  123. package/build/services/notifications/miniappNotifications.d.ts.map +1 -0
  124. package/build/services/notifications/miniappNotifications.js +24 -0
  125. package/build/services/notifications/miniappNotifications.js.map +1 -0
  126. package/build/services/pages/pageEligibility.d.ts +1 -2
  127. package/build/services/pages/pageEligibility.d.ts.map +1 -1
  128. package/build/services/pages/pageEligibility.js +4 -2
  129. package/build/services/pages/pageEligibility.js.map +1 -1
  130. package/build/services/settingsBridge.d.ts.map +1 -1
  131. package/build/services/settingsBridge.js +0 -2
  132. package/build/services/settingsBridge.js.map +1 -1
  133. package/build/services/uiShim.d.ts.map +1 -1
  134. package/build/services/uiShim.js +14 -0
  135. package/build/services/uiShim.js.map +1 -1
  136. package/build/stores/appVisibility.d.ts.map +1 -1
  137. package/build/stores/appVisibility.js.map +1 -1
  138. package/build/stores/gallerySync.d.ts.map +1 -1
  139. package/build/stores/gallerySync.js.map +1 -1
  140. package/build/stores/glasses.d.ts.map +1 -1
  141. package/build/stores/glasses.js +1 -0
  142. package/build/stores/glasses.js.map +1 -1
  143. package/build/stores/settings.d.ts.map +1 -1
  144. package/build/stores/settings.js +27 -28
  145. package/build/stores/settings.js.map +1 -1
  146. package/build/types/capabilities/even-realities-g1.d.ts.map +1 -1
  147. package/build/types/capabilities/even-realities-g1.js +4 -0
  148. package/build/types/capabilities/even-realities-g1.js.map +1 -1
  149. package/build/types/capabilities/nimo.d.ts.map +1 -1
  150. package/build/types/capabilities/nimo.js +21 -7
  151. package/build/types/capabilities/nimo.js.map +1 -1
  152. package/build/types/enums.d.ts +2 -1
  153. package/build/types/enums.d.ts.map +1 -1
  154. package/build/types/enums.js +1 -0
  155. package/build/types/enums.js.map +1 -1
  156. package/build/utils/display/index.d.ts +1 -0
  157. package/build/utils/display/index.d.ts.map +1 -1
  158. package/build/utils/display/index.js +2 -2
  159. package/build/utils/display/index.js.map +1 -1
  160. package/build/utils/display/profiles/index.d.ts +1 -0
  161. package/build/utils/display/profiles/index.d.ts.map +1 -1
  162. package/build/utils/display/profiles/index.js +1 -0
  163. package/build/utils/display/profiles/index.js.map +1 -1
  164. package/build/utils/display/profiles/nimo.d.ts +2 -0
  165. package/build/utils/display/profiles/nimo.d.ts.map +1 -0
  166. package/build/utils/display/profiles/nimo.js +3 -0
  167. package/build/utils/display/profiles/nimo.js.map +1 -0
  168. package/build/utils/display/scene/svgPath.js.map +1 -1
  169. package/build/utils/memory/search.d.ts.map +1 -1
  170. package/build/utils/memory/search.js +17 -7
  171. package/build/utils/memory/search.js.map +1 -1
  172. package/package.json +15 -15
  173. package/src/engine.ts +9 -7
  174. package/src/facades/display.ts +1 -1
  175. package/src/facades/glasses.ts +1 -1
  176. package/src/facades/permissions.ts +0 -2
  177. package/src/facades/phoneNotifications.ts +10 -0
  178. package/src/facades/providerKeys.ts +1 -1
  179. package/src/facades/reports.ts +41 -0
  180. package/src/facades/settings.ts +1 -1
  181. package/src/generated/releaseMetadata.ts +4 -4
  182. package/src/index.ts +2 -0
  183. package/src/react/useMentraLiveOta.ts +1 -4
  184. package/src/runtime/config.ts +1 -6
  185. package/src/services/AiTierResolution.ts +1 -1
  186. package/src/services/AppRegistry.ts +38 -1
  187. package/src/services/AutomaticReportResult.ts +1 -3
  188. package/src/services/CloudClientService.ts +17 -11
  189. package/src/services/DisplayProcessor.ts +2 -0
  190. package/src/services/GestureDotsService.ts +1 -1
  191. package/src/services/HotspotOtaTransport.ts +1 -5
  192. package/src/services/JSRouter.ts +2 -2
  193. package/src/services/LocalLlmModelManager.ts +4 -3
  194. package/src/services/LocalLlmProvider.ts +6 -7
  195. package/src/services/LocalMiniappRuntime.ts +170 -40
  196. package/src/services/LocalSttFallbackCoordinator.ts +1 -5
  197. package/src/services/NativeNavigationService.ts +1 -2
  198. package/src/services/NavigationHandlers.ts +2 -2
  199. package/src/services/NavigationService.ts +12 -11
  200. package/src/services/PairingIdentity.ts +1 -3
  201. package/src/services/PhoneCalendarService.ts +1 -3
  202. package/src/services/PhoneNotificationsSync.ts +2 -1
  203. package/src/services/PhoneStreamCoordinator.ts +1 -2
  204. package/src/services/ProviderKeyValidation.ts +1 -1
  205. package/src/services/STTModelManager.ts +1 -6
  206. package/src/services/SupportProfileSync.ts +2 -0
  207. package/src/services/TranscriptionRouting.ts +18 -0
  208. package/src/services/UIRouter.ts +3 -9
  209. package/src/services/controllers.ts +1 -0
  210. package/src/services/effectiveCapabilities.ts +3 -2
  211. package/src/services/gestureDotGlyphs.ts +10 -5
  212. package/src/services/manifestActions.ts +2 -1
  213. package/src/services/neuralBand/MetaAuth.ts +37 -8
  214. package/src/services/notificationAppPayload.ts +15 -0
  215. package/src/services/notifications/miniappNotifications.ts +53 -0
  216. package/src/services/pages/pageEligibility.ts +3 -2
  217. package/src/services/settingsBridge.ts +1 -4
  218. package/src/services/uiShim.ts +14 -0
  219. package/src/stores/appVisibility.ts +2 -5
  220. package/src/stores/gallerySync.ts +1 -7
  221. package/src/stores/glasses.ts +1 -0
  222. package/src/stores/settings.ts +27 -28
  223. package/src/types/capabilities/even-realities-g1.ts +5 -0
  224. package/src/types/capabilities/nimo.ts +23 -7
  225. package/src/types/enums.ts +1 -0
  226. package/src/utils/display/index.ts +2 -2
  227. package/src/utils/display/profiles/index.ts +2 -0
  228. package/src/utils/display/profiles/nimo.ts +2 -0
  229. package/src/utils/display/scene/svgPath.ts +1 -1
  230. package/src/utils/memory/search.ts +17 -7
@@ -83,6 +83,8 @@ function armDebounce(generation: number): void {
83
83
 
84
84
  async function sendCurrentSnapshot(force: boolean, generation = syncGeneration): Promise<void> {
85
85
  if (!unsubscribe || generation !== syncGeneration) return
86
+ // Every send would fail into the retry backoff with no client to send through.
87
+ if (cloudClientService.isCloudDisabled()) return
86
88
  if (sendingGeneration === generation) {
87
89
  const fingerprint = snapshotFingerprint(buildSnapshot())
88
90
  if (fingerprint !== lastSentFingerprint) queuedFingerprint = fingerprint
@@ -1,3 +1,21 @@
1
+ import type {TranscriptionSubscription} from "@glassly/cloud-protocol"
2
+
3
+ /** Match a provider result to a logical miniapp subscription. */
4
+ export function matchesTranscriptionSubscription(
5
+ stream: string,
6
+ resolvedStream: string,
7
+ subscription: TranscriptionSubscription,
8
+ ): boolean {
9
+ const prefix = "transcription:"
10
+ if (!stream.startsWith(prefix) || !resolvedStream.startsWith(prefix)) return false
11
+ if (subscription.language.mode === "specific") return stream === `${prefix}${subscription.language.code}`
12
+ if (stream === `${prefix}auto`) return true
13
+ // Providers detect ISO language codes; miniapps request regional BCP-47 tags.
14
+ // Unknown language stays auto-only until the recognizer identifies it.
15
+ const detected = resolvedStream.slice(prefix.length).split("-")[0].toLowerCase()
16
+ return detected !== "auto" && detected !== "" && stream.slice(prefix.length).split("-")[0].toLowerCase() === detected
17
+ }
18
+
1
19
  export interface TranscriptionRouteSummary {
2
20
  hasCloudSubscriber: boolean
3
21
  hasForceLocalSubscriber: boolean
@@ -193,9 +193,7 @@ export class UIRouter {
193
193
  const cancel = {type: "cancel", requestId: uiSendPayload.requestId}
194
194
  const literal = JSON.stringify(cancel)
195
195
  const escaped = JSON.stringify(literal)
196
- binding.inject(
197
- `if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`,
198
- )
196
+ binding.inject(`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`)
199
197
  return
200
198
  }
201
199
  const outbound: Record<string, unknown> = {
@@ -207,9 +205,7 @@ export class UIRouter {
207
205
  if (typeof uiSendPayload.requestId === "string") outbound.requestId = uiSendPayload.requestId
208
206
  const literal = JSON.stringify(outbound)
209
207
  const escaped = JSON.stringify(literal)
210
- binding.inject(
211
- `if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`,
212
- )
208
+ binding.inject(`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`)
213
209
  }
214
210
 
215
211
  /**
@@ -222,9 +218,7 @@ export class UIRouter {
222
218
  if (!binding) return
223
219
  const literal = JSON.stringify(frame)
224
220
  const escaped = JSON.stringify(literal)
225
- binding.inject(
226
- `if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`,
227
- )
221
+ binding.inject(`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`)
228
222
  }
229
223
 
230
224
  /**
@@ -57,6 +57,7 @@ const INPUT_SURFACES: Readonly<Record<string, readonly InputSurface[]>> = {
57
57
  [ControllerTypes.IINE_ANANKE]: [GAMEBRICK_SURFACE],
58
58
  [ControllerTypes.ZERO2]: [GAMEBRICK_SURFACE],
59
59
  [ControllerTypes.MICRO]: [GAMEBRICK_SURFACE],
60
+ [ControllerTypes.CRKD_ATOM_PLUS]: [GAMEBRICK_SURFACE],
60
61
  [ControllerTypes.R1]: [],
61
62
  }
62
63
 
@@ -61,8 +61,9 @@ function glassesCapabilities(model: DeviceTypes): Capabilities {
61
61
  // Glassly CFW supports the complete vector contract, including SVG and rotation.
62
62
  // Upstream g2flash publishes a raster canvas with sceneVector=false.
63
63
  const vector = useGlassesStore.getState().sceneVector === true
64
- // Depth is a disparity between the lenses; one panel (MemoMind One) draws every element flat.
65
- const stereo = (base.display.count ?? 1) >= 2
64
+ // Depth is a disparity between the eyes: a lens per eye, or firmware that
65
+ // presents a separate raster to each eye's panel (MemoMind One CFW 22+).
66
+ const stereo = (base.display.count ?? 1) >= 2 || useGlassesStore.getState().sceneStereo === true
66
67
  return {
67
68
  ...(volumeGate ? withVolumeGate(base) : base),
68
69
  hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),
@@ -4,18 +4,23 @@
4
4
  */
5
5
  export const GESTURE_DOT_GLYPH_SIZE = 24
6
6
 
7
- export type GestureDotGlyph = "dot" | "double" | "ring" | "doubleRing" | "up" | "down" | "left" | "right" | "hold" | "holdRing" | "blank"
7
+ export type GestureDotGlyph =
8
+ "dot" | "double" | "ring" | "doubleRing" | "up" | "down" | "left" | "right" | "hold" | "holdRing" | "blank"
8
9
 
9
10
  export const GESTURE_DOT_GLYPHS: Record<GestureDotGlyph, string> = {
10
11
  dot: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAALklEQVR4nM2QsQ0AAAjC/P/pmrhqZ+xIF6DqBQyWbwO3gduAmKjQuj7QL/ETEzQmMptl7Q9NBAAAAABJRU5ErkJggg==",
11
- double: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAG0lEQVR4nGNgGD7gPxjgYkN4yOyBlcDp3GEBAB3sd4lPaQxDAAAAAElFTkSuQmCC",
12
+ double:
13
+ "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAG0lEQVR4nGNgGD7gPxjgYkN4yOyBlcDp3GEBAB3sd4lPaQxDAAAAAElFTkSuQmCC",
12
14
  ring: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAANElEQVR4nGNgGBTgPxjgEseUgQphyCD4qDL/kSX+Y9WApoWqEjgsx+lc3B7EHSS4A3EgAAC+cEu1JTNREQAAAABJRU5ErkJggg==",
13
- doubleRing: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAIklEQVR4nGNgGD7gPxjgYkN4yGwGmBA6SV0JHJbjdO6wAADBMlep8zl7TAAAAABJRU5ErkJggg==",
15
+ doubleRing:
16
+ "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAIklEQVR4nGNgGD7gPxjgYkN4yGwGmBA6SV0JHJbjdO6wAADBMlep8zl7TAAAAABJRU5ErkJggg==",
14
17
  up: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAMElEQVR4nGNgoCv4////f1zi2GT+/4ciTHE4RhdHIlDFUUhkcTQKQx9WBw8PCVoBAIcbOcf3xeeBAAAAAElFTkSuQmCC",
15
18
  down: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAALUlEQVR4nGNgoC/4/3+4SkBFUCkkNjKJIoMg0GRgGEMGgrC4CQSwuxanR2gEAO1/Ocfgp8uvAAAAAElFTkSuQmCC",
16
19
  left: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAI0lEQVR4nGNgoCv4j0sch8RAiTP8RwPESOAwajDI4JKgGQAAKO05x7DqJxIAAAAASUVORK5CYII=",
17
- right: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAIElEQVR4nGNgoDf4j1NiMMn8RwOEJYizZKDEcQc7bQAAS7w5x+ro2lsAAAAASUVORK5CYII=",
20
+ right:
21
+ "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAIElEQVR4nGNgoDf4j1NiMMn8RwOEJYizZKDEcQc7bQAAS7w5x+ro2lsAAAAASUVORK5CYII=",
18
22
  hold: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAR0lEQVR4nGNgIAj+gwF2UUy5//+xyyDzcLGRedjMhTEwXAKl0Z0Lof7DKbgMhBpQCZzOxedBHEGCMxAJBTXhKGRAA9hFMQEANr7jHQU9fO0AAAAASUVORK5CYII=",
19
- holdRing: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAQ0lEQVR4nGNgIAj+gwF2UUy5//+xyyDzcLGRedjMhTEwXAKlMd2IVQNMaEAlcDoXnwdxBAnOQCQU1ISjEIvjsYhiAgAo9KdZzePY3AAAAABJRU5ErkJggg==",
23
+ holdRing:
24
+ "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAQ0lEQVR4nGNgIAj+gwF2UUy5//+xyyDzcLGRedjMhTEwXAKlMd2IVQNMaEAlcDoXnwdxBAnOQCQU1ISjEIvjsYhiAgAo9KdZzePY3AAAAABJRU5ErkJggg==",
20
25
  blank: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAD0lEQVR4nGNgGAWjgPoAAAJYAAGgxYwDAAAAAElFTkSuQmCC",
21
26
  }
@@ -80,7 +80,8 @@ export function normalizeManifestActions(raw: unknown): DeclaredAction[] {
80
80
  }
81
81
 
82
82
  const WIDGET_SIZES: readonly DeclaredWidgetSize[] = ["XS", "XSM", "SM", "MD", "LG"]
83
- const isWidgetSize = (value: unknown): value is DeclaredWidgetSize => (WIDGET_SIZES as readonly unknown[]).includes(value)
83
+ const isWidgetSize = (value: unknown): value is DeclaredWidgetSize =>
84
+ (WIDGET_SIZES as readonly unknown[]).includes(value)
84
85
 
85
86
  /**
86
87
  * Normalize manifest widgets for installed and dev-sideloaded bundles: an
@@ -41,17 +41,45 @@ export function isLoginCallback(url: string): boolean {
41
41
  return url.toLowerCase().startsWith("fb-viewapp:") || url.includes("frl_login")
42
42
  }
43
43
 
44
+ function queryPairs(url: string): [string, string][] {
45
+ const withoutFragment = url.includes("#") ? url.slice(0, url.indexOf("#")) : url
46
+ const query = withoutFragment.includes("?") ? withoutFragment.slice(withoutFragment.indexOf("?") + 1) : ""
47
+ const pairs: [string, string][] = []
48
+ for (const pair of query.split("&")) {
49
+ if (!pair) continue
50
+ const eq = pair.indexOf("=")
51
+ pairs.push(eq === -1 ? [pair, ""] : [pair.slice(0, eq), pair.slice(eq + 1)])
52
+ }
53
+ return pairs
54
+ }
55
+
56
+ function decodeValue(value: string): string {
57
+ try {
58
+ return decodeURIComponent(value)
59
+ } catch {
60
+ return value
61
+ }
62
+ }
63
+
44
64
  export function callbackQuery(url: string): {token?: string; blob?: string} {
45
- const query = url.includes("?") ? url.slice(url.indexOf("?") + 1) : ""
46
65
  const out: {token?: string; blob?: string} = {}
47
- for (const pair of query.split("&")) {
48
- const [key, value = ""] = pair.split("=")
49
- if (key === "token") out.token = decodeURIComponent(value)
50
- if (key === "blob") out.blob = decodeURIComponent(value)
66
+ for (const [key, value] of queryPairs(url)) {
67
+ if (key === "token") out.token = decodeValue(value)
68
+ if (key === "blob") out.blob = decodeValue(value)
51
69
  }
52
70
  return out
53
71
  }
54
72
 
73
+ export function callbackShape(url: string): string {
74
+ const scheme = url.includes(":") ? url.slice(0, url.indexOf(":")) : "-"
75
+ const afterScheme = url.slice(scheme.length + 1)
76
+ const pathEnd = afterScheme.search(/[?#]/)
77
+ const path = pathEnd === -1 ? afterScheme : afterScheme.slice(0, pathEnd)
78
+ const keys = queryPairs(url).map(([key, value]) => `${key}:${value.length}`)
79
+ const fragment = url.includes("#") ? ` fragment=${url.slice(url.indexOf("#") + 1).length}` : ""
80
+ return `${scheme}:${path} query=[${keys.join(",")}]${fragment}`
81
+ }
82
+
55
83
  /** The first 16 hex digits of the request token's SHA-256, which the callback must present. */
56
84
  export function expectedCallbackToken(nativeSsoToken: string): string {
57
85
  return sha256Hex(nativeSsoToken).slice(0, 16)
@@ -99,11 +127,12 @@ async function post(url: string, headers: Record<string, string>, body: string):
99
127
  } catch {
100
128
  json = null
101
129
  }
130
+ const error = json?.error as {code?: number; error_subcode?: number} | null | undefined
102
131
  if (response.status !== 200 || json === null) {
103
- throw new MetaAuthError(`Meta sign-in failed (HTTP ${response.status}). Try again.`)
132
+ const code = error ? `, code ${error.code ?? "-"}` : ""
133
+ throw new MetaAuthError(`Meta sign-in failed (HTTP ${response.status}${code}). Try again.`)
104
134
  }
105
- if (json.error !== undefined) {
106
- const error = json.error as {code?: number; error_subcode?: number} | null
135
+ if (error !== undefined) {
107
136
  throw new MetaAuthError(
108
137
  `Meta sign-in failed (code ${error?.code ?? "-"}, subcode ${error?.error_subcode ?? "-"}). Try again.`,
109
138
  )
@@ -0,0 +1,15 @@
1
+ /** Keep installed-app icons from exhausting the miniapp's 32 MiB JS heap.
2
+ * Icons are optional; clients already render a fallback when one is absent.
3
+ * Bound both individual icons and their total before JSON crosses the bridge.
4
+ */
5
+ export function notificationAppPayload(
6
+ apps: Array<{packageName: string; appName: string; icon: string | null}>,
7
+ ): Array<{packageName: string; appName: string; icon: string | null}> {
8
+ let remaining = 128 * 1024
9
+ return apps.map(({packageName, appName, icon}) => {
10
+ const size = icon?.length ?? 0
11
+ const keep = size <= 8 * 1024 && size <= remaining
12
+ if (keep) remaining -= size
13
+ return {packageName, appName, icon: keep ? icon : null}
14
+ })
15
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Notifications miniapps post (`session.phone.notifications.post`) on their
3
+ * way into the host's phone-notification path. The runtime validates and
4
+ * emits here; the host subscribes through `engine.phoneNotifications` and
5
+ * feeds them to the same fan-out as the phone's own, with `system: false`
6
+ * and `source` set, so the dashboard, other readers and presentation treat
7
+ * them alike.
8
+ */
9
+ export interface MiniappNotificationPosted {
10
+ type: "posted"
11
+ notificationId: string
12
+ source: string
13
+ app: string
14
+ title: string
15
+ content: string
16
+ timestamp: number
17
+ }
18
+
19
+ export interface MiniappNotificationDismissed {
20
+ type: "dismissed"
21
+ notificationId: string
22
+ source: string
23
+ timestamp: number
24
+ }
25
+
26
+ export type MiniappNotificationEvent = MiniappNotificationPosted | MiniappNotificationDismissed
27
+
28
+ export const MINIAPP_NOTIFICATION_TITLE_MAX = 200
29
+ export const MINIAPP_NOTIFICATION_CONTENT_MAX = 2_000
30
+ export const MINIAPP_NOTIFICATION_ID_MAX = 128
31
+
32
+ const listeners = new Set<(event: MiniappNotificationEvent) => void>()
33
+
34
+ export function miniappNotificationId(source: string, id: string): string {
35
+ return `${source}:${id}`
36
+ }
37
+
38
+ export function emitMiniappNotification(event: MiniappNotificationEvent): void {
39
+ for (const listener of listeners) {
40
+ try {
41
+ listener(event)
42
+ } catch (err) {
43
+ console.warn(`miniappNotifications: listener threw: ${(err as Error)?.message ?? err}`)
44
+ }
45
+ }
46
+ }
47
+
48
+ export function onMiniappNotification(listener: (event: MiniappNotificationEvent) => void): () => void {
49
+ listeners.add(listener)
50
+ return () => {
51
+ listeners.delete(listener)
52
+ }
53
+ }
@@ -21,11 +21,12 @@ export function isCfwFirmwareVersion(firmwareVersion: string | null | undefined)
21
21
 
22
22
  /**
23
23
  * Only models that ship a Glassly custom firmware, and only while they run it.
24
- * Stock firmware on those models and every other model (NIMO, G1, ...) are
25
- * intentionally excluded.
24
+ * Stock firmware and every other model are intentionally excluded.
26
25
  */
27
26
  export function isCustomDashboardEligible(info: {model?: string | null; firmwareVersion?: string | null}): boolean {
28
27
  if (!info.model) return false
28
+ // The NIMO drivers draw no scenes through its CFW yet; the dashboard would land on the text page.
29
+ if (info.model === DeviceTypes.NIMO) return false
29
30
  if (info.model === DeviceTypes.MEMOMIND_ONE) {
30
31
  const match = /^GLASSLYCFW\/([0-9]+)$/.exec(info.firmwareVersion ?? "")
31
32
  const revision = match ? Number(match[1]) : 0
@@ -23,7 +23,6 @@ export type SettingsBridgeAccess = "read" | "write"
23
23
  export const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>> = {
24
24
  // The dashboard's master switch is a phone-side debug knob.
25
25
  custom_dashboard_enabled: "read",
26
- contextual_dashboard: "write",
27
26
  head_up_angle: "write",
28
27
  // Where the glasses draw; each model's steps are its display.positionRange.
29
28
  dashboard_depth: "write",
@@ -33,7 +32,6 @@ export const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>
33
32
  auto_brightness: "write",
34
33
  // Per paired model: the store resolves the indexed key.
35
34
  preferred_mic: "write",
36
- twelve_hour_time: "write",
37
35
  menu_apps: "write",
38
36
  notifications_enabled: "write",
39
37
  notifications_popups: "write",
@@ -82,8 +80,7 @@ export function selectSettingsBridgeKeys(requested: unknown): string[] {
82
80
  }
83
81
 
84
82
  export type SettingsSetValidation =
85
- | {ok: true; key: string; value: unknown}
86
- | {ok: false; code: "INVALID_ARGUMENT" | "NOT_PERMITTED"; message: string}
83
+ {ok: true; key: string; value: unknown} | {ok: false; code: "INVALID_ARGUMENT" | "NOT_PERMITTED"; message: string}
87
84
 
88
85
  /** A SETTINGS_SET payload: a writable key and a JSON value (undefined is not a value). */
89
86
  export function validateSettingsSet(payload: Record<string, unknown>): SettingsSetValidation {
@@ -304,6 +304,13 @@ export function buildUiShim(options: UiShimOptions): string {
304
304
  postEnvelope({ type: 'set_background', color: color });
305
305
  }
306
306
 
307
+ var backHandler = null;
308
+
309
+ function setBackActionFn(handler) {
310
+ backHandler = typeof handler === 'function' ? handler : null;
311
+ postEnvelope({ type: 'set_back_action', shown: backHandler !== null });
312
+ }
313
+
307
314
  function fireChannel(channel, payload) {
308
315
  var list = channelHandlers[channel];
309
316
  if (!list || list.length === 0) {
@@ -348,6 +355,12 @@ export function buildUiShim(options: UiShimOptions): string {
348
355
  }
349
356
  return;
350
357
  }
358
+ if (type === 'back') {
359
+ if (backHandler) {
360
+ try { backHandler(); } catch (e) {}
361
+ }
362
+ return;
363
+ }
351
364
  if (type === 'cancel' && typeof envelope.requestId === 'string') {
352
365
  // Background-side cancel (reserved; currently unused). If we had a
353
366
  // future bidirectional RPC this would abort an in-flight UI handler.
@@ -366,6 +379,7 @@ export function buildUiShim(options: UiShimOptions): string {
366
379
  onClose: onClose,
367
380
  ready: readyFn,
368
381
  setBackgroundColor: setBackgroundColorFn,
382
+ setBackAction: setBackActionFn,
369
383
  /** @internal — packageName for diagnostic logs only. */
370
384
  _packageName: packageName,
371
385
  };
@@ -44,8 +44,5 @@ export const isGatedByDevTools = (packageName: string, devToolsEnabled: boolean)
44
44
  export const PAIRED_GLASSES_PACKAGES: ReadonlySet<string> = new Set(["com.glassly.store"])
45
45
 
46
46
  /** True when the app waits on a pairing that hasn't happened and dev tools are off. */
47
- export const isGatedUntilPaired = (
48
- packageName: string,
49
- devToolsEnabled: boolean,
50
- hasPairedGlasses: boolean,
51
- ): boolean => !devToolsEnabled && !hasPairedGlasses && PAIRED_GLASSES_PACKAGES.has(packageName)
47
+ export const isGatedUntilPaired = (packageName: string, devToolsEnabled: boolean, hasPairedGlasses: boolean): boolean =>
48
+ !devToolsEnabled && !hasPairedGlasses && PAIRED_GLASSES_PACKAGES.has(packageName)
@@ -29,13 +29,7 @@ export interface PhotoInfo {
29
29
 
30
30
  // Sync state machine states
31
31
  export type SyncState =
32
- | "idle"
33
- | "requesting_hotspot"
34
- | "connecting_wifi"
35
- | "syncing"
36
- | "complete"
37
- | "error"
38
- | "cancelled"
32
+ "idle" | "requesting_hotspot" | "connecting_wifi" | "syncing" | "complete" | "error" | "cancelled"
39
33
 
40
34
  export interface HotspotInfo {
41
35
  ssid: string
@@ -144,6 +144,7 @@ const initialState: GlassesStore = {
144
144
  sceneCanvasWidth: 0,
145
145
  sceneCanvasHeight: 0,
146
146
  sceneVector: false,
147
+ sceneStereo: false,
147
148
  sceneSettle: false,
148
149
  volumeGate: false,
149
150
  firmwareUpdateAvailable: false,
@@ -132,13 +132,6 @@ export const SETTINGS: Record<string, Setting> = {
132
132
  saveOnServer: true,
133
133
  persist: true,
134
134
  },
135
- memomind_accessory_picker: {
136
- key: "memomind_accessory_picker",
137
- defaultValue: () => false,
138
- writable: true,
139
- saveOnServer: false,
140
- persist: true,
141
- },
142
135
  debug_console: {
143
136
  key: "debug_console",
144
137
  defaultValue: () => false,
@@ -333,7 +326,7 @@ export const SETTINGS: Record<string, Setting> = {
333
326
  saveOnServer: false,
334
327
  persist: true,
335
328
  },
336
- // User-supplied AI/voice provider keys (Settings > API Keys). Not consumed
329
+ // User-supplied AI/voice provider keys (Settings > Privacy > API Keys). Not consumed
337
330
  // by the app itself — they exist so miniapps declaring the API_KEYS manifest
338
331
  // permission can read them via session.keys.get() (see
339
332
  // MINIAPP_READABLE_API_KEYS in LocalMiniappRuntime; mapbox is also readable
@@ -715,13 +708,6 @@ export const SETTINGS: Record<string, Setting> = {
715
708
  saveOnServer: true,
716
709
  persist: true,
717
710
  },
718
- twelve_hour_time: {
719
- key: "twelve_hour_time",
720
- defaultValue: () => true,
721
- writable: true,
722
- saveOnServer: true,
723
- persist: true,
724
- },
725
711
  enforce_local_transcription: {
726
712
  key: "enforce_local_transcription",
727
713
  defaultValue: () => false,
@@ -762,20 +748,14 @@ export const SETTINGS: Record<string, Setting> = {
762
748
  saveOnServer: true,
763
749
  persist: true,
764
750
  },
751
+ // Session-only: Position settings holds it on for stock G1, and a persisted
752
+ // true (app killed on that screen) blanks every glasses' display at boot.
765
753
  screen_disabled: {
766
754
  key: "screen_disabled",
767
755
  defaultValue: () => false,
768
756
  writable: true,
769
757
  saveOnServer: false,
770
- persist: true,
771
- },
772
- // glasses settings:
773
- contextual_dashboard: {
774
- key: "contextual_dashboard",
775
- defaultValue: () => false,
776
- writable: true,
777
- saveOnServer: true,
778
- persist: true,
758
+ persist: false,
779
759
  },
780
760
  // Debug escape hatch: G2 scene rendering through the Glassly CFW
781
761
  // direct-framebuffer path (rasterized mode-3 frames into the carrier).
@@ -948,6 +928,13 @@ export const SETTINGS: Record<string, Setting> = {
948
928
  saveOnServer: false,
949
929
  persist: true,
950
930
  },
931
+ debug_mirror_rail: {
932
+ key: "debug_mirror_rail",
933
+ defaultValue: () => false,
934
+ writable: true,
935
+ saveOnServer: false,
936
+ persist: true,
937
+ },
951
938
  home_offline_card: {
952
939
  key: "home_offline_card",
953
940
  defaultValue: () => true,
@@ -955,6 +942,13 @@ export const SETTINGS: Record<string, Setting> = {
955
942
  saveOnServer: false,
956
943
  persist: true,
957
944
  },
945
+ home_tips_seen: {
946
+ key: "home_tips_seen",
947
+ defaultValue: (): string[] => [],
948
+ writable: true,
949
+ saveOnServer: false,
950
+ persist: true,
951
+ },
958
952
  // Gesture-dots debug indicator: flash a glyph on the glasses when a touch
959
953
  // gesture is received while the display is empty. Works on any
960
954
  // scene-capable device, not only CFW. Device-local debug knob, default off.
@@ -988,6 +982,13 @@ export const SETTINGS: Record<string, Setting> = {
988
982
  saveOnServer: true,
989
983
  persist: true,
990
984
  },
985
+ dashboard_size: {
986
+ key: "dashboard_size",
987
+ defaultValue: () => 340,
988
+ writable: true,
989
+ saveOnServer: true,
990
+ persist: true,
991
+ },
991
992
  menu_apps: {
992
993
  key: "menu_apps",
993
994
  defaultValue: () => null,
@@ -1252,22 +1253,20 @@ export const BLUETOOTH_SETTING_KEYS: string[] = [
1252
1253
  SETTINGS.screen_disabled.key,
1253
1254
  SETTINGS.auth_email.key,
1254
1255
  SETTINGS.core_token.key,
1255
- // glasses settings (contextual_dashboard is phone-side policy now — native
1256
- // no longer reads it):
1256
+ // glasses settings:
1257
1257
  SETTINGS.head_up_angle.key,
1258
1258
  SETTINGS.brightness.key,
1259
1259
  SETTINGS.auto_brightness.key,
1260
1260
  SETTINGS.dashboard_height.key,
1261
1261
  SETTINGS.dashboard_depth.key,
1262
+ SETTINGS.dashboard_size.key,
1262
1263
  SETTINGS.menu_apps.key,
1263
1264
  SETTINGS.calendar_events.key,
1264
1265
  SETTINGS.cfw_scene_renderer.key,
1265
1266
  SETTINGS.cfw_fast_ble.key,
1266
1267
  SETTINGS.perf_logs.key,
1267
- SETTINGS.memomind_accessory_picker.key,
1268
1268
  SETTINGS.ring_via_glasses.key,
1269
1269
  SETTINGS.display_max_fps.key,
1270
- SETTINGS.twelve_hour_time.key,
1271
1270
  SETTINGS.metric_system.key,
1272
1271
  // button:
1273
1272
  SETTINGS.button_photo_size.key,
@@ -72,4 +72,9 @@ export const evenRealitiesG1: Capabilities = {
72
72
  // The app flashes the bundled Glassly CFW over MCUmgr SMP (see
73
73
  // bluetooth-sdk/ios/Source/Resources/G1Firmware/README.md).
74
74
  hasCustomFirmware: true,
75
+
76
+ // On iOS the Glassly CFW (G1CFW/51) relays the notifications the phone shares
77
+ // with the glasses over ANCS; the dashboard presents them.
78
+ hasNativeNotifications: false,
79
+ hasNotificationRelay: true,
75
80
  }
@@ -18,21 +18,34 @@ export const nimo: Capabilities = {
18
18
  hasCamera: false,
19
19
  camera: null,
20
20
 
21
- // Display capabilities - monochrome binocular display
21
+ // Display capabilities - monochrome binocular display. Firmware 0.1.1.1+
22
+ // has a phone-drawn page (app 0xFD): a 500x220 canvas of positioned text,
23
+ // rectangles, circles, lines and bitmaps that the drivers render scene
24
+ // frames onto (NimoDynamicLayout.kt / .swift). Older firmware shows the
25
+ // frame's text on the ASR note page instead.
22
26
  hasDisplay: true,
23
27
  display: {
24
28
  count: 2,
25
29
  isColor: false,
26
30
  color: "green",
27
31
  canDisplayBitmap: true,
28
- // Bitmaps render into the nav mini-map widget (appId 0x01, 160x160 2bpp);
29
- // see displayBitmap in Nimo.kt / Nimo.swift. The resolution below is the
30
- // text-display placeholder, not the bitmap target.
31
- // TODO: placeholder - verify against hardware screen info
32
- resolution: {width: 640, height: 400},
33
- maxTextLines: 5,
32
+ resolution: {width: 540, height: 280},
33
+ maxTextLines: 10,
34
34
  adjustBrightness: true,
35
35
  positionRange: {height: {min: 0, max: 10}, depth: {min: 0, max: 10}},
36
+
37
+ // Scene display API - the Dynamic Layout canvas.
38
+ width: 500,
39
+ height: 220,
40
+ canPosition: true,
41
+ // One page holds 64 objects; the text pool is shared with rectangles.
42
+ maxTextElements: 32,
43
+ maxImageElements: 4,
44
+ maxImagePx: {width: 500, height: 220},
45
+ shapes: ["rect", "circle", "line"],
46
+ intensityLevels: 16,
47
+ // Every frame replaces the page; the firmware has no per-object update.
48
+ partialUpdate: false,
36
49
  },
37
50
 
38
51
  // Microphone capabilities - Nimo streams Opus audio from both arms
@@ -82,4 +95,7 @@ export const nimo: Capabilities = {
82
95
 
83
96
  // OTA capabilities - Nimo firmware updates are not driven by the ASG OTA flow
84
97
  hasOta: false,
98
+
99
+ // The drivers flash a bundled Glassly CFW over the GATT OTA characteristics.
100
+ hasCustomFirmware: true,
85
101
  }
@@ -25,4 +25,5 @@ export enum ControllerTypes {
25
25
  IINE_ANANKE = "IINE Ananke Gen 2",
26
26
  ZERO2 = "8BitDo Zero 2",
27
27
  MICRO = "8BitDo Micro",
28
+ CRKD_ATOM_PLUS = "CRKD ATOM+",
28
29
  }
@@ -36,7 +36,7 @@
36
36
  // =============================================================================
37
37
 
38
38
  // Import for factory functions
39
- import {G1_PROFILE_LEGACY, G2_PROFILE, Z100_PROFILE, NEX_PROFILE} from "./profiles"
39
+ import {G1_PROFILE_LEGACY, G2_PROFILE, Z100_PROFILE, NEX_PROFILE, G1_PROFILE} from "./profiles"
40
40
 
41
41
  // =============================================================================
42
42
  // Convenience factory functions
@@ -46,7 +46,6 @@ import {TextMeasurer} from "./measurer"
46
46
  import {TextWrapper} from "./wrapper"
47
47
  import {DisplayHelpers} from "./helpers"
48
48
  import {ColumnComposer} from "./composer"
49
- import {G1_PROFILE} from "./profiles"
50
49
  import type {DisplayProfile} from "./profiles"
51
50
  import type {WrapOptions} from "./wrapper"
52
51
 
@@ -276,3 +275,4 @@ export function createNexToolkit(): {
276
275
  }
277
276
 
278
277
  export {MEMOMIND_ONE_PROFILE} from "./profiles"
278
+ export {NIMO_PROFILE} from "./profiles"
@@ -36,3 +36,5 @@ export {
36
36
  } from "./rayneo-io"
37
37
 
38
38
  export {MEMOMIND_ONE_PROFILE} from "@glassly/miniapp/display"
39
+
40
+ export {NIMO_PROFILE} from "./nimo"
@@ -0,0 +1,2 @@
1
+ // Moved into the SDK (@glassly/miniapp/display) so miniapps lay text out the way the host measures it; this path re-exports it for the engine.
2
+ export {NIMO_PROFILE} from "@glassly/miniapp/display"
@@ -128,7 +128,7 @@ export function compileSvgPath(d: string): {commands: number[]; edges: number} {
128
128
  curve(a, depth + 1)
129
129
  curve(b, depth + 1)
130
130
  }
131
- for (let i = 0; i < commands.length; ) {
131
+ for (let i = 0; i < commands.length;) {
132
132
  const op = commands[i++],
133
133
  pairs = op === 4 ? 0 : op === 3 ? 3 : op === 2 ? 2 : 1
134
134
  if (++count > 1024) throw new Error("SVG path exceeds 1024 commands")