@glassly/miniapp 0.1.0-dev.98 → 0.2.8-dev.444

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 (250) hide show
  1. package/README.md +218 -73
  2. package/dist/background/index.d.ts +14 -8
  3. package/dist/background/index.d.ts.map +1 -1
  4. package/dist/background/index.js +4 -0
  5. package/dist/background/index.js.map +1 -1
  6. package/dist/background/register.d.ts +1 -1
  7. package/dist/background/register.js +1 -1
  8. package/dist/display/index.d.ts +23 -0
  9. package/dist/display/index.d.ts.map +1 -0
  10. package/dist/display/index.js +17 -0
  11. package/dist/display/index.js.map +1 -0
  12. package/dist/display/measurer/TextMeasurer.d.ts +160 -0
  13. package/dist/display/measurer/TextMeasurer.d.ts.map +1 -0
  14. package/dist/display/measurer/TextMeasurer.js +272 -0
  15. package/dist/display/measurer/TextMeasurer.js.map +1 -0
  16. package/dist/display/measurer/index.d.ts +10 -0
  17. package/dist/display/measurer/index.d.ts.map +1 -0
  18. package/dist/display/measurer/index.js +11 -0
  19. package/dist/display/measurer/index.js.map +1 -0
  20. package/dist/display/measurer/script-detection.d.ts +53 -0
  21. package/dist/display/measurer/script-detection.d.ts.map +1 -0
  22. package/dist/display/measurer/script-detection.js +184 -0
  23. package/dist/display/measurer/script-detection.js.map +1 -0
  24. package/dist/display/pixelArt.d.ts +22 -0
  25. package/dist/display/pixelArt.d.ts.map +1 -0
  26. package/dist/display/pixelArt.js +126 -0
  27. package/dist/display/pixelArt.js.map +1 -0
  28. package/dist/display/profiles/g1.d.ts +33 -0
  29. package/dist/display/profiles/g1.d.ts.map +1 -0
  30. package/dist/display/profiles/g1.js +214 -0
  31. package/dist/display/profiles/g1.js.map +1 -0
  32. package/dist/display/profiles/g2.d.ts +13 -0
  33. package/dist/display/profiles/g2.d.ts.map +1 -0
  34. package/dist/display/profiles/g2.js +34 -0
  35. package/dist/display/profiles/g2.js.map +1 -0
  36. package/dist/display/profiles/index.d.ts +12 -0
  37. package/dist/display/profiles/index.d.ts.map +1 -0
  38. package/dist/display/profiles/index.js +15 -0
  39. package/dist/display/profiles/index.js.map +1 -0
  40. package/dist/display/profiles/nex.d.ts +10 -0
  41. package/dist/display/profiles/nex.d.ts.map +1 -0
  42. package/dist/display/profiles/nex.js +150 -0
  43. package/dist/display/profiles/nex.js.map +1 -0
  44. package/dist/display/profiles/types.d.ts +103 -0
  45. package/dist/display/profiles/types.d.ts.map +1 -0
  46. package/dist/display/profiles/types.js +2 -0
  47. package/dist/display/profiles/types.js.map +1 -0
  48. package/dist/display/profiles/z100.d.ts +33 -0
  49. package/dist/display/profiles/z100.d.ts.map +1 -0
  50. package/dist/display/profiles/z100.js +182 -0
  51. package/dist/display/profiles/z100.js.map +1 -0
  52. package/dist/display/scene/types.d.ts +292 -0
  53. package/dist/display/scene/types.d.ts.map +1 -0
  54. package/dist/display/scene/types.js +0 -0
  55. package/dist/display/scene/types.js.map +1 -0
  56. package/dist/display/wrapper/TextWrapper.d.ts +102 -0
  57. package/dist/display/wrapper/TextWrapper.d.ts.map +1 -0
  58. package/dist/display/wrapper/TextWrapper.js +469 -0
  59. package/dist/display/wrapper/TextWrapper.js.map +1 -0
  60. package/dist/display/wrapper/index.d.ts +12 -0
  61. package/dist/display/wrapper/index.d.ts.map +1 -0
  62. package/dist/display/wrapper/index.js +12 -0
  63. package/dist/display/wrapper/index.js.map +1 -0
  64. package/dist/display/wrapper/types.d.ts +75 -0
  65. package/dist/display/wrapper/types.d.ts.map +1 -0
  66. package/dist/display/wrapper/types.js +11 -0
  67. package/dist/display/wrapper/types.js.map +1 -0
  68. package/dist/globals.d.ts +5 -5
  69. package/dist/globals.d.ts.map +1 -1
  70. package/dist/globals.js +1 -1
  71. package/dist/globals.js.map +1 -1
  72. package/dist/index.d.ts +16 -10
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +3 -1
  75. package/dist/index.js.map +1 -1
  76. package/dist/knownApps.d.ts +26 -0
  77. package/dist/knownApps.d.ts.map +1 -0
  78. package/dist/knownApps.js +140 -0
  79. package/dist/knownApps.js.map +1 -0
  80. package/dist/mirror/canvas.d.ts +22 -0
  81. package/dist/mirror/canvas.d.ts.map +1 -0
  82. package/dist/mirror/canvas.js +311 -0
  83. package/dist/mirror/canvas.js.map +1 -0
  84. package/dist/mirror/geometry.d.ts +69 -0
  85. package/dist/mirror/geometry.d.ts.map +1 -0
  86. package/dist/mirror/geometry.js +75 -0
  87. package/dist/mirror/geometry.js.map +1 -0
  88. package/dist/mirror/index.d.ts +3 -0
  89. package/dist/mirror/index.d.ts.map +1 -0
  90. package/dist/mirror/index.js +2 -0
  91. package/dist/mirror/index.js.map +1 -0
  92. package/dist/mirror/types.d.ts +40 -0
  93. package/dist/mirror/types.d.ts.map +1 -0
  94. package/dist/mirror/types.js +2 -0
  95. package/dist/mirror/types.js.map +1 -0
  96. package/dist/modules/auth.d.ts +1 -1
  97. package/dist/modules/auth.d.ts.map +1 -1
  98. package/dist/modules/auth.js +2 -2
  99. package/dist/modules/auth.js.map +1 -1
  100. package/dist/modules/compass.d.ts +47 -0
  101. package/dist/modules/compass.d.ts.map +1 -0
  102. package/dist/modules/compass.js +39 -0
  103. package/dist/modules/compass.js.map +1 -0
  104. package/dist/modules/display.d.ts +189 -12
  105. package/dist/modules/display.d.ts.map +1 -1
  106. package/dist/modules/display.js +40 -3
  107. package/dist/modules/display.js.map +1 -1
  108. package/dist/modules/events.d.ts +30 -2
  109. package/dist/modules/events.d.ts.map +1 -1
  110. package/dist/modules/events.js.map +1 -1
  111. package/dist/modules/input.d.ts +34 -11
  112. package/dist/modules/input.d.ts.map +1 -1
  113. package/dist/modules/input.js +35 -11
  114. package/dist/modules/input.js.map +1 -1
  115. package/dist/modules/latest-render-scheduler.d.ts +19 -0
  116. package/dist/modules/latest-render-scheduler.d.ts.map +1 -0
  117. package/dist/modules/latest-render-scheduler.js +54 -0
  118. package/dist/modules/latest-render-scheduler.js.map +1 -0
  119. package/dist/modules/lightSensor.d.ts +44 -0
  120. package/dist/modules/lightSensor.d.ts.map +1 -0
  121. package/dist/modules/lightSensor.js +33 -0
  122. package/dist/modules/lightSensor.js.map +1 -0
  123. package/dist/modules/mic.d.ts +3 -2
  124. package/dist/modules/mic.d.ts.map +1 -1
  125. package/dist/modules/mic.js +3 -2
  126. package/dist/modules/mic.js.map +1 -1
  127. package/dist/modules/miniapps.d.ts +2 -0
  128. package/dist/modules/miniapps.d.ts.map +1 -1
  129. package/dist/modules/miniapps.js.map +1 -1
  130. package/dist/modules/mirror.d.ts +16 -0
  131. package/dist/modules/mirror.d.ts.map +1 -0
  132. package/dist/modules/mirror.js +40 -0
  133. package/dist/modules/mirror.js.map +1 -0
  134. package/dist/modules/mono-text-rows.d.ts +20 -0
  135. package/dist/modules/mono-text-rows.d.ts.map +1 -0
  136. package/dist/modules/mono-text-rows.js +26 -0
  137. package/dist/modules/mono-text-rows.js.map +1 -0
  138. package/dist/modules/navigation.d.ts.map +1 -1
  139. package/dist/modules/navigation.js +21 -14
  140. package/dist/modules/navigation.js.map +1 -1
  141. package/dist/modules/phone.d.ts +26 -1
  142. package/dist/modules/phone.d.ts.map +1 -1
  143. package/dist/modules/phone.js +28 -0
  144. package/dist/modules/phone.js.map +1 -1
  145. package/dist/modules/settings.d.ts +45 -0
  146. package/dist/modules/settings.d.ts.map +1 -0
  147. package/dist/modules/settings.js +54 -0
  148. package/dist/modules/settings.js.map +1 -0
  149. package/dist/modules/system.d.ts +21 -0
  150. package/dist/modules/system.d.ts.map +1 -1
  151. package/dist/modules/system.js +12 -0
  152. package/dist/modules/system.js.map +1 -1
  153. package/dist/modules/transcription.d.ts +3 -2
  154. package/dist/modules/transcription.d.ts.map +1 -1
  155. package/dist/modules/transcription.js +3 -2
  156. package/dist/modules/transcription.js.map +1 -1
  157. package/dist/protocol.d.ts +59 -5
  158. package/dist/protocol.d.ts.map +1 -1
  159. package/dist/protocol.js +60 -5
  160. package/dist/protocol.js.map +1 -1
  161. package/dist/react/MirrorCanvas.d.ts +10 -0
  162. package/dist/react/MirrorCanvas.d.ts.map +1 -0
  163. package/dist/react/MirrorCanvas.js +23 -0
  164. package/dist/react/MirrorCanvas.js.map +1 -0
  165. package/dist/react/index.d.ts +2 -0
  166. package/dist/react/index.d.ts.map +1 -1
  167. package/dist/react/index.js +1 -0
  168. package/dist/react/index.js.map +1 -1
  169. package/dist/react/useColorScheme.d.ts +2 -3
  170. package/dist/react/useColorScheme.d.ts.map +1 -1
  171. package/dist/react/useColorScheme.js +15 -8
  172. package/dist/react/useColorScheme.js.map +1 -1
  173. package/dist/react/useRpc.js +2 -2
  174. package/dist/react/useRpc.js.map +1 -1
  175. package/dist/react/useSafeArea.d.ts.map +1 -1
  176. package/dist/react/useSafeArea.js +2 -2
  177. package/dist/react/useSafeArea.js.map +1 -1
  178. package/dist/session.d.ts +50 -4
  179. package/dist/session.d.ts.map +1 -1
  180. package/dist/session.js +35 -6
  181. package/dist/session.js.map +1 -1
  182. package/dist/transport/auto.d.ts +2 -2
  183. package/dist/transport/auto.js +2 -2
  184. package/dist/transport/dispatch.d.ts +1 -1
  185. package/dist/transport/dispatch.js +1 -1
  186. package/dist/transport/mock.d.ts +4 -4
  187. package/dist/transport/mock.d.ts.map +1 -1
  188. package/dist/transport/mock.js +31 -7
  189. package/dist/transport/mock.js.map +1 -1
  190. package/dist/ui/index.d.ts +2 -0
  191. package/dist/ui/index.d.ts.map +1 -1
  192. package/dist/ui/index.js +1 -0
  193. package/dist/ui/index.js.map +1 -1
  194. package/package.json +15 -3
  195. package/src/background/index.ts +26 -4
  196. package/src/background/register.ts +1 -1
  197. package/src/display/index.ts +83 -0
  198. package/src/display/measurer/TextMeasurer.ts +346 -0
  199. package/src/display/measurer/index.ts +21 -0
  200. package/src/display/measurer/script-detection.ts +218 -0
  201. package/src/display/pixelArt.ts +141 -0
  202. package/src/display/profiles/g1.ts +237 -0
  203. package/src/display/profiles/g2.ts +39 -0
  204. package/src/display/profiles/index.ts +28 -0
  205. package/src/display/profiles/nex.ts +166 -0
  206. package/src/display/profiles/types.ts +132 -0
  207. package/src/display/profiles/z100.ts +198 -0
  208. package/src/display/scene/types.ts +335 -0
  209. package/src/display/wrapper/TextWrapper.ts +546 -0
  210. package/src/display/wrapper/index.ts +15 -0
  211. package/src/display/wrapper/types.ts +102 -0
  212. package/src/globals.ts +5 -5
  213. package/src/index.ts +34 -6
  214. package/src/knownApps.ts +170 -0
  215. package/src/mirror/canvas.ts +322 -0
  216. package/src/mirror/geometry.ts +128 -0
  217. package/src/mirror/index.ts +2 -0
  218. package/src/mirror/types.ts +38 -0
  219. package/src/modules/auth.ts +2 -2
  220. package/src/modules/compass.ts +69 -0
  221. package/src/modules/display.ts +219 -16
  222. package/src/modules/events.ts +27 -1
  223. package/src/modules/input.ts +44 -12
  224. package/src/modules/latest-render-scheduler.ts +55 -0
  225. package/src/modules/lightSensor.ts +61 -0
  226. package/src/modules/mic.ts +3 -2
  227. package/src/modules/miniapps.ts +2 -0
  228. package/src/modules/mirror.ts +42 -0
  229. package/src/modules/mono-text-rows.ts +30 -0
  230. package/src/modules/navigation.ts +20 -14
  231. package/src/modules/phone.ts +40 -1
  232. package/src/modules/settings.ts +64 -0
  233. package/src/modules/system.ts +30 -1
  234. package/src/modules/transcription.ts +3 -2
  235. package/src/protocol.ts +65 -5
  236. package/src/react/MirrorCanvas.tsx +35 -0
  237. package/src/react/index.ts +3 -0
  238. package/src/react/useColorScheme.ts +17 -8
  239. package/src/react/useRpc.ts +2 -2
  240. package/src/react/useSafeArea.ts +2 -2
  241. package/src/session.ts +78 -8
  242. package/src/transport/auto.ts +2 -2
  243. package/src/transport/dispatch.ts +1 -1
  244. package/src/transport/mock.ts +42 -11
  245. package/src/ui/index.ts +3 -0
  246. package/dist/modules/heading.d.ts +0 -16
  247. package/dist/modules/heading.d.ts.map +0 -1
  248. package/dist/modules/heading.js +0 -20
  249. package/dist/modules/heading.js.map +0 -1
  250. package/src/modules/heading.ts +0 -23
package/src/protocol.ts CHANGED
@@ -25,9 +25,9 @@ export enum MiniappRequestType {
25
25
  SUBSCRIBE = "miniapp_subscribe",
26
26
 
27
27
  /**
28
- * Declare the touch gestures this miniapp is handling itself, suppressing
29
- * the host's default behavior for them (e.g. double_tap opens the
30
- * dashboard) while the app is foreground on the glasses.
28
+ * Declare the touch gestures this miniapp is handling itself. The most
29
+ * recent claimer of a gesture gets it alone and the host's default for it
30
+ * is skipped; releasing hands it back to the previous claimer.
31
31
  */
32
32
  CLAIM_GESTURES = "miniapp_claim_gestures",
33
33
 
@@ -41,6 +41,17 @@ export enum MiniappRequestType {
41
41
  /** Render a whole scene of positioned elements (replace-the-frame). */
42
42
  RENDER = "miniapp_render",
43
43
 
44
+ /** Read the globally visible intended glasses canvas. DISPLAY_MIRROR permission. */
45
+ MIRROR_GET = "miniapp_mirror_get",
46
+
47
+ /**
48
+ * Ask the host for a display view (display.showView). "main" is every
49
+ * app's; "dashboard" is the overlay slot above the main content, granted
50
+ * only to the running miniapp whose manifest type is "system_dashboard"
51
+ * while the glasses can show it.
52
+ */
53
+ SHOW_VIEW = "miniapp_show_view",
54
+
44
55
  /**
45
56
  * Feed the device's firmware-rendered navigation UI (capability
46
57
  * `hasNativeNavigation`): structured turn-by-turn card fields and
@@ -77,6 +88,21 @@ export enum MiniappRequestType {
77
88
  /** Read a bounded snapshot of phone calendar events. */
78
89
  CALENDAR_LIST_EVENTS = "miniapp_calendar_list_events",
79
90
 
91
+ /**
92
+ * Apps that post phone notifications, for a notification filter UI.
93
+ * SYSTEM-only. Android: installed apps with icons; iOS: the seeded
94
+ * catalog plus apps the glasses reported.
95
+ */
96
+ NOTIFICATION_APPS_LIST = "miniapp_notification_apps_list",
97
+
98
+ /**
99
+ * The engine's settings store behind an allowlist (session.settings).
100
+ * SYSTEM-only. GET takes optional `keys` and answers `{values}`; SET
101
+ * takes `key` and `value` and writes through the store.
102
+ */
103
+ SETTINGS_GET = "miniapp_settings_get",
104
+ SETTINGS_SET = "miniapp_settings_set",
105
+
80
106
  /** Start a turn-by-turn navigation trip. Android only. */
81
107
  NAVIGATION_START = "miniapp_navigation_start",
82
108
  /** Stop the active navigation trip (if any). */
@@ -182,6 +208,7 @@ export enum MiniappRequestType {
182
208
  * for callers who want to control the sensor directly. G2 only today.
183
209
  */
184
210
  IMU_SET_ENABLED = "miniapp_imu_set_enabled",
211
+ GLASSES_SENSOR_CONTROL = "miniapp_glasses_sensor_control",
185
212
 
186
213
  /** Explicitly enable/disable glasses-side VAD (GX8002). Mirrors the app's mic settings toggle. */
187
214
  MIC_SET_VAD_ENABLED = "miniapp_mic_set_vad_enabled",
@@ -199,6 +226,7 @@ export enum MiniappRequestType {
199
226
  /** Open a URL in the system browser. */
200
227
  OPEN_URL = "miniapp_open_url",
201
228
  /** Copy text to the system clipboard. */
229
+ DISPLAY_DIAGNOSTICS = "miniapp_display_diagnostics",
202
230
  COPY_CLIPBOARD = "miniapp_copy_clipboard",
203
231
  /** Download a file (triggers OS share sheet for save location). */
204
232
  DOWNLOAD = "miniapp_download",
@@ -302,6 +330,9 @@ export enum MiniappResponseType {
302
330
  /** Push: glasses capabilities changed. */
303
331
  CAPABILITIES_UPDATE = "miniapp_capabilities_update",
304
332
 
333
+ /** Push: host user preferences (units, clock format) changed. */
334
+ PREFERENCES_UPDATE = "miniapp_preferences_update",
335
+
305
336
  /** Push: miniapp's foreground/background state changed. */
306
337
  VISIBILITY_CHANGE = "miniapp_visibility_change",
307
338
 
@@ -359,7 +390,25 @@ export enum MiniappStreamType {
359
390
  // Hardware / input events
360
391
  BUTTON_PRESS = "button_press",
361
392
  TOUCH_EVENT = "touch_event",
393
+ /**
394
+ * Physical keyboard paired to the glasses (input.onKey): DOM-style
395
+ * `{phase, key, code, modifiers, text?, repeat, timestamp}`. G2 Glassly CFW
396
+ * revision 39+ only; broadcast to every subscriber like button_press.
397
+ */
398
+ KEYBOARD_EVENT = "keyboard_event",
362
399
  HEAD_POSITION = "head_position",
400
+ /**
401
+ * The dashboard view's state (display.onViewChange): `{kind: "change",
402
+ * view, reason}` when the host grants or takes the overlay slot.
403
+ */
404
+ DISPLAY_VIEW = "display_view",
405
+ /** Initial mirror snapshot and subsequent visible display changes. */
406
+ MIRROR_UPDATE = "mirror_update",
407
+ /**
408
+ * A gesture claim changed hands (input.onClaimChange): `{gesture, held}`,
409
+ * sent to the app that gained or lost the gesture.
410
+ */
411
+ GESTURE_CLAIM = "gesture_claim",
363
412
  /**
364
413
  * Raw accelerometer reading from the glasses IMU — `{x, y, z}` in g, plus a
365
414
  * timestamp. G2 only today. A richer combined IMU stream (accel + gyro +
@@ -382,19 +431,30 @@ export enum MiniappStreamType {
382
431
 
383
432
  // Phone sensors
384
433
  LOCATION_UPDATE = "location_update",
385
- /** Compass heading in degrees (0=N, 90=E). */
434
+ /** Phone compass heading in degrees (0=N, 90=E). */
386
435
  HEADING_UPDATE = "heading_update",
436
+ // Glasses sensors
437
+ LIGHT_SENSOR = "light_sensor",
438
+ /** Glasses compass heading; auto-starts on custom firmware, nav mode only on stock. */
439
+ GLASSES_COMPASS = "glasses_compass",
440
+ COMPASS_CALIBRATION = "compass_calibration",
387
441
  /** Turn-by-turn navigation event (maneuver / rerouting / arrived / error). */
388
442
  NAVIGATION_UPDATE = "navigation_update",
389
443
  /** Active navigation route polyline — full path, fired once per route build. */
390
444
  NAVIGATION_ROUTE = "navigation_route",
391
445
  PHONE_NOTIFICATION = "phone_notification",
446
+ /**
447
+ * An allowlisted engine setting changed (session.settings.onChange), as
448
+ * `{key, value}`. SYSTEM-only; every key's current value is replayed on
449
+ * subscribe.
450
+ */
451
+ SETTINGS_CHANGED = "settings_changed",
392
452
  /**
393
453
  * A previously-posted notification was dismissed by the user.
394
454
  *
395
455
  * Android-only today. iOS does not expose dismiss callbacks to apps
396
456
  * (Apple privacy restriction); subscribing on iOS succeeds but no events
397
- * fire. See agents/miniapp-speaker-state-and-notif-dismissed-plan.md.
457
+ * fire. See notes/archive/agents/miniapp-speaker-state-and-notif-dismissed-plan.md.
398
458
  */
399
459
  PHONE_NOTIFICATION_DISMISSED = "phone_notification_dismissed",
400
460
  // Photos, streaming
@@ -0,0 +1,35 @@
1
+ import {useEffect, useRef, type CSSProperties} from "react"
2
+ import {MirrorCanvasRenderer} from "../mirror/canvas"
3
+ import type {MirrorSnapshot} from "../mirror/types"
4
+
5
+ export interface MirrorCanvasProps {
6
+ frame: MirrorSnapshot | null
7
+ className?: string
8
+ style?: CSSProperties
9
+ }
10
+
11
+ /** Browser preview of intended glasses output; pass snapshots over a UI channel. */
12
+ export function MirrorCanvas({frame, className, style}: MirrorCanvasProps) {
13
+ const canvas = useRef<HTMLCanvasElement>(null)
14
+ const renderer = useRef<MirrorCanvasRenderer | null>(null)
15
+ useEffect(() => {
16
+ if (!canvas.current) return
17
+ renderer.current = new MirrorCanvasRenderer(canvas.current)
18
+ return () => {
19
+ renderer.current?.dispose()
20
+ renderer.current = null
21
+ }
22
+ }, [])
23
+ useEffect(() => {
24
+ if (frame) renderer.current?.update(frame)
25
+ }, [frame])
26
+ return (
27
+ <canvas
28
+ ref={canvas}
29
+ className={className}
30
+ role="img"
31
+ aria-label="Glasses display mirror"
32
+ style={{display: "block", width: "100%", height: "auto", ...style}}
33
+ />
34
+ )
35
+ }
@@ -10,3 +10,6 @@ export type {MiniappHeaderProps} from "./MiniappHeader"
10
10
  export {MiniappProvider} from "./MiniappProvider"
11
11
  export type {MiniappProviderProps} from "./MiniappProvider"
12
12
  export {useRpc, type RpcCallable} from "./useRpc"
13
+
14
+ export {MirrorCanvas, type MirrorCanvasProps} from "./MirrorCanvas"
15
+ export type {MirrorSnapshot, MirrorContent, MirrorElement} from "../mirror/types"
@@ -3,9 +3,8 @@
3
3
  * color scheme.
4
4
  *
5
5
  * WebView-side hook: reads `window.Glassly.colorScheme`, injected by the host
6
- * before the miniapp's content loads. Like `useSafeArea`, this is read once at
7
- * mount — the host doesn't push color-scheme changes to the WebView at runtime;
8
- * flipping the phone's theme forces a reload.
6
+ * before the miniapp's content loads, and follows the host's
7
+ * `glassly:color-scheme-change` events while the WebView remains mounted.
9
8
  *
10
9
  * IMPORTANT: this hook must NOT construct a `MiniappSession`. `MiniappSession`
11
10
  * is the *background* (JSContext) API; inside a UI WebView its transport falls
@@ -14,13 +13,23 @@
14
13
  * read host-injected globals instead (see `useSafeArea`).
15
14
  */
16
15
 
17
- import {useState} from "react"
16
+ import {useEffect, useState} from "react"
18
17
 
19
- import {getGlasslyGlobals, type MiniappColorScheme} from "../globals"
18
+ import {getHostGlobals, type MiniappColorScheme} from "../globals"
19
+
20
+ const readScheme = (): MiniappColorScheme => (getHostGlobals().colorScheme === "dark" ? "dark" : "light")
20
21
 
21
22
  export function useColorScheme(): MiniappColorScheme {
22
- const [scheme] = useState<MiniappColorScheme>(() => {
23
- return getGlasslyGlobals().colorScheme === "dark" ? "dark" : "light"
24
- })
23
+ const [scheme, setScheme] = useState<MiniappColorScheme>(readScheme)
24
+
25
+ useEffect(() => {
26
+ if (typeof window === "undefined") return
27
+ const update = () => setScheme(readScheme())
28
+ window.addEventListener("glassly:color-scheme-change", update)
29
+ // Catch an update delivered between the initial render and subscription.
30
+ update()
31
+ return () => window.removeEventListener("glassly:color-scheme-change", update)
32
+ }, [])
33
+
25
34
  return scheme
26
35
  }
@@ -19,7 +19,7 @@ import type {RpcReq, RpcRes, RpcRequestOptions} from "../modules/ui"
19
19
  type RequestFn = (channel: string, payload: unknown, options?: RpcRequestOptions) => Promise<unknown>
20
20
 
21
21
  /** Walk to the global `glassly.request` (typed). */
22
- function getGlasslyRequest(): RequestFn {
22
+ function getHostRequest(): RequestFn {
23
23
  const m = (globalThis as unknown as {glassly?: {request?: RequestFn}}).glassly
24
24
  if (!m || typeof m.request !== "function") {
25
25
  throw new Error(
@@ -89,7 +89,7 @@ export function useRpc<TChannels extends object, C extends keyof TChannels & str
89
89
  const signals: AbortSignal[] = [ctrl.signal, mountRef.current!.signal]
90
90
  if (options?.signal) signals.push(options.signal)
91
91
  const signal = mergeSignals(signals)
92
- return getGlasslyRequest()(channel, payload, {signal, timeout: options?.timeout}) as Promise<RpcRes<TChannels[C]>>
92
+ return getHostRequest()(channel, payload, {signal, timeout: options?.timeout}) as Promise<RpcRes<TChannels[C]>>
93
93
  },
94
94
  [channel],
95
95
  ) as RpcCallable<TChannels, C>
@@ -9,7 +9,7 @@
9
9
 
10
10
  import {useState} from "react"
11
11
 
12
- import {getGlasslyGlobals, type MiniappCapsuleMenuRect, type MiniappSafeAreaInsets} from "../globals"
12
+ import {getHostGlobals, type MiniappCapsuleMenuRect, type MiniappSafeAreaInsets} from "../globals"
13
13
 
14
14
  const EMPTY_INSETS: MiniappSafeAreaInsets = {top: 0, bottom: 0, left: 0, right: 0}
15
15
 
@@ -26,7 +26,7 @@ export interface UseSafeAreaResult {
26
26
 
27
27
  export function useSafeArea(): UseSafeAreaResult {
28
28
  const [result] = useState<UseSafeAreaResult>(() => {
29
- const globals = getGlasslyGlobals()
29
+ const globals = getHostGlobals()
30
30
  return {
31
31
  insets: globals.safeAreaInsets ?? EMPTY_INSETS,
32
32
  capsuleMenu: globals.capsuleMenu ?? null,
package/src/session.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  import {EventEmitter} from "eventemitter3"
16
16
 
17
17
  import {makeRequestId, MiniappEnvelope, parseEnvelope, serializeEnvelope} from "./envelope"
18
- import {getGlasslyGlobals, MiniappColorScheme} from "./globals"
18
+ import {getHostGlobals, MiniappColorScheme} from "./globals"
19
19
  import {MiniappErrorCode, MiniappRequestType, MiniappResponseType} from "./protocol"
20
20
  import {createTransport, CreateTransportOptions} from "./transport/auto"
21
21
  import {Transport} from "./transport/types"
@@ -26,15 +26,17 @@ import {DashboardAPI} from "./modules/dashboard"
26
26
  import {DisplayManager} from "./modules/display"
27
27
  import {EventManager, type TranscriptionEventRoute, type UnsubscribeFn} from "./modules/events"
28
28
  import {GlassesModule} from "./modules/glasses"
29
- import {HeadingModule} from "./modules/heading"
30
29
  import {AiModule} from "./modules/ai"
31
30
  import {ImuModule} from "./modules/imu"
31
+ import {CompassModule} from "./modules/compass"
32
+ import {LightSensorModule} from "./modules/lightSensor"
32
33
  import {InputModule} from "./modules/input"
33
34
  import {KeysModule} from "./modules/keys"
34
35
  import {LedModule} from "./modules/led"
35
36
  import {LocationModule} from "./modules/location"
36
37
  import {MemoryModule} from "./modules/memory"
37
38
  import {SpeakersModule} from "./modules/speakers"
39
+ import {MirrorModule} from "./modules/mirror"
38
40
  import {MicModule} from "./modules/mic"
39
41
  import {NavigationModule} from "./modules/navigation"
40
42
  import {PermissionsModule} from "./modules/permissions"
@@ -42,6 +44,7 @@ import {PhoneModule} from "./modules/phone"
42
44
  import {TranscriptionModule} from "./modules/transcription"
43
45
  import {TranslationModule} from "./modules/translation"
44
46
  import {UIModuleImpl, type UIModule} from "./modules/ui"
47
+ import {SettingsModule} from "./modules/settings"
45
48
  import {SimpleStorage} from "./modules/storage"
46
49
  import {SpeakerModule} from "./modules/speaker"
47
50
  import {StreamModule} from "./modules/stream"
@@ -70,9 +73,23 @@ export interface DisplayCapabilities {
70
73
  maxImageElements?: number
71
74
  /** Per-image dimension cap (box-level), when the device has one. */
72
75
  maxImagePx?: {width: number; height: number}
76
+ /** Shape element types the device draws ("rect", "circle", "line", …). */
73
77
  shapes?: string[]
78
+ /** True ⇒ element `transition`s animate on the device. */
79
+ animation?: boolean
80
+ /** Geometric and SVG rotations, including duration-based transitions. */
81
+ rotation?: boolean
82
+ /** Per-element stereo `depth` (a lens disparity) is honored; otherwise elements draw flat. */
83
+ depth?: boolean
74
84
  intensityLevels?: number
85
+
75
86
  partialUpdate?: boolean
87
+ /**
88
+ * The views this app may draw right now. "main" always; "dashboard" only
89
+ * for the running app whose manifest type is "system_dashboard", while the
90
+ * connected glasses can show the overlay slot (see display.showView).
91
+ */
92
+ views?: Array<"main" | "dashboard">
76
93
  /** Legacy capability fields (resolution, isColor, maxTextLines, …) ride along. */
77
94
  [key: string]: unknown
78
95
  }
@@ -87,6 +104,8 @@ export interface GlassesCapabilities {
87
104
  }
88
105
 
89
106
  export type MiniappVisibility = "foreground" | "background"
107
+ /** Where the user started this session from: the phone app or the glasses menu. */
108
+ export type MiniappLaunchSource = "phone" | "glasses"
90
109
 
91
110
  export interface MiniappSessionOptions extends CreateTransportOptions {
92
111
  /** Override auto-detected packageName. Normally provided via window.Glassly. */
@@ -95,13 +114,30 @@ export interface MiniappSessionOptions extends CreateTransportOptions {
95
114
  connectTimeoutMs?: number
96
115
  }
97
116
 
117
+ /** Distance/temperature unit family. Mirrors the host's "Use Metric System" toggle. */
118
+ export type MiniappUnitSystem = "metric" | "imperial"
119
+
120
+ /**
121
+ * Host user preferences a miniapp should follow by default (Settings >
122
+ * Appearance). Sent in CONNECT_ACK and pushed on change. Miniapps may still
123
+ * offer their own override on top.
124
+ */
125
+ export interface MiniappPreferences {
126
+ unitSystem: MiniappUnitSystem
127
+ /** True for 12-hour (AM/PM) clocks, false for 24-hour. */
128
+ twelveHourTime: boolean
129
+ }
130
+
98
131
  export interface ConnectAckPayload {
99
132
  type: MiniappResponseType.CONNECT_ACK
100
133
  userId: string
101
134
  packageName: string
102
135
  capabilities: GlassesCapabilities | null
136
+ /** Absent on runtimes older than the preferences surface. */
137
+ preferences?: MiniappPreferences
103
138
  visibility?: MiniappVisibility
104
139
  colorScheme?: MiniappColorScheme
140
+ launchSource?: MiniappLaunchSource
105
141
  /**
106
142
  * Manifest-declared permission record. Mirrors cloud SDK v3's PermissionRecord:
107
143
  * `{location, microphone, camera, notifications, calendar, apiKeys}` —
@@ -114,7 +150,7 @@ export interface ConnectAckPayload {
114
150
  }
115
151
 
116
152
  export interface MiniappAuthState {
117
- glasslyUserId: string
153
+ userId: string
118
154
  oemId?: string
119
155
  token: string
120
156
  expiresAt: number
@@ -143,6 +179,7 @@ export type PermissionType =
143
179
  | "memory"
144
180
  | "ai"
145
181
  | "speakers"
182
+ | "displayMirror"
146
183
  export type PermissionRecord = Record<PermissionType, boolean>
147
184
 
148
185
  const ALL_PERMISSION_TYPES: readonly PermissionType[] = [
@@ -155,6 +192,7 @@ const ALL_PERMISSION_TYPES: readonly PermissionType[] = [
155
192
  "memory",
156
193
  "ai",
157
194
  "speakers",
195
+ "displayMirror",
158
196
  ] as const
159
197
 
160
198
  export class NotConnectedError extends Error {
@@ -224,6 +262,7 @@ type SessionEmitterEvents = {
224
262
  disconnect: (reason: string) => void
225
263
  visibility: (v: MiniappVisibility) => void
226
264
  capabilities: (cap: GlassesCapabilities | null) => void
265
+ preferences: (prefs: MiniappPreferences) => void
227
266
  colorScheme: (scheme: MiniappColorScheme) => void
228
267
  permissions: (perms: PermissionRecord) => void
229
268
  speakerState: (event: import("./modules/speaker").SpeakerStateEvent) => void
@@ -236,6 +275,7 @@ type SessionEmitterEvents = {
236
275
 
237
276
  export class MiniappSession<TChannels extends object = any> {
238
277
  public readonly auth: AuthModule
278
+ public readonly mirror: MirrorModule
239
279
  public readonly display: DisplayManager
240
280
  /**
241
281
  * Internal subscription registry + escape hatch.
@@ -251,8 +291,9 @@ export class MiniappSession<TChannels extends object = any> {
251
291
  public readonly cloud: CloudModule
252
292
  public readonly dashboard: DashboardAPI
253
293
  public readonly glasses: GlassesModule
254
- public readonly heading: HeadingModule
255
294
  public readonly imu: ImuModule
295
+ public readonly lightSensor: LightSensorModule
296
+ public readonly compass: CompassModule
256
297
  public readonly input: InputModule
257
298
  /**
258
299
  * User-supplied provider API keys (Settings > API Keys). Requires the
@@ -286,6 +327,11 @@ export class MiniappSession<TChannels extends object = any> {
286
327
  public readonly navigation: NavigationModule
287
328
  public readonly permissions: PermissionsModule
288
329
  public readonly phone: PhoneModule
330
+ /**
331
+ * The engine's settings store behind an allowlist. SYSTEM-only — calls
332
+ * reject with NOT_PERMITTED unless this miniapp is a system app.
333
+ */
334
+ public readonly settings: SettingsModule
289
335
  public readonly storage: SimpleStorage
290
336
  /**
291
337
  * Phone-local persistent BINARY storage (`session.blob`) — the binary
@@ -318,9 +364,13 @@ export class MiniappSession<TChannels extends object = any> {
318
364
 
319
365
  /** Phone-declared glasses capabilities. Null until CONNECT_ACK arrives. */
320
366
  public capabilities: GlassesCapabilities | null = null
367
+ /** Host user preferences. Null until CONNECT_ACK arrives (or on runtimes that don't send them). */
368
+ public preferences: MiniappPreferences | null = null
321
369
  public userId = ""
322
370
  public packageName = ""
323
371
  public visibility: MiniappVisibility = "foreground"
372
+ /** Where the user started this session from. Null until CONNECT_ACK arrives. */
373
+ public launchSource: MiniappLaunchSource | null = null
324
374
  /** Host color scheme. Seeded from window.Glassly, updated via session events. */
325
375
  public colorScheme: MiniappColorScheme = "light"
326
376
 
@@ -359,13 +409,14 @@ export class MiniappSession<TChannels extends object = any> {
359
409
  memory: false,
360
410
  ai: false,
361
411
  speakers: false,
412
+ displayMirror: false,
362
413
  }
363
414
 
364
415
  constructor(options: MiniappSessionOptions = {}) {
365
416
  this.transport = createTransport(options)
366
417
  this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS
367
418
 
368
- const injected = getGlasslyGlobals()
419
+ const injected = getHostGlobals()
369
420
  this.packageName = options.packageName ?? injected.packageName ?? ""
370
421
  if (injected.colorScheme === "light" || injected.colorScheme === "dark") {
371
422
  this.colorScheme = injected.colorScheme
@@ -377,10 +428,12 @@ export class MiniappSession<TChannels extends object = any> {
377
428
  this.camera = new CameraModule(this)
378
429
  this.cloud = new CloudModule(this)
379
430
  this.dashboard = new DashboardAPI(this)
431
+ this.mirror = new MirrorModule(this)
380
432
  this.display = new DisplayManager(this)
381
433
  this.glasses = new GlassesModule(this)
382
- this.heading = new HeadingModule(this)
383
434
  this.imu = new ImuModule(this)
435
+ this.lightSensor = new LightSensorModule(this)
436
+ this.compass = new CompassModule(this)
384
437
  this.input = new InputModule(this)
385
438
  this.ai = new AiModule(this)
386
439
  this.keys = new KeysModule(this)
@@ -392,6 +445,7 @@ export class MiniappSession<TChannels extends object = any> {
392
445
  this.navigation = new NavigationModule(this)
393
446
  this.permissions = new PermissionsModule(this)
394
447
  this.phone = new PhoneModule(this)
448
+ this.settings = new SettingsModule(this)
395
449
  this.storage = new SimpleStorage(this)
396
450
  this.blob = new BlobModule(this)
397
451
  this.stream = new StreamModule(this)
@@ -646,6 +700,10 @@ export class MiniappSession<TChannels extends object = any> {
646
700
  return this.on("capabilities", handler)
647
701
  }
648
702
 
703
+ onPreferencesChange(handler: (prefs: MiniappPreferences) => void): () => void {
704
+ return this.on("preferences", handler)
705
+ }
706
+
649
707
  onColorSchemeChange(handler: (scheme: MiniappColorScheme) => void): () => void {
650
708
  return this.on("colorScheme", handler)
651
709
  }
@@ -691,13 +749,15 @@ export class MiniappSession<TChannels extends object = any> {
691
749
  this.userId = ack.userId ?? ""
692
750
  if (ack.packageName) this.packageName = ack.packageName
693
751
  this.capabilities = ack.capabilities ?? null
752
+ if (ack.preferences) this.preferences = ack.preferences
694
753
  if (ack.visibility) this.visibility = ack.visibility
754
+ if (ack.launchSource) this.launchSource = ack.launchSource
695
755
  if (ack.colorScheme === "light" || ack.colorScheme === "dark") {
696
756
  this.colorScheme = ack.colorScheme
697
757
  }
698
758
  if (ack.auth) {
699
759
  this.applyAuth(ack.auth)
700
- if (!this.userId) this.userId = ack.auth.glasslyUserId
760
+ if (!this.userId) this.userId = ack.auth.userId
701
761
  }
702
762
  // Populate the manifest-declared permission cache. Older runtimes
703
763
  // that don't send `permissions` leave the all-false default in place
@@ -714,7 +774,7 @@ export class MiniappSession<TChannels extends object = any> {
714
774
  const next = (payload as unknown as AuthUpdatePayload).auth
715
775
  if (next) {
716
776
  this.applyAuth(next)
717
- if (!this.userId) this.userId = next.glasslyUserId
777
+ if (!this.userId) this.userId = next.userId
718
778
  }
719
779
  return
720
780
  }
@@ -785,6 +845,14 @@ export class MiniappSession<TChannels extends object = any> {
785
845
  return
786
846
  }
787
847
 
848
+ case MiniappResponseType.PREFERENCES_UPDATE: {
849
+ const prefs = payload.preferences as MiniappPreferences | undefined
850
+ if (!prefs) return
851
+ this.preferences = prefs
852
+ this.emitter.emit("preferences", prefs)
853
+ return
854
+ }
855
+
788
856
  case MiniappResponseType.VISIBILITY_CHANGE: {
789
857
  const next = payload.visibility as MiniappVisibility | undefined
790
858
  if (next === "foreground" || next === "background") {
@@ -956,6 +1024,8 @@ function manifestKeyToCanonical(manifestKey: string): PermissionType | null {
956
1024
  return "memory"
957
1025
  case "AI":
958
1026
  return "ai"
1027
+ case "DISPLAY_MIRROR":
1028
+ return "displayMirror"
959
1029
  case "SPEAKERS":
960
1030
  return "speakers"
961
1031
  default:
@@ -24,12 +24,12 @@ export interface CreateTransportOptions {
24
24
  *
25
25
  * 1. **DispatchTransport** — when running in a per-miniapp JSContext
26
26
  * (no DOM, but `__dispatch` is installed on `globalThis` by the
27
- * GlasslyJS runtime). This is the production path.
27
+ * JSRuntime runtime). This is the production path.
28
28
  * 2. **PostMessageTransport** — inside a Glassly WebView
29
29
  * (`window.ReactNativeWebView` defined). This is the existing UI
30
30
  * layer path and the only transport miniapp UI bundles need.
31
31
  * 3. **MockTransport** — explicit opt-in via `?glassly=mock` or
32
- * `localStorage.GLASSLY_MOCK=1`. Useful for Storybook / unit tests.
32
+ * `localStorage.MINIAPP_MOCK=1`. Useful for Storybook / unit tests.
33
33
  * 4. **LocalSocketWithMockFallback** — external browser, races a
34
34
  * `ws://127.0.0.1` LocalSocketTransport open against a 500ms timeout
35
35
  * and falls back to MockTransport so the page doesn't hang.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview DispatchTransport — Transport implementation that rides
3
3
  * the native `__dispatch(iface, method, argsJson)` bridge exposed by the
4
- * GlasslyJS runtime (per-miniapp JSContext on iOS-JSC, Zipline/QuickJS
4
+ * JSRuntime runtime (per-miniapp JSContext on iOS-JSC, Zipline/QuickJS
5
5
  * context on Android).
6
6
  *
7
7
  * The existing `MiniappSession` (session.ts) is transport-agnostic — it
@@ -4,7 +4,7 @@
4
4
  * Activates when:
5
5
  * - `window.ReactNativeWebView` is undefined (not in Glassly WebView), AND
6
6
  * - The first LocalSocketTransport connection attempt fails fast, OR
7
- * - The author opts in via `?glassly=mock` query param / `localStorage.GLASSLY_MOCK = "1"`.
7
+ * - The author opts in via `?glassly=mock` query param / `localStorage.MINIAPP_MOCK = "1"`.
8
8
  *
9
9
  * Behaviors:
10
10
  * - On `open()`: synthesize a CONNECT_ACK envelope so `session.connect()` resolves.
@@ -12,20 +12,32 @@
12
12
  * auto-reply with synthetic results for any request that needs one.
13
13
  * - Does NOT emit any glasses events. Subscribing succeeds silently.
14
14
  *
15
- * This is the Stage-1 stopgap from `agents/miniapp-quick-fixes-spec.md` #6.
15
+ * This is the Stage-1 stopgap from `notes/archive/agents/miniapp-quick-fixes-spec.md` #6.
16
16
  * The full simulator (event injection, glasses-display preview, hardware bridge)
17
- * is Stage 2 — see `agents/miniapp-browser-testing-simulator-spec.md`.
17
+ * is Stage 2 — see `notes/archive/agents/miniapp-browser-testing-simulator-spec.md`.
18
18
  */
19
19
 
20
20
  import {parseEnvelope, serializeEnvelope, type MiniappEnvelope} from "../envelope"
21
- import {MiniappRequestType, MiniappResponseType} from "../protocol"
21
+ import {MiniappRequestType, MiniappResponseType, MiniappStreamType} from "../protocol"
22
22
  import type {Transport, TransportDisconnectHandler, TransportMessageHandler} from "./types"
23
23
 
24
+ const mockMirror = () => ({
25
+ revision: 0,
26
+ updatedAt: 0,
27
+ connected: false,
28
+ model: null,
29
+ canvas: null,
30
+ view: null,
31
+ appId: null,
32
+ content: {kind: "unavailable", reason: "mock-host"},
33
+ glyphWidths: {},
34
+ })
35
+
24
36
  const LOG_PREFIX = "[mock-transport]"
25
37
 
26
38
  /**
27
39
  * Returns true if the current environment requested the mock transport
28
- * explicitly. Checks `?glassly=mock` query param and `localStorage.GLASSLY_MOCK`.
40
+ * explicitly. Checks `?glassly=mock` query param and `localStorage.MINIAPP_MOCK`.
29
41
  */
30
42
  export function isMockExplicitlyRequested(): boolean {
31
43
  if (typeof window === "undefined") return false
@@ -38,7 +50,7 @@ export function isMockExplicitlyRequested(): boolean {
38
50
  // ignore
39
51
  }
40
52
  try {
41
- if (typeof localStorage !== "undefined" && localStorage.getItem("GLASSLY_MOCK") === "1") {
53
+ if (typeof localStorage !== "undefined" && localStorage.getItem("MINIAPP_MOCK") === "1") {
42
54
  return true
43
55
  }
44
56
  } catch {
@@ -105,6 +117,19 @@ export class MockTransport implements Transport {
105
117
  return
106
118
 
107
119
  case MiniappRequestType.SUBSCRIBE:
120
+ if (Array.isArray(payload.subscriptions) && payload.subscriptions.includes(MiniappStreamType.MIRROR_UPDATE)) {
121
+ queueMicrotask(() =>
122
+ this.messageHandler?.(
123
+ serializeEnvelope({
124
+ payload: {
125
+ type: MiniappResponseType.EVENT,
126
+ streamType: MiniappStreamType.MIRROR_UPDATE,
127
+ data: mockMirror(),
128
+ },
129
+ }),
130
+ ),
131
+ )
132
+ }
108
133
  // Subscribe is fire-and-forget; no response expected. Stage 1 emits no
109
134
  // events. Future Stage 2 simulator can synthesize events here.
110
135
  return
@@ -149,7 +174,7 @@ export class MockTransport implements Transport {
149
174
  visibility: "foreground",
150
175
  colorScheme: "light",
151
176
  auth: {
152
- glasslyUserId: this.userId,
177
+ userId: this.userId,
153
178
  oemId: "mock",
154
179
  token: this.authToken,
155
180
  expiresAt: Date.now() + 60 * 60 * 1000,
@@ -195,10 +220,10 @@ function syntheticDataFor(requestId: string, requestType: string, requestPayload
195
220
  requestPayload.preset === "narrow"
196
221
  ? 82
197
222
  : requestPayload.preset === "wide"
198
- ? 118
199
- : requestPayload.preset === "standard"
200
- ? 102
201
- : undefined
223
+ ? 118
224
+ : requestPayload.preset === "standard"
225
+ ? 102
226
+ : undefined
202
227
  const fov = typeof requestPayload.fov === "number" ? requestPayload.fov : (presetFov ?? 102)
203
228
  const roi = requestPayload.roiPosition
204
229
  const roiPosition = roi === "bottom" ? "bottom" : roi === "top" ? "top" : "center"
@@ -221,6 +246,9 @@ function syntheticDataFor(requestId: string, requestType: string, requestPayload
221
246
  case MiniappRequestType.RGB_LED:
222
247
  return {type: "rgb_led_control_response", state: "success", requestId}
223
248
 
249
+ case MiniappRequestType.MIRROR_GET:
250
+ return mockMirror()
251
+
224
252
  case MiniappRequestType.LOCATION_POLL:
225
253
  // San Francisco fallback — using 0,0 puts dev sessions in the
226
254
  // Atlantic, which makes maps/navigation unusable in the WebView.
@@ -246,6 +274,9 @@ function syntheticDataFor(requestId: string, requestType: string, requestPayload
246
274
  case MiniappRequestType.SET_WIFI_ADB_STATE:
247
275
  return {ok: true}
248
276
 
277
+ case MiniappRequestType.DISPLAY_DIAGNOSTICS:
278
+ return {enabled: false, generatedAt: Date.now(), records: []}
279
+
249
280
  case MiniappRequestType.SCAN_QR:
250
281
  return {cancelled: true}
251
282