@glassly/engine 0.3.7 → 0.3.8-dev.977

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 (176) hide show
  1. package/README.md +4 -0
  2. package/build/engine.d.ts +8 -2
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +7 -4
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/backup.d.ts +14 -0
  7. package/build/facades/backup.d.ts.map +1 -0
  8. package/build/facades/backup.js +22 -0
  9. package/build/facades/backup.js.map +1 -0
  10. package/build/facades/glasses.d.ts +4 -4
  11. package/build/facades/glasses.d.ts.map +1 -1
  12. package/build/facades/glasses.js +4 -4
  13. package/build/facades/glasses.js.map +1 -1
  14. package/build/generated/releaseMetadata.js +4 -4
  15. package/build/generated/releaseMetadata.js.map +1 -1
  16. package/build/index.d.ts +1 -0
  17. package/build/index.d.ts.map +1 -1
  18. package/build/index.js.map +1 -1
  19. package/build/services/AppRegistry.d.ts +2 -1
  20. package/build/services/AppRegistry.d.ts.map +1 -1
  21. package/build/services/AppRegistry.js +38 -8
  22. package/build/services/AppRegistry.js.map +1 -1
  23. package/build/services/AppStateBackup.d.ts +98 -0
  24. package/build/services/AppStateBackup.d.ts.map +1 -0
  25. package/build/services/AppStateBackup.js +224 -0
  26. package/build/services/AppStateBackup.js.map +1 -0
  27. package/build/services/BlobStore.d.ts.map +1 -1
  28. package/build/services/BlobStore.js +5 -12
  29. package/build/services/BlobStore.js.map +1 -1
  30. package/build/services/CloudClientService.d.ts +1 -0
  31. package/build/services/CloudClientService.d.ts.map +1 -1
  32. package/build/services/CloudClientService.js +34 -2
  33. package/build/services/CloudClientService.js.map +1 -1
  34. package/build/services/DisplayProcessor.d.ts +2 -0
  35. package/build/services/DisplayProcessor.d.ts.map +1 -1
  36. package/build/services/DisplayProcessor.js +15 -1
  37. package/build/services/DisplayProcessor.js.map +1 -1
  38. package/build/services/GlassesTouchInput.d.ts.map +1 -1
  39. package/build/services/GlassesTouchInput.js +4 -2
  40. package/build/services/GlassesTouchInput.js.map +1 -1
  41. package/build/services/JSCrashloopReportService.d.ts.map +1 -1
  42. package/build/services/JSCrashloopReportService.js +5 -0
  43. package/build/services/JSCrashloopReportService.js.map +1 -1
  44. package/build/services/JSRouter.d.ts +11 -3
  45. package/build/services/JSRouter.d.ts.map +1 -1
  46. package/build/services/JSRouter.js +36 -6
  47. package/build/services/JSRouter.js.map +1 -1
  48. package/build/services/LocalMiniappRuntime.d.ts +13 -0
  49. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  50. package/build/services/LocalMiniappRuntime.js +54 -20
  51. package/build/services/LocalMiniappRuntime.js.map +1 -1
  52. package/build/services/MiniappEngine.d.ts +9 -0
  53. package/build/services/MiniappEngine.d.ts.map +1 -1
  54. package/build/services/MiniappEngine.js +60 -27
  55. package/build/services/MiniappEngine.js.map +1 -1
  56. package/build/services/MiniappLauncher.d.ts +2 -0
  57. package/build/services/MiniappLauncher.d.ts.map +1 -1
  58. package/build/services/MiniappLauncher.js +6 -0
  59. package/build/services/MiniappLauncher.js.map +1 -1
  60. package/build/services/NavigationService.d.ts +1 -0
  61. package/build/services/NavigationService.d.ts.map +1 -1
  62. package/build/services/NavigationService.js +20 -6
  63. package/build/services/NavigationService.js.map +1 -1
  64. package/build/services/PhoneCalendarService.d.ts.map +1 -1
  65. package/build/services/PhoneCalendarService.js +8 -0
  66. package/build/services/PhoneCalendarService.js.map +1 -1
  67. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  68. package/build/services/PhoneNotificationsSync.js +7 -3
  69. package/build/services/PhoneNotificationsSync.js.map +1 -1
  70. package/build/services/SceneRenderer.js +1 -1
  71. package/build/services/SceneRenderer.js.map +1 -1
  72. package/build/services/appStateBackupFile.d.ts +32 -0
  73. package/build/services/appStateBackupFile.d.ts.map +1 -0
  74. package/build/services/appStateBackupFile.js +135 -0
  75. package/build/services/appStateBackupFile.js.map +1 -0
  76. package/build/services/controllers.d.ts.map +1 -1
  77. package/build/services/controllers.js +11 -3
  78. package/build/services/controllers.js.map +1 -1
  79. package/build/services/effectiveCapabilities.js +3 -0
  80. package/build/services/effectiveCapabilities.js.map +1 -1
  81. package/build/services/pages/pageEligibility.d.ts.map +1 -1
  82. package/build/services/pages/pageEligibility.js +8 -1
  83. package/build/services/pages/pageEligibility.js.map +1 -1
  84. package/build/services/settingsBridge.d.ts.map +1 -1
  85. package/build/services/settingsBridge.js +1 -0
  86. package/build/services/settingsBridge.js.map +1 -1
  87. package/build/services/{appleWatch/AppleWatchLink.d.ts → watch/WatchLink.d.ts} +8 -7
  88. package/build/services/watch/WatchLink.d.ts.map +1 -0
  89. package/build/services/{appleWatch/AppleWatchLink.js → watch/WatchLink.js} +14 -15
  90. package/build/services/watch/WatchLink.js.map +1 -0
  91. package/build/stores/apps.d.ts.map +1 -1
  92. package/build/stores/apps.js +14 -2
  93. package/build/stores/apps.js.map +1 -1
  94. package/build/stores/settings.d.ts.map +1 -1
  95. package/build/stores/settings.js +53 -3
  96. package/build/stores/settings.js.map +1 -1
  97. package/build/types/capabilities/even-realities-g1.d.ts.map +1 -1
  98. package/build/types/capabilities/even-realities-g1.js +3 -0
  99. package/build/types/capabilities/even-realities-g1.js.map +1 -1
  100. package/build/types/capabilities/memomind-one.d.ts +3 -0
  101. package/build/types/capabilities/memomind-one.d.ts.map +1 -0
  102. package/build/types/capabilities/memomind-one.js +41 -0
  103. package/build/types/capabilities/memomind-one.js.map +1 -0
  104. package/build/types/enums.d.ts +6 -2
  105. package/build/types/enums.d.ts.map +1 -1
  106. package/build/types/enums.js +4 -0
  107. package/build/types/enums.js.map +1 -1
  108. package/build/types/hardware.d.ts +3 -1
  109. package/build/types/hardware.d.ts.map +1 -1
  110. package/build/types/hardware.js +3 -1
  111. package/build/types/hardware.js.map +1 -1
  112. package/build/utils/display/index.d.ts +2 -1
  113. package/build/utils/display/index.d.ts.map +1 -1
  114. package/build/utils/display/index.js +3 -2
  115. package/build/utils/display/index.js.map +1 -1
  116. package/build/utils/display/profiles/index.d.ts +2 -1
  117. package/build/utils/display/profiles/index.d.ts.map +1 -1
  118. package/build/utils/display/profiles/index.js +3 -2
  119. package/build/utils/display/profiles/index.js.map +1 -1
  120. package/build/utils/display/profiles/rayneo-io.d.ts +1 -1
  121. package/build/utils/display/profiles/rayneo-io.d.ts.map +1 -1
  122. package/build/utils/display/profiles/rayneo-io.js +1 -1
  123. package/build/utils/display/profiles/rayneo-io.js.map +1 -1
  124. package/build/utils/display/scene/index.d.ts +1 -1
  125. package/build/utils/display/scene/index.d.ts.map +1 -1
  126. package/build/utils/display/scene/index.js +1 -1
  127. package/build/utils/display/scene/index.js.map +1 -1
  128. package/build/utils/display/scene/process.d.ts +6 -0
  129. package/build/utils/display/scene/process.d.ts.map +1 -1
  130. package/build/utils/display/scene/process.js +22 -7
  131. package/build/utils/display/scene/process.js.map +1 -1
  132. package/build/utils/miniappGlobals.d.ts +13 -17
  133. package/build/utils/miniappGlobals.d.ts.map +1 -1
  134. package/build/utils/miniappGlobals.js +24 -56
  135. package/build/utils/miniappGlobals.js.map +1 -1
  136. package/package.json +7 -7
  137. package/src/engine.ts +7 -4
  138. package/src/facades/backup.ts +28 -0
  139. package/src/facades/glasses.ts +5 -5
  140. package/src/generated/releaseMetadata.ts +4 -4
  141. package/src/index.ts +1 -0
  142. package/src/services/AppRegistry.ts +36 -9
  143. package/src/services/AppStateBackup.ts +283 -0
  144. package/src/services/BlobStore.ts +5 -11
  145. package/src/services/CloudClientService.ts +35 -4
  146. package/src/services/DisplayProcessor.ts +20 -8
  147. package/src/services/GlassesTouchInput.ts +6 -2
  148. package/src/services/JSCrashloopReportService.ts +5 -0
  149. package/src/services/JSRouter.ts +37 -6
  150. package/src/services/LocalMiniappRuntime.ts +55 -20
  151. package/src/services/MiniappEngine.ts +62 -27
  152. package/src/services/MiniappLauncher.ts +7 -0
  153. package/src/services/NavigationService.ts +28 -8
  154. package/src/services/PhoneCalendarService.ts +7 -0
  155. package/src/services/PhoneNotificationsSync.ts +8 -3
  156. package/src/services/SceneRenderer.ts +1 -1
  157. package/src/services/appStateBackupFile.ts +171 -0
  158. package/src/services/controllers.ts +12 -3
  159. package/src/services/effectiveCapabilities.ts +3 -0
  160. package/src/services/pages/pageEligibility.ts +9 -1
  161. package/src/services/settingsBridge.ts +1 -0
  162. package/src/services/{appleWatch/AppleWatchLink.ts → watch/WatchLink.ts} +20 -20
  163. package/src/stores/apps.ts +13 -2
  164. package/src/stores/settings.ts +60 -7
  165. package/src/types/capabilities/even-realities-g1.ts +4 -0
  166. package/src/types/capabilities/memomind-one.ts +42 -0
  167. package/src/types/enums.ts +4 -0
  168. package/src/types/hardware.ts +14 -1
  169. package/src/utils/display/index.ts +4 -2
  170. package/src/utils/display/profiles/index.ts +9 -2
  171. package/src/utils/display/profiles/rayneo-io.ts +6 -1
  172. package/src/utils/display/scene/index.ts +1 -1
  173. package/src/utils/display/scene/process.ts +22 -7
  174. package/src/utils/miniappGlobals.ts +32 -64
  175. package/build/services/appleWatch/AppleWatchLink.d.ts.map +0 -1
  176. package/build/services/appleWatch/AppleWatchLink.js.map +0 -1
@@ -7,7 +7,7 @@
7
7
  * same API surface.
8
8
  */
9
9
 
10
- import {Dimensions, Platform} from "react-native"
10
+ import {Platform} from "react-native"
11
11
 
12
12
  export interface MiniappSafeArea {
13
13
  top: number
@@ -16,6 +16,11 @@ export interface MiniappSafeArea {
16
16
  right: number
17
17
  }
18
18
 
19
+ /**
20
+ * The host's capsule menu in the WebView's own coordinate space. `corner`
21
+ * floats over the page's top-right; `rail` sits in the iPhone Duo's status
22
+ * rail, inside the side safe-area inset and clear of the page content.
23
+ */
19
24
  export interface CapsuleMenuRect {
20
25
  top: number
21
26
  right: number
@@ -23,36 +28,7 @@ export interface CapsuleMenuRect {
23
28
  left: number
24
29
  width: number
25
30
  height: number
26
- }
27
-
28
- /**
29
- * The capsule menu bounding rect in the WebView's own coordinate space.
30
- *
31
- * Keep in sync with CapsuleMenu.tsx:
32
- * - CapsuleButton height ≈ h-7.5 (30px), width ≈ 73px
33
- * - Positioned at right-2 (8px), top = theme.spacing.s2 (8px) below insets.top
34
- */
35
- const CAPSULE_MENU_HEIGHT = 30
36
- const CAPSULE_MENU_WIDTH = 73
37
- const CAPSULE_MENU_PADDING = 8
38
-
39
- /**
40
- * @param topInsetOffset extra top offset to add when the WebView container does
41
- * NOT already pad by the safe-area inset (i.e. WebView fills edge-to-edge under
42
- * the status bar). Pass `insets.top` in that case, or 0 when the container
43
- * already applies top padding.
44
- */
45
- export function getCapsuleMenuRect(topInsetOffset = 0): CapsuleMenuRect {
46
- const screenWidth = Dimensions.get("window").width
47
- const top = topInsetOffset + CAPSULE_MENU_PADDING
48
- return {
49
- top,
50
- right: CAPSULE_MENU_PADDING,
51
- bottom: top + CAPSULE_MENU_HEIGHT,
52
- left: screenWidth - CAPSULE_MENU_PADDING - CAPSULE_MENU_WIDTH,
53
- width: CAPSULE_MENU_WIDTH,
54
- height: CAPSULE_MENU_HEIGHT,
55
- }
31
+ placement: "corner" | "rail"
56
32
  }
57
33
 
58
34
  export type MiniappColorScheme = "light" | "dark"
@@ -63,37 +39,48 @@ export interface BuildMiniappGlobalsOptions {
63
39
  miniappLocal?: boolean
64
40
  miniappDeveloperMode?: boolean
65
41
  safeAreaInsets: MiniappSafeArea
66
- /**
67
- * True when the WebView container renders edge-to-edge (no top padding for
68
- * the status bar). In that case the capsule menu rect needs to shift down by
69
- * the top inset so its coords match where it's actually drawn on screen.
70
- */
71
- webviewFillsStatusBar?: boolean
42
+ /** Where the host draws its capsule menu over this WebView. */
43
+ capsuleMenu: CapsuleMenuRect
72
44
  /** Current host color scheme. Miniapps may follow this to match the phone. */
73
45
  colorScheme?: MiniappColorScheme
74
46
  }
75
47
 
76
- function safeAreaCssVars(insets: MiniappSafeArea): Record<string, string> {
48
+ // CSS custom properties that mirror the safe-area and capsule menu data, so
49
+ // miniapp CSS (or Tailwind arbitrary values) can read them without touching
50
+ // JS: e.g. `style="margin-top: var(--glassly-capsule-top)"`.
51
+ function layoutCssVars(insets: MiniappSafeArea, capsule: CapsuleMenuRect): Record<string, string> {
77
52
  return {
78
53
  "--glassly-safe-top": `${insets.top}px`,
79
54
  "--glassly-safe-bottom": `${insets.bottom}px`,
80
55
  "--glassly-safe-left": `${insets.left}px`,
81
56
  "--glassly-safe-right": `${insets.right}px`,
57
+ "--glassly-capsule-top": `${capsule.top}px`,
58
+ "--glassly-capsule-bottom": `${capsule.bottom}px`,
59
+ "--glassly-capsule-left": `${capsule.left}px`,
60
+ "--glassly-capsule-right": `${capsule.right}px`,
61
+ "--glassly-capsule-width": `${capsule.width}px`,
62
+ "--glassly-capsule-height": `${capsule.height}px`,
63
+ "--glassly-capsule-center-y": `${capsule.top + capsule.height / 2}px`,
64
+ // Right-side gutter to reserve so content doesn't slide under the
65
+ // capsule: its width + 16px breathing room, none when it sits in the rail.
66
+ "--glassly-capsule-gutter": `${capsule.placement === "rail" ? 0 : capsule.width + 16}px`,
82
67
  }
83
68
  }
84
69
 
85
70
  /**
86
- * JS to inject into a running miniapp when the host's safe area changes (an
87
- * iPhone Duo folding moves its status rail): updates window.Glassly and the
88
- * CSS variables, then fires `glassly:safe-area-change`.
71
+ * JS to inject into a running miniapp when the host's safe area or capsule
72
+ * menu moves (an iPhone Duo folding, or its rail's reserved regions growing):
73
+ * updates window.Glassly and the CSS variables, then fires
74
+ * `glassly:safe-area-change`.
89
75
  */
90
- export function buildSafeAreaUpdateScript(insets: MiniappSafeArea): string {
91
- const sets = Object.entries(safeAreaCssVars(insets))
76
+ export function buildSafeAreaUpdateScript(insets: MiniappSafeArea, capsuleMenu: CapsuleMenuRect): string {
77
+ const sets = Object.entries(layoutCssVars(insets, capsuleMenu))
92
78
  .map(([k, v]) => `document.documentElement.style.setProperty(${JSON.stringify(k)}, ${JSON.stringify(v)});`)
93
79
  .join("\n ")
94
80
  return `
95
81
  window.Glassly = window.Glassly || {};
96
82
  window.Glassly.safeAreaInsets = ${JSON.stringify(insets)};
83
+ window.Glassly.capsuleMenu = ${JSON.stringify(capsuleMenu)};
97
84
  try {
98
85
  ${sets}
99
86
  } catch (e) { /* ignore */ }
@@ -115,37 +102,18 @@ export function buildSafeAreaUpdateScript(insets: MiniappSafeArea): string {
115
102
  * assign to it before the native bridge wires in.
116
103
  */
117
104
  export function buildMiniappGlobalsScript(opts: BuildMiniappGlobalsOptions): string {
118
- const capsuleTopOffset = opts.webviewFillsStatusBar ? opts.safeAreaInsets.top : 0
119
105
  const globals: Record<string, unknown> = {
120
106
  platform: Platform.OS,
121
107
  capabilities: opts.capabilities ?? ["share", "open_url", "copy_clipboard", "download"],
122
108
  safeAreaInsets: opts.safeAreaInsets,
123
- capsuleMenu: getCapsuleMenuRect(capsuleTopOffset),
109
+ capsuleMenu: opts.capsuleMenu,
124
110
  }
125
111
  if (opts.packageName) globals.packageName = opts.packageName
126
112
  if (opts.miniappLocal) globals.miniappLocal = true
127
113
  if (opts.miniappDeveloperMode) globals.miniappDeveloperMode = true
128
114
  if (opts.colorScheme) globals.colorScheme = opts.colorScheme
129
115
 
130
- // CSS custom properties that mirror the capsule menu / safe-area data.
131
- // Miniapp CSS (or Tailwind arbitrary values) can read these without
132
- // touching JS: e.g. `style="margin-top: var(--glassly-capsule-top)"`.
133
- const capsule = globals.capsuleMenu as CapsuleMenuRect
134
- const insets = opts.safeAreaInsets
135
- const capsuleCenter = capsule.top + capsule.height / 2
136
- const cssVars: Record<string, string> = {
137
- ...safeAreaCssVars(insets),
138
- "--glassly-capsule-top": `${capsule.top}px`,
139
- "--glassly-capsule-bottom": `${capsule.bottom}px`,
140
- "--glassly-capsule-left": `${capsule.left}px`,
141
- "--glassly-capsule-right": `${capsule.right}px`,
142
- "--glassly-capsule-width": `${capsule.width}px`,
143
- "--glassly-capsule-height": `${capsule.height}px`,
144
- "--glassly-capsule-center-y": `${capsuleCenter}px`,
145
- // Right-side gutter to reserve so content doesn't slide under the
146
- // capsule: capsule width + 16px breathing room.
147
- "--glassly-capsule-gutter": `${capsule.width + 16}px`,
148
- }
116
+ const cssVars = layoutCssVars(opts.safeAreaInsets, opts.capsuleMenu)
149
117
  const cssVarsBlock = Object.entries(cssVars)
150
118
  .map(([k, v]) => `${k}: ${v};`)
151
119
  .join(" ")
@@ -1 +0,0 @@
1
- {"version":3,"file":"AppleWatchLink.d.ts","sourceRoot":"","sources":["../../../src/services/appleWatch/AppleWatchLink.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,wBAAwB,CAAA;AAElE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAOpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAA;AAO1C,eAAO,MAAM,cAAc,0BAA0B,CAAA;AACrD,eAAO,MAAM,aAAa,QAAQ,CAAA;AAQlC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAA;IAC7D,IAAI,EAAE,SAAS,SAAS,EAAE,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,GAAG;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,EAAE,CAMhG;AAED,wBAAgB,UAAU,CAAC,EAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAC,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUpG;AAOD,+FAA+F;AAC/F,wBAAgB,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CA4EpF;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,SAAS,SAAS,EAAE,CAAA;IAChC,KAAK,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAChD,GAAG,EAAE,MAAM,MAAM,CAAA;CAClB;AAWD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,sBAAsB,EAC7B,IAAI,GAAE,qBAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AA6DD,wBAAgB,mBAAmB,IAAI,IAAI,CAqC1C;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"AppleWatchLink.js","sourceRoot":"","sources":["../../../src/services/appleWatch/AppleWatchLink.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Apple Watch remote's phone half: the watch app's status context (glasses, the miniapp in\n * front, launchable miniapps, whether Assist takes questions, the mirror setting), mirror frames\n * while the setting is on, and the commands the watch sends back. Native adds whether the active\n * watch is paired and keeps the newest frame for when the watch app opens. Gestures ride\n * `touch_event` like any controller's.\n */\nimport type {AppleWatchCommandEvent} from \"@glassly/bluetooth-sdk\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {MirrorSnapshot} from \"@glassly/miniapp\"\nimport {MiniappStreamType} from \"@glassly/miniapp\"\nimport {Platform} from \"react-native\"\n\nimport {useAppStatusStore} from \"../../stores/apps\"\nimport {useGlassesStore} from \"../../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../../stores/settings\"\nimport type {ClientApp} from \"../../types\"\nimport {BgTimer} from \"../../utils/timers\"\nimport {isGlassesConnected} from \"../GlassesReadiness\"\nimport localMiniappRuntime from \"../LocalMiniappRuntime\"\nimport {mirrorService} from \"../MirrorService\"\nimport {keyEventsForTextChange} from \"../neuralBand/NeuralBandInput\"\n\nexport const ASSIST_PACKAGE = \"com.glassly.assistant\"\nexport const ASSIST_ACTION = \"ask\"\n/** WatchConnectivity messages top out near 64 KB; frames past this are skipped, not truncated. */\nconst MAX_FRAME_BYTES = 48_000\nconst MAX_IMAGE_CHARS = 16_000\nconst MAX_APPS = 24\nconst STATE_DEBOUNCE_MS = 250\nconst MIRROR_INTERVAL_MS = 500\n\nexport interface WatchStateInput {\n glasses: {connected: boolean; model: string; battery: number}\n apps: readonly ClientApp[]\n assist: boolean\n mirror: boolean\n}\n\n/** Miniapps the watch can open: shown, compatible, and drawing on the glasses. */\nexport function launchableApps(apps: readonly ClientApp[]): {packageName: string; name: string}[] {\n return apps\n .filter((app) => app.type === \"standard\" && !app.hidden && app.compatibility?.isCompatible !== false)\n .filter((app) => app.packageName && !app.packageName.includes(\"@empty\"))\n .slice(0, MAX_APPS)\n .map((app) => ({packageName: app.packageName, name: app.name}))\n}\n\nexport function watchState({glasses, apps, assist, mirror}: WatchStateInput): Record<string, unknown> {\n const front = apps.find((app) => app.foregrounded && app.running && app.type === \"standard\")\n const state: Record<string, unknown> = {\n glasses: {connected: glasses.connected, name: glasses.model || \"Glasses\", battery: glasses.battery},\n apps: launchableApps(apps),\n assist,\n mirror,\n }\n if (front) state.app = {packageName: front.packageName, name: front.name}\n return state\n}\n\ntype WatchElement = Record<string, string | number | boolean | number[]>\n\nconst level = (color: unknown, fallback = 15) =>\n typeof color === \"number\" ? Math.max(0, Math.min(15, color)) : fallback\n\n/** The glasses frame in the watch's drawing vocabulary; null when there is nothing to draw. */\nexport function mirrorFrame(snapshot: MirrorSnapshot): Record<string, unknown> | null {\n const canvas = snapshot.canvas\n if (!canvas) return null\n const content = snapshot.content\n if (content.kind === \"bitmap\") {\n if (content.data.length > MAX_FRAME_BYTES) return null\n return {width: canvas.width, height: canvas.height, bitmap: content.data, elements: []}\n }\n if (content.kind !== \"scene\") return null\n const elements: WatchElement[] = []\n for (const element of content.elements) {\n const {x, y, w, h} = element.box\n const style = element.style ?? {}\n switch (element.type) {\n case \"text\":\n if (!element.text) break\n elements.push({\n type: \"text\",\n x,\n y,\n w,\n h,\n text: element.text,\n size: style.fontSize ?? 20,\n color: level(style.color),\n })\n break\n case \"rect\":\n elements.push({\n type: \"rect\",\n x,\n y,\n w,\n h,\n fill: style.fill === true,\n border: style.border ?? 1,\n radius: style.radius ?? 0,\n color: level(style.color),\n })\n break\n case \"circle\":\n elements.push({\n type: \"circle\",\n x,\n y,\n w,\n h,\n fill: style.fill === true,\n border: style.border ?? 1,\n color: level(style.color),\n })\n break\n case \"line\":\n case \"triangle\":\n case \"quad\": {\n const points = (element.points ?? []).flatMap((point) => [point.x, point.y])\n if (points.length < 4) break\n const closed = element.type !== \"line\"\n elements.push({\n type: \"line\",\n points,\n width: style.width ?? style.border ?? 1,\n color: level(style.color),\n closed,\n fill: closed && style.fill === true,\n })\n break\n }\n case \"image\":\n if (element.data && element.data.length <= MAX_IMAGE_CHARS)\n elements.push({type: \"image\", x, y, w, h, data: element.data})\n break\n }\n }\n const frame = {width: canvas.width, height: canvas.height, elements}\n return JSON.stringify(frame).length > MAX_FRAME_BYTES ? null : frame\n}\n\nexport interface AppleWatchCommandDeps {\n apps: () => readonly ClientApp[]\n start: (app: ClientApp) => Promise<unknown>\n stop: (packageName: string) => Promise<unknown>\n ask: (query: string) => Promise<unknown>\n type: (stream: string, payload: unknown) => void\n now: () => number\n}\n\nconst defaultDeps: AppleWatchCommandDeps = {\n apps: () => useAppStatusStore.getState().apps,\n start: (app) => useAppStatusStore.getState().start(app),\n stop: (packageName) => useAppStatusStore.getState().stop(packageName),\n ask: (query) => localMiniappRuntime.invokeActionFromHost(ASSIST_PACKAGE, ASSIST_ACTION, {query}),\n type: (stream, payload) => localMiniappRuntime.forwardEvent(stream, payload),\n now: () => Date.now(),\n}\n\nexport async function handleAppleWatchCommand(\n event: AppleWatchCommandEvent,\n deps: AppleWatchCommandDeps = defaultDeps,\n): Promise<void> {\n switch (event.command) {\n case \"launch\": {\n const app = launchableApps(deps.apps()).some((entry) => entry.packageName === event.packageName)\n ? deps.apps().find((entry) => entry.packageName === event.packageName)\n : undefined\n if (!app) {\n console.warn(`AppleWatch: launch of unknown miniapp ${event.packageName}`)\n return\n }\n await deps.start(app)\n return\n }\n case \"stop\": {\n const front = deps.apps().find((app) => app.foregrounded && app.running && app.type === \"standard\")\n if (front) await deps.stop(front.packageName)\n return\n }\n case \"assist\": {\n const query = event.text.trim()\n if (query) await deps.ask(query)\n return\n }\n case \"text\":\n for (const key of keyEventsForTextChange(\"\", event.text, deps.now()))\n deps.type(MiniappStreamType.KEYBOARD_EVENT, key)\n return\n }\n}\n\nlet stops: (() => void)[] = []\nlet stopMirror: (() => void) | null = null\n\nfunction currentState(): Record<string, unknown> {\n const glasses = useGlassesStore.getState()\n return watchState({\n glasses: {\n connected: isGlassesConnected(glasses.connection),\n model: glasses.deviceModel,\n battery: glasses.batteryLevel,\n },\n apps: useAppStatusStore.getState().apps,\n assist: localMiniappRuntime.hostActionAvailable(ASSIST_PACKAGE, ASSIST_ACTION),\n mirror: useSettingsStore.getState().getSetting(SETTINGS.apple_watch_mirror.key) === true,\n })\n}\n\nfunction syncMirror(enabled: boolean): void {\n if (!enabled) {\n if (stopMirror) {\n stopMirror()\n stopMirror = null\n void BluetoothSdk.setWatchMirror(null).catch(() => {})\n }\n return\n }\n if (stopMirror) return\n let lastSentAt = 0\n let pending: MirrorSnapshot | null = null\n let timer: ReturnType<typeof BgTimer.setTimeout> | null = null\n const send = (snapshot: MirrorSnapshot) => {\n lastSentAt = Date.now()\n void BluetoothSdk.setWatchMirror(mirrorFrame(snapshot)).catch((err) =>\n console.warn(\"AppleWatch: mirror push failed:\", err),\n )\n }\n const unsubscribe = mirrorService.subscribe((snapshot) => {\n const wait = lastSentAt + MIRROR_INTERVAL_MS - Date.now()\n if (wait <= 0 && !timer) {\n send(snapshot)\n return\n }\n pending = snapshot\n timer ??= BgTimer.setTimeout(\n () => {\n timer = null\n if (pending) send(pending)\n pending = null\n },\n Math.max(wait, 0),\n )\n })\n stopMirror = () => {\n unsubscribe()\n if (timer) BgTimer.clearTimeout(timer)\n timer = null\n }\n}\n\nexport function startAppleWatchLink(): void {\n if (Platform.OS !== \"ios\" || stops.length > 0) return\n let lastKey = \"\"\n let timer: ReturnType<typeof BgTimer.setTimeout> | null = null\n const push = () => {\n timer = null\n const state = currentState()\n const key = JSON.stringify(state)\n if (key === lastKey) return\n lastKey = key\n void BluetoothSdk.setWatchState(state).catch((err) => console.warn(\"AppleWatch: state push failed:\", err))\n }\n const schedule = () => {\n timer ??= BgTimer.setTimeout(push, STATE_DEBOUNCE_MS)\n }\n const mirrorEnabled = () => useSettingsStore.getState().getSetting(SETTINGS.apple_watch_mirror.key) === true\n const commands = BluetoothSdk.addListener(\"apple_watch_command\", (event) => {\n handleAppleWatchCommand(event).catch((err) => console.warn(`AppleWatch: ${event.command} failed:`, err))\n })\n stops = [\n () => commands.remove(),\n useGlassesStore.subscribe(schedule),\n useAppStatusStore.subscribe(schedule),\n useSettingsStore.subscribe(\n (s) => s.getSetting(SETTINGS.apple_watch_mirror.key),\n () => {\n syncMirror(mirrorEnabled())\n schedule()\n },\n ),\n () => {\n if (timer) BgTimer.clearTimeout(timer)\n timer = null\n },\n ]\n syncMirror(mirrorEnabled())\n schedule()\n}\n\nexport function stopAppleWatchLink(): void {\n stops.splice(0).forEach((stop) => stop())\n syncMirror(false)\n}\n"]}