@glassly/engine 0.4.0-dev.1037 → 0.4.0-dev.1039

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 (113) hide show
  1. package/build/engine.d.ts +1 -1
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/engine.js +1 -1
  4. package/build/engine.js.map +1 -1
  5. package/build/facades/glasses.js +1 -1
  6. package/build/facades/glasses.js.map +1 -1
  7. package/build/facades/providerKeys.d.ts +1 -1
  8. package/build/facades/providerKeys.js +1 -1
  9. package/build/facades/providerKeys.js.map +1 -1
  10. package/build/generated/releaseMetadata.js +3 -3
  11. package/build/generated/releaseMetadata.js.map +1 -1
  12. package/build/react/useMentraLiveOta.d.ts.map +1 -1
  13. package/build/react/useMentraLiveOta.js.map +1 -1
  14. package/build/runtime/config.d.ts.map +1 -1
  15. package/build/runtime/config.js.map +1 -1
  16. package/build/services/AiTierResolution.d.ts +1 -1
  17. package/build/services/AiTierResolution.d.ts.map +1 -1
  18. package/build/services/AiTierResolution.js.map +1 -1
  19. package/build/services/AutomaticReportResult.d.ts.map +1 -1
  20. package/build/services/AutomaticReportResult.js.map +1 -1
  21. package/build/services/GestureDotsService.js +1 -1
  22. package/build/services/GestureDotsService.js.map +1 -1
  23. package/build/services/HotspotOtaTransport.d.ts.map +1 -1
  24. package/build/services/HotspotOtaTransport.js.map +1 -1
  25. package/build/services/LocalMiniappRuntime.d.ts +5 -2
  26. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  27. package/build/services/LocalMiniappRuntime.js +53 -22
  28. package/build/services/LocalMiniappRuntime.js.map +1 -1
  29. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  30. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  31. package/build/services/NativeNavigationService.d.ts.map +1 -1
  32. package/build/services/NativeNavigationService.js.map +1 -1
  33. package/build/services/NavigationHandlers.d.ts +1 -1
  34. package/build/services/NavigationHandlers.js +2 -2
  35. package/build/services/NavigationHandlers.js.map +1 -1
  36. package/build/services/NavigationService.d.ts +2 -2
  37. package/build/services/NavigationService.d.ts.map +1 -1
  38. package/build/services/NavigationService.js +11 -11
  39. package/build/services/NavigationService.js.map +1 -1
  40. package/build/services/PairingIdentity.d.ts.map +1 -1
  41. package/build/services/PairingIdentity.js.map +1 -1
  42. package/build/services/PhoneCalendarService.d.ts.map +1 -1
  43. package/build/services/PhoneCalendarService.js.map +1 -1
  44. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  45. package/build/services/PhoneNotificationsSync.js +2 -1
  46. package/build/services/PhoneNotificationsSync.js.map +1 -1
  47. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  48. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  49. package/build/services/ProviderKeyValidation.d.ts +1 -1
  50. package/build/services/ProviderKeyValidation.js +1 -1
  51. package/build/services/ProviderKeyValidation.js.map +1 -1
  52. package/build/services/STTModelManager.d.ts.map +1 -1
  53. package/build/services/STTModelManager.js +1 -6
  54. package/build/services/STTModelManager.js.map +1 -1
  55. package/build/services/TranscriptionRouting.d.ts +3 -0
  56. package/build/services/TranscriptionRouting.d.ts.map +1 -1
  57. package/build/services/TranscriptionRouting.js +14 -0
  58. package/build/services/TranscriptionRouting.js.map +1 -1
  59. package/build/services/UIRouter.d.ts.map +1 -1
  60. package/build/services/UIRouter.js.map +1 -1
  61. package/build/services/gestureDotGlyphs.d.ts.map +1 -1
  62. package/build/services/gestureDotGlyphs.js.map +1 -1
  63. package/build/services/manifestActions.d.ts.map +1 -1
  64. package/build/services/manifestActions.js.map +1 -1
  65. package/build/services/notificationAppPayload.d.ts +14 -0
  66. package/build/services/notificationAppPayload.d.ts.map +1 -0
  67. package/build/services/notificationAppPayload.js +15 -0
  68. package/build/services/notificationAppPayload.js.map +1 -0
  69. package/build/services/settingsBridge.d.ts.map +1 -1
  70. package/build/services/settingsBridge.js.map +1 -1
  71. package/build/stores/appVisibility.d.ts.map +1 -1
  72. package/build/stores/appVisibility.js.map +1 -1
  73. package/build/stores/gallerySync.d.ts.map +1 -1
  74. package/build/stores/gallerySync.js.map +1 -1
  75. package/build/stores/settings.js +1 -1
  76. package/build/stores/settings.js.map +1 -1
  77. package/build/utils/display/scene/svgPath.js.map +1 -1
  78. package/build/utils/memory/search.d.ts.map +1 -1
  79. package/build/utils/memory/search.js +17 -7
  80. package/build/utils/memory/search.js.map +1 -1
  81. package/package.json +7 -7
  82. package/src/engine.ts +1 -1
  83. package/src/facades/glasses.ts +1 -1
  84. package/src/facades/providerKeys.ts +1 -1
  85. package/src/generated/releaseMetadata.ts +3 -3
  86. package/src/react/useMentraLiveOta.ts +1 -4
  87. package/src/runtime/config.ts +1 -6
  88. package/src/services/AiTierResolution.ts +1 -1
  89. package/src/services/AutomaticReportResult.ts +1 -3
  90. package/src/services/GestureDotsService.ts +1 -1
  91. package/src/services/HotspotOtaTransport.ts +1 -4
  92. package/src/services/LocalMiniappRuntime.ts +65 -23
  93. package/src/services/LocalSttFallbackCoordinator.ts +1 -5
  94. package/src/services/NativeNavigationService.ts +1 -2
  95. package/src/services/NavigationHandlers.ts +2 -2
  96. package/src/services/NavigationService.ts +12 -11
  97. package/src/services/PairingIdentity.ts +1 -3
  98. package/src/services/PhoneCalendarService.ts +1 -3
  99. package/src/services/PhoneNotificationsSync.ts +2 -1
  100. package/src/services/PhoneStreamCoordinator.ts +1 -2
  101. package/src/services/ProviderKeyValidation.ts +1 -1
  102. package/src/services/STTModelManager.ts +1 -6
  103. package/src/services/TranscriptionRouting.ts +18 -0
  104. package/src/services/UIRouter.ts +3 -9
  105. package/src/services/gestureDotGlyphs.ts +10 -5
  106. package/src/services/manifestActions.ts +2 -1
  107. package/src/services/notificationAppPayload.ts +15 -0
  108. package/src/services/settingsBridge.ts +1 -2
  109. package/src/stores/appVisibility.ts +2 -5
  110. package/src/stores/gallerySync.ts +1 -7
  111. package/src/stores/settings.ts +1 -1
  112. package/src/utils/display/scene/svgPath.ts +1 -1
  113. package/src/utils/memory/search.ts +17 -7
@@ -211,7 +211,7 @@ class NavigationService {
211
211
  const mapboxRenderToken = await resolveMapboxRenderToken()
212
212
  if (!mapboxRenderToken) {
213
213
  const error =
214
- "No Mapbox token available — add your key in Settings > API Keys" +
214
+ "No Mapbox token available — add your key in Settings > Privacy > API Keys" +
215
215
  " (required while Glassly Cloud is disabled or unreachable)"
216
216
  console.warn(`${LOG_TAG}: start blocked — ${error}`)
217
217
  return {ok: false, error}
@@ -329,7 +329,7 @@ class NavigationService {
329
329
  * Compute one or more routes without starting a trip.
330
330
  *
331
331
  * Reached from the miniapp bridge (NavigationHandlers.handleComputeRoute)
332
- * when the user has saved their own Mapbox key in Settings > API Keys —
332
+ * when the user has saved their own Mapbox key in Settings > Privacy > API Keys —
333
333
  * that key exists so navigation works without the cloud. Without a user
334
334
  * key the bridge uses the v2 cloud maps service, which holds a provider
335
335
  * token server-side and caches results. Implemented by
@@ -379,7 +379,7 @@ class NavigationService {
379
379
  return !!resolveMapboxAccessToken()
380
380
  }
381
381
 
382
- /** True when the user saved their own Mapbox key in Settings > API Keys. */
382
+ /** True when the user saved their own Mapbox key in Settings > Privacy > API Keys. */
383
383
  public hasUserMapboxKey(): boolean {
384
384
  return !!userMapboxApiKey()
385
385
  }
@@ -501,7 +501,7 @@ export default navigationService
501
501
  // ---------------------------------------------------------------------------
502
502
 
503
503
  // Routes + Geocoding go DIRECT to Mapbox from the phone process ONLY when the
504
- // user saved their own key (Settings > API Keys) — "use my account". Without
504
+ // user saved their own key (Settings > Privacy > API Keys) — "use my account". Without
505
505
  // one, callers (NavigationHandlers) route these operations through the v2
506
506
  // cloud maps proxy instead; the binary ships no baked Mapbox key.
507
507
  const MAPBOX_DIRECTIONS_BASE = "https://api.mapbox.com/directions/v5/mapbox"
@@ -532,7 +532,7 @@ function mapboxToken(): string {
532
532
 
533
533
  /**
534
534
  * Phone-side helper: hit the Mapbox Directions API directly with the user's own
535
- * key (Settings > API Keys). Preferred over the v2 cloud maps service whenever
535
+ * key (Settings > Privacy > API Keys). Preferred over the v2 cloud maps service whenever
536
536
  * that key is set, so routing works without the cloud. Lives here
537
537
  * (not in Kotlin) so it's platform-independent across the JS bridge.
538
538
  * Returns the SDK-shaped result with primary + alternates.
@@ -583,7 +583,8 @@ async function computeRouteViaRoutesApi(payload: Record<string, unknown>): Promi
583
583
  }
584
584
 
585
585
  const token = mapboxToken()
586
- if (!token) return {ok: false, error: "computeRoute: no Mapbox key saved (add yours in Settings > API Keys)"}
586
+ if (!token)
587
+ return {ok: false, error: "computeRoute: no Mapbox key saved (add yours in Settings > Privacy > API Keys)"}
587
588
 
588
589
  // Mapbox path is a semicolon-joined `lng,lat` list: origin then each stop.
589
590
  const coords = [`${oLng},${oLat}`, ...stops.map((s) => `${s.lng},${s.lat}`)].join(";")
@@ -739,7 +740,7 @@ function mapboxManeuverToKind(type?: string, modifier?: string): string | undefi
739
740
 
740
741
  /**
741
742
  * Reverse-geocode a coordinate via the Mapbox Geocoding v6 API directly, using
742
- * the user's own key (Settings > API Keys). Preferred over the v2 cloud maps
743
+ * the user's own key (Settings > Privacy > API Keys). Preferred over the v2 cloud maps
743
744
  * service whenever that key is set, so pin labels work without the cloud.
744
745
  *
745
746
  * Returns the road/street name (e.g. "Octavia Blvd") plus the full formatted
@@ -767,7 +768,7 @@ async function reverseGeocodeRoadViaGeocodingApi(coord: {lat: number; lng: numbe
767
768
  if (!token)
768
769
  return {
769
770
  ok: false,
770
- error: "reverseGeocode: no Mapbox key saved (add yours in Settings > API Keys)",
771
+ error: "reverseGeocode: no Mapbox key saved (add yours in Settings > Privacy > API Keys)",
771
772
  }
772
773
  const params = new URLSearchParams({
773
774
  longitude: String(coord.lng),
@@ -813,7 +814,7 @@ async function reverseGeocodeRoadViaGeocodingApi(coord: {lat: number; lng: numbe
813
814
 
814
815
  /**
815
816
  * Type-ahead place search straight from the device via Mapbox Search Box,
816
- * using the user's own key (Settings > API Keys). This is the offline-from-
817
+ * using the user's own key (Settings > Privacy > API Keys). This is the offline-from-
817
818
  * the-cloud path: with a key saved, place search works with no runtime
818
819
  * involvement at all.
819
820
  *
@@ -840,7 +841,7 @@ async function placeAutocompleteViaSearchBox(
840
841
  if (!token) {
841
842
  return {
842
843
  ok: false,
843
- error: "placeAutocomplete: no Mapbox key saved (add yours in Settings > API Keys)",
844
+ error: "placeAutocomplete: no Mapbox key saved (add yours in Settings > Privacy > API Keys)",
844
845
  }
845
846
  }
846
847
  const params = new URLSearchParams({
@@ -904,7 +905,7 @@ async function placeDetailsViaSearchBox(
904
905
  if (!token) {
905
906
  return {
906
907
  ok: false,
907
- error: "placeDetails: no Mapbox key saved (add yours in Settings > API Keys)",
908
+ error: "placeDetails: no Mapbox key saved (add yours in Settings > Privacy > API Keys)",
908
909
  }
909
910
  }
910
911
  const params = new URLSearchParams({access_token: token, session_token: sessionToken})
@@ -40,9 +40,7 @@ import {useSettingsStore, SETTINGS} from "../stores/settings"
40
40
  import {DeviceTypes} from "../types"
41
41
 
42
42
  export type IdentitySnapshot =
43
- | {kind: "none"}
44
- | {kind: "pending"; model: string}
45
- | {kind: "paired"; model: string; name: string; address?: string}
43
+ {kind: "none"} | {kind: "pending"; model: string} | {kind: "paired"; model: string; name: string; address?: string}
46
44
 
47
45
  // Identity keys hold model/name strings; anything else (legacy null, a stray
48
46
  // non-string) reads as absent, matching native currentDefaultDevice()'s
@@ -14,9 +14,7 @@ import {
14
14
 
15
15
  export {PhoneCalendarError} from "./PhoneCalendarHelpers"
16
16
 
17
- type CalendarPermissionStatus = Awaited<
18
- ReturnType<typeof Calendar.getCalendarPermissionsAsync>
19
- >["status"]
17
+ type CalendarPermissionStatus = Awaited<ReturnType<typeof Calendar.getCalendarPermissionsAsync>>["status"]
20
18
 
21
19
  /**
22
20
  * expo-calendar reports anything below EventKit full access as not granted,
@@ -1,3 +1,4 @@
1
+ import {notificationAppPayload} from "./notificationAppPayload"
1
2
  /**
2
3
  * Phone-notifications sync — engine-owned, started by `engine.start()`. Keeps
3
4
  * three things in step with settings and the connected glasses, for any host:
@@ -78,7 +79,7 @@ export function getKnownNotificationApps(): NotificationApp[] {
78
79
  export function installedNotificationApps(): Promise<
79
80
  Array<{packageName: string; appName: string; icon: string | null}>
80
81
  > {
81
- if (Platform.OS === "android") return CrustModule.getInstalledApps()
82
+ if (Platform.OS === "android") return CrustModule.getInstalledApps().then(notificationAppPayload)
82
83
  if (Platform.OS !== "ios") return Promise.resolve([])
83
84
  return Promise.resolve(
84
85
  getKnownNotificationApps().map((app) => ({packageName: app.appId, appName: app.displayName, icon: null})),
@@ -339,8 +339,7 @@ export class PhoneStreamCoordinator {
339
339
  // block subsequent start/stop transitions on this coordinator.
340
340
  assertGlassesConnected()
341
341
  type JoinDecision =
342
- | {kind: "join"; entry: ManagedEntry; immediate: ManagedStartResult | null}
343
- | {kind: "fresh"; entry: ManagedEntry}
342
+ {kind: "join"; entry: ManagedEntry; immediate: ManagedStartResult | null} | {kind: "fresh"; entry: ManagedEntry}
344
343
 
345
344
  const decision = await this.runExclusive(async (): Promise<JoinDecision> => {
346
345
  if (this.current && this.current.kind === "unmanaged") {
@@ -2,7 +2,7 @@
2
2
  * ProviderKeyValidation
3
3
  *
4
4
  * Cheap validate-before-save probes for the AI/voice provider keys on the
5
- * Keys screen (Settings > API Keys), mirroring validateMapboxApiKey's shape:
5
+ * Keys screen (Settings > Privacy > API Keys), mirroring validateMapboxApiKey's shape:
6
6
  * each probe hits the provider's cheapest authenticated read endpoint (no
7
7
  * metered usage), maps auth failures to `invalid_key`, and maps everything
8
8
  * else to `network` so the caller can offer "save anyway".
@@ -151,12 +151,7 @@ class STTModelManager {
151
151
  fileName: "sherpa-onnx-nemo-streaming-fast-conformer-transducer-en-480ms-int8",
152
152
  size: 101 * 1024 * 1024,
153
153
  type: "transducer",
154
- requiredFiles: [
155
- "encoder.int8.onnx",
156
- "decoder.int8.onnx",
157
- "joiner.int8.onnx",
158
- "tokens.txt",
159
- ],
154
+ requiredFiles: ["encoder.int8.onnx", "decoder.int8.onnx", "joiner.int8.onnx", "tokens.txt"],
160
155
  languageCode: "en-US",
161
156
  },
162
157
  // NVIDIA Nemotron streaming ASR. These are model choices rather than plain
@@ -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
  /**
@@ -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
@@ -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
+ }
@@ -82,8 +82,7 @@ export function selectSettingsBridgeKeys(requested: unknown): string[] {
82
82
  }
83
83
 
84
84
  export type SettingsSetValidation =
85
- | {ok: true; key: string; value: unknown}
86
- | {ok: false; code: "INVALID_ARGUMENT" | "NOT_PERMITTED"; message: string}
85
+ {ok: true; key: string; value: unknown} | {ok: false; code: "INVALID_ARGUMENT" | "NOT_PERMITTED"; message: string}
87
86
 
88
87
  /** A SETTINGS_SET payload: a writable key and a JSON value (undefined is not a value). */
89
88
  export function validateSettingsSet(payload: Record<string, unknown>): SettingsSetValidation {
@@ -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
@@ -333,7 +333,7 @@ export const SETTINGS: Record<string, Setting> = {
333
333
  saveOnServer: false,
334
334
  persist: true,
335
335
  },
336
- // User-supplied AI/voice provider keys (Settings > API Keys). Not consumed
336
+ // User-supplied AI/voice provider keys (Settings > Privacy > API Keys). Not consumed
337
337
  // by the app itself — they exist so miniapps declaring the API_KEYS manifest
338
338
  // permission can read them via session.keys.get() (see
339
339
  // MINIAPP_READABLE_API_KEYS in LocalMiniappRuntime; mapbox is also readable
@@ -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")
@@ -66,9 +66,7 @@ export function searchMemoryEntries<T extends MemorySearchableEntry>(
66
66
  const windowCount = Math.max(0, hi - lo)
67
67
 
68
68
  const needle = q.contains?.trim().toLowerCase() || null
69
- const termWords = (q.terms ?? [])
70
- .map((t) => tokenize(t))
71
- .filter((words) => words.length > 0)
69
+ const termWords = (q.terms ?? []).map((t) => tokenize(t)).filter((words) => words.length > 0)
72
70
 
73
71
  const picked: T[] = []
74
72
  for (let i = hi - 1; i >= lo && picked.length < q.limit; i--) {
@@ -154,11 +152,23 @@ export function editDistanceAtMost(a: string, b: string, max: number): boolean {
154
152
  }
155
153
 
156
154
  const SOUNDEX_CODES: Record<string, string> = {
157
- b: "1", f: "1", p: "1", v: "1",
158
- c: "2", g: "2", j: "2", k: "2", q: "2", s: "2", x: "2", z: "2",
159
- d: "3", t: "3",
155
+ b: "1",
156
+ f: "1",
157
+ p: "1",
158
+ v: "1",
159
+ c: "2",
160
+ g: "2",
161
+ j: "2",
162
+ k: "2",
163
+ q: "2",
164
+ s: "2",
165
+ x: "2",
166
+ z: "2",
167
+ d: "3",
168
+ t: "3",
160
169
  l: "4",
161
- m: "5", n: "5",
170
+ m: "5",
171
+ n: "5",
162
172
  r: "6",
163
173
  }
164
174