@glassly/miniapp 0.3.0-dev.1 → 0.3.0-dev.460

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 (255) hide show
  1. package/README.md +219 -74
  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/hardware.d.ts +4 -18
  73. package/dist/hardware.d.ts.map +1 -1
  74. package/dist/hardware.js +2 -13
  75. package/dist/hardware.js.map +1 -1
  76. package/dist/index.d.ts +16 -10
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +3 -1
  79. package/dist/index.js.map +1 -1
  80. package/dist/knownApps.d.ts +26 -0
  81. package/dist/knownApps.d.ts.map +1 -0
  82. package/dist/knownApps.js +140 -0
  83. package/dist/knownApps.js.map +1 -0
  84. package/dist/mirror/canvas.d.ts +22 -0
  85. package/dist/mirror/canvas.d.ts.map +1 -0
  86. package/dist/mirror/canvas.js +311 -0
  87. package/dist/mirror/canvas.js.map +1 -0
  88. package/dist/mirror/geometry.d.ts +69 -0
  89. package/dist/mirror/geometry.d.ts.map +1 -0
  90. package/dist/mirror/geometry.js +75 -0
  91. package/dist/mirror/geometry.js.map +1 -0
  92. package/dist/mirror/index.d.ts +3 -0
  93. package/dist/mirror/index.d.ts.map +1 -0
  94. package/dist/mirror/index.js +2 -0
  95. package/dist/mirror/index.js.map +1 -0
  96. package/dist/mirror/types.d.ts +40 -0
  97. package/dist/mirror/types.d.ts.map +1 -0
  98. package/dist/mirror/types.js +2 -0
  99. package/dist/mirror/types.js.map +1 -0
  100. package/dist/modules/auth.d.ts +1 -1
  101. package/dist/modules/auth.d.ts.map +1 -1
  102. package/dist/modules/auth.js +2 -2
  103. package/dist/modules/auth.js.map +1 -1
  104. package/dist/modules/compass.d.ts +47 -0
  105. package/dist/modules/compass.d.ts.map +1 -0
  106. package/dist/modules/compass.js +39 -0
  107. package/dist/modules/compass.js.map +1 -0
  108. package/dist/modules/display.d.ts +192 -8
  109. package/dist/modules/display.d.ts.map +1 -1
  110. package/dist/modules/display.js +40 -3
  111. package/dist/modules/display.js.map +1 -1
  112. package/dist/modules/events.d.ts +30 -2
  113. package/dist/modules/events.d.ts.map +1 -1
  114. package/dist/modules/events.js.map +1 -1
  115. package/dist/modules/input.d.ts +40 -1
  116. package/dist/modules/input.d.ts.map +1 -1
  117. package/dist/modules/input.js +45 -1
  118. package/dist/modules/input.js.map +1 -1
  119. package/dist/modules/latest-render-scheduler.d.ts +19 -0
  120. package/dist/modules/latest-render-scheduler.d.ts.map +1 -0
  121. package/dist/modules/latest-render-scheduler.js +54 -0
  122. package/dist/modules/latest-render-scheduler.js.map +1 -0
  123. package/dist/modules/lightSensor.d.ts +44 -0
  124. package/dist/modules/lightSensor.d.ts.map +1 -0
  125. package/dist/modules/lightSensor.js +33 -0
  126. package/dist/modules/lightSensor.js.map +1 -0
  127. package/dist/modules/mic.d.ts +3 -2
  128. package/dist/modules/mic.d.ts.map +1 -1
  129. package/dist/modules/mic.js +3 -2
  130. package/dist/modules/mic.js.map +1 -1
  131. package/dist/modules/miniapps.d.ts +2 -0
  132. package/dist/modules/miniapps.d.ts.map +1 -1
  133. package/dist/modules/miniapps.js.map +1 -1
  134. package/dist/modules/mirror.d.ts +16 -0
  135. package/dist/modules/mirror.d.ts.map +1 -0
  136. package/dist/modules/mirror.js +40 -0
  137. package/dist/modules/mirror.js.map +1 -0
  138. package/dist/modules/mono-text-rows.d.ts +20 -0
  139. package/dist/modules/mono-text-rows.d.ts.map +1 -0
  140. package/dist/modules/mono-text-rows.js +26 -0
  141. package/dist/modules/mono-text-rows.js.map +1 -0
  142. package/dist/modules/navigation.d.ts.map +1 -1
  143. package/dist/modules/navigation.js +21 -14
  144. package/dist/modules/navigation.js.map +1 -1
  145. package/dist/modules/phone.d.ts +26 -1
  146. package/dist/modules/phone.d.ts.map +1 -1
  147. package/dist/modules/phone.js +28 -0
  148. package/dist/modules/phone.js.map +1 -1
  149. package/dist/modules/settings.d.ts +45 -0
  150. package/dist/modules/settings.d.ts.map +1 -0
  151. package/dist/modules/settings.js +54 -0
  152. package/dist/modules/settings.js.map +1 -0
  153. package/dist/modules/system.d.ts +21 -0
  154. package/dist/modules/system.d.ts.map +1 -1
  155. package/dist/modules/system.js +12 -0
  156. package/dist/modules/system.js.map +1 -1
  157. package/dist/modules/transcription.d.ts +3 -2
  158. package/dist/modules/transcription.d.ts.map +1 -1
  159. package/dist/modules/transcription.js +3 -2
  160. package/dist/modules/transcription.js.map +1 -1
  161. package/dist/protocol.d.ts +65 -5
  162. package/dist/protocol.d.ts.map +1 -1
  163. package/dist/protocol.js +66 -5
  164. package/dist/protocol.js.map +1 -1
  165. package/dist/react/MirrorCanvas.d.ts +10 -0
  166. package/dist/react/MirrorCanvas.d.ts.map +1 -0
  167. package/dist/react/MirrorCanvas.js +23 -0
  168. package/dist/react/MirrorCanvas.js.map +1 -0
  169. package/dist/react/index.d.ts +2 -0
  170. package/dist/react/index.d.ts.map +1 -1
  171. package/dist/react/index.js +1 -0
  172. package/dist/react/index.js.map +1 -1
  173. package/dist/react/useColorScheme.d.ts +2 -3
  174. package/dist/react/useColorScheme.d.ts.map +1 -1
  175. package/dist/react/useColorScheme.js +15 -8
  176. package/dist/react/useColorScheme.js.map +1 -1
  177. package/dist/react/useRpc.js +2 -2
  178. package/dist/react/useRpc.js.map +1 -1
  179. package/dist/react/useSafeArea.d.ts.map +1 -1
  180. package/dist/react/useSafeArea.js +2 -2
  181. package/dist/react/useSafeArea.js.map +1 -1
  182. package/dist/session.d.ts +50 -4
  183. package/dist/session.d.ts.map +1 -1
  184. package/dist/session.js +35 -6
  185. package/dist/session.js.map +1 -1
  186. package/dist/transport/auto.d.ts +2 -2
  187. package/dist/transport/auto.js +2 -2
  188. package/dist/transport/dispatch.d.ts +1 -1
  189. package/dist/transport/dispatch.js +1 -1
  190. package/dist/transport/mock.d.ts +4 -4
  191. package/dist/transport/mock.d.ts.map +1 -1
  192. package/dist/transport/mock.js +31 -7
  193. package/dist/transport/mock.js.map +1 -1
  194. package/dist/ui/index.d.ts +2 -0
  195. package/dist/ui/index.d.ts.map +1 -1
  196. package/dist/ui/index.js +1 -0
  197. package/dist/ui/index.js.map +1 -1
  198. package/package.json +19 -3
  199. package/src/background/index.ts +26 -4
  200. package/src/background/register.ts +1 -1
  201. package/src/display/index.ts +83 -0
  202. package/src/display/measurer/TextMeasurer.ts +346 -0
  203. package/src/display/measurer/index.ts +21 -0
  204. package/src/display/measurer/script-detection.ts +218 -0
  205. package/src/display/pixelArt.ts +141 -0
  206. package/src/display/profiles/g1.ts +237 -0
  207. package/src/display/profiles/g2.ts +39 -0
  208. package/src/display/profiles/index.ts +28 -0
  209. package/src/display/profiles/nex.ts +166 -0
  210. package/src/display/profiles/types.ts +132 -0
  211. package/src/display/profiles/z100.ts +198 -0
  212. package/src/display/scene/types.ts +335 -0
  213. package/src/display/wrapper/TextWrapper.ts +546 -0
  214. package/src/display/wrapper/index.ts +15 -0
  215. package/src/display/wrapper/types.ts +102 -0
  216. package/src/globals.ts +5 -5
  217. package/src/hardware.ts +6 -21
  218. package/src/index.ts +34 -6
  219. package/src/knownApps.ts +170 -0
  220. package/src/mirror/canvas.ts +322 -0
  221. package/src/mirror/geometry.ts +128 -0
  222. package/src/mirror/index.ts +2 -0
  223. package/src/mirror/types.ts +38 -0
  224. package/src/modules/auth.ts +2 -2
  225. package/src/modules/compass.ts +69 -0
  226. package/src/modules/display.ts +222 -12
  227. package/src/modules/events.ts +27 -1
  228. package/src/modules/input.ts +55 -2
  229. package/src/modules/latest-render-scheduler.ts +55 -0
  230. package/src/modules/lightSensor.ts +61 -0
  231. package/src/modules/mic.ts +3 -2
  232. package/src/modules/miniapps.ts +2 -0
  233. package/src/modules/mirror.ts +42 -0
  234. package/src/modules/mono-text-rows.ts +30 -0
  235. package/src/modules/navigation.ts +20 -14
  236. package/src/modules/phone.ts +40 -1
  237. package/src/modules/settings.ts +64 -0
  238. package/src/modules/system.ts +30 -1
  239. package/src/modules/transcription.ts +3 -2
  240. package/src/protocol.ts +72 -5
  241. package/src/react/MirrorCanvas.tsx +35 -0
  242. package/src/react/index.ts +3 -0
  243. package/src/react/useColorScheme.ts +17 -8
  244. package/src/react/useRpc.ts +2 -2
  245. package/src/react/useSafeArea.ts +2 -2
  246. package/src/session.ts +78 -8
  247. package/src/transport/auto.ts +2 -2
  248. package/src/transport/dispatch.ts +1 -1
  249. package/src/transport/mock.ts +42 -11
  250. package/src/ui/index.ts +3 -0
  251. package/dist/modules/heading.d.ts +0 -16
  252. package/dist/modules/heading.d.ts.map +0 -1
  253. package/dist/modules/heading.js +0 -20
  254. package/dist/modules/heading.js.map +0 -1
  255. package/src/modules/heading.ts +0 -23
@@ -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
 
package/src/ui/index.ts CHANGED
@@ -127,3 +127,6 @@ export {useRpc, type RpcCallable} from "../react/useRpc"
127
127
  * so the typed shape is consistent across the bridge.
128
128
  */
129
129
  export type TypedUiGlobal<TChannels extends object> = UiGlobal<TChannels>
130
+
131
+ export {MirrorCanvas, type MirrorCanvasProps} from "../react/MirrorCanvas"
132
+ export type {MirrorSnapshot, MirrorContent, MirrorElement} from "../mirror/types"
@@ -1,16 +0,0 @@
1
- /**
2
- * @fileoverview HeadingModule — phone compass heading events.
3
- *
4
- * Works on both Android and iOS. LOCATION permission must be declared in miniapp.json.
5
- */
6
- import { MiniappSession } from "../session";
7
- import type { HeadingData, UnsubscribeFn } from "./events";
8
- export declare class HeadingModule {
9
- private readonly session;
10
- constructor(session: MiniappSession);
11
- /** True iff `LOCATION` is declared in the miniapp's manifest. */
12
- get hasPermission(): boolean;
13
- /** Subscribe to continuous compass heading updates. */
14
- onUpdate(handler: (data: HeadingData) => void): UnsubscribeFn;
15
- }
16
- //# sourceMappingURL=heading.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"heading.d.ts","sourceRoot":"","sources":["../../src/modules/heading.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAC,cAAc,EAAC,MAAM,YAAY,CAAA;AACzC,OAAO,KAAK,EAAC,WAAW,EAAE,aAAa,EAAC,MAAM,UAAU,CAAA;AAExD,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAEpD,iEAAiE;IACjE,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,aAAa;CAG9D"}
@@ -1,20 +0,0 @@
1
- /**
2
- * @fileoverview HeadingModule — phone compass heading events.
3
- *
4
- * Works on both Android and iOS. LOCATION permission must be declared in miniapp.json.
5
- */
6
- import { MiniappStreamType } from "../protocol";
7
- export class HeadingModule {
8
- constructor(session) {
9
- this.session = session;
10
- }
11
- /** True iff `LOCATION` is declared in the miniapp's manifest. */
12
- get hasPermission() {
13
- return this.session._hasManifestPermission("LOCATION");
14
- }
15
- /** Subscribe to continuous compass heading updates. */
16
- onUpdate(handler) {
17
- return this.session._subscribe(MiniappStreamType.HEADING_UPDATE, handler);
18
- }
19
- }
20
- //# sourceMappingURL=heading.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"heading.js","sourceRoot":"","sources":["../../src/modules/heading.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,iBAAiB,EAAC,MAAM,aAAa,CAAA;AAI7C,MAAM,OAAO,aAAa;IACxB,YAA6B,OAAuB;QAAvB,YAAO,GAAP,OAAO,CAAgB;IAAG,CAAC;IAExD,iEAAiE;IACjE,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC,UAAU,CAAC,CAAA;IACxD,CAAC;IAED,uDAAuD;IACvD,QAAQ,CAAC,OAAoC;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,cAAc,EAAE,OAAkC,CAAC,CAAA;IACtG,CAAC;CACF"}
@@ -1,23 +0,0 @@
1
- /**
2
- * @fileoverview HeadingModule — phone compass heading events.
3
- *
4
- * Works on both Android and iOS. LOCATION permission must be declared in miniapp.json.
5
- */
6
-
7
- import {MiniappStreamType} from "../protocol"
8
- import {MiniappSession} from "../session"
9
- import type {HeadingData, UnsubscribeFn} from "./events"
10
-
11
- export class HeadingModule {
12
- constructor(private readonly session: MiniappSession) {}
13
-
14
- /** True iff `LOCATION` is declared in the miniapp's manifest. */
15
- get hasPermission(): boolean {
16
- return this.session._hasManifestPermission("LOCATION")
17
- }
18
-
19
- /** Subscribe to continuous compass heading updates. */
20
- onUpdate(handler: (data: HeadingData) => void): UnsubscribeFn {
21
- return this.session._subscribe(MiniappStreamType.HEADING_UPDATE, handler as (data: unknown) => void)
22
- }
23
- }