@glassly/engine 0.3.5 → 0.3.6-dev.846

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 (206) hide show
  1. package/build/engine.d.ts +11 -1
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/engine.js +13 -1
  4. package/build/engine.js.map +1 -1
  5. package/build/facades/ai.d.ts +3 -3
  6. package/build/facades/ai.d.ts.map +1 -1
  7. package/build/facades/ai.js +3 -3
  8. package/build/facades/ai.js.map +1 -1
  9. package/build/facades/glasses.d.ts +5 -1
  10. package/build/facades/glasses.d.ts.map +1 -1
  11. package/build/facades/glasses.js +39 -30
  12. package/build/facades/glasses.js.map +1 -1
  13. package/build/facades/memory.d.ts +10 -2
  14. package/build/facades/memory.d.ts.map +1 -1
  15. package/build/facades/memory.js +10 -0
  16. package/build/facades/memory.js.map +1 -1
  17. package/build/facades/miniapps.d.ts +2 -0
  18. package/build/facades/miniapps.d.ts.map +1 -1
  19. package/build/facades/miniapps.js +2 -0
  20. package/build/facades/miniapps.js.map +1 -1
  21. package/build/facades/pairing.d.ts.map +1 -1
  22. package/build/facades/pairing.js +3 -2
  23. package/build/facades/pairing.js.map +1 -1
  24. package/build/facades/providerKeys.d.ts +6 -2
  25. package/build/facades/providerKeys.d.ts.map +1 -1
  26. package/build/facades/providerKeys.js +6 -2
  27. package/build/facades/providerKeys.js.map +1 -1
  28. package/build/generated/releaseMetadata.js +4 -4
  29. package/build/generated/releaseMetadata.js.map +1 -1
  30. package/build/services/AiTierResolution.d.ts +3 -2
  31. package/build/services/AiTierResolution.d.ts.map +1 -1
  32. package/build/services/AiTierResolution.js +7 -2
  33. package/build/services/AiTierResolution.js.map +1 -1
  34. package/build/services/AppRegistry.d.ts +4 -1
  35. package/build/services/AppRegistry.d.ts.map +1 -1
  36. package/build/services/AppRegistry.js +16 -2
  37. package/build/services/AppRegistry.js.map +1 -1
  38. package/build/services/BlobStore.js +3 -3
  39. package/build/services/BlobStore.js.map +1 -1
  40. package/build/services/BluetoothRestoreLaunch.d.ts +8 -0
  41. package/build/services/BluetoothRestoreLaunch.d.ts.map +1 -0
  42. package/build/services/BluetoothRestoreLaunch.js +34 -0
  43. package/build/services/BluetoothRestoreLaunch.js.map +1 -0
  44. package/build/services/CloudClientService.d.ts +4 -4
  45. package/build/services/ForegroundLocationPermission.d.ts +1 -1
  46. package/build/services/ForegroundLocationPermission.d.ts.map +1 -1
  47. package/build/services/ForegroundLocationPermission.js.map +1 -1
  48. package/build/services/GestureDotsService.d.ts.map +1 -1
  49. package/build/services/GestureDotsService.js +2 -1
  50. package/build/services/GestureDotsService.js.map +1 -1
  51. package/build/services/GlassesTouchInput.d.ts +6 -4
  52. package/build/services/GlassesTouchInput.d.ts.map +1 -1
  53. package/build/services/GlassesTouchInput.js +81 -41
  54. package/build/services/GlassesTouchInput.js.map +1 -1
  55. package/build/services/LocalDisplayManager.d.ts +12 -4
  56. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  57. package/build/services/LocalDisplayManager.js +21 -7
  58. package/build/services/LocalDisplayManager.js.map +1 -1
  59. package/build/services/LocalLlmModelManager.d.ts +3 -1
  60. package/build/services/LocalLlmModelManager.d.ts.map +1 -1
  61. package/build/services/LocalLlmModelManager.js +13 -5
  62. package/build/services/LocalLlmModelManager.js.map +1 -1
  63. package/build/services/LocalLlmPrompt.d.ts +10 -9
  64. package/build/services/LocalLlmPrompt.d.ts.map +1 -1
  65. package/build/services/LocalLlmPrompt.js +43 -32
  66. package/build/services/LocalLlmPrompt.js.map +1 -1
  67. package/build/services/LocalLlmProvider.d.ts +1 -0
  68. package/build/services/LocalLlmProvider.d.ts.map +1 -1
  69. package/build/services/LocalLlmProvider.js +62 -22
  70. package/build/services/LocalLlmProvider.js.map +1 -1
  71. package/build/services/LocalMiniappRuntime.d.ts +28 -6
  72. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  73. package/build/services/LocalMiniappRuntime.js +190 -148
  74. package/build/services/LocalMiniappRuntime.js.map +1 -1
  75. package/build/services/MemoryStore.d.ts +13 -1
  76. package/build/services/MemoryStore.d.ts.map +1 -1
  77. package/build/services/MemoryStore.js +98 -1
  78. package/build/services/MemoryStore.js.map +1 -1
  79. package/build/services/MiniappExitGesture.d.ts +6 -3
  80. package/build/services/MiniappExitGesture.d.ts.map +1 -1
  81. package/build/services/MiniappExitGesture.js +4 -4
  82. package/build/services/MiniappExitGesture.js.map +1 -1
  83. package/build/services/MiniappWidgetMode.d.ts +6 -4
  84. package/build/services/MiniappWidgetMode.d.ts.map +1 -1
  85. package/build/services/MiniappWidgetMode.js +7 -6
  86. package/build/services/MiniappWidgetMode.js.map +1 -1
  87. package/build/services/ProviderKeyValidation.d.ts +2 -0
  88. package/build/services/ProviderKeyValidation.d.ts.map +1 -1
  89. package/build/services/ProviderKeyValidation.js +4 -0
  90. package/build/services/ProviderKeyValidation.js.map +1 -1
  91. package/build/services/SceneRenderer.d.ts.map +1 -1
  92. package/build/services/SceneRenderer.js +3 -1
  93. package/build/services/SceneRenderer.js.map +1 -1
  94. package/build/services/ScreenshotFixture.d.ts +56 -0
  95. package/build/services/ScreenshotFixture.d.ts.map +1 -0
  96. package/build/services/ScreenshotFixture.js +72 -0
  97. package/build/services/ScreenshotFixture.js.map +1 -0
  98. package/build/services/actionInvoke.d.ts +37 -0
  99. package/build/services/actionInvoke.d.ts.map +1 -0
  100. package/build/services/actionInvoke.js +89 -0
  101. package/build/services/actionInvoke.js.map +1 -0
  102. package/build/services/appleWatch/AppleWatchLink.d.ts +42 -0
  103. package/build/services/appleWatch/AppleWatchLink.d.ts.map +1 -0
  104. package/build/services/appleWatch/AppleWatchLink.js +259 -0
  105. package/build/services/appleWatch/AppleWatchLink.js.map +1 -0
  106. package/build/services/controllers.d.ts.map +1 -1
  107. package/build/services/controllers.js +16 -0
  108. package/build/services/controllers.js.map +1 -1
  109. package/build/services/manifestActions.d.ts +4 -2
  110. package/build/services/manifestActions.d.ts.map +1 -1
  111. package/build/services/manifestActions.js +9 -3
  112. package/build/services/manifestActions.js.map +1 -1
  113. package/build/services/memoryBackupFile.d.ts +9 -0
  114. package/build/services/memoryBackupFile.d.ts.map +1 -0
  115. package/build/services/memoryBackupFile.js +47 -0
  116. package/build/services/memoryBackupFile.js.map +1 -0
  117. package/build/services/settingsBridge.d.ts +4 -4
  118. package/build/services/settingsBridge.d.ts.map +1 -1
  119. package/build/services/settingsBridge.js +9 -4
  120. package/build/services/settingsBridge.js.map +1 -1
  121. package/build/services/widgetPlacements.d.ts +8 -7
  122. package/build/services/widgetPlacements.d.ts.map +1 -1
  123. package/build/services/widgetPlacements.js +7 -6
  124. package/build/services/widgetPlacements.js.map +1 -1
  125. package/build/stores/apps.d.ts +6 -0
  126. package/build/stores/apps.d.ts.map +1 -1
  127. package/build/stores/apps.js +29 -12
  128. package/build/stores/apps.js.map +1 -1
  129. package/build/stores/bluetoothSettingKeys.d.ts +2 -0
  130. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  131. package/build/stores/bluetoothSettingKeys.js +17 -0
  132. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  133. package/build/stores/settings.d.ts.map +1 -1
  134. package/build/stores/settings.js +42 -2
  135. package/build/stores/settings.js.map +1 -1
  136. package/build/types/applet.d.ts +5 -1
  137. package/build/types/applet.d.ts.map +1 -1
  138. package/build/types/applet.js.map +1 -1
  139. package/build/types/capabilities/rayneo-io.d.ts.map +1 -1
  140. package/build/types/capabilities/rayneo-io.js +2 -0
  141. package/build/types/capabilities/rayneo-io.js.map +1 -1
  142. package/build/types/enums.d.ts +2 -1
  143. package/build/types/enums.d.ts.map +1 -1
  144. package/build/types/enums.js +1 -0
  145. package/build/types/enums.js.map +1 -1
  146. package/build/utils/cloudClient/RnUdpAdapter.d.ts.map +1 -1
  147. package/build/utils/cloudClient/RnUdpAdapter.js +13 -2
  148. package/build/utils/cloudClient/RnUdpAdapter.js.map +1 -1
  149. package/build/utils/display/scene/index.d.ts +1 -1
  150. package/build/utils/display/scene/index.d.ts.map +1 -1
  151. package/build/utils/display/scene/index.js.map +1 -1
  152. package/build/utils/display/scene/process.d.ts.map +1 -1
  153. package/build/utils/display/scene/process.js +20 -1
  154. package/build/utils/display/scene/process.js.map +1 -1
  155. package/build/utils/display/scene/types.d.ts +1 -1
  156. package/build/utils/display/scene/types.d.ts.map +1 -1
  157. package/build/utils/display/scene/types.js.map +1 -1
  158. package/build/utils/miniappGlobals.d.ts +6 -0
  159. package/build/utils/miniappGlobals.d.ts.map +1 -1
  160. package/build/utils/miniappGlobals.js +28 -4
  161. package/build/utils/miniappGlobals.js.map +1 -1
  162. package/package.json +9 -9
  163. package/src/engine.ts +13 -1
  164. package/src/facades/ai.ts +3 -3
  165. package/src/facades/glasses.ts +65 -42
  166. package/src/facades/memory.ts +11 -2
  167. package/src/facades/miniapps.ts +3 -0
  168. package/src/facades/pairing.ts +4 -2
  169. package/src/facades/providerKeys.ts +6 -1
  170. package/src/generated/releaseMetadata.ts +4 -4
  171. package/src/services/AiTierResolution.ts +9 -3
  172. package/src/services/AppRegistry.ts +18 -3
  173. package/src/services/BlobStore.ts +3 -3
  174. package/src/services/BluetoothRestoreLaunch.ts +37 -0
  175. package/src/services/ForegroundLocationPermission.ts +1 -1
  176. package/src/services/GestureDotsService.ts +2 -1
  177. package/src/services/GlassesTouchInput.ts +86 -38
  178. package/src/services/LocalDisplayManager.ts +22 -8
  179. package/src/services/LocalLlmModelManager.ts +16 -6
  180. package/src/services/LocalLlmPrompt.ts +56 -37
  181. package/src/services/LocalLlmProvider.ts +74 -24
  182. package/src/services/LocalMiniappRuntime.ts +228 -147
  183. package/src/services/MemoryStore.ts +111 -2
  184. package/src/services/MiniappExitGesture.ts +8 -5
  185. package/src/services/MiniappWidgetMode.ts +9 -7
  186. package/src/services/ProviderKeyValidation.ts +5 -0
  187. package/src/services/SceneRenderer.ts +3 -1
  188. package/src/services/ScreenshotFixture.ts +113 -0
  189. package/src/services/actionInvoke.ts +105 -0
  190. package/src/services/appleWatch/AppleWatchLink.ts +297 -0
  191. package/src/services/controllers.ts +17 -0
  192. package/src/services/manifestActions.ts +16 -4
  193. package/src/services/memoryBackupFile.ts +50 -0
  194. package/src/services/settingsBridge.ts +9 -4
  195. package/src/services/widgetPlacements.ts +23 -9
  196. package/src/stores/apps.ts +35 -11
  197. package/src/stores/bluetoothSettingKeys.ts +20 -0
  198. package/src/stores/settings.ts +44 -2
  199. package/src/types/applet.ts +5 -1
  200. package/src/types/capabilities/rayneo-io.ts +2 -0
  201. package/src/types/enums.ts +1 -0
  202. package/src/utils/cloudClient/RnUdpAdapter.ts +13 -1
  203. package/src/utils/display/scene/index.ts +1 -0
  204. package/src/utils/display/scene/process.ts +17 -1
  205. package/src/utils/display/scene/types.ts +1 -0
  206. package/src/utils/miniappGlobals.ts +30 -4
@@ -1 +1 @@
1
- {"version":3,"file":"ProviderKeyValidation.js","sourceRoot":"","sources":["../../src/services/ProviderKeyValidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,KAAK,UAAU,KAAK,CAClB,GAAW,EACX,OAA+B,EAC/B,eAAyB;IAEzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAC,CAAC,CAAA;QACtD,IAAI,GAAG,CAAC,EAAE;YAAE,OAAO,EAAC,EAAE,EAAE,IAAI,EAAC,CAAA;QAC7B,IAAI,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAC,CAAA;QACnF,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAC,CAAA;IACvC,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,OAAO,KAAK,CACV,qCAAqC,EACrC,EAAC,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,mBAAmB,EAAE,YAAY,EAAC,EAC/D,CAAC,GAAG,EAAE,GAAG,CAAC,CACX,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,KAAK,CACV,+DAA+D,kBAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,EAClG,EAAE,EACF,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAChB,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,KAAK,CAAC,kCAAkC,EAAE,EAAC,aAAa,EAAE,UAAU,MAAM,CAAC,IAAI,EAAE,EAAE,EAAC,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAC1G,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAO,KAAK,CAAC,mCAAmC,EAAE,EAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,EAAC,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAC9F,CAAC","sourcesContent":["/**\n * ProviderKeyValidation\n *\n * Cheap validate-before-save probes for the AI/voice provider keys on the\n * Keys screen (Settings > API Keys), mirroring validateMapboxApiKey's shape:\n * each probe hits the provider's cheapest authenticated read endpoint (no\n * metered usage), maps auth failures to `invalid_key`, and maps everything\n * else to `network` so the caller can offer \"save anyway\".\n */\n\nexport type KeyValidationResult = {ok: true} | {ok: false; reason: \"invalid_key\" | \"network\"}\n\nasync function probe(\n url: string,\n headers: Record<string, string>,\n invalidStatuses: number[],\n): Promise<KeyValidationResult> {\n try {\n const res = await fetch(url, {method: \"GET\", headers})\n if (res.ok) return {ok: true}\n if (invalidStatuses.includes(res.status)) return {ok: false, reason: \"invalid_key\"}\n return {ok: false, reason: \"network\"}\n } catch {\n return {ok: false, reason: \"network\"}\n }\n}\n\n/** GET /v1/models with the candidate key; Anthropic answers 401 for bad keys. */\nexport function validateAnthropicApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\n \"https://api.anthropic.com/v1/models\",\n {\"x-api-key\": apiKey.trim(), \"anthropic-version\": \"2023-06-01\"},\n [401, 403],\n )\n}\n\n/**\n * GET the models list with the candidate key. Gemini rejects a malformed or\n * revoked key with 400 (API_KEY_INVALID) or 403, not 401.\n */\nexport function validateGeminiApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey.trim())}`,\n {},\n [400, 401, 403],\n )\n}\n\n/** GET /v1/models with the candidate key; OpenAI answers 401 for bad keys. */\nexport function validateOpenAiApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\"https://api.openai.com/v1/models\", {Authorization: `Bearer ${apiKey.trim()}`}, [401, 403])\n}\n\n/** GET /v1/user with the candidate key; ElevenLabs answers 401 for bad keys. */\nexport function validateElevenLabsApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\"https://api.elevenlabs.io/v1/user\", {\"xi-api-key\": apiKey.trim()}, [401, 403])\n}\n"]}
1
+ {"version":3,"file":"ProviderKeyValidation.js","sourceRoot":"","sources":["../../src/services/ProviderKeyValidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,KAAK,UAAU,KAAK,CAClB,GAAW,EACX,OAA+B,EAC/B,eAAyB;IAEzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAC,CAAC,CAAA;QACtD,IAAI,GAAG,CAAC,EAAE;YAAE,OAAO,EAAC,EAAE,EAAE,IAAI,EAAC,CAAA;QAC7B,IAAI,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAC,CAAA;QACnF,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAC,CAAA;IACvC,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,OAAO,KAAK,CACV,qCAAqC,EACrC,EAAC,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,mBAAmB,EAAE,YAAY,EAAC,EAC/D,CAAC,GAAG,EAAE,GAAG,CAAC,CACX,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,KAAK,CACV,+DAA+D,kBAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,EAClG,EAAE,EACF,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAChB,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,KAAK,CAAC,kCAAkC,EAAE,EAAC,aAAa,EAAE,UAAU,MAAM,CAAC,IAAI,EAAE,EAAE,EAAC,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAC1G,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,KAAK,CAAC,iCAAiC,EAAE,EAAC,aAAa,EAAE,UAAU,MAAM,CAAC,IAAI,EAAE,EAAE,EAAC,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AACzG,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAO,KAAK,CAAC,mCAAmC,EAAE,EAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,EAAC,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAC9F,CAAC","sourcesContent":["/**\n * ProviderKeyValidation\n *\n * Cheap validate-before-save probes for the AI/voice provider keys on the\n * Keys screen (Settings > API Keys), mirroring validateMapboxApiKey's shape:\n * each probe hits the provider's cheapest authenticated read endpoint (no\n * metered usage), maps auth failures to `invalid_key`, and maps everything\n * else to `network` so the caller can offer \"save anyway\".\n */\n\nexport type KeyValidationResult = {ok: true} | {ok: false; reason: \"invalid_key\" | \"network\"}\n\nasync function probe(\n url: string,\n headers: Record<string, string>,\n invalidStatuses: number[],\n): Promise<KeyValidationResult> {\n try {\n const res = await fetch(url, {method: \"GET\", headers})\n if (res.ok) return {ok: true}\n if (invalidStatuses.includes(res.status)) return {ok: false, reason: \"invalid_key\"}\n return {ok: false, reason: \"network\"}\n } catch {\n return {ok: false, reason: \"network\"}\n }\n}\n\n/** GET /v1/models with the candidate key; Anthropic answers 401 for bad keys. */\nexport function validateAnthropicApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\n \"https://api.anthropic.com/v1/models\",\n {\"x-api-key\": apiKey.trim(), \"anthropic-version\": \"2023-06-01\"},\n [401, 403],\n )\n}\n\n/**\n * GET the models list with the candidate key. Gemini rejects a malformed or\n * revoked key with 400 (API_KEY_INVALID) or 403, not 401.\n */\nexport function validateGeminiApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey.trim())}`,\n {},\n [400, 401, 403],\n )\n}\n\n/** GET /v1/models with the candidate key; OpenAI answers 401 for bad keys. */\nexport function validateOpenAiApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\"https://api.openai.com/v1/models\", {Authorization: `Bearer ${apiKey.trim()}`}, [401, 403])\n}\n\n/** GET /models with the candidate key; DeepSeek answers 401 for bad keys. */\nexport function validateDeepSeekApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\"https://api.deepseek.com/models\", {Authorization: `Bearer ${apiKey.trim()}`}, [401, 403])\n}\n\n/** GET /v1/user with the candidate key; ElevenLabs answers 401 for bad keys. */\nexport function validateElevenLabsApiKey(apiKey: string): Promise<KeyValidationResult> {\n return probe(\"https://api.elevenlabs.io/v1/user\", {\"xi-api-key\": apiKey.trim()}, [401, 403])\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SceneRenderer.d.ts","sourceRoot":"","sources":["../../src/services/SceneRenderer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SceneRenderer.d.ts","sourceRoot":"","sources":["../../src/services/SceneRenderer.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,9 @@ const MAX_SETTLE_MS = 255 * 33;
26
26
  function settleTimeOf(elements) {
27
27
  let ms = 0;
28
28
  for (const el of elements) {
29
- if (el.change !== "updated" && el.change !== "moved")
29
+ // Entering and leaving elements tween too, from or to their offset.
30
+ const edge = el.transition?.exit !== undefined || (el.change === "created" && el.transition?.enter !== undefined);
31
+ if (el.change !== "updated" && el.change !== "moved" && !edge)
30
32
  continue;
31
33
  const t = el.transition?.durationMs ?? 0;
32
34
  // Rotation has its own millisecond clock, beyond the geometry frame limit.
@@ -1 +1 @@
1
- {"version":3,"file":"SceneRenderer.js","sourceRoot":"","sources":["../../src/services/SceneRenderer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SceneRenderer — the device-aware boundary of the scene display pipeline.\n *\n * LocalDisplayManager owns WHO may draw (arbitration/boot/expiry); this service\n * owns WHAT gets sent for a scene: capabilities lookup, legacy-layout→scene\n * conversion (sugar and old bundles ride the same pipeline), process → diff →\n * SceneFrame emission, retained-scene replay, and the degrade branch for\n * devices that can't position elements.\n *\n * Everything device-specific here is DATA (capability fields, display profile);\n * the code paths are generic. Spec: notes/miniapp-display-render-implementation-spec.md\n */\n\nimport displayProcessor from \"./DisplayProcessor\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport {useDisplayStore} from \"../stores/display\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {DeviceTypes} from \"../types\"\nimport {getEffectiveCapabilities} from \"./effectiveCapabilities\"\nimport {BgTimer} from \"../utils/timers\"\nimport {\n degradeScene,\n diffScene,\n processScene,\n resolveAnchors,\n SceneStore,\n type FrameElement,\n type SceneBreakMode,\n type SceneDisplayCapabilities,\n type SceneElementInput,\n type SceneFrame,\n} from \"../utils/display/scene\"\n\nconst LOG_TAG = \"SCENE_RENDERER\"\n\n/** Legacy layout shape (matches DisplayPayload.layout). */\nexport type LegacyLayout = {layoutType: string; [key: string]: unknown}\n\nexport type SceneEmitResult =\n /**\n * A SceneFrame was committed. `sent` is false when the frame stayed\n * phone-side (dashboard covering main). `settleMs` is how long the device\n * keeps animating after it has the frame: the longest transition among the\n * elements this frame changed, 0 on devices that jump.\n */\n | {kind: \"scene\"; degraded: boolean; dropped: string[]; seq: number; sent: boolean; settleMs: number}\n /** Degrade path: caller routes this legacy layout through the existing pipeline (null layout ⇒ clear). */\n | {kind: \"legacy\"; layout: LegacyLayout | null; degraded: boolean; dropped: string[]}\n /** No display capabilities on the connected device. */\n | {kind: \"no-display\"}\n\n/** How a frame ended up, as reported by native (or inferred from a later frame's report). */\nexport interface ScenePresentation {\n /** The device had the frame's content (this frame or a later one that folded it in). */\n presented: boolean\n /** A later frame's report settled this one. */\n superseded: boolean\n reason?: string\n}\n\n/** Longest geometry tween (255 frames at ~30 fps); rotation uses a separate millisecond clock. */\nconst MAX_SETTLE_MS = 255 * 33\n\n/** Longest transition among the elements that actually change on the device this frame. */\nfunction settleTimeOf(elements: FrameElement[]): number {\n let ms = 0\n for (const el of elements) {\n if (el.change !== \"updated\" && el.change !== \"moved\") continue\n const t = el.transition?.durationMs ?? 0\n // Rotation has its own millisecond clock, beyond the geometry frame limit.\n const duration = Math.min(t, el.rotation !== undefined ? 65535 : MAX_SETTLE_MS)\n if (duration > ms) ms = duration\n }\n return ms\n}\n\nclass SceneRenderer {\n private static instance: SceneRenderer | null = null\n private store = new SceneStore()\n private seqCounter = 0\n private pending = new Map<number, {appId: string; resolve: (p: ScenePresentation) => void}>()\n private presentedSub: {remove: () => void} | null = null\n private settledSub: {remove: () => void} | null = null\n private settledListeners = new Set<(seq: number) => void>()\n\n public static getInstance(): SceneRenderer {\n if (!SceneRenderer.instance) {\n SceneRenderer.instance = new SceneRenderer()\n }\n return SceneRenderer.instance\n }\n\n // ===========================================================================\n // Capabilities\n // ===========================================================================\n\n /**\n * Scene capabilities of the currently-selected device, or null when it has no\n * display block. Read on demand — same model source the CONNECT_ACK uses.\n */\n public currentCapabilities(): SceneDisplayCapabilities | null {\n // dev read these via the (since-removed) settings/glassesStatus runtime\n // hooks; engine reads its own stores directly.\n const model =\n (useSettingsStore.getState().getSetting(SETTINGS.default_wearable.key) as string | undefined) ??\n (useGlassesStore.getState().deviceModel as string | undefined)\n const display = getEffectiveCapabilities((model ?? DeviceTypes.NONE) as DeviceTypes)?.display\n if (!display) return null\n return {\n width: display.width ?? display.resolution?.width ?? displayProcessor.getProfile().displayWidthPx,\n height: display.height ?? display.resolution?.height ?? displayProcessor.getProfile().displayHeightPx ?? 288,\n canPosition: display.canPosition === true,\n maxTextElements: display.maxTextElements ?? 6,\n maxImageElements: display.maxImageElements ?? 0,\n maxImagePx: display.maxImagePx,\n shapes: display.shapes ?? [],\n animation: display.animation ?? false,\n rotation: display.rotation ?? false,\n depth: display.depth ?? false,\n intensityLevels: display.intensityLevels ?? 2,\n\n partialUpdate: display.partialUpdate ?? false,\n }\n }\n\n // ===========================================================================\n // Legacy layout → scene conversion (sugar + old bundles on the scene path)\n // ===========================================================================\n\n /**\n * Convert a legacy layout into scene elements for a positioning device.\n * Returns null for layouts that must stay on the legacy path (dashboard_card,\n * clear_view, unknown types). Sugar semantics: full-canvas / split boxes.\n */\n public convertLegacyLayout(layout: LegacyLayout, caps: SceneDisplayCapabilities): SceneElementInput[] | null {\n const W = caps.width\n const H = caps.height\n const breakMode = layout.breakMode as SceneBreakMode | undefined\n\n switch (layout.layoutType) {\n case \"text_wall\":\n return [\n {\n type: \"text\",\n id: \"sugar:wall\",\n box: {x: 0, y: 0, w: W, h: H},\n text: String(layout.text ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n ]\n case \"double_text_wall\": {\n const half = Math.floor(W / 2)\n return [\n {\n type: \"text\",\n id: \"sugar:left\",\n box: {x: 0, y: 0, w: half, h: H},\n text: String(layout.topText ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n {\n type: \"text\",\n id: \"sugar:right\",\n box: {x: half, y: 0, w: W - half, h: H},\n text: String(layout.bottomText ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n ]\n }\n case \"reference_card\": {\n const titleH = Math.max(24, Math.floor(H / 5))\n return [\n {type: \"text\", id: \"sugar:title\", box: {x: 0, y: 0, w: W, h: titleH}, text: String(layout.title ?? \"\")},\n {\n type: \"text\",\n id: \"sugar:body\",\n box: {x: 0, y: titleH, w: W, h: H - titleH},\n text: String(layout.text ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n ]\n }\n case \"bitmap_view\": {\n // Dimensionless legacy bitmaps historically landed in the device's\n // default ~200×100 container (G2 seeded 200×100 at 188,44) — NOT the\n // full canvas, which would tile a small image across the whole frame.\n const w = (layout.width as number | undefined) ?? Math.min(200, W)\n const h = (layout.height as number | undefined) ?? Math.min(100, H)\n return [\n {\n type: \"image\",\n id: \"sugar:bmp\",\n box: {\n x: (layout.x as number | undefined) ?? Math.max(0, Math.round((W - w) / 2)),\n y: (layout.y as number | undefined) ?? Math.max(0, Math.round((H - h) / 4)),\n w,\n h,\n },\n data: String(layout.data ?? \"\"),\n },\n ]\n }\n case \"positioned_text\": {\n const border = (layout.borderWidth as number | undefined) ?? 0\n const radius = (layout.borderRadius as number | undefined) ?? 0\n return [\n {\n type: \"text\",\n id: \"sugar:pos\",\n box: {\n x: (layout.x as number | undefined) ?? 0,\n y: (layout.y as number | undefined) ?? 0,\n w: (layout.width as number | undefined) ?? W,\n h: (layout.height as number | undefined) ?? H,\n },\n text: String(layout.text ?? \"\"),\n ...(border || radius ? {style: {border, radius}} : {}),\n },\n ]\n }\n default:\n return null\n }\n }\n\n // ===========================================================================\n // Emit\n // ===========================================================================\n\n /**\n * Process + diff + send a scene for (app, view). On non-positioning devices\n * this returns the degraded legacy layout instead — the caller routes it\n * through the existing DisplayProcessor path (which wraps it, keeping G1/Z100\n * output byte-identical to today).\n */\n public emitScene(appId: string, view: \"main\" | \"dashboard\", elements: SceneElementInput[]): SceneEmitResult {\n const caps = this.currentCapabilities()\n if (!caps) return {kind: \"no-display\"}\n\n // Corner-anchored boxes resolve against the REAL canvas before either\n // branch: processScene clamps concrete coords, and degradeScene's reading\n // order sorts by resolved y (a bottom-anchored y:0 must not sort first).\n elements = resolveAnchors(elements, caps.width, caps.height)\n\n if (!caps.canPosition) {\n const {layout, degraded, dropped} = degradeScene(elements)\n return {kind: \"legacy\", layout, degraded, dropped}\n }\n\n const processed = processScene(elements, caps, displayProcessor.getProfile())\n // A stale baseline means the glasses were cleared since the last commit —\n // diff from empty so everything repaints (the retained frame stays valid\n // as the replay source only).\n const prevFrame = this.store.isBaselineStale(appId, view) ? [] : this.store.lastFrame(appId, view)\n const {elements: diffed, removed} = diffScene(\n prevFrame,\n processed.elements,\n this.store.nextSyntheticId(appId, view),\n )\n const canvas = {width: caps.width, height: caps.height}\n this.store.commit(appId, view, diffed, canvas)\n\n const frame: SceneFrame = {\n appId,\n view,\n sceneEpoch: this.store.currentEpoch(appId, view),\n seq: ++this.seqCounter,\n elements: diffed,\n removed,\n canvas,\n }\n const sent = this.sendFrame(frame)\n return {\n kind: \"scene\",\n degraded: processed.degraded,\n dropped: processed.dropped,\n seq: frame.seq,\n sent,\n settleMs: caps.animation ? settleTimeOf(diffed) : 0,\n }\n }\n\n /**\n * Re-emit an app's retained scene (reconnect / arbitration restore). Replay\n * is always create-based — the device rebuilds from scratch (spec §4).\n * Returns false when nothing is retained.\n */\n public replayApp(appId: string, view: \"main\" | \"dashboard\"): boolean {\n const frame = this.store.buildReplayFrame(appId, view, ++this.seqCounter)\n if (!frame) return false\n this.sendFrame(frame)\n return true\n }\n\n /** Forget an app's retained scenes (app stop). Frames still in flight will never show. */\n public clearApp(appId: string): void {\n this.store.clear(appId)\n this.settlePending((p) => p.appId === appId, {presented: false, superseded: true, reason: \"cleared\"})\n }\n\n // ===========================================================================\n // Presentation — native reports when the device has a frame\n // ===========================================================================\n\n /**\n * Resolves once native reports frame `seq` on the device, or a later frame\n * that folded it in. Never rejects; `timeoutMs` bounds a frame the device\n * never acknowledges (link stalls, page rebuilding).\n */\n public whenPresented(seq: number, timeoutMs: number, appId: string): Promise<ScenePresentation> {\n this.ensurePresentedListener()\n return new Promise((resolve) => {\n const timer = BgTimer.setTimeout(() => {\n if (this.pending.delete(seq)) resolve({presented: false, superseded: false, reason: \"no presentation report\"})\n }, timeoutMs)\n this.pending.set(seq, {\n appId,\n resolve: (p) => {\n BgTimer.clearTimeout(timer)\n resolve(p)\n },\n })\n })\n }\n\n private ensurePresentedListener(): void {\n if (this.presentedSub) return\n this.presentedSub = BluetoothSdk.addListener(\"scene_frame_presented\", (event) => {\n this.onFramePresented(event.seq, event.appId, event.presented, event.reason)\n })\n }\n\n /**\n * Fires with the host frame number once the glasses report that every\n * animation the frame started has ended (Glassly CFW revision 27+). The\n * device echoes the low 16 bits of the seq; the full number is recovered\n * against the latest frame sent.\n */\n public onSettled(listener: (seq: number) => void): {remove: () => void} {\n this.settledListeners.add(listener)\n if (!this.settledSub) {\n this.settledSub = BluetoothSdk.addListener(\"scene_settled\", (event) => {\n const seq = this.seqCounter - ((this.seqCounter - event.tag) & 0xffff)\n if (seq <= 0) return\n for (const settled of [...this.settledListeners]) settled(seq)\n })\n }\n return {remove: () => this.settledListeners.delete(listener)}\n }\n\n /** A report for `seq` settles it and every earlier frame (folded in by the native pacer). */\n private onFramePresented(seq: number, appId: string, presented: boolean, reason?: string): void {\n this.settlePending(\n (p, s) => s <= seq,\n (p, s) =>\n s === seq\n ? {presented, superseded: false, reason}\n : p.appId === appId\n ? {presented, superseded: true, reason}\n : {presented: false, superseded: true, reason: `superseded by ${appId}`},\n )\n }\n\n private settlePending(\n match: (p: {appId: string}, seq: number) => boolean,\n outcome: ScenePresentation | ((p: {appId: string}, seq: number) => ScenePresentation),\n ): void {\n for (const [seq, p] of [...this.pending]) {\n if (!match(p, seq)) continue\n this.pending.delete(seq)\n p.resolve(typeof outcome === \"function\" ? outcome(p, seq) : outcome)\n }\n }\n\n /**\n * Reset the diff baseline for (app, view) without touching the device.\n * Used when a legacy layout bypasses the scene path (clear_view /\n * dashboard_card / unknown layoutTypes go to native raw): after that, the\n * retained frame no longer matches the glasses, so the next render() must\n * diff from empty — otherwise \"unchanged\" elements would be skipped and\n * never repainted.\n */\n public resetBaseline(appId: string, view: \"main\" | \"dashboard\"): void {\n this.store.commit(appId, view, [])\n }\n\n /**\n * A system-level clear wiped the view (durationMs expiry, unmount fallback,\n * boot-window expiry): mark every app's baseline for that view stale. Unlike\n * resetBaseline this KEEPS the retained frames — they remain the\n * restore-as-replay source; only incremental diffing restarts from empty.\n */\n public markViewStale(view: \"main\" | \"dashboard\"): void {\n this.store.markViewStale(view)\n // Whatever was in flight for the view got wiped with it.\n this.settlePending(() => true, {presented: false, superseded: true, reason: \"cleared\"})\n }\n\n /** Returns false when the frame stayed phone-side (no presentation report will follow). */\n private sendFrame(frame: SceneFrame): boolean {\n // Single native view: only the visible surface's frames go out. Suppressed\n // main frames stay committed; setOverlayOwner(null) replays them.\n const overlayHeld = useDisplayStore.getState().overlayOwner !== null\n if (overlayHeld !== (frame.view === \"dashboard\")) return false\n try {\n // Frames carry the canvas they were laid out against; only a frame from\n // a path that predates the tag falls back to the current capabilities.\n const caps = this.currentCapabilities()\n if (!frame.canvas && caps) frame = {...frame, canvas: {width: caps.width, height: caps.height}}\n // sendDisplayEvent hook -> direct BluetoothSdk.displayEvent (the exact\n // wiring the deleted MantleManager hook provided).\n void Promise.resolve(BluetoothSdk.displayEvent({view: frame.view, scene: frame})).catch((err) => {\n console.error(`${LOG_TAG}: native scene send failed:`, err)\n })\n\n // Keep the phone-side glasses mirror on the same frame as native. Legacy\n // layouts update this store in LocalDisplayManager.sendToNative; scene\n // frames bypass that method, so without an explicit write every\n // positioning device (including G2) leaves the mirror blank/stale.\n useDisplayStore.getState().setDisplayEvent({\n view: frame.view,\n layout: {\n layoutType: \"scene\",\n appId: frame.appId,\n view: frame.view,\n sceneEpoch: frame.sceneEpoch,\n width: frame.canvas?.width,\n height: frame.canvas?.height,\n elements: frame.elements,\n },\n })\n return true\n } catch (err) {\n console.error(`${LOG_TAG}: native scene send failed:`, err)\n return false\n }\n }\n\n // ===========================================================================\n // Test hooks\n // ===========================================================================\n\n public _resetForTest(): void {\n this.store.clearAll()\n this.settlePending(() => true, {presented: false, superseded: false, reason: \"reset\"})\n this.presentedSub?.remove()\n this.presentedSub = null\n this.settledSub?.remove()\n this.settledSub = null\n this.settledListeners.clear()\n this.seqCounter = 0\n }\n\n public _lastFrameForTest(appId: string, view: string): FrameElement[] {\n return this.store.lastFrame(appId, view)\n }\n}\n\nconst sceneRenderer = SceneRenderer.getInstance()\nexport default sceneRenderer\nexport {SceneRenderer}\n"]}
1
+ {"version":3,"file":"SceneRenderer.js","sourceRoot":"","sources":["../../src/services/SceneRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AACjD,OAAO,YAAY,MAAM,iCAAiC,CAAA;AAC1D,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,WAAW,EAAC,MAAM,UAAU,CAAA;AACpC,OAAO,EAAC,wBAAwB,EAAC,MAAM,yBAAyB,CAAA;AAChE,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AACvC,OAAO,EACL,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,cAAc,EACd,UAAU,GAMX,MAAM,wBAAwB,CAAA;AAE/B,MAAM,OAAO,GAAG,gBAAgB,CAAA;AA2BhC,kGAAkG;AAClG,MAAM,aAAa,GAAG,GAAG,GAAG,EAAE,CAAA;AAE9B,2FAA2F;AAC3F,SAAS,YAAY,CAAC,QAAwB;IAC5C,IAAI,EAAE,GAAG,CAAC,CAAA;IACV,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,oEAAoE;QACpE,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,EAAE,IAAI,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,CAAC,UAAU,EAAE,KAAK,KAAK,SAAS,CAAC,CAAA;QACjH,IAAI,EAAE,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,IAAI;YAAE,SAAQ;QACvE,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,UAAU,IAAI,CAAC,CAAA;QACxC,2EAA2E;QAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAA;QAC/E,IAAI,QAAQ,GAAG,EAAE;YAAE,EAAE,GAAG,QAAQ,CAAA;IAClC,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC;AAED,MAAM,aAAa;IACT,MAAM,CAAC,QAAQ,GAAyB,IAAI,CAAA;IAC5C,KAAK,GAAG,IAAI,UAAU,EAAE,CAAA;IACxB,UAAU,GAAG,CAAC,CAAA;IACd,OAAO,GAAG,IAAI,GAAG,EAAoE,CAAA;IACrF,YAAY,GAAgC,IAAI,CAAA;IAChD,UAAU,GAAgC,IAAI,CAAA;IAC9C,gBAAgB,GAAG,IAAI,GAAG,EAAyB,CAAA;IAEpD,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YAC5B,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAA;QAC9C,CAAC;QACD,OAAO,aAAa,CAAC,QAAQ,CAAA;IAC/B,CAAC;IAED,8EAA8E;IAC9E,eAAe;IACf,8EAA8E;IAE9E;;;OAGG;IACI,mBAAmB;QACxB,wEAAwE;QACxE,+CAA+C;QAC/C,MAAM,KAAK,GACR,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAwB;YAC5F,eAAe,CAAC,QAAQ,EAAE,CAAC,WAAkC,CAAA;QAChE,MAAM,OAAO,GAAG,wBAAwB,CAAC,CAAC,KAAK,IAAI,WAAW,CAAC,IAAI,CAAgB,CAAC,EAAE,OAAO,CAAA;QAC7F,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QACzB,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,UAAU,EAAE,KAAK,IAAI,gBAAgB,CAAC,UAAU,EAAE,CAAC,cAAc;YACjG,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,UAAU,EAAE,MAAM,IAAI,gBAAgB,CAAC,UAAU,EAAE,CAAC,eAAe,IAAI,GAAG;YAC5G,WAAW,EAAE,OAAO,CAAC,WAAW,KAAK,IAAI;YACzC,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,CAAC;YAC7C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,CAAC;YAC/C,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK;YACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,KAAK;YACnC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;YAC7B,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,CAAC;YAE7C,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,KAAK;SAC9C,CAAA;IACH,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,8EAA8E;IAE9E;;;;OAIG;IACI,mBAAmB,CAAC,MAAoB,EAAE,IAA8B;QAC7E,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAA;QACpB,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAA;QACrB,MAAM,SAAS,GAAG,MAAM,CAAC,SAAuC,CAAA;QAEhE,QAAQ,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1B,KAAK,WAAW;gBACd,OAAO;oBACL;wBACE,IAAI,EAAE,MAAM;wBACZ,EAAE,EAAE,YAAY;wBAChB,GAAG,EAAE,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAC;wBAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;wBAC/B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAC,KAAK,EAAE,EAAC,SAAS,EAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC3C;iBACF,CAAA;YACH,KAAK,kBAAkB,CAAC,CAAC,CAAC;gBACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;gBAC9B,OAAO;oBACL;wBACE,IAAI,EAAE,MAAM;wBACZ,EAAE,EAAE,YAAY;wBAChB,GAAG,EAAE,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAC;wBAChC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;wBAClC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAC,KAAK,EAAE,EAAC,SAAS,EAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC3C;oBACD;wBACE,IAAI,EAAE,MAAM;wBACZ,EAAE,EAAE,aAAa;wBACjB,GAAG,EAAE,EAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC,EAAC;wBACvC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;wBACrC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAC,KAAK,EAAE,EAAC,SAAS,EAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC3C;iBACF,CAAA;YACH,CAAC;YACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;gBAC9C,OAAO;oBACL,EAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAC;oBACvG;wBACE,IAAI,EAAE,MAAM;wBACZ,EAAE,EAAE,YAAY;wBAChB,GAAG,EAAE,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,EAAC;wBAC3C,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;wBAC/B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAC,KAAK,EAAE,EAAC,SAAS,EAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC3C;iBACF,CAAA;YACH,CAAC;YACD,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,mEAAmE;gBACnE,qEAAqE;gBACrE,sEAAsE;gBACtE,MAAM,CAAC,GAAI,MAAM,CAAC,KAA4B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;gBAClE,MAAM,CAAC,GAAI,MAAM,CAAC,MAA6B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;gBACnE,OAAO;oBACL;wBACE,IAAI,EAAE,OAAO;wBACb,EAAE,EAAE,WAAW;wBACf,GAAG,EAAE;4BACH,CAAC,EAAG,MAAM,CAAC,CAAwB,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;4BAC3E,CAAC,EAAG,MAAM,CAAC,CAAwB,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;4BAC3E,CAAC;4BACD,CAAC;yBACF;wBACD,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;qBAChC;iBACF,CAAA;YACH,CAAC;YACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACvB,MAAM,MAAM,GAAI,MAAM,CAAC,WAAkC,IAAI,CAAC,CAAA;gBAC9D,MAAM,MAAM,GAAI,MAAM,CAAC,YAAmC,IAAI,CAAC,CAAA;gBAC/D,OAAO;oBACL;wBACE,IAAI,EAAE,MAAM;wBACZ,EAAE,EAAE,WAAW;wBACf,GAAG,EAAE;4BACH,CAAC,EAAG,MAAM,CAAC,CAAwB,IAAI,CAAC;4BACxC,CAAC,EAAG,MAAM,CAAC,CAAwB,IAAI,CAAC;4BACxC,CAAC,EAAG,MAAM,CAAC,KAA4B,IAAI,CAAC;4BAC5C,CAAC,EAAG,MAAM,CAAC,MAA6B,IAAI,CAAC;yBAC9C;wBACD,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;wBAC/B,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,EAAC,KAAK,EAAE,EAAC,MAAM,EAAE,MAAM,EAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;qBACvD;iBACF,CAAA;YACH,CAAC;YACD;gBACE,OAAO,IAAI,CAAA;QACf,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,OAAO;IACP,8EAA8E;IAE9E;;;;;OAKG;IACI,SAAS,CAAC,KAAa,EAAE,IAA0B,EAAE,QAA6B;QACvF,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAA;QACvC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAC,IAAI,EAAE,YAAY,EAAC,CAAA;QAEtC,sEAAsE;QACtE,0EAA0E;QAC1E,yEAAyE;QACzE,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAE5D,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,EAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;YAC1D,OAAO,EAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAA;QACpD,CAAC;QAED,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,IAAI,EAAE,gBAAgB,CAAC,UAAU,EAAE,CAAC,CAAA;QAC7E,0EAA0E;QAC1E,yEAAyE;QACzE,8BAA8B;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAClG,MAAM,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAC,GAAG,SAAS,CAC3C,SAAS,EACT,SAAS,CAAC,QAAQ,EAClB,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CACxC,CAAA;QACD,MAAM,MAAM,GAAG,EAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAA;QACvD,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAE9C,MAAM,KAAK,GAAe;YACxB,KAAK;YACL,IAAI;YACJ,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC;YAChD,GAAG,EAAE,EAAE,IAAI,CAAC,UAAU;YACtB,QAAQ,EAAE,MAAM;YAChB,OAAO;YACP,MAAM;SACP,CAAA;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAClC,OAAO;YACL,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI;YACJ,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;SACpD,CAAA;IACH,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,KAAa,EAAE,IAA0B;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QACzE,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAA;QACxB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QACrB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,0FAA0F;IACnF,QAAQ,CAAC,KAAa;QAC3B,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACvB,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,EAAE,EAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAC,CAAC,CAAA;IACvG,CAAC;IAED,8EAA8E;IAC9E,4DAA4D;IAC5D,8EAA8E;IAE9E;;;;OAIG;IACI,aAAa,CAAC,GAAW,EAAE,SAAiB,EAAE,KAAa;QAChE,IAAI,CAAC,uBAAuB,EAAE,CAAA;QAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;gBACpC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;oBAAE,OAAO,CAAC,EAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,EAAC,CAAC,CAAA;YAChH,CAAC,EAAE,SAAS,CAAC,CAAA;YACb,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE;gBACpB,KAAK;gBACL,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;oBACb,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;oBAC3B,OAAO,CAAC,CAAC,CAAC,CAAA;gBACZ,CAAC;aACF,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;IAEO,uBAAuB;QAC7B,IAAI,IAAI,CAAC,YAAY;YAAE,OAAM;QAC7B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE;YAC9E,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;QAC9E,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACI,SAAS,CAAC,QAA+B;QAC9C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,GAAG,YAAY,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;gBACpE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAA;gBACtE,IAAI,GAAG,IAAI,CAAC;oBAAE,OAAM;gBACpB,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC;oBAAE,OAAO,CAAC,GAAG,CAAC,CAAA;YAChE,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,EAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAC,CAAA;IAC/D,CAAC;IAED,6FAA6F;IACrF,gBAAgB,CAAC,GAAW,EAAE,KAAa,EAAE,SAAkB,EAAE,MAAe;QACtF,IAAI,CAAC,aAAa,CAChB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,EAClB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,KAAK,GAAG;YACP,CAAC,CAAC,EAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAC;YACxC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK;gBACjB,CAAC,CAAC,EAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAC;gBACvC,CAAC,CAAC,EAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,KAAK,EAAE,EAAC,CAC/E,CAAA;IACH,CAAC;IAEO,aAAa,CACnB,KAAmD,EACnD,OAAqF;QAErF,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;gBAAE,SAAQ;YAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACxB,CAAC,CAAC,OAAO,CAAC,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACI,aAAa,CAAC,KAAa,EAAE,IAA0B;QAC5D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IACpC,CAAC;IAED;;;;;OAKG;IACI,aAAa,CAAC,IAA0B;QAC7C,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;QAC9B,yDAAyD;QACzD,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAC,CAAC,CAAA;IACzF,CAAC;IAED,2FAA2F;IACnF,SAAS,CAAC,KAAiB;QACjC,2EAA2E;QAC3E,kEAAkE;QAClE,MAAM,WAAW,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,YAAY,KAAK,IAAI,CAAA;QACpE,IAAI,WAAW,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;YAAE,OAAO,KAAK,CAAA;QAC9D,IAAI,CAAC;YACH,wEAAwE;YACxE,uEAAuE;YACvE,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAA;YACvC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI;gBAAE,KAAK,GAAG,EAAC,GAAG,KAAK,EAAE,MAAM,EAAE,EAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,EAAC,CAAA;YAC/F,uEAAuE;YACvE,mDAAmD;YACnD,KAAK,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,EAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC9F,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,6BAA6B,EAAE,GAAG,CAAC,CAAA;YAC7D,CAAC,CAAC,CAAA;YAEF,yEAAyE;YACzE,uEAAuE;YACvE,gEAAgE;YAChE,mEAAmE;YACnE,eAAe,CAAC,QAAQ,EAAE,CAAC,eAAe,CAAC;gBACzC,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE;oBACN,UAAU,EAAE,OAAO;oBACnB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK;oBAC1B,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM;oBAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;iBACzB;aACF,CAAC,CAAA;YACF,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,6BAA6B,EAAE,GAAG,CAAC,CAAA;YAC3D,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,aAAa;IACb,8EAA8E;IAEvE,aAAa;QAClB,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAA;QACrB,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAC,CAAC,CAAA;QACtF,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,CAAA;QAC3B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QACxB,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,CAAA;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QACtB,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAA;QAC7B,IAAI,CAAC,UAAU,GAAG,CAAC,CAAA;IACrB,CAAC;IAEM,iBAAiB,CAAC,KAAa,EAAE,IAAY;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAC1C,CAAC;;AAGH,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,EAAE,CAAA;AACjD,eAAe,aAAa,CAAA;AAC5B,OAAO,EAAC,aAAa,EAAC,CAAA","sourcesContent":["/**\n * SceneRenderer — the device-aware boundary of the scene display pipeline.\n *\n * LocalDisplayManager owns WHO may draw (arbitration/boot/expiry); this service\n * owns WHAT gets sent for a scene: capabilities lookup, legacy-layout→scene\n * conversion (sugar and old bundles ride the same pipeline), process → diff →\n * SceneFrame emission, retained-scene replay, and the degrade branch for\n * devices that can't position elements.\n *\n * Everything device-specific here is DATA (capability fields, display profile);\n * the code paths are generic. Spec: notes/miniapp-display-render-implementation-spec.md\n */\n\nimport displayProcessor from \"./DisplayProcessor\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport {useDisplayStore} from \"../stores/display\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {DeviceTypes} from \"../types\"\nimport {getEffectiveCapabilities} from \"./effectiveCapabilities\"\nimport {BgTimer} from \"../utils/timers\"\nimport {\n degradeScene,\n diffScene,\n processScene,\n resolveAnchors,\n SceneStore,\n type FrameElement,\n type SceneBreakMode,\n type SceneDisplayCapabilities,\n type SceneElementInput,\n type SceneFrame,\n} from \"../utils/display/scene\"\n\nconst LOG_TAG = \"SCENE_RENDERER\"\n\n/** Legacy layout shape (matches DisplayPayload.layout). */\nexport type LegacyLayout = {layoutType: string; [key: string]: unknown}\n\nexport type SceneEmitResult =\n /**\n * A SceneFrame was committed. `sent` is false when the frame stayed\n * phone-side (dashboard covering main). `settleMs` is how long the device\n * keeps animating after it has the frame: the longest transition among the\n * elements this frame changed, 0 on devices that jump.\n */\n | {kind: \"scene\"; degraded: boolean; dropped: string[]; seq: number; sent: boolean; settleMs: number}\n /** Degrade path: caller routes this legacy layout through the existing pipeline (null layout ⇒ clear). */\n | {kind: \"legacy\"; layout: LegacyLayout | null; degraded: boolean; dropped: string[]}\n /** No display capabilities on the connected device. */\n | {kind: \"no-display\"}\n\n/** How a frame ended up, as reported by native (or inferred from a later frame's report). */\nexport interface ScenePresentation {\n /** The device had the frame's content (this frame or a later one that folded it in). */\n presented: boolean\n /** A later frame's report settled this one. */\n superseded: boolean\n reason?: string\n}\n\n/** Longest geometry tween (255 frames at ~30 fps); rotation uses a separate millisecond clock. */\nconst MAX_SETTLE_MS = 255 * 33\n\n/** Longest transition among the elements that actually change on the device this frame. */\nfunction settleTimeOf(elements: FrameElement[]): number {\n let ms = 0\n for (const el of elements) {\n // Entering and leaving elements tween too, from or to their offset.\n const edge = el.transition?.exit !== undefined || (el.change === \"created\" && el.transition?.enter !== undefined)\n if (el.change !== \"updated\" && el.change !== \"moved\" && !edge) continue\n const t = el.transition?.durationMs ?? 0\n // Rotation has its own millisecond clock, beyond the geometry frame limit.\n const duration = Math.min(t, el.rotation !== undefined ? 65535 : MAX_SETTLE_MS)\n if (duration > ms) ms = duration\n }\n return ms\n}\n\nclass SceneRenderer {\n private static instance: SceneRenderer | null = null\n private store = new SceneStore()\n private seqCounter = 0\n private pending = new Map<number, {appId: string; resolve: (p: ScenePresentation) => void}>()\n private presentedSub: {remove: () => void} | null = null\n private settledSub: {remove: () => void} | null = null\n private settledListeners = new Set<(seq: number) => void>()\n\n public static getInstance(): SceneRenderer {\n if (!SceneRenderer.instance) {\n SceneRenderer.instance = new SceneRenderer()\n }\n return SceneRenderer.instance\n }\n\n // ===========================================================================\n // Capabilities\n // ===========================================================================\n\n /**\n * Scene capabilities of the currently-selected device, or null when it has no\n * display block. Read on demand — same model source the CONNECT_ACK uses.\n */\n public currentCapabilities(): SceneDisplayCapabilities | null {\n // dev read these via the (since-removed) settings/glassesStatus runtime\n // hooks; engine reads its own stores directly.\n const model =\n (useSettingsStore.getState().getSetting(SETTINGS.default_wearable.key) as string | undefined) ??\n (useGlassesStore.getState().deviceModel as string | undefined)\n const display = getEffectiveCapabilities((model ?? DeviceTypes.NONE) as DeviceTypes)?.display\n if (!display) return null\n return {\n width: display.width ?? display.resolution?.width ?? displayProcessor.getProfile().displayWidthPx,\n height: display.height ?? display.resolution?.height ?? displayProcessor.getProfile().displayHeightPx ?? 288,\n canPosition: display.canPosition === true,\n maxTextElements: display.maxTextElements ?? 6,\n maxImageElements: display.maxImageElements ?? 0,\n maxImagePx: display.maxImagePx,\n shapes: display.shapes ?? [],\n animation: display.animation ?? false,\n rotation: display.rotation ?? false,\n depth: display.depth ?? false,\n intensityLevels: display.intensityLevels ?? 2,\n\n partialUpdate: display.partialUpdate ?? false,\n }\n }\n\n // ===========================================================================\n // Legacy layout → scene conversion (sugar + old bundles on the scene path)\n // ===========================================================================\n\n /**\n * Convert a legacy layout into scene elements for a positioning device.\n * Returns null for layouts that must stay on the legacy path (dashboard_card,\n * clear_view, unknown types). Sugar semantics: full-canvas / split boxes.\n */\n public convertLegacyLayout(layout: LegacyLayout, caps: SceneDisplayCapabilities): SceneElementInput[] | null {\n const W = caps.width\n const H = caps.height\n const breakMode = layout.breakMode as SceneBreakMode | undefined\n\n switch (layout.layoutType) {\n case \"text_wall\":\n return [\n {\n type: \"text\",\n id: \"sugar:wall\",\n box: {x: 0, y: 0, w: W, h: H},\n text: String(layout.text ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n ]\n case \"double_text_wall\": {\n const half = Math.floor(W / 2)\n return [\n {\n type: \"text\",\n id: \"sugar:left\",\n box: {x: 0, y: 0, w: half, h: H},\n text: String(layout.topText ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n {\n type: \"text\",\n id: \"sugar:right\",\n box: {x: half, y: 0, w: W - half, h: H},\n text: String(layout.bottomText ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n ]\n }\n case \"reference_card\": {\n const titleH = Math.max(24, Math.floor(H / 5))\n return [\n {type: \"text\", id: \"sugar:title\", box: {x: 0, y: 0, w: W, h: titleH}, text: String(layout.title ?? \"\")},\n {\n type: \"text\",\n id: \"sugar:body\",\n box: {x: 0, y: titleH, w: W, h: H - titleH},\n text: String(layout.text ?? \"\"),\n ...(breakMode ? {style: {breakMode}} : {}),\n },\n ]\n }\n case \"bitmap_view\": {\n // Dimensionless legacy bitmaps historically landed in the device's\n // default ~200×100 container (G2 seeded 200×100 at 188,44) — NOT the\n // full canvas, which would tile a small image across the whole frame.\n const w = (layout.width as number | undefined) ?? Math.min(200, W)\n const h = (layout.height as number | undefined) ?? Math.min(100, H)\n return [\n {\n type: \"image\",\n id: \"sugar:bmp\",\n box: {\n x: (layout.x as number | undefined) ?? Math.max(0, Math.round((W - w) / 2)),\n y: (layout.y as number | undefined) ?? Math.max(0, Math.round((H - h) / 4)),\n w,\n h,\n },\n data: String(layout.data ?? \"\"),\n },\n ]\n }\n case \"positioned_text\": {\n const border = (layout.borderWidth as number | undefined) ?? 0\n const radius = (layout.borderRadius as number | undefined) ?? 0\n return [\n {\n type: \"text\",\n id: \"sugar:pos\",\n box: {\n x: (layout.x as number | undefined) ?? 0,\n y: (layout.y as number | undefined) ?? 0,\n w: (layout.width as number | undefined) ?? W,\n h: (layout.height as number | undefined) ?? H,\n },\n text: String(layout.text ?? \"\"),\n ...(border || radius ? {style: {border, radius}} : {}),\n },\n ]\n }\n default:\n return null\n }\n }\n\n // ===========================================================================\n // Emit\n // ===========================================================================\n\n /**\n * Process + diff + send a scene for (app, view). On non-positioning devices\n * this returns the degraded legacy layout instead — the caller routes it\n * through the existing DisplayProcessor path (which wraps it, keeping G1/Z100\n * output byte-identical to today).\n */\n public emitScene(appId: string, view: \"main\" | \"dashboard\", elements: SceneElementInput[]): SceneEmitResult {\n const caps = this.currentCapabilities()\n if (!caps) return {kind: \"no-display\"}\n\n // Corner-anchored boxes resolve against the REAL canvas before either\n // branch: processScene clamps concrete coords, and degradeScene's reading\n // order sorts by resolved y (a bottom-anchored y:0 must not sort first).\n elements = resolveAnchors(elements, caps.width, caps.height)\n\n if (!caps.canPosition) {\n const {layout, degraded, dropped} = degradeScene(elements)\n return {kind: \"legacy\", layout, degraded, dropped}\n }\n\n const processed = processScene(elements, caps, displayProcessor.getProfile())\n // A stale baseline means the glasses were cleared since the last commit —\n // diff from empty so everything repaints (the retained frame stays valid\n // as the replay source only).\n const prevFrame = this.store.isBaselineStale(appId, view) ? [] : this.store.lastFrame(appId, view)\n const {elements: diffed, removed} = diffScene(\n prevFrame,\n processed.elements,\n this.store.nextSyntheticId(appId, view),\n )\n const canvas = {width: caps.width, height: caps.height}\n this.store.commit(appId, view, diffed, canvas)\n\n const frame: SceneFrame = {\n appId,\n view,\n sceneEpoch: this.store.currentEpoch(appId, view),\n seq: ++this.seqCounter,\n elements: diffed,\n removed,\n canvas,\n }\n const sent = this.sendFrame(frame)\n return {\n kind: \"scene\",\n degraded: processed.degraded,\n dropped: processed.dropped,\n seq: frame.seq,\n sent,\n settleMs: caps.animation ? settleTimeOf(diffed) : 0,\n }\n }\n\n /**\n * Re-emit an app's retained scene (reconnect / arbitration restore). Replay\n * is always create-based — the device rebuilds from scratch (spec §4).\n * Returns false when nothing is retained.\n */\n public replayApp(appId: string, view: \"main\" | \"dashboard\"): boolean {\n const frame = this.store.buildReplayFrame(appId, view, ++this.seqCounter)\n if (!frame) return false\n this.sendFrame(frame)\n return true\n }\n\n /** Forget an app's retained scenes (app stop). Frames still in flight will never show. */\n public clearApp(appId: string): void {\n this.store.clear(appId)\n this.settlePending((p) => p.appId === appId, {presented: false, superseded: true, reason: \"cleared\"})\n }\n\n // ===========================================================================\n // Presentation — native reports when the device has a frame\n // ===========================================================================\n\n /**\n * Resolves once native reports frame `seq` on the device, or a later frame\n * that folded it in. Never rejects; `timeoutMs` bounds a frame the device\n * never acknowledges (link stalls, page rebuilding).\n */\n public whenPresented(seq: number, timeoutMs: number, appId: string): Promise<ScenePresentation> {\n this.ensurePresentedListener()\n return new Promise((resolve) => {\n const timer = BgTimer.setTimeout(() => {\n if (this.pending.delete(seq)) resolve({presented: false, superseded: false, reason: \"no presentation report\"})\n }, timeoutMs)\n this.pending.set(seq, {\n appId,\n resolve: (p) => {\n BgTimer.clearTimeout(timer)\n resolve(p)\n },\n })\n })\n }\n\n private ensurePresentedListener(): void {\n if (this.presentedSub) return\n this.presentedSub = BluetoothSdk.addListener(\"scene_frame_presented\", (event) => {\n this.onFramePresented(event.seq, event.appId, event.presented, event.reason)\n })\n }\n\n /**\n * Fires with the host frame number once the glasses report that every\n * animation the frame started has ended (Glassly CFW revision 27+). The\n * device echoes the low 16 bits of the seq; the full number is recovered\n * against the latest frame sent.\n */\n public onSettled(listener: (seq: number) => void): {remove: () => void} {\n this.settledListeners.add(listener)\n if (!this.settledSub) {\n this.settledSub = BluetoothSdk.addListener(\"scene_settled\", (event) => {\n const seq = this.seqCounter - ((this.seqCounter - event.tag) & 0xffff)\n if (seq <= 0) return\n for (const settled of [...this.settledListeners]) settled(seq)\n })\n }\n return {remove: () => this.settledListeners.delete(listener)}\n }\n\n /** A report for `seq` settles it and every earlier frame (folded in by the native pacer). */\n private onFramePresented(seq: number, appId: string, presented: boolean, reason?: string): void {\n this.settlePending(\n (p, s) => s <= seq,\n (p, s) =>\n s === seq\n ? {presented, superseded: false, reason}\n : p.appId === appId\n ? {presented, superseded: true, reason}\n : {presented: false, superseded: true, reason: `superseded by ${appId}`},\n )\n }\n\n private settlePending(\n match: (p: {appId: string}, seq: number) => boolean,\n outcome: ScenePresentation | ((p: {appId: string}, seq: number) => ScenePresentation),\n ): void {\n for (const [seq, p] of [...this.pending]) {\n if (!match(p, seq)) continue\n this.pending.delete(seq)\n p.resolve(typeof outcome === \"function\" ? outcome(p, seq) : outcome)\n }\n }\n\n /**\n * Reset the diff baseline for (app, view) without touching the device.\n * Used when a legacy layout bypasses the scene path (clear_view /\n * dashboard_card / unknown layoutTypes go to native raw): after that, the\n * retained frame no longer matches the glasses, so the next render() must\n * diff from empty — otherwise \"unchanged\" elements would be skipped and\n * never repainted.\n */\n public resetBaseline(appId: string, view: \"main\" | \"dashboard\"): void {\n this.store.commit(appId, view, [])\n }\n\n /**\n * A system-level clear wiped the view (durationMs expiry, unmount fallback,\n * boot-window expiry): mark every app's baseline for that view stale. Unlike\n * resetBaseline this KEEPS the retained frames — they remain the\n * restore-as-replay source; only incremental diffing restarts from empty.\n */\n public markViewStale(view: \"main\" | \"dashboard\"): void {\n this.store.markViewStale(view)\n // Whatever was in flight for the view got wiped with it.\n this.settlePending(() => true, {presented: false, superseded: true, reason: \"cleared\"})\n }\n\n /** Returns false when the frame stayed phone-side (no presentation report will follow). */\n private sendFrame(frame: SceneFrame): boolean {\n // Single native view: only the visible surface's frames go out. Suppressed\n // main frames stay committed; setOverlayOwner(null) replays them.\n const overlayHeld = useDisplayStore.getState().overlayOwner !== null\n if (overlayHeld !== (frame.view === \"dashboard\")) return false\n try {\n // Frames carry the canvas they were laid out against; only a frame from\n // a path that predates the tag falls back to the current capabilities.\n const caps = this.currentCapabilities()\n if (!frame.canvas && caps) frame = {...frame, canvas: {width: caps.width, height: caps.height}}\n // sendDisplayEvent hook -> direct BluetoothSdk.displayEvent (the exact\n // wiring the deleted MantleManager hook provided).\n void Promise.resolve(BluetoothSdk.displayEvent({view: frame.view, scene: frame})).catch((err) => {\n console.error(`${LOG_TAG}: native scene send failed:`, err)\n })\n\n // Keep the phone-side glasses mirror on the same frame as native. Legacy\n // layouts update this store in LocalDisplayManager.sendToNative; scene\n // frames bypass that method, so without an explicit write every\n // positioning device (including G2) leaves the mirror blank/stale.\n useDisplayStore.getState().setDisplayEvent({\n view: frame.view,\n layout: {\n layoutType: \"scene\",\n appId: frame.appId,\n view: frame.view,\n sceneEpoch: frame.sceneEpoch,\n width: frame.canvas?.width,\n height: frame.canvas?.height,\n elements: frame.elements,\n },\n })\n return true\n } catch (err) {\n console.error(`${LOG_TAG}: native scene send failed:`, err)\n return false\n }\n }\n\n // ===========================================================================\n // Test hooks\n // ===========================================================================\n\n public _resetForTest(): void {\n this.store.clearAll()\n this.settlePending(() => true, {presented: false, superseded: false, reason: \"reset\"})\n this.presentedSub?.remove()\n this.presentedSub = null\n this.settledSub?.remove()\n this.settledSub = null\n this.settledListeners.clear()\n this.seqCounter = 0\n }\n\n public _lastFrameForTest(appId: string, view: string): FrameElement[] {\n return this.store.lastFrame(appId, view)\n }\n}\n\nconst sceneRenderer = SceneRenderer.getInstance()\nexport default sceneRenderer\nexport {SceneRenderer}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Screenshot fixture — dresses Phone Mode up as a paired Even Realities G2
3
+ * with a Meta Neural Band and an R1 ring, for the store-screenshot build only
4
+ * (EXPO_PUBLIC_SCREENSHOT_MODE=1, set by scripts/maestro-screenshots-*.mjs
5
+ * for its own Release build and never by a shipped one).
6
+ *
7
+ * The fixture is applied where hosts READ — engine.pairing.identity and
8
+ * engine.glasses.status/info/controllers.* — not where native writes: the
9
+ * Simulated driver keeps running underneath (so the mirror has frames),
10
+ * nothing is persisted, and native never learns of the fixture, so no
11
+ * reconnect, demotion or driver path can act on it. It is active only while
12
+ * the underlying identity is the simulated device: home enters Phone Mode as
13
+ * usual (identity `none` passes through untouched), the simulated pairing
14
+ * then flips the read-models to the fixture, and real glasses on a fixture
15
+ * build stay real.
16
+ */
17
+ import type { ControllerStatus, PairedController } from "@glassly/bluetooth-sdk";
18
+ import { DeviceTypes } from "../types/enums";
19
+ import type { IdentitySnapshot } from "./PairingIdentity";
20
+ export declare function isScreenshotMode(): boolean;
21
+ export declare const FIXTURE_GLASSES: {
22
+ readonly model: DeviceTypes.G2;
23
+ readonly name: "Even G2_7F3A";
24
+ readonly battery: 87;
25
+ readonly caseBattery: 100;
26
+ readonly signal: -58;
27
+ };
28
+ interface FixtureController extends PairedController {
29
+ batteryLevel: number;
30
+ }
31
+ export declare const FIXTURE_CONTROLLERS: readonly FixtureController[];
32
+ /** The fixture dresses Phone Mode only. */
33
+ export declare function fixtureActive(identity: IdentitySnapshot): boolean;
34
+ export declare function fixtureIdentity(identity: IdentitySnapshot): IdentitySnapshot;
35
+ interface StatusLike {
36
+ battery: number;
37
+ charging: boolean;
38
+ case: {
39
+ battery: number;
40
+ charging: boolean;
41
+ open: boolean;
42
+ removed: boolean;
43
+ };
44
+ signal: number;
45
+ }
46
+ export declare function fixtureStatus<T extends StatusLike>(status: T, identity: IdentitySnapshot): T;
47
+ interface InfoLike {
48
+ model: string;
49
+ firmwareUpdateAvailable: boolean;
50
+ }
51
+ export declare function fixtureInfo<T extends InfoLike>(info: T, identity: IdentitySnapshot): T;
52
+ /** Fixture controllers first, then any real records that are not the fixture's. */
53
+ export declare function fixtureControllerList(list: PairedController[], identity: IdentitySnapshot): PairedController[];
54
+ export declare function fixtureControllerStatuses(statuses: Record<string, ControllerStatus>, identity: IdentitySnapshot): Record<string, ControllerStatus>;
55
+ export {};
56
+ //# sourceMappingURL=ScreenshotFixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScreenshotFixture.d.ts","sourceRoot":"","sources":["../../src/services/ScreenshotFixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAE9E,OAAO,EAAkB,WAAW,EAAC,MAAM,gBAAgB,CAAA;AAC3D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAA;AAEvD,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,eAAO,MAAM,eAAe;;;;;;CAMlB,CAAA;AAEV,UAAU,iBAAkB,SAAQ,gBAAgB;IAClD,YAAY,EAAE,MAAM,CAAA;CACrB;AAKD,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAG3D,CAAA;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAEjE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CAG5E;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,CAAA;IAC3E,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,GAAG,CAAC,CAS5F;AAED,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,uBAAuB,EAAE,OAAO,CAAA;CACjC;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,GAAG,CAAC,CAGtF;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAK9G;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC1C,QAAQ,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAkBlC"}
@@ -0,0 +1,72 @@
1
+ import { ControllerTypes, DeviceTypes } from "../types/enums";
2
+ export function isScreenshotMode() {
3
+ return process.env.EXPO_PUBLIC_SCREENSHOT_MODE === "1";
4
+ }
5
+ export const FIXTURE_GLASSES = {
6
+ model: DeviceTypes.G2,
7
+ name: "Even G2_7F3A",
8
+ battery: 87,
9
+ caseBattery: 100,
10
+ signal: -58,
11
+ };
12
+ // The R1's id is its ring id (from the BLE name); the band's is an opaque
13
+ // peripheral id. Both are stable fixtures so flows can target
14
+ // `controller-tile-<id>`.
15
+ export const FIXTURE_CONTROLLERS = [
16
+ { id: "7C1A2B9E", model: ControllerTypes.NEURAL_BAND, name: "Meta Neural Band", batteryLevel: 73 },
17
+ { id: "CEC5BA", model: ControllerTypes.R1, name: "CEC5BA", batteryLevel: 58 },
18
+ ];
19
+ /** The fixture dresses Phone Mode only. */
20
+ export function fixtureActive(identity) {
21
+ return isScreenshotMode() && identity.kind === "paired" && identity.model.includes(DeviceTypes.SIMULATED);
22
+ }
23
+ export function fixtureIdentity(identity) {
24
+ if (!fixtureActive(identity))
25
+ return identity;
26
+ return { kind: "paired", model: FIXTURE_GLASSES.model, name: FIXTURE_GLASSES.name };
27
+ }
28
+ export function fixtureStatus(status, identity) {
29
+ if (!fixtureActive(identity))
30
+ return status;
31
+ return {
32
+ ...status,
33
+ battery: FIXTURE_GLASSES.battery,
34
+ charging: false,
35
+ case: { battery: FIXTURE_GLASSES.caseBattery, charging: false, open: false, removed: false },
36
+ signal: FIXTURE_GLASSES.signal,
37
+ };
38
+ }
39
+ export function fixtureInfo(info, identity) {
40
+ if (!fixtureActive(identity))
41
+ return info;
42
+ return { ...info, model: FIXTURE_GLASSES.model, firmwareUpdateAvailable: false };
43
+ }
44
+ /** Fixture controllers first, then any real records that are not the fixture's. */
45
+ export function fixtureControllerList(list, identity) {
46
+ if (!fixtureActive(identity))
47
+ return list;
48
+ const fixture = FIXTURE_CONTROLLERS.map(({ id, model, name }) => ({ id, model, name }));
49
+ const ids = new Set(fixture.map((record) => record.id));
50
+ return [...fixture, ...list.filter((record) => !ids.has(record.id))];
51
+ }
52
+ export function fixtureControllerStatuses(statuses, identity) {
53
+ if (!fixtureActive(identity))
54
+ return statuses;
55
+ const result = { ...statuses };
56
+ for (const { id, model, name, batteryLevel } of FIXTURE_CONTROLLERS) {
57
+ result[id] = {
58
+ id,
59
+ model,
60
+ name,
61
+ connected: true,
62
+ fullyBooted: true,
63
+ searching: false,
64
+ batteryLevel,
65
+ charging: false,
66
+ macAddress: "",
67
+ signalStrength: -62,
68
+ };
69
+ }
70
+ return result;
71
+ }
72
+ //# sourceMappingURL=ScreenshotFixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScreenshotFixture.js","sourceRoot":"","sources":["../../src/services/ScreenshotFixture.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Screenshot fixture — dresses Phone Mode up as a paired Even Realities G2\n * with a Meta Neural Band and an R1 ring, for the store-screenshot build only\n * (EXPO_PUBLIC_SCREENSHOT_MODE=1, set by scripts/maestro-screenshots-*.mjs\n * for its own Release build and never by a shipped one).\n *\n * The fixture is applied where hosts READ — engine.pairing.identity and\n * engine.glasses.status/info/controllers.* — not where native writes: the\n * Simulated driver keeps running underneath (so the mirror has frames),\n * nothing is persisted, and native never learns of the fixture, so no\n * reconnect, demotion or driver path can act on it. It is active only while\n * the underlying identity is the simulated device: home enters Phone Mode as\n * usual (identity `none` passes through untouched), the simulated pairing\n * then flips the read-models to the fixture, and real glasses on a fixture\n * build stay real.\n */\nimport type {ControllerStatus, PairedController} from \"@glassly/bluetooth-sdk\"\n\nimport {ControllerTypes, DeviceTypes} from \"../types/enums\"\nimport type {IdentitySnapshot} from \"./PairingIdentity\"\n\nexport function isScreenshotMode(): boolean {\n return process.env.EXPO_PUBLIC_SCREENSHOT_MODE === \"1\"\n}\n\nexport const FIXTURE_GLASSES = {\n model: DeviceTypes.G2,\n name: \"Even G2_7F3A\",\n battery: 87,\n caseBattery: 100,\n signal: -58,\n} as const\n\ninterface FixtureController extends PairedController {\n batteryLevel: number\n}\n\n// The R1's id is its ring id (from the BLE name); the band's is an opaque\n// peripheral id. Both are stable fixtures so flows can target\n// `controller-tile-<id>`.\nexport const FIXTURE_CONTROLLERS: readonly FixtureController[] = [\n {id: \"7C1A2B9E\", model: ControllerTypes.NEURAL_BAND, name: \"Meta Neural Band\", batteryLevel: 73},\n {id: \"CEC5BA\", model: ControllerTypes.R1, name: \"CEC5BA\", batteryLevel: 58},\n]\n\n/** The fixture dresses Phone Mode only. */\nexport function fixtureActive(identity: IdentitySnapshot): boolean {\n return isScreenshotMode() && identity.kind === \"paired\" && identity.model.includes(DeviceTypes.SIMULATED)\n}\n\nexport function fixtureIdentity(identity: IdentitySnapshot): IdentitySnapshot {\n if (!fixtureActive(identity)) return identity\n return {kind: \"paired\", model: FIXTURE_GLASSES.model, name: FIXTURE_GLASSES.name}\n}\n\ninterface StatusLike {\n battery: number\n charging: boolean\n case: {battery: number; charging: boolean; open: boolean; removed: boolean}\n signal: number\n}\n\nexport function fixtureStatus<T extends StatusLike>(status: T, identity: IdentitySnapshot): T {\n if (!fixtureActive(identity)) return status\n return {\n ...status,\n battery: FIXTURE_GLASSES.battery,\n charging: false,\n case: {battery: FIXTURE_GLASSES.caseBattery, charging: false, open: false, removed: false},\n signal: FIXTURE_GLASSES.signal,\n }\n}\n\ninterface InfoLike {\n model: string\n firmwareUpdateAvailable: boolean\n}\n\nexport function fixtureInfo<T extends InfoLike>(info: T, identity: IdentitySnapshot): T {\n if (!fixtureActive(identity)) return info\n return {...info, model: FIXTURE_GLASSES.model, firmwareUpdateAvailable: false}\n}\n\n/** Fixture controllers first, then any real records that are not the fixture's. */\nexport function fixtureControllerList(list: PairedController[], identity: IdentitySnapshot): PairedController[] {\n if (!fixtureActive(identity)) return list\n const fixture = FIXTURE_CONTROLLERS.map(({id, model, name}) => ({id, model, name}))\n const ids = new Set(fixture.map((record) => record.id))\n return [...fixture, ...list.filter((record) => !ids.has(record.id))]\n}\n\nexport function fixtureControllerStatuses(\n statuses: Record<string, ControllerStatus>,\n identity: IdentitySnapshot,\n): Record<string, ControllerStatus> {\n if (!fixtureActive(identity)) return statuses\n const result = {...statuses}\n for (const {id, model, name, batteryLevel} of FIXTURE_CONTROLLERS) {\n result[id] = {\n id,\n model,\n name,\n connected: true,\n fullyBooted: true,\n searching: false,\n batteryLevel,\n charging: false,\n macAddress: \"\",\n signalStrength: -62,\n }\n }\n return result\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import type { ClientApp } from "../types/applet";
2
+ export type ActionError = {
3
+ code: string;
4
+ message: string;
5
+ } & Record<string, unknown>;
6
+ export type ActionOutcome = {
7
+ ok: true;
8
+ data: unknown;
9
+ } | {
10
+ ok: false;
11
+ error: ActionError;
12
+ };
13
+ export declare const actionFailure: (code: string, message: string) => ActionOutcome;
14
+ /** Installed, compatible, and declares the action. */
15
+ export declare function appDeclaresAction(app: ClientApp | undefined, actionId: string): boolean;
16
+ /**
17
+ * Gates shared by miniapp->miniapp invoke and host invoke. Declared actions are
18
+ * public by default; `visibility: "system"` ones need a system caller.
19
+ */
20
+ export declare function checkActionAccess(app: ClientApp | undefined, target: string | undefined, actionId: string | undefined, callerIsSystem: boolean): ActionError | null;
21
+ /** BgTimer's shape; passed in so this module stays loadable without the native timer. */
22
+ export interface ActionTimers {
23
+ setTimeout: (cb: () => void, ms: number) => number;
24
+ clearTimeout: (id: number) => void;
25
+ }
26
+ /** Outstanding ACTION_CALLs awaiting an ACTION_RESULT, keyed by host-generated callId. */
27
+ export declare class PendingActionCalls {
28
+ private readonly timers;
29
+ private readonly payloadTooLarge;
30
+ private readonly calls;
31
+ constructor(timers: ActionTimers, payloadTooLarge: (value: unknown) => boolean);
32
+ /** Register a call; resolves with its outcome, or ACTION_TIMEOUT. */
33
+ register(callId: string, target: string, actionId: string, timeoutMs: number): Promise<ActionOutcome>;
34
+ /** Settle from a target's ACTION_RESULT payload. Only the invoked target may resolve a call. */
35
+ handleResult(targetPackageName: string, payload: Record<string, unknown>): void;
36
+ }
37
+ //# sourceMappingURL=actionInvoke.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionInvoke.d.ts","sourceRoot":"","sources":["../../src/services/actionInvoke.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAA;AAE9C,MAAM,MAAM,WAAW,GAAG;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AACnF,MAAM,MAAM,aAAa,GAAG;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAC,GAAG;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAC,CAAA;AAEvF,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,aAAsD,CAAA;AAEpH,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIvF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,SAAS,GAAG,SAAS,EAC1B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,cAAc,EAAE,OAAO,GACtB,WAAW,GAAG,IAAI,CAyBpB;AAED,yFAAyF;AACzF,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAA;IAClD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAED,0FAA0F;AAC1F,qBAAa,kBAAkB;IAI3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAJlC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyF;gBAG5F,MAAM,EAAE,YAAY,EACpB,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO;IAG/D,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAUrG,gGAAgG;IAChG,YAAY,CAAC,iBAAiB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAyBhF"}
@@ -0,0 +1,89 @@
1
+ import { MiniappErrorCode } from "@glassly/miniapp";
2
+ export const actionFailure = (code, message) => ({ ok: false, error: { code, message } });
3
+ /** Installed, compatible, and declares the action. */
4
+ export function appDeclaresAction(app, actionId) {
5
+ if (!app)
6
+ return false;
7
+ if (app.compatibility && app.compatibility.isCompatible === false)
8
+ return false;
9
+ return (app.actions ?? []).some((a) => a.id === actionId);
10
+ }
11
+ /**
12
+ * Gates shared by miniapp->miniapp invoke and host invoke. Declared actions are
13
+ * public by default; `visibility: "system"` ones need a system caller.
14
+ */
15
+ export function checkActionAccess(app, target, actionId, callerIsSystem) {
16
+ if (!target || !app) {
17
+ return {
18
+ code: MiniappErrorCode.APP_NOT_FOUND,
19
+ message: `Miniapp not found: ${target ?? "(missing targetPackageName)"}`,
20
+ };
21
+ }
22
+ if (!actionId)
23
+ return { code: MiniappErrorCode.ACTION_NOT_FOUND, message: "invoke requires an actionId" };
24
+ if (app.compatibility && app.compatibility.isCompatible === false) {
25
+ return {
26
+ code: MiniappErrorCode.APP_NOT_COMPATIBLE,
27
+ message: `${target} is not compatible with the connected glasses`,
28
+ };
29
+ }
30
+ const declared = (app.actions ?? []).find((a) => a.id === actionId);
31
+ if (!declared) {
32
+ return { code: MiniappErrorCode.ACTION_NOT_FOUND, message: `${target} does not declare action "${actionId}"` };
33
+ }
34
+ if (declared.visibility === "system" && !callerIsSystem) {
35
+ return {
36
+ code: MiniappErrorCode.NOT_PERMITTED,
37
+ message: `Action "${actionId}" on ${target} is restricted to system apps`,
38
+ };
39
+ }
40
+ return null;
41
+ }
42
+ /** Outstanding ACTION_CALLs awaiting an ACTION_RESULT, keyed by host-generated callId. */
43
+ export class PendingActionCalls {
44
+ timers;
45
+ payloadTooLarge;
46
+ calls = new Map();
47
+ constructor(timers, payloadTooLarge) {
48
+ this.timers = timers;
49
+ this.payloadTooLarge = payloadTooLarge;
50
+ }
51
+ /** Register a call; resolves with its outcome, or ACTION_TIMEOUT. */
52
+ register(callId, target, actionId, timeoutMs) {
53
+ return new Promise((resolve) => {
54
+ const timer = this.timers.setTimeout(() => {
55
+ this.calls.delete(callId);
56
+ resolve(actionFailure(MiniappErrorCode.ACTION_TIMEOUT, `action "${actionId}" timed out after ${timeoutMs}ms`));
57
+ }, timeoutMs);
58
+ this.calls.set(callId, { target, settle: resolve, timer });
59
+ });
60
+ }
61
+ /** Settle from a target's ACTION_RESULT payload. Only the invoked target may resolve a call. */
62
+ handleResult(targetPackageName, payload) {
63
+ const callId = payload.callId;
64
+ if (!callId)
65
+ return;
66
+ const pending = this.calls.get(callId);
67
+ // callIds are guessable (timestamp + seq): a different miniapp must not spoof someone else's result.
68
+ if (!pending || pending.target !== targetPackageName)
69
+ return;
70
+ this.calls.delete(callId);
71
+ this.timers.clearTimeout(pending.timer);
72
+ if (payload.ok === true) {
73
+ if (this.payloadTooLarge(payload.result)) {
74
+ pending.settle(actionFailure(MiniappErrorCode.PAYLOAD_TOO_LARGE, "action result exceeded the 256 KB cap"));
75
+ return;
76
+ }
77
+ pending.settle({ ok: true, data: payload.result ?? null });
78
+ return;
79
+ }
80
+ pending.settle({
81
+ ok: false,
82
+ error: payload.error ?? {
83
+ code: MiniappErrorCode.INTERNAL,
84
+ message: "action handler error",
85
+ },
86
+ });
87
+ }
88
+ }
89
+ //# sourceMappingURL=actionInvoke.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionInvoke.js","sourceRoot":"","sources":["../../src/services/actionInvoke.ts"],"names":[],"mappings":"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","sourcesContent":["import {MiniappErrorCode} from \"@glassly/miniapp\"\n\nimport type {ClientApp} from \"../types/applet\"\n\nexport type ActionError = {code: string; message: string} & Record<string, unknown>\nexport type ActionOutcome = {ok: true; data: unknown} | {ok: false; error: ActionError}\n\nexport const actionFailure = (code: string, message: string): ActionOutcome => ({ok: false, error: {code, message}})\n\n/** Installed, compatible, and declares the action. */\nexport function appDeclaresAction(app: ClientApp | undefined, actionId: string): boolean {\n if (!app) return false\n if (app.compatibility && app.compatibility.isCompatible === false) return false\n return (app.actions ?? []).some((a) => a.id === actionId)\n}\n\n/**\n * Gates shared by miniapp->miniapp invoke and host invoke. Declared actions are\n * public by default; `visibility: \"system\"` ones need a system caller.\n */\nexport function checkActionAccess(\n app: ClientApp | undefined,\n target: string | undefined,\n actionId: string | undefined,\n callerIsSystem: boolean,\n): ActionError | null {\n if (!target || !app) {\n return {\n code: MiniappErrorCode.APP_NOT_FOUND,\n message: `Miniapp not found: ${target ?? \"(missing targetPackageName)\"}`,\n }\n }\n if (!actionId) return {code: MiniappErrorCode.ACTION_NOT_FOUND, message: \"invoke requires an actionId\"}\n if (app.compatibility && app.compatibility.isCompatible === false) {\n return {\n code: MiniappErrorCode.APP_NOT_COMPATIBLE,\n message: `${target} is not compatible with the connected glasses`,\n }\n }\n const declared = (app.actions ?? []).find((a) => a.id === actionId)\n if (!declared) {\n return {code: MiniappErrorCode.ACTION_NOT_FOUND, message: `${target} does not declare action \"${actionId}\"`}\n }\n if (declared.visibility === \"system\" && !callerIsSystem) {\n return {\n code: MiniappErrorCode.NOT_PERMITTED,\n message: `Action \"${actionId}\" on ${target} is restricted to system apps`,\n }\n }\n return null\n}\n\n/** BgTimer's shape; passed in so this module stays loadable without the native timer. */\nexport interface ActionTimers {\n setTimeout: (cb: () => void, ms: number) => number\n clearTimeout: (id: number) => void\n}\n\n/** Outstanding ACTION_CALLs awaiting an ACTION_RESULT, keyed by host-generated callId. */\nexport class PendingActionCalls {\n private readonly calls = new Map<string, {target: string; settle: (o: ActionOutcome) => void; timer: number}>()\n\n constructor(\n private readonly timers: ActionTimers,\n private readonly payloadTooLarge: (value: unknown) => boolean,\n ) {}\n\n /** Register a call; resolves with its outcome, or ACTION_TIMEOUT. */\n register(callId: string, target: string, actionId: string, timeoutMs: number): Promise<ActionOutcome> {\n return new Promise((resolve) => {\n const timer = this.timers.setTimeout(() => {\n this.calls.delete(callId)\n resolve(actionFailure(MiniappErrorCode.ACTION_TIMEOUT, `action \"${actionId}\" timed out after ${timeoutMs}ms`))\n }, timeoutMs)\n this.calls.set(callId, {target, settle: resolve, timer})\n })\n }\n\n /** Settle from a target's ACTION_RESULT payload. Only the invoked target may resolve a call. */\n handleResult(targetPackageName: string, payload: Record<string, unknown>): void {\n const callId = payload.callId as string | undefined\n if (!callId) return\n const pending = this.calls.get(callId)\n // callIds are guessable (timestamp + seq): a different miniapp must not spoof someone else's result.\n if (!pending || pending.target !== targetPackageName) return\n this.calls.delete(callId)\n this.timers.clearTimeout(pending.timer)\n\n if (payload.ok === true) {\n if (this.payloadTooLarge(payload.result)) {\n pending.settle(actionFailure(MiniappErrorCode.PAYLOAD_TOO_LARGE, \"action result exceeded the 256 KB cap\"))\n return\n }\n pending.settle({ok: true, data: payload.result ?? null})\n return\n }\n pending.settle({\n ok: false,\n error: (payload.error as ActionError | undefined) ?? {\n code: MiniappErrorCode.INTERNAL,\n message: \"action handler error\",\n },\n })\n }\n}\n"]}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The Apple Watch remote's phone half: the watch app's status context (glasses, the miniapp in
3
+ * front, launchable miniapps, whether Assist takes questions, the mirror setting), mirror frames
4
+ * while the setting is on, and the commands the watch sends back. Native adds whether the active
5
+ * watch is paired and keeps the newest frame for when the watch app opens. Gestures ride
6
+ * `touch_event` like any controller's.
7
+ */
8
+ import type { AppleWatchCommandEvent } from "@glassly/bluetooth-sdk";
9
+ import type { MirrorSnapshot } from "@glassly/miniapp";
10
+ import type { ClientApp } from "../../types";
11
+ export declare const ASSIST_PACKAGE = "com.glassly.assistant";
12
+ export declare const ASSIST_ACTION = "ask";
13
+ export interface WatchStateInput {
14
+ glasses: {
15
+ connected: boolean;
16
+ model: string;
17
+ battery: number;
18
+ };
19
+ apps: readonly ClientApp[];
20
+ assist: boolean;
21
+ mirror: boolean;
22
+ }
23
+ /** Miniapps the watch can open: shown, compatible, and drawing on the glasses. */
24
+ export declare function launchableApps(apps: readonly ClientApp[]): {
25
+ packageName: string;
26
+ name: string;
27
+ }[];
28
+ export declare function watchState({ glasses, apps, assist, mirror }: WatchStateInput): Record<string, unknown>;
29
+ /** The glasses frame in the watch's drawing vocabulary; null when there is nothing to draw. */
30
+ export declare function mirrorFrame(snapshot: MirrorSnapshot): Record<string, unknown> | null;
31
+ export interface AppleWatchCommandDeps {
32
+ apps: () => readonly ClientApp[];
33
+ start: (app: ClientApp) => Promise<unknown>;
34
+ stop: (packageName: string) => Promise<unknown>;
35
+ ask: (query: string) => Promise<unknown>;
36
+ type: (stream: string, payload: unknown) => void;
37
+ now: () => number;
38
+ }
39
+ export declare function handleAppleWatchCommand(event: AppleWatchCommandEvent, deps?: AppleWatchCommandDeps): Promise<void>;
40
+ export declare function startAppleWatchLink(): void;
41
+ export declare function stopAppleWatchLink(): void;
42
+ //# sourceMappingURL=AppleWatchLink.d.ts.map
@@ -0,0 +1 @@
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"}