@glassly/engine 0.3.1-dev.496 → 0.3.1-dev.525

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 (125) hide show
  1. package/build/engine.d.ts +9 -2
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/engine.js +5 -0
  4. package/build/engine.js.map +1 -1
  5. package/build/facades/dev.js +1 -1
  6. package/build/facades/dev.js.map +1 -1
  7. package/build/facades/miniapps.d.ts +5 -5
  8. package/build/facades/miniapps.d.ts.map +1 -1
  9. package/build/facades/miniapps.js +5 -6
  10. package/build/facades/miniapps.js.map +1 -1
  11. package/build/facades/speech.d.ts +10 -0
  12. package/build/facades/speech.d.ts.map +1 -1
  13. package/build/facades/speech.js +10 -0
  14. package/build/facades/speech.js.map +1 -1
  15. package/build/generated/releaseMetadata.js +3 -3
  16. package/build/generated/releaseMetadata.js.map +1 -1
  17. package/build/index.d.ts +1 -1
  18. package/build/index.d.ts.map +1 -1
  19. package/build/index.js +1 -1
  20. package/build/index.js.map +1 -1
  21. package/build/services/AppRegistry.d.ts +1 -7
  22. package/build/services/AppRegistry.d.ts.map +1 -1
  23. package/build/services/AppRegistry.js +15 -26
  24. package/build/services/AppRegistry.js.map +1 -1
  25. package/build/services/DevServerBridge.d.ts +1 -1
  26. package/build/services/DevServerBridge.js +2 -2
  27. package/build/services/DevServerBridge.js.map +1 -1
  28. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  29. package/build/services/DeviceEventRouter.js +2 -0
  30. package/build/services/DeviceEventRouter.js.map +1 -1
  31. package/build/services/JSRouter.js +1 -1
  32. package/build/services/JSRouter.js.map +1 -1
  33. package/build/services/LocalMiniappRuntime.d.ts +21 -6
  34. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  35. package/build/services/LocalMiniappRuntime.js +130 -48
  36. package/build/services/LocalMiniappRuntime.js.map +1 -1
  37. package/build/services/LocalTranslationService.d.ts +51 -0
  38. package/build/services/LocalTranslationService.d.ts.map +1 -0
  39. package/build/services/LocalTranslationService.js +141 -0
  40. package/build/services/LocalTranslationService.js.map +1 -0
  41. package/build/services/MiniappLauncher.d.ts +17 -23
  42. package/build/services/MiniappLauncher.d.ts.map +1 -1
  43. package/build/services/MiniappLauncher.js +30 -34
  44. package/build/services/MiniappLauncher.js.map +1 -1
  45. package/build/services/MiniappPauseCoordinator.d.ts +15 -0
  46. package/build/services/MiniappPauseCoordinator.d.ts.map +1 -0
  47. package/build/services/MiniappPauseCoordinator.js +93 -0
  48. package/build/services/MiniappPauseCoordinator.js.map +1 -0
  49. package/build/services/OfflineTranslationManager.d.ts +19 -0
  50. package/build/services/OfflineTranslationManager.d.ts.map +1 -0
  51. package/build/services/OfflineTranslationManager.js +35 -0
  52. package/build/services/OfflineTranslationManager.js.map +1 -0
  53. package/build/services/pages/OverlayPages.d.ts +5 -5
  54. package/build/services/pages/OverlayPages.d.ts.map +1 -1
  55. package/build/services/pages/OverlayPages.js +5 -5
  56. package/build/services/pages/OverlayPages.js.map +1 -1
  57. package/build/services/pages/overlayApps.d.ts +14 -11
  58. package/build/services/pages/overlayApps.d.ts.map +1 -1
  59. package/build/services/pages/overlayApps.js +21 -19
  60. package/build/services/pages/overlayApps.js.map +1 -1
  61. package/build/services/settingsBridge.d.ts +8 -8
  62. package/build/services/settingsBridge.d.ts.map +1 -1
  63. package/build/services/settingsBridge.js +12 -13
  64. package/build/services/settingsBridge.js.map +1 -1
  65. package/build/stores/apps.d.ts +0 -5
  66. package/build/stores/apps.d.ts.map +1 -1
  67. package/build/stores/apps.js +32 -52
  68. package/build/stores/apps.js.map +1 -1
  69. package/build/stores/settings.d.ts.map +1 -1
  70. package/build/stores/settings.js +0 -10
  71. package/build/stores/settings.js.map +1 -1
  72. package/build/types/applet.d.ts +3 -1
  73. package/build/types/applet.d.ts.map +1 -1
  74. package/build/types/applet.js.map +1 -1
  75. package/build/types/capabilities/even-realities-g1.d.ts.map +1 -1
  76. package/build/types/capabilities/even-realities-g1.js +1 -0
  77. package/build/types/capabilities/even-realities-g1.js.map +1 -1
  78. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
  79. package/build/types/capabilities/even-realities-g2.js +1 -0
  80. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  81. package/build/types/capabilities/mentra-display.d.ts.map +1 -1
  82. package/build/types/capabilities/mentra-display.js +1 -0
  83. package/build/types/capabilities/mentra-display.js.map +1 -1
  84. package/build/types/capabilities/nimo.d.ts.map +1 -1
  85. package/build/types/capabilities/nimo.js +1 -0
  86. package/build/types/capabilities/nimo.js.map +1 -1
  87. package/build/types/hardware.d.ts +9 -0
  88. package/build/types/hardware.d.ts.map +1 -1
  89. package/build/types/hardware.js +2 -0
  90. package/build/types/hardware.js.map +1 -1
  91. package/build/utils/miniappGlobals.js +1 -1
  92. package/build/utils/miniappGlobals.js.map +1 -1
  93. package/package.json +7 -7
  94. package/src/engine.ts +5 -0
  95. package/src/facades/dev.ts +1 -1
  96. package/src/facades/miniapps.ts +7 -7
  97. package/src/facades/speech.ts +12 -0
  98. package/src/generated/releaseMetadata.ts +3 -3
  99. package/src/index.ts +0 -2
  100. package/src/services/AppRegistry.ts +15 -27
  101. package/src/services/DevServerBridge.ts +2 -2
  102. package/src/services/DeviceEventRouter.ts +2 -0
  103. package/src/services/JSRouter.ts +1 -1
  104. package/src/services/LocalMiniappRuntime.ts +165 -66
  105. package/src/services/LocalTranslationService.ts +184 -0
  106. package/src/services/MiniappLauncher.ts +31 -34
  107. package/src/services/MiniappPauseCoordinator.ts +100 -0
  108. package/src/services/OfflineTranslationManager.ts +40 -0
  109. package/src/services/pages/OverlayPages.ts +5 -5
  110. package/src/services/pages/overlayApps.ts +19 -19
  111. package/src/services/settingsBridge.ts +12 -13
  112. package/src/stores/apps.ts +30 -58
  113. package/src/stores/settings.ts +0 -10
  114. package/src/types/applet.ts +3 -1
  115. package/src/types/capabilities/even-realities-g1.ts +1 -0
  116. package/src/types/capabilities/even-realities-g2.ts +1 -0
  117. package/src/types/capabilities/mentra-display.ts +1 -0
  118. package/src/types/capabilities/nimo.ts +1 -0
  119. package/src/types/hardware.ts +9 -0
  120. package/src/utils/miniappGlobals.ts +1 -1
  121. package/build/services/pages/activeDashboard.d.ts +0 -7
  122. package/build/services/pages/activeDashboard.d.ts.map +0 -1
  123. package/build/services/pages/activeDashboard.js +0 -21
  124. package/build/services/pages/activeDashboard.js.map +0 -1
  125. package/src/services/pages/activeDashboard.ts +0 -24
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MiniappPauseCoordinator.js","sourceRoot":"","sources":["../../src/services/MiniappPauseCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MiniappPauseCoordinator — pauses every local miniapp while the paired\n * glasses are disconnected.\n *\n * A paused miniapp keeps its JSContext and state. The runtime tells it\n * (`session.on(\"pause\")`), stops fanning out streams to it, and drops the mic\n * and transcription demand its subscriptions were holding, until the glasses\n * are back. The pause waits out a grace window so a BLE blip does not pause\n * and resume an app within a second; resume is immediate on reconnect.\n *\n * Only a phone paired to real glasses pauses. With nothing paired, or with the\n * simulated device, there is nothing to reconnect to and miniapps keep running\n * (the mirror and dev flows depend on that).\n */\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {BgTimer} from \"../utils/timers\"\nimport {isGlassesConnected, type GlassesConnectionStatus} from \"./GlassesReadiness\"\nimport {isPairedToRealGlasses} from \"./PairedGlassesLatch\"\nimport {projectPairingIdentity, subscribePairingIdentity, type IdentitySnapshot} from \"./PairingIdentity\"\n\nexport const MINIAPP_PAUSE_GRACE_MS = 10_000\n\nexport type MiniappPauseReason = \"glasses_disconnected\"\n\ntype Listener = (paused: boolean) => void\n\nlet started = false\nlet paused = false\nlet graceTimer: number | null = null\nconst listeners = new Set<Listener>()\nlet unsubscribers: Array<() => void> = []\n\n/** Paired to real glasses that are not connected. */\nexport function shouldPauseMiniapps(identity: IdentitySnapshot, connection: GlassesConnectionStatus): boolean {\n return isPairedToRealGlasses(identity) && !isGlassesConnected(connection)\n}\n\nexport function isMiniappsPaused(): boolean {\n return paused\n}\n\n/** Fires on pause/resume transitions only. */\nexport function onMiniappsPauseChange(listener: Listener): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\nfunction setPaused(next: boolean): void {\n if (paused === next) return\n paused = next\n console.log(`MiniappPauseCoordinator: ${next ? \"paused\" : \"resumed\"} local miniapps`)\n for (const listener of listeners) {\n try {\n listener(next)\n } catch (e) {\n console.warn(\"MiniappPauseCoordinator: listener threw\", e)\n }\n }\n}\n\nfunction clearGrace(): void {\n if (graceTimer === null) return\n BgTimer.clearTimeout(graceTimer)\n graceTimer = null\n}\n\nfunction wanted(): boolean {\n return shouldPauseMiniapps(projectPairingIdentity(), useGlassesStore.getState().connection)\n}\n\nfunction evaluate(): void {\n if (!wanted()) {\n clearGrace()\n setPaused(false)\n return\n }\n if (paused || graceTimer !== null) return\n graceTimer = BgTimer.setTimeout(() => {\n graceTimer = null\n if (wanted()) setPaused(true)\n }, MINIAPP_PAUSE_GRACE_MS)\n}\n\n/** Start after the persisted settings are loaded so the pairing identity reads true. */\nexport function startMiniappPauseCoordinator(): void {\n if (started) return\n started = true\n unsubscribers = [useGlassesStore.subscribe((s) => s.connection.state, evaluate), subscribePairingIdentity(evaluate)]\n evaluate()\n}\n\nexport function stopMiniappPauseCoordinator(): void {\n for (const unsubscribe of unsubscribers) unsubscribe()\n unsubscribers = []\n clearGrace()\n paused = false\n started = false\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import type { OfflineTranslationLanguage, OfflineTranslationStatus } from "@glassly/bluetooth-sdk";
2
+ export type { OfflineTranslationLanguage, OfflineTranslationStatus };
3
+ /** Headless on-device translation needs the iOS 26 Translation framework. */
4
+ export declare function offlineTranslationSupported(): boolean;
5
+ /**
6
+ * On-device translation language packs. The OS owns the assets: there is no
7
+ * download progress or removal API, only install state and a system prompt
8
+ * that downloads a pair.
9
+ */
10
+ export declare const offlineTranslationManager: {
11
+ isSupported: typeof offlineTranslationSupported;
12
+ /** Languages `source` can be translated into, with their install state. */
13
+ languages(source: string): Promise<OfflineTranslationLanguage[]>;
14
+ status(source: string, target: string): Promise<OfflineTranslationStatus>;
15
+ /** Shows the system download prompt; resolves with the pair's status afterwards. */
16
+ download(source: string, target: string): Promise<OfflineTranslationStatus>;
17
+ translate(text: string, source: string, target: string): Promise<string>;
18
+ };
19
+ //# sourceMappingURL=OfflineTranslationManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OfflineTranslationManager.d.ts","sourceRoot":"","sources":["../../src/services/OfflineTranslationManager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,0BAA0B,EAAE,wBAAwB,EAAC,MAAM,wBAAwB,CAAA;AAEhG,YAAY,EAAC,0BAA0B,EAAE,wBAAwB,EAAC,CAAA;AAElE,6EAA6E;AAC7E,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;;IAGpC,2EAA2E;sBACnD,MAAM,GAAG,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBAKjD,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAK/E,oFAAoF;qBAC7D,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;oBAKjE,MAAM,UAAU,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGzE,CAAA"}
@@ -0,0 +1,35 @@
1
+ import { Platform } from "react-native";
2
+ import BluetoothSdk from "@glassly/bluetooth-sdk/internal";
3
+ /** Headless on-device translation needs the iOS 26 Translation framework. */
4
+ export function offlineTranslationSupported() {
5
+ return Platform.OS === "ios" && parseInt(String(Platform.Version), 10) >= 26;
6
+ }
7
+ /**
8
+ * On-device translation language packs. The OS owns the assets: there is no
9
+ * download progress or removal API, only install state and a system prompt
10
+ * that downloads a pair.
11
+ */
12
+ export const offlineTranslationManager = {
13
+ isSupported: offlineTranslationSupported,
14
+ /** Languages `source` can be translated into, with their install state. */
15
+ async languages(source) {
16
+ if (!offlineTranslationSupported())
17
+ return [];
18
+ return BluetoothSdk.getOfflineTranslationLanguages(source);
19
+ },
20
+ async status(source, target) {
21
+ if (!offlineTranslationSupported())
22
+ return "unavailable";
23
+ return BluetoothSdk.getOfflineTranslationStatus(source, target);
24
+ },
25
+ /** Shows the system download prompt; resolves with the pair's status afterwards. */
26
+ async download(source, target) {
27
+ if (!offlineTranslationSupported())
28
+ return "unavailable";
29
+ return BluetoothSdk.downloadOfflineTranslation(source, target);
30
+ },
31
+ translate(text, source, target) {
32
+ return BluetoothSdk.translateOffline(text, source, target);
33
+ },
34
+ };
35
+ //# sourceMappingURL=OfflineTranslationManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OfflineTranslationManager.js","sourceRoot":"","sources":["../../src/services/OfflineTranslationManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AACrC,OAAO,YAAY,MAAM,iCAAiC,CAAA;AAK1D,6EAA6E;AAC7E,MAAM,UAAU,2BAA2B;IACzC,OAAO,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,WAAW,EAAE,2BAA2B;IAExC,2EAA2E;IAC3E,KAAK,CAAC,SAAS,CAAC,MAAc;QAC5B,IAAI,CAAC,2BAA2B,EAAE;YAAE,OAAO,EAAE,CAAA;QAC7C,OAAO,YAAY,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAA;IAC5D,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,MAAc;QACzC,IAAI,CAAC,2BAA2B,EAAE;YAAE,OAAO,aAAa,CAAA;QACxD,OAAO,YAAY,CAAC,2BAA2B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjE,CAAC;IAED,oFAAoF;IACpF,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,MAAc;QAC3C,IAAI,CAAC,2BAA2B,EAAE;YAAE,OAAO,aAAa,CAAA;QACxD,OAAO,YAAY,CAAC,0BAA0B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChE,CAAC;IAED,SAAS,CAAC,IAAY,EAAE,MAAc,EAAE,MAAc;QACpD,OAAO,YAAY,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5D,CAAC;CACF,CAAA","sourcesContent":["import {Platform} from \"react-native\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {OfflineTranslationLanguage, OfflineTranslationStatus} from \"@glassly/bluetooth-sdk\"\n\nexport type {OfflineTranslationLanguage, OfflineTranslationStatus}\n\n/** Headless on-device translation needs the iOS 26 Translation framework. */\nexport function offlineTranslationSupported(): boolean {\n return Platform.OS === \"ios\" && parseInt(String(Platform.Version), 10) >= 26\n}\n\n/**\n * On-device translation language packs. The OS owns the assets: there is no\n * download progress or removal API, only install state and a system prompt\n * that downloads a pair.\n */\nexport const offlineTranslationManager = {\n isSupported: offlineTranslationSupported,\n\n /** Languages `source` can be translated into, with their install state. */\n async languages(source: string): Promise<OfflineTranslationLanguage[]> {\n if (!offlineTranslationSupported()) return []\n return BluetoothSdk.getOfflineTranslationLanguages(source)\n },\n\n async status(source: string, target: string): Promise<OfflineTranslationStatus> {\n if (!offlineTranslationSupported()) return \"unavailable\"\n return BluetoothSdk.getOfflineTranslationStatus(source, target)\n },\n\n /** Shows the system download prompt; resolves with the pair's status afterwards. */\n async download(source: string, target: string): Promise<OfflineTranslationStatus> {\n if (!offlineTranslationSupported()) return \"unavailable\"\n return BluetoothSdk.downloadOfflineTranslation(source, target)\n },\n\n translate(text: string, source: string, target: string): Promise<string> {\n return BluetoothSdk.translateOffline(text, source, target)\n },\n}\n"]}
@@ -1,12 +1,12 @@
1
1
  /**
2
- * The overlay app's page (ADR-0006, ADR-0009): one Page per dashboard that
2
+ * The overlay app's page (ADR-0006, ADR-0010): one Page per dashboard that
3
3
  * has taken the slot, the only Page implementation the page host shows. The
4
4
  * engine keeps the slot and the eligibility watch; a page only relays:
5
5
  * holding the slot is reported to its app as a view change, and the app's
6
6
  * frames reach the slot through `renderOverlay` under its package name. Only
7
- * the dashboard that holds the role may show; when the role moves, the
8
- * previous holder hears it was replaced. The runtime supplies the sink that
9
- * delivers to apps, so this module never imports it.
7
+ * the connected dashboard may show; a stopping one is removed without a
8
+ * word. The runtime supplies the sink that delivers to apps, so this module
9
+ * never imports it.
10
10
  */
11
11
  import { type PageHiddenReason } from "./PageHost";
12
12
  export type DashboardViewChangeReason = "shown" | "hidden" | "replaced" | "ineligible";
@@ -24,7 +24,7 @@ declare class OverlayPages {
24
24
  get showingPackage(): string | null;
25
25
  /** Give the app the slot (a no-op when it already shows or pages are not eligible). */
26
26
  show(packageName: string): boolean;
27
- /** Release the slot if the app holds it; "replaced" when the dashboard role moved on. */
27
+ /** Release the slot if the app holds it. */
28
28
  hide(packageName: string, reason?: PageHiddenReason): void;
29
29
  /** The app stopped: release the slot without telling it, and forget its page. */
30
30
  remove(packageName: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayPages.d.ts","sourceRoot":"","sources":["../../../src/services/pages/OverlayPages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAiB,EAAY,KAAK,gBAAgB,EAAC,MAAM,YAAY,CAAA;AAIrE,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAA;AAEtF,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAChE;AAED,cAAM,YAAY;IAChB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,KAAK,CAA0B;IACvC,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAoB;IAElC,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAI5C,kDAAkD;IAClD,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAKlC;IAED,uFAAuF;IACvF,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAOlC,yFAAyF;IACzF,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,GAAE,gBAA2B,GAAG,IAAI;IAKpE,iFAAiF;IACjF,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAYjC,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,IAAI;CAIb;AAED,QAAA,MAAM,YAAY,cAAqB,CAAA;AACvC,eAAe,YAAY,CAAA;AAC3B,OAAO,EAAC,YAAY,EAAC,CAAA"}
1
+ {"version":3,"file":"OverlayPages.d.ts","sourceRoot":"","sources":["../../../src/services/pages/OverlayPages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAiB,EAAY,KAAK,gBAAgB,EAAC,MAAM,YAAY,CAAA;AAIrE,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAA;AAEtF,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAChE;AAED,cAAM,YAAY;IAChB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,KAAK,CAA0B;IACvC,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAoB;IAElC,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAI5C,kDAAkD;IAClD,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAKlC;IAED,uFAAuF;IACvF,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAOlC,4CAA4C;IAC5C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,GAAE,gBAA2B,GAAG,IAAI;IAKpE,iFAAiF;IACjF,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAYjC,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,IAAI;CAIb;AAED,QAAA,MAAM,YAAY,cAAqB,CAAA;AACvC,eAAe,YAAY,CAAA;AAC3B,OAAO,EAAC,YAAY,EAAC,CAAA"}
@@ -1,12 +1,12 @@
1
1
  /**
2
- * The overlay app's page (ADR-0006, ADR-0009): one Page per dashboard that
2
+ * The overlay app's page (ADR-0006, ADR-0010): one Page per dashboard that
3
3
  * has taken the slot, the only Page implementation the page host shows. The
4
4
  * engine keeps the slot and the eligibility watch; a page only relays:
5
5
  * holding the slot is reported to its app as a view change, and the app's
6
6
  * frames reach the slot through `renderOverlay` under its package name. Only
7
- * the dashboard that holds the role may show; when the role moves, the
8
- * previous holder hears it was replaced. The runtime supplies the sink that
9
- * delivers to apps, so this module never imports it.
7
+ * the connected dashboard may show; a stopping one is removed without a
8
+ * word. The runtime supplies the sink that delivers to apps, so this module
9
+ * never imports it.
10
10
  */
11
11
  import pageHost from "./PageHost";
12
12
  import { isOverlayApp } from "./overlayApps";
@@ -37,7 +37,7 @@ class OverlayPages {
37
37
  pageHost.show(page);
38
38
  return pageHost.showing === page;
39
39
  }
40
- /** Release the slot if the app holds it; "replaced" when the dashboard role moved on. */
40
+ /** Release the slot if the app holds it. */
41
41
  hide(packageName, reason = "hidden") {
42
42
  const page = this.pages.get(packageName);
43
43
  if (page)
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayPages.js","sourceRoot":"","sources":["../../../src/services/pages/OverlayPages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The overlay app's page (ADR-0006, ADR-0009): one Page per dashboard that\n * has taken the slot, the only Page implementation the page host shows. The\n * engine keeps the slot and the eligibility watch; a page only relays:\n * holding the slot is reported to its app as a view change, and the app's\n * frames reach the slot through `renderOverlay` under its package name. Only\n * the dashboard that holds the role may show; when the role moves, the\n * previous holder hears it was replaced. The runtime supplies the sink that\n * delivers to apps, so this module never imports it.\n */\nimport pageHost, {type Page, type PageHiddenReason} from \"./PageHost\"\nimport {isOverlayApp} from \"./overlayApps\"\nimport {pagesEligible} from \"./pageEligibility\"\n\nexport type DashboardViewChangeReason = \"shown\" | \"hidden\" | \"replaced\" | \"ineligible\"\n\nexport interface OverlayPagesSink {\n /** Deliver a display_view event to the app. */\n send(packageName: string, event: Record<string, unknown>): void\n}\n\nclass OverlayPages {\n private sink: OverlayPagesSink | null = null\n private pages = new Map<string, Page>()\n /** Set while an app's page is torn down with the app; it hears nothing then. */\n private silent = new Set<string>()\n\n setSink(sink: OverlayPagesSink | null): void {\n this.sink = sink\n }\n\n /** The app whose page holds the slot, or null. */\n get showingPackage(): string | null {\n const showing = pageHost.showing\n if (!showing) return null\n for (const [packageName, page] of this.pages) if (page === showing) return packageName\n return null\n }\n\n /** Give the app the slot (a no-op when it already shows or pages are not eligible). */\n show(packageName: string): boolean {\n if (!isOverlayApp(packageName) || !pagesEligible()) return false\n const page = this.pageFor(packageName)\n pageHost.show(page)\n return pageHost.showing === page\n }\n\n /** Release the slot if the app holds it; \"replaced\" when the dashboard role moved on. */\n hide(packageName: string, reason: PageHiddenReason = \"hidden\"): void {\n const page = this.pages.get(packageName)\n if (page) pageHost.hide(page, reason)\n }\n\n /** The app stopped: release the slot without telling it, and forget its page. */\n remove(packageName: string): void {\n const page = this.pages.get(packageName)\n if (!page) return\n this.silent.add(packageName)\n try {\n pageHost.hide(page)\n } finally {\n this.silent.delete(packageName)\n }\n this.pages.delete(packageName)\n }\n\n private pageFor(packageName: string): Page {\n let page = this.pages.get(packageName)\n if (page) return page\n page = {\n id: packageName,\n onShown: () => this.emit(packageName, {kind: \"change\", view: \"dashboard\", reason: \"shown\"}),\n onHidden: (reason: PageHiddenReason) => this.emit(packageName, {kind: \"change\", view: \"main\", reason}),\n }\n this.pages.set(packageName, page)\n return page\n }\n\n private emit(packageName: string, event: Record<string, unknown>): void {\n if (this.silent.has(packageName) || !isOverlayApp(packageName)) return\n this.sink?.send(packageName, event)\n }\n}\n\nconst overlayPages = new OverlayPages()\nexport default overlayPages\nexport {OverlayPages}\n"]}
1
+ {"version":3,"file":"OverlayPages.js","sourceRoot":"","sources":["../../../src/services/pages/OverlayPages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The overlay app's page (ADR-0006, ADR-0010): one Page per dashboard that\n * has taken the slot, the only Page implementation the page host shows. The\n * engine keeps the slot and the eligibility watch; a page only relays:\n * holding the slot is reported to its app as a view change, and the app's\n * frames reach the slot through `renderOverlay` under its package name. Only\n * the connected dashboard may show; a stopping one is removed without a\n * word. The runtime supplies the sink that delivers to apps, so this module\n * never imports it.\n */\nimport pageHost, {type Page, type PageHiddenReason} from \"./PageHost\"\nimport {isOverlayApp} from \"./overlayApps\"\nimport {pagesEligible} from \"./pageEligibility\"\n\nexport type DashboardViewChangeReason = \"shown\" | \"hidden\" | \"replaced\" | \"ineligible\"\n\nexport interface OverlayPagesSink {\n /** Deliver a display_view event to the app. */\n send(packageName: string, event: Record<string, unknown>): void\n}\n\nclass OverlayPages {\n private sink: OverlayPagesSink | null = null\n private pages = new Map<string, Page>()\n /** Set while an app's page is torn down with the app; it hears nothing then. */\n private silent = new Set<string>()\n\n setSink(sink: OverlayPagesSink | null): void {\n this.sink = sink\n }\n\n /** The app whose page holds the slot, or null. */\n get showingPackage(): string | null {\n const showing = pageHost.showing\n if (!showing) return null\n for (const [packageName, page] of this.pages) if (page === showing) return packageName\n return null\n }\n\n /** Give the app the slot (a no-op when it already shows or pages are not eligible). */\n show(packageName: string): boolean {\n if (!isOverlayApp(packageName) || !pagesEligible()) return false\n const page = this.pageFor(packageName)\n pageHost.show(page)\n return pageHost.showing === page\n }\n\n /** Release the slot if the app holds it. */\n hide(packageName: string, reason: PageHiddenReason = \"hidden\"): void {\n const page = this.pages.get(packageName)\n if (page) pageHost.hide(page, reason)\n }\n\n /** The app stopped: release the slot without telling it, and forget its page. */\n remove(packageName: string): void {\n const page = this.pages.get(packageName)\n if (!page) return\n this.silent.add(packageName)\n try {\n pageHost.hide(page)\n } finally {\n this.silent.delete(packageName)\n }\n this.pages.delete(packageName)\n }\n\n private pageFor(packageName: string): Page {\n let page = this.pages.get(packageName)\n if (page) return page\n page = {\n id: packageName,\n onShown: () => this.emit(packageName, {kind: \"change\", view: \"dashboard\", reason: \"shown\"}),\n onHidden: (reason: PageHiddenReason) => this.emit(packageName, {kind: \"change\", view: \"main\", reason}),\n }\n this.pages.set(packageName, page)\n return page\n }\n\n private emit(packageName: string, event: Record<string, unknown>): void {\n if (this.silent.has(packageName) || !isOverlayApp(packageName)) return\n this.sink?.send(packageName, event)\n }\n}\n\nconst overlayPages = new OverlayPages()\nexport default overlayPages\nexport {OverlayPages}\n"]}
@@ -1,23 +1,26 @@
1
1
  /**
2
- * The overlay app: the connected miniapp that holds the dashboard role
3
- * (ADR-0009). It alone may take the overlay slot through
4
- * `session.display.showView("dashboard")` and has "dashboard" in its
5
- * `display.views` capability while the glasses are eligible. The runtime
6
- * records each dashboard miniapp (manifest type "dashboard") as it connects
7
- * and stops, and who holds the role; the engine never knows what a dashboard
8
- * draws. Kept free of imports so the runtime and the notification policy can
9
- * both read it without a cycle.
2
+ * The overlay app: the connected dashboard miniapp (ADR-0010). It alone may
3
+ * take the overlay slot through `session.display.showView("dashboard")` and
4
+ * has "dashboard" in its `display.views` capability while the glasses are
5
+ * eligible. The runtime records each dashboard miniapp (manifest type
6
+ * "dashboard") as it connects and stops; the first connected one holds the
7
+ * role until it stops, and the engine never knows what a dashboard draws.
8
+ * Only one dashboard runs at a time (the apps store stops the previous one
9
+ * before starting another), so two connected at once is a transient. Kept
10
+ * free of imports so the runtime and the notification policy can both read
11
+ * it without a cycle.
10
12
  */
11
13
  export type DisplayView = "main" | "dashboard";
12
- /** `packageName` holds the dashboard role and is connected. */
14
+ /** `packageName` holds the dashboard role. */
13
15
  export declare function isOverlayApp(packageName: string): boolean;
14
16
  /** `packageName` is a connected dashboard miniapp, whether or not it holds the role. */
15
17
  export declare function isDashboardApp(packageName: string): boolean;
16
18
  export declare function overlayApps(): readonly string[];
17
- /** The package that holds the dashboard role, connected or not. */
19
+ /** The connected dashboard that holds the role, or null for nobody. */
18
20
  export declare function activeDashboard(): string | null;
19
- export declare function setActiveDashboard(packageName: string | null): void;
21
+ /** A dashboard connected: it takes the role when nobody holds it. */
20
22
  export declare function addDashboardApp(packageName: string): void;
23
+ /** A dashboard stopped: the role passes to another connected one, by package name, else to nobody. */
21
24
  export declare function removeDashboardApp(packageName: string): void;
22
25
  /** Record whether a dashboard subscribes to phone notifications (the overlay app is the viewer while it does). */
23
26
  export declare function setOverlayViewer(packageName: string, subscribed: boolean): void;
@@ -1 +1 @@
1
- {"version":3,"file":"overlayApps.d.ts","sourceRoot":"","sources":["../../../src/services/pages/overlayApps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAA;AAS9C,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,wBAAgB,WAAW,IAAI,SAAS,MAAM,EAAE,CAE/C;AAED,mEAAmE;AACnE,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAInE;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAIzD;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI5D;AAED,kHAAkH;AAClH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CAK/E;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKpE;AAED,kHAAkH;AAClH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,WAAW,EAAE,CAErF;AAMD,kBAAkB;AAClB,wBAAgB,wBAAwB,IAAI,IAAI,CAI/C"}
1
+ {"version":3,"file":"overlayApps.d.ts","sourceRoot":"","sources":["../../../src/services/pages/overlayApps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAA;AAS9C,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,wBAAgB,WAAW,IAAI,SAAS,MAAM,EAAE,CAE/C;AAED,uEAAuE;AACvE,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAE/C;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAKzD;AAED,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAK5D;AAED,kHAAkH;AAClH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CAK/E;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKpE;AAED,kHAAkH;AAClH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,WAAW,EAAE,CAErF;AAMD,kBAAkB;AAClB,wBAAgB,wBAAwB,IAAI,IAAI,CAI/C"}
@@ -1,12 +1,14 @@
1
1
  /**
2
- * The overlay app: the connected miniapp that holds the dashboard role
3
- * (ADR-0009). It alone may take the overlay slot through
4
- * `session.display.showView("dashboard")` and has "dashboard" in its
5
- * `display.views` capability while the glasses are eligible. The runtime
6
- * records each dashboard miniapp (manifest type "dashboard") as it connects
7
- * and stops, and who holds the role; the engine never knows what a dashboard
8
- * draws. Kept free of imports so the runtime and the notification policy can
9
- * both read it without a cycle.
2
+ * The overlay app: the connected dashboard miniapp (ADR-0010). It alone may
3
+ * take the overlay slot through `session.display.showView("dashboard")` and
4
+ * has "dashboard" in its `display.views` capability while the glasses are
5
+ * eligible. The runtime records each dashboard miniapp (manifest type
6
+ * "dashboard") as it connects and stops; the first connected one holds the
7
+ * role until it stops, and the engine never knows what a dashboard draws.
8
+ * Only one dashboard runs at a time (the apps store stops the previous one
9
+ * before starting another), so two connected at once is a transient. Kept
10
+ * free of imports so the runtime and the notification policy can both read
11
+ * it without a cycle.
10
12
  */
11
13
  /** Connected dashboard miniapps, role or not. */
12
14
  const connected = new Set();
@@ -14,37 +16,37 @@ const connected = new Set();
14
16
  const listening = new Set();
15
17
  let active = null;
16
18
  const listeners = new Set();
17
- /** `packageName` holds the dashboard role and is connected. */
19
+ /** `packageName` holds the dashboard role. */
18
20
  export function isOverlayApp(packageName) {
19
- return packageName === active && connected.has(packageName);
21
+ return packageName === active;
20
22
  }
21
23
  /** `packageName` is a connected dashboard miniapp, whether or not it holds the role. */
22
24
  export function isDashboardApp(packageName) {
23
25
  return connected.has(packageName);
24
26
  }
25
27
  export function overlayApps() {
26
- return active !== null && connected.has(active) ? [active] : [];
28
+ return active !== null ? [active] : [];
27
29
  }
28
- /** The package that holds the dashboard role, connected or not. */
30
+ /** The connected dashboard that holds the role, or null for nobody. */
29
31
  export function activeDashboard() {
30
32
  return active;
31
33
  }
32
- export function setActiveDashboard(packageName) {
33
- if (active === packageName)
34
- return;
35
- active = packageName;
36
- emit();
37
- }
34
+ /** A dashboard connected: it takes the role when nobody holds it. */
38
35
  export function addDashboardApp(packageName) {
39
36
  if (connected.has(packageName))
40
37
  return;
41
38
  connected.add(packageName);
39
+ if (active === null)
40
+ active = packageName;
42
41
  emit();
43
42
  }
43
+ /** A dashboard stopped: the role passes to another connected one, by package name, else to nobody. */
44
44
  export function removeDashboardApp(packageName) {
45
45
  listening.delete(packageName);
46
46
  if (!connected.delete(packageName))
47
47
  return;
48
+ if (active === packageName)
49
+ active = [...connected].sort()[0] ?? null;
48
50
  emit();
49
51
  }
50
52
  /** Record whether a dashboard subscribes to phone notifications (the overlay app is the viewer while it does). */
@@ -59,7 +61,7 @@ export function setOverlayViewer(packageName, subscribed) {
59
61
  }
60
62
  /** The overlay app presents phone notifications. */
61
63
  export function overlayViewerRunning() {
62
- return active !== null && connected.has(active) && listening.has(active);
64
+ return active !== null && listening.has(active);
63
65
  }
64
66
  export function onOverlayAppsChange(listener) {
65
67
  listeners.add(listener);
@@ -1 +1 @@
1
- {"version":3,"file":"overlayApps.js","sourceRoot":"","sources":["../../../src/services/pages/overlayApps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The overlay app: the connected miniapp that holds the dashboard role\n * (ADR-0009). It alone may take the overlay slot through\n * `session.display.showView(\"dashboard\")` and has \"dashboard\" in its\n * `display.views` capability while the glasses are eligible. The runtime\n * records each dashboard miniapp (manifest type \"dashboard\") as it connects\n * and stops, and who holds the role; the engine never knows what a dashboard\n * draws. Kept free of imports so the runtime and the notification policy can\n * both read it without a cycle.\n */\n\nexport type DisplayView = \"main\" | \"dashboard\"\n\n/** Connected dashboard miniapps, role or not. */\nconst connected = new Set<string>()\n/** Connected dashboards listening to the phone's notifications. */\nconst listening = new Set<string>()\nlet active: string | null = null\nconst listeners = new Set<() => void>()\n\n/** `packageName` holds the dashboard role and is connected. */\nexport function isOverlayApp(packageName: string): boolean {\n return packageName === active && connected.has(packageName)\n}\n\n/** `packageName` is a connected dashboard miniapp, whether or not it holds the role. */\nexport function isDashboardApp(packageName: string): boolean {\n return connected.has(packageName)\n}\n\nexport function overlayApps(): readonly string[] {\n return active !== null && connected.has(active) ? [active] : []\n}\n\n/** The package that holds the dashboard role, connected or not. */\nexport function activeDashboard(): string | null {\n return active\n}\n\nexport function setActiveDashboard(packageName: string | null): void {\n if (active === packageName) return\n active = packageName\n emit()\n}\n\nexport function addDashboardApp(packageName: string): void {\n if (connected.has(packageName)) return\n connected.add(packageName)\n emit()\n}\n\nexport function removeDashboardApp(packageName: string): void {\n listening.delete(packageName)\n if (!connected.delete(packageName)) return\n emit()\n}\n\n/** Record whether a dashboard subscribes to phone notifications (the overlay app is the viewer while it does). */\nexport function setOverlayViewer(packageName: string, subscribed: boolean): void {\n const was = overlayViewerRunning()\n if (subscribed && connected.has(packageName)) listening.add(packageName)\n else listening.delete(packageName)\n if (was !== overlayViewerRunning()) emit()\n}\n\n/** The overlay app presents phone notifications. */\nexport function overlayViewerRunning(): boolean {\n return active !== null && connected.has(active) && listening.has(active)\n}\n\nexport function onOverlayAppsChange(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** The views `packageName` may draw: main for everyone, the dashboard for the overlay app on eligible glasses. */\nexport function displayViewsFor(packageName: string, eligible: boolean): DisplayView[] {\n return isOverlayApp(packageName) && eligible ? [\"main\", \"dashboard\"] : [\"main\"]\n}\n\nfunction emit(): void {\n for (const listener of Array.from(listeners)) listener()\n}\n\n/** Tests only. */\nexport function _resetOverlayAppsForTest(): void {\n connected.clear()\n listening.clear()\n active = null\n}\n"]}
1
+ {"version":3,"file":"overlayApps.js","sourceRoot":"","sources":["../../../src/services/pages/overlayApps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The overlay app: the connected dashboard miniapp (ADR-0010). It alone may\n * take the overlay slot through `session.display.showView(\"dashboard\")` and\n * has \"dashboard\" in its `display.views` capability while the glasses are\n * eligible. The runtime records each dashboard miniapp (manifest type\n * \"dashboard\") as it connects and stops; the first connected one holds the\n * role until it stops, and the engine never knows what a dashboard draws.\n * Only one dashboard runs at a time (the apps store stops the previous one\n * before starting another), so two connected at once is a transient. Kept\n * free of imports so the runtime and the notification policy can both read\n * it without a cycle.\n */\n\nexport type DisplayView = \"main\" | \"dashboard\"\n\n/** Connected dashboard miniapps, role or not. */\nconst connected = new Set<string>()\n/** Connected dashboards listening to the phone's notifications. */\nconst listening = new Set<string>()\nlet active: string | null = null\nconst listeners = new Set<() => void>()\n\n/** `packageName` holds the dashboard role. */\nexport function isOverlayApp(packageName: string): boolean {\n return packageName === active\n}\n\n/** `packageName` is a connected dashboard miniapp, whether or not it holds the role. */\nexport function isDashboardApp(packageName: string): boolean {\n return connected.has(packageName)\n}\n\nexport function overlayApps(): readonly string[] {\n return active !== null ? [active] : []\n}\n\n/** The connected dashboard that holds the role, or null for nobody. */\nexport function activeDashboard(): string | null {\n return active\n}\n\n/** A dashboard connected: it takes the role when nobody holds it. */\nexport function addDashboardApp(packageName: string): void {\n if (connected.has(packageName)) return\n connected.add(packageName)\n if (active === null) active = packageName\n emit()\n}\n\n/** A dashboard stopped: the role passes to another connected one, by package name, else to nobody. */\nexport function removeDashboardApp(packageName: string): void {\n listening.delete(packageName)\n if (!connected.delete(packageName)) return\n if (active === packageName) active = [...connected].sort()[0] ?? null\n emit()\n}\n\n/** Record whether a dashboard subscribes to phone notifications (the overlay app is the viewer while it does). */\nexport function setOverlayViewer(packageName: string, subscribed: boolean): void {\n const was = overlayViewerRunning()\n if (subscribed && connected.has(packageName)) listening.add(packageName)\n else listening.delete(packageName)\n if (was !== overlayViewerRunning()) emit()\n}\n\n/** The overlay app presents phone notifications. */\nexport function overlayViewerRunning(): boolean {\n return active !== null && listening.has(active)\n}\n\nexport function onOverlayAppsChange(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** The views `packageName` may draw: main for everyone, the dashboard for the overlay app on eligible glasses. */\nexport function displayViewsFor(packageName: string, eligible: boolean): DisplayView[] {\n return isOverlayApp(packageName) && eligible ? [\"main\", \"dashboard\"] : [\"main\"]\n}\n\nfunction emit(): void {\n for (const listener of Array.from(listeners)) listener()\n}\n\n/** Tests only. */\nexport function _resetOverlayAppsForTest(): void {\n connected.clear()\n listening.clear()\n active = null\n}\n"]}
@@ -5,10 +5,10 @@
5
5
  * request validation. Pure, so the runtime's handlers stay thin and this can
6
6
  * be tested without the native import surface the runtime pulls in.
7
7
  *
8
- * A dashboard miniapp is the usual consumer: it edits the notification and
9
- * glasses-menu keys the phone's settings screens used to, through the store,
10
- * so persistence, server sync and the Bluetooth sync of `menu_apps` behave as
11
- * if a phone screen wrote them. A dashboard's own look is not here; it keeps
8
+ * A dashboard miniapp is the usual consumer: it edits the notification,
9
+ * glasses-menu and display-position keys the phone's settings screens used
10
+ * to, through the store, so persistence, server sync and the Bluetooth sync
11
+ * of `menu_apps` and the position behave as if a phone screen wrote them. A dashboard's own look is not here; it keeps
12
12
  * that in its storage.
13
13
  */
14
14
  export type SettingsBridgeAccess = "read" | "write";
@@ -16,11 +16,11 @@ export type SettingsBridgeAccess = "read" | "write";
16
16
  export declare const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>>;
17
17
  /**
18
18
  * Read-only keys the engine computes: whether the wearer can see the phone,
19
- * whether the foreground miniapp is drawing on the glasses, the ring's
20
- * battery, and the package that holds the dashboard role (null for nobody).
21
- * They change on their own clocks; the runtime pushes them like stored keys.
19
+ * whether the foreground miniapp is drawing on the glasses, and the ring's
20
+ * battery. They change on their own clocks; the runtime pushes them like
21
+ * stored keys.
22
22
  */
23
- export declare const DERIVED_SETTINGS_KEYS: readonly ["phone_active", "foreground_app_on_screen", "ring_battery", "active_dashboard"];
23
+ export declare const DERIVED_SETTINGS_KEYS: readonly ["phone_active", "foreground_app_on_screen", "ring_battery"];
24
24
  export type DerivedSettingsKey = (typeof DERIVED_SETTINGS_KEYS)[number];
25
25
  export declare function isDerivedSettingsKey(key: string): key is DerivedSettingsKey;
26
26
  /** What a permitted app may do with `key`, or null when the bridge does not serve it. */
@@ -1 +1 @@
1
- {"version":3,"file":"settingsBridge.d.ts","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAc/E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,2FAKxB,CAAA;AACV,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAG7E;AAED,wDAAwD;AACxD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,EAAE,CAIrE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAC,GACvC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAA;AAE5E,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,qBAAqB,CAY3F;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,MAAM,CAAA;CAC/B,GAAG,WAAW,CAMd"}
1
+ {"version":3,"file":"settingsBridge.d.ts","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAkB/E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,uEAAwE,CAAA;AAC1G,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAG7E;AAED,wDAAwD;AACxD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,EAAE,CAIrE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAC,GACvC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAA;AAE5E,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,qBAAqB,CAY3F;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,MAAM,CAAA;CAC/B,GAAG,WAAW,CAMd"}
@@ -5,10 +5,10 @@
5
5
  * request validation. Pure, so the runtime's handlers stay thin and this can
6
6
  * be tested without the native import surface the runtime pulls in.
7
7
  *
8
- * A dashboard miniapp is the usual consumer: it edits the notification and
9
- * glasses-menu keys the phone's settings screens used to, through the store,
10
- * so persistence, server sync and the Bluetooth sync of `menu_apps` behave as
11
- * if a phone screen wrote them. A dashboard's own look is not here; it keeps
8
+ * A dashboard miniapp is the usual consumer: it edits the notification,
9
+ * glasses-menu and display-position keys the phone's settings screens used
10
+ * to, through the store, so persistence, server sync and the Bluetooth sync
11
+ * of `menu_apps` and the position behave as if a phone screen wrote them. A dashboard's own look is not here; it keeps
12
12
  * that in its storage.
13
13
  */
14
14
  /** Stored settings the bridge serves, by key, with what a permitted app may do to each. */
@@ -16,6 +16,10 @@ export const SETTINGS_BRIDGE_KEYS = {
16
16
  // The dashboard's master switch is a phone-side debug knob.
17
17
  custom_dashboard_enabled: "read",
18
18
  contextual_dashboard: "write",
19
+ head_up_angle: "write",
20
+ // Where the glasses draw; each model's steps are its display.positionRange.
21
+ dashboard_depth: "write",
22
+ dashboard_height: "write",
19
23
  twelve_hour_time: "write",
20
24
  menu_apps: "write",
21
25
  notifications_enabled: "write",
@@ -29,16 +33,11 @@ export const SETTINGS_BRIDGE_KEYS = {
29
33
  };
30
34
  /**
31
35
  * Read-only keys the engine computes: whether the wearer can see the phone,
32
- * whether the foreground miniapp is drawing on the glasses, the ring's
33
- * battery, and the package that holds the dashboard role (null for nobody).
34
- * They change on their own clocks; the runtime pushes them like stored keys.
36
+ * whether the foreground miniapp is drawing on the glasses, and the ring's
37
+ * battery. They change on their own clocks; the runtime pushes them like
38
+ * stored keys.
35
39
  */
36
- export const DERIVED_SETTINGS_KEYS = [
37
- "phone_active",
38
- "foreground_app_on_screen",
39
- "ring_battery",
40
- "active_dashboard",
41
- ];
40
+ export const DERIVED_SETTINGS_KEYS = ["phone_active", "foreground_app_on_screen", "ring_battery"];
42
41
  export function isDerivedSettingsKey(key) {
43
42
  return DERIVED_SETTINGS_KEYS.includes(key);
44
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"settingsBridge.js","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The settings bridge's vocabulary (session.settings): which engine settings\n * a miniapp with the PLATFORM_SETTINGS permission may read or write, the\n * derived read-only keys the engine computes rather than stores, and the\n * request validation. Pure, so the runtime's handlers stay thin and this can\n * be tested without the native import surface the runtime pulls in.\n *\n * A dashboard miniapp is the usual consumer: it edits the notification and\n * glasses-menu keys the phone's settings screens used to, through the store,\n * so persistence, server sync and the Bluetooth sync of `menu_apps` behave as\n * if a phone screen wrote them. A dashboard's own look is not here; it keeps\n * that in its storage.\n */\n\nexport type SettingsBridgeAccess = \"read\" | \"write\"\n\n/** Stored settings the bridge serves, by key, with what a permitted app may do to each. */\nexport const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>> = {\n // The dashboard's master switch is a phone-side debug knob.\n custom_dashboard_enabled: \"read\",\n contextual_dashboard: \"write\",\n twelve_hour_time: \"write\",\n menu_apps: \"write\",\n notifications_enabled: \"write\",\n notifications_popups: \"write\",\n notifications_popup_seconds: \"write\",\n notifications_hold_during_apps: \"write\",\n notifications_ignore_while_active: \"write\",\n notifications_blocklist: \"write\",\n // Apps the glasses reported posting notifications; the engine records it.\n notification_seen_apps: \"read\",\n}\n\n/**\n * Read-only keys the engine computes: whether the wearer can see the phone,\n * whether the foreground miniapp is drawing on the glasses, the ring's\n * battery, and the package that holds the dashboard role (null for nobody).\n * They change on their own clocks; the runtime pushes them like stored keys.\n */\nexport const DERIVED_SETTINGS_KEYS = [\n \"phone_active\",\n \"foreground_app_on_screen\",\n \"ring_battery\",\n \"active_dashboard\",\n] as const\nexport type DerivedSettingsKey = (typeof DERIVED_SETTINGS_KEYS)[number]\n\nexport function isDerivedSettingsKey(key: string): key is DerivedSettingsKey {\n return (DERIVED_SETTINGS_KEYS as readonly string[]).includes(key)\n}\n\n/** What a permitted app may do with `key`, or null when the bridge does not serve it. */\nexport function settingsBridgeAccess(key: string): SettingsBridgeAccess | null {\n if (isDerivedSettingsKey(key)) return \"read\"\n return SETTINGS_BRIDGE_KEYS[key] ?? null\n}\n\n/** Every key the bridge serves, stored then derived. */\nexport function allSettingsBridgeKeys(): string[] {\n return [...Object.keys(SETTINGS_BRIDGE_KEYS), ...DERIVED_SETTINGS_KEYS]\n}\n\n/**\n * The keys a SETTINGS_GET asks for: everything when `requested` is absent,\n * else the known ones among the requested strings (unknown keys are left\n * out, never rejected).\n */\nexport function selectSettingsBridgeKeys(requested: unknown): string[] {\n if (requested === undefined || requested === null) return allSettingsBridgeKeys()\n if (!Array.isArray(requested)) return []\n return requested.filter((key): key is string => typeof key === \"string\" && settingsBridgeAccess(key) !== null)\n}\n\nexport type SettingsSetValidation =\n | {ok: true; key: string; value: unknown}\n | {ok: false; code: \"INVALID_ARGUMENT\" | \"NOT_PERMITTED\"; message: string}\n\n/** A SETTINGS_SET payload: a writable key and a JSON value (undefined is not a value). */\nexport function validateSettingsSet(payload: Record<string, unknown>): SettingsSetValidation {\n const key = payload.key\n if (typeof key !== \"string\" || !key) {\n return {ok: false, code: \"INVALID_ARGUMENT\", message: \"settings.set requires a key\"}\n }\n const access = settingsBridgeAccess(key)\n if (access === null) return {ok: false, code: \"INVALID_ARGUMENT\", message: `Unknown setting \"${key}\"`}\n if (access !== \"write\") return {ok: false, code: \"NOT_PERMITTED\", message: `Setting \"${key}\" is read-only`}\n if (!(\"value\" in payload) || payload.value === undefined) {\n return {ok: false, code: \"INVALID_ARGUMENT\", message: \"settings.set requires a value\"}\n }\n return {ok: true, key, value: payload.value}\n}\n\n/** The ring's battery as the dashboard shows it: the relayed ring first, else the controller. */\nexport interface RingBattery {\n connected: boolean\n /** -1 when unknown. */\n level: number\n}\n\nexport function ringBatteryFrom(glasses: {\n ringConnected: boolean\n ringBatteryLevel: number\n controllerConnected: boolean\n controllerBatteryLevel: number\n}): RingBattery {\n const relayed = glasses.ringConnected && glasses.ringBatteryLevel >= 0\n return {\n connected: relayed || glasses.controllerConnected,\n level: relayed ? glasses.ringBatteryLevel : glasses.controllerBatteryLevel,\n }\n}\n"]}
1
+ {"version":3,"file":"settingsBridge.js","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The settings bridge's vocabulary (session.settings): which engine settings\n * a miniapp with the PLATFORM_SETTINGS permission may read or write, the\n * derived read-only keys the engine computes rather than stores, and the\n * request validation. Pure, so the runtime's handlers stay thin and this can\n * be tested without the native import surface the runtime pulls in.\n *\n * A dashboard miniapp is the usual consumer: it edits the notification,\n * glasses-menu and display-position keys the phone's settings screens used\n * to, through the store, so persistence, server sync and the Bluetooth sync\n * of `menu_apps` and the position behave as if a phone screen wrote them. A dashboard's own look is not here; it keeps\n * that in its storage.\n */\n\nexport type SettingsBridgeAccess = \"read\" | \"write\"\n\n/** Stored settings the bridge serves, by key, with what a permitted app may do to each. */\nexport const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>> = {\n // The dashboard's master switch is a phone-side debug knob.\n custom_dashboard_enabled: \"read\",\n contextual_dashboard: \"write\",\n head_up_angle: \"write\",\n // Where the glasses draw; each model's steps are its display.positionRange.\n dashboard_depth: \"write\",\n dashboard_height: \"write\",\n twelve_hour_time: \"write\",\n menu_apps: \"write\",\n notifications_enabled: \"write\",\n notifications_popups: \"write\",\n notifications_popup_seconds: \"write\",\n notifications_hold_during_apps: \"write\",\n notifications_ignore_while_active: \"write\",\n notifications_blocklist: \"write\",\n // Apps the glasses reported posting notifications; the engine records it.\n notification_seen_apps: \"read\",\n}\n\n/**\n * Read-only keys the engine computes: whether the wearer can see the phone,\n * whether the foreground miniapp is drawing on the glasses, and the ring's\n * battery. They change on their own clocks; the runtime pushes them like\n * stored keys.\n */\nexport const DERIVED_SETTINGS_KEYS = [\"phone_active\", \"foreground_app_on_screen\", \"ring_battery\"] as const\nexport type DerivedSettingsKey = (typeof DERIVED_SETTINGS_KEYS)[number]\n\nexport function isDerivedSettingsKey(key: string): key is DerivedSettingsKey {\n return (DERIVED_SETTINGS_KEYS as readonly string[]).includes(key)\n}\n\n/** What a permitted app may do with `key`, or null when the bridge does not serve it. */\nexport function settingsBridgeAccess(key: string): SettingsBridgeAccess | null {\n if (isDerivedSettingsKey(key)) return \"read\"\n return SETTINGS_BRIDGE_KEYS[key] ?? null\n}\n\n/** Every key the bridge serves, stored then derived. */\nexport function allSettingsBridgeKeys(): string[] {\n return [...Object.keys(SETTINGS_BRIDGE_KEYS), ...DERIVED_SETTINGS_KEYS]\n}\n\n/**\n * The keys a SETTINGS_GET asks for: everything when `requested` is absent,\n * else the known ones among the requested strings (unknown keys are left\n * out, never rejected).\n */\nexport function selectSettingsBridgeKeys(requested: unknown): string[] {\n if (requested === undefined || requested === null) return allSettingsBridgeKeys()\n if (!Array.isArray(requested)) return []\n return requested.filter((key): key is string => typeof key === \"string\" && settingsBridgeAccess(key) !== null)\n}\n\nexport type SettingsSetValidation =\n | {ok: true; key: string; value: unknown}\n | {ok: false; code: \"INVALID_ARGUMENT\" | \"NOT_PERMITTED\"; message: string}\n\n/** A SETTINGS_SET payload: a writable key and a JSON value (undefined is not a value). */\nexport function validateSettingsSet(payload: Record<string, unknown>): SettingsSetValidation {\n const key = payload.key\n if (typeof key !== \"string\" || !key) {\n return {ok: false, code: \"INVALID_ARGUMENT\", message: \"settings.set requires a key\"}\n }\n const access = settingsBridgeAccess(key)\n if (access === null) return {ok: false, code: \"INVALID_ARGUMENT\", message: `Unknown setting \"${key}\"`}\n if (access !== \"write\") return {ok: false, code: \"NOT_PERMITTED\", message: `Setting \"${key}\" is read-only`}\n if (!(\"value\" in payload) || payload.value === undefined) {\n return {ok: false, code: \"INVALID_ARGUMENT\", message: \"settings.set requires a value\"}\n }\n return {ok: true, key, value: payload.value}\n}\n\n/** The ring's battery as the dashboard shows it: the relayed ring first, else the controller. */\nexport interface RingBattery {\n connected: boolean\n /** -1 when unknown. */\n level: number\n}\n\nexport function ringBatteryFrom(glasses: {\n ringConnected: boolean\n ringBatteryLevel: number\n controllerConnected: boolean\n controllerBatteryLevel: number\n}): RingBattery {\n const relayed = glasses.ringConnected && glasses.ringBatteryLevel >= 0\n return {\n connected: relayed || glasses.controllerConnected,\n level: relayed ? glasses.ringBatteryLevel : glasses.controllerBatteryLevel,\n }\n}\n"]}
@@ -70,7 +70,6 @@ interface AppStatusState {
70
70
  * off leaves it running. Persists the wearer's choice beside the hidden flag.
71
71
  */
72
72
  setAlwaysOn: (packageName: string, status: boolean) => Promise<void>;
73
- setActiveDashboard: (packageName: string) => Promise<void>;
74
73
  /** Replace the whole apps array. Hosts use this when their extra source changes. */
75
74
  setApps: (apps: ClientApp[]) => void;
76
75
  }
@@ -100,10 +99,6 @@ export declare const useUninstall: () => (packageName: string, version?: string)
100
99
  export declare const useActiveApps: () => ClientApp[];
101
100
  /** Always On apps whose background task is running, card or no card. */
102
101
  export declare const useRunningAlwaysOnApps: () => ClientApp[];
103
- /** Installed miniapps that can hold the dashboard role: manifest type "dashboard". */
104
- export declare const useDashboards: () => ClientApp[];
105
- /** The miniapp that holds the dashboard role (ADR-0009), or null when none is installed. */
106
- export declare const useActiveDashboard: () => ClientApp | null;
107
102
  export declare const useActiveBackgroundApps: () => ClientApp[];
108
103
  export declare const useBackgroundApps: () => {
109
104
  active: ClientApp[];
@@ -1 +1 @@
1
- {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/stores/apps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/stores/apps.ts"],"names":[],"mappings":"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"}