@glassly/engine 0.3.5-beta.710 → 0.3.5-beta.760

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 (119) hide show
  1. package/build/engine.d.ts +4 -1
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/engine.js +5 -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 +5 -0
  12. package/build/facades/glasses.js.map +1 -1
  13. package/build/generated/releaseMetadata.js +3 -3
  14. package/build/generated/releaseMetadata.js.map +1 -1
  15. package/build/services/GestureDotsService.d.ts.map +1 -1
  16. package/build/services/GestureDotsService.js +2 -1
  17. package/build/services/GestureDotsService.js.map +1 -1
  18. package/build/services/GlassesTouchInput.d.ts +6 -4
  19. package/build/services/GlassesTouchInput.d.ts.map +1 -1
  20. package/build/services/GlassesTouchInput.js +76 -37
  21. package/build/services/GlassesTouchInput.js.map +1 -1
  22. package/build/services/LocalDisplayManager.d.ts +12 -4
  23. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  24. package/build/services/LocalDisplayManager.js +21 -7
  25. package/build/services/LocalDisplayManager.js.map +1 -1
  26. package/build/services/LocalLlmModelManager.d.ts +3 -1
  27. package/build/services/LocalLlmModelManager.d.ts.map +1 -1
  28. package/build/services/LocalLlmModelManager.js +13 -5
  29. package/build/services/LocalLlmModelManager.js.map +1 -1
  30. package/build/services/LocalLlmPrompt.d.ts +10 -9
  31. package/build/services/LocalLlmPrompt.d.ts.map +1 -1
  32. package/build/services/LocalLlmPrompt.js +43 -32
  33. package/build/services/LocalLlmPrompt.js.map +1 -1
  34. package/build/services/LocalLlmProvider.d.ts.map +1 -1
  35. package/build/services/LocalLlmProvider.js +39 -20
  36. package/build/services/LocalLlmProvider.js.map +1 -1
  37. package/build/services/LocalMiniappRuntime.d.ts +22 -4
  38. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  39. package/build/services/LocalMiniappRuntime.js +108 -134
  40. package/build/services/LocalMiniappRuntime.js.map +1 -1
  41. package/build/services/MiniappExitGesture.d.ts +6 -3
  42. package/build/services/MiniappExitGesture.d.ts.map +1 -1
  43. package/build/services/MiniappExitGesture.js +4 -4
  44. package/build/services/MiniappExitGesture.js.map +1 -1
  45. package/build/services/MiniappWidgetMode.d.ts +6 -4
  46. package/build/services/MiniappWidgetMode.d.ts.map +1 -1
  47. package/build/services/MiniappWidgetMode.js +7 -6
  48. package/build/services/MiniappWidgetMode.js.map +1 -1
  49. package/build/services/SceneRenderer.d.ts.map +1 -1
  50. package/build/services/SceneRenderer.js +3 -1
  51. package/build/services/SceneRenderer.js.map +1 -1
  52. package/build/services/actionInvoke.d.ts +37 -0
  53. package/build/services/actionInvoke.d.ts.map +1 -0
  54. package/build/services/actionInvoke.js +89 -0
  55. package/build/services/actionInvoke.js.map +1 -0
  56. package/build/services/appleWatch/AppleWatchLink.d.ts +42 -0
  57. package/build/services/appleWatch/AppleWatchLink.d.ts.map +1 -0
  58. package/build/services/appleWatch/AppleWatchLink.js +259 -0
  59. package/build/services/appleWatch/AppleWatchLink.js.map +1 -0
  60. package/build/services/controllers.d.ts.map +1 -1
  61. package/build/services/controllers.js +16 -0
  62. package/build/services/controllers.js.map +1 -1
  63. package/build/services/settingsBridge.d.ts +4 -4
  64. package/build/services/settingsBridge.d.ts.map +1 -1
  65. package/build/services/settingsBridge.js +9 -4
  66. package/build/services/settingsBridge.js.map +1 -1
  67. package/build/stores/apps.d.ts.map +1 -1
  68. package/build/stores/apps.js +13 -11
  69. package/build/stores/apps.js.map +1 -1
  70. package/build/stores/bluetoothSettingKeys.d.ts +2 -0
  71. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  72. package/build/stores/bluetoothSettingKeys.js +17 -0
  73. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  74. package/build/stores/settings.d.ts.map +1 -1
  75. package/build/stores/settings.js +32 -2
  76. package/build/stores/settings.js.map +1 -1
  77. package/build/types/capabilities/rayneo-io.d.ts.map +1 -1
  78. package/build/types/capabilities/rayneo-io.js +2 -0
  79. package/build/types/capabilities/rayneo-io.js.map +1 -1
  80. package/build/types/enums.d.ts +2 -1
  81. package/build/types/enums.d.ts.map +1 -1
  82. package/build/types/enums.js +1 -0
  83. package/build/types/enums.js.map +1 -1
  84. package/build/utils/display/scene/index.d.ts +1 -1
  85. package/build/utils/display/scene/index.d.ts.map +1 -1
  86. package/build/utils/display/scene/index.js.map +1 -1
  87. package/build/utils/display/scene/process.d.ts.map +1 -1
  88. package/build/utils/display/scene/process.js +20 -1
  89. package/build/utils/display/scene/process.js.map +1 -1
  90. package/build/utils/display/scene/types.d.ts +1 -1
  91. package/build/utils/display/scene/types.d.ts.map +1 -1
  92. package/build/utils/display/scene/types.js.map +1 -1
  93. package/package.json +7 -7
  94. package/src/engine.ts +5 -1
  95. package/src/facades/ai.ts +3 -3
  96. package/src/facades/glasses.ts +7 -0
  97. package/src/generated/releaseMetadata.ts +3 -3
  98. package/src/services/GestureDotsService.ts +2 -1
  99. package/src/services/GlassesTouchInput.ts +81 -34
  100. package/src/services/LocalDisplayManager.ts +22 -8
  101. package/src/services/LocalLlmModelManager.ts +16 -6
  102. package/src/services/LocalLlmPrompt.ts +56 -37
  103. package/src/services/LocalLlmProvider.ts +49 -22
  104. package/src/services/LocalMiniappRuntime.ts +140 -134
  105. package/src/services/MiniappExitGesture.ts +8 -5
  106. package/src/services/MiniappWidgetMode.ts +9 -7
  107. package/src/services/SceneRenderer.ts +3 -1
  108. package/src/services/actionInvoke.ts +105 -0
  109. package/src/services/appleWatch/AppleWatchLink.ts +297 -0
  110. package/src/services/controllers.ts +17 -0
  111. package/src/services/settingsBridge.ts +9 -4
  112. package/src/stores/apps.ts +12 -10
  113. package/src/stores/bluetoothSettingKeys.ts +20 -0
  114. package/src/stores/settings.ts +34 -2
  115. package/src/types/capabilities/rayneo-io.ts +2 -0
  116. package/src/types/enums.ts +1 -0
  117. package/src/utils/display/scene/index.ts +1 -0
  118. package/src/utils/display/scene/process.ts +17 -1
  119. package/src/utils/display/scene/types.ts +1 -0
@@ -3,6 +3,8 @@ interface ExitGestureApp {
3
3
  packageName: string;
4
4
  name: string;
5
5
  running: boolean;
6
+ /** Running only for a placed widget: the wearer is not in it, so there is nothing to leave. */
7
+ widgetOnly?: boolean;
6
8
  }
7
9
  interface ExitGestureState {
8
10
  apps: ExitGestureApp[];
@@ -12,7 +14,8 @@ interface ExitGestureState {
12
14
  }
13
15
  export interface ExitGestureDependencies {
14
16
  getState: () => ExitGestureState;
15
- getVisiblePackageName: () => string | null;
17
+ /** The miniapp a double tap leaves right now, or null when the tap is the system's. */
18
+ getTargetPackageName: () => string | null;
16
19
  beginExitNotice: (packageName: string, text: string) => boolean;
17
20
  finishExitNotice: (packageName: string) => void;
18
21
  cancelExitNotice: (packageName: string) => void;
@@ -24,8 +27,8 @@ export declare class MiniappExitGesture {
24
27
  private readonly deps;
25
28
  private pending;
26
29
  constructor(deps: ExitGestureDependencies);
27
- /** Returns true when the system gesture was consumed by a visible miniapp exit. */
28
- tryExitVisibleMiniapp(): boolean;
30
+ /** Returns true when the system gesture was consumed by a miniapp exit. */
31
+ tryExitMiniapp(): boolean;
29
32
  /** Cancel an in-flight handoff when the device-event router shuts down. */
30
33
  reset(): void;
31
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappExitGesture.d.ts","sourceRoot":"","sources":["../../src/services/MiniappExitGesture.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,OAAO,CAAA;AAE1C,UAAU,cAAc;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,cAAc,EAAE,CAAA;IACtB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7C;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,gBAAgB,CAAA;IAChC,qBAAqB,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IAC1C,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IAC/D,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAC3D,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED,qEAAqE;AACrE,qBAAa,kBAAkB;IAGV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFxC,OAAO,CAAC,OAAO,CAAsD;gBAEjC,IAAI,EAAE,uBAAuB;IAEjE,mFAAmF;IAC5E,qBAAqB,IAAI,OAAO;IAmCvC,2EAA2E;IACpE,KAAK,IAAI,IAAI;CAOrB"}
1
+ {"version":3,"file":"MiniappExitGesture.d.ts","sourceRoot":"","sources":["../../src/services/MiniappExitGesture.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,OAAO,CAAA;AAE1C,UAAU,cAAc;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,cAAc,EAAE,CAAA;IACtB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7C;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,gBAAgB,CAAA;IAChC,uFAAuF;IACvF,oBAAoB,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IACzC,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IAC/D,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAC3D,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED,qEAAqE;AACrE,qBAAa,kBAAkB;IAGV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFxC,OAAO,CAAC,OAAO,CAAsD;gBAEjC,IAAI,EAAE,uBAAuB;IAEjE,2EAA2E;IACpE,cAAc,IAAI,OAAO;IAmChC,2EAA2E;IACpE,KAAK,IAAI,IAAI;CAOrB"}
@@ -6,16 +6,16 @@ export class MiniappExitGesture {
6
6
  constructor(deps) {
7
7
  this.deps = deps;
8
8
  }
9
- /** Returns true when the system gesture was consumed by a visible miniapp exit. */
10
- tryExitVisibleMiniapp() {
9
+ /** Returns true when the system gesture was consumed by a miniapp exit. */
10
+ tryExitMiniapp() {
11
11
  // Swallow repeat taps while the notice is already counting down.
12
12
  if (this.pending)
13
13
  return true;
14
- const packageName = this.deps.getVisiblePackageName();
14
+ const packageName = this.deps.getTargetPackageName();
15
15
  if (!packageName)
16
16
  return false;
17
17
  const app = this.deps.getState().apps.find((candidate) => candidate.packageName === packageName);
18
- if (!app?.running)
18
+ if (!app?.running || app.widgetOnly)
19
19
  return false;
20
20
  const name = app.name.trim() || packageName;
21
21
  if (!this.deps.beginExitNotice(packageName, `Exiting ${name}…`))
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappExitGesture.js","sourceRoot":"","sources":["../../src/services/MiniappExitGesture.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAA;AAyB1C,qEAAqE;AACrE,MAAM,OAAO,kBAAkB;IAGO;IAF5B,OAAO,GAAkD,IAAI,CAAA;IAErE,YAAoC,IAA6B;QAA7B,SAAI,GAAJ,IAAI,CAAyB;IAAG,CAAC;IAErE,mFAAmF;IAC5E,qBAAqB;QAC1B,iEAAiE;QACjE,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QAE7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAA;QACrD,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,KAAK,WAAW,CAAC,CAAA;QAChG,IAAI,CAAC,GAAG,EAAE,OAAO;YAAE,OAAO,KAAK,CAAA;QAE/B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,WAAW,CAAA;QAC3C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,WAAW,IAAI,GAAG,CAAC;YAAE,OAAO,KAAK,CAAA;QAE7E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAA;YAClC,IAAI,KAAK,CAAC,mBAAmB,KAAK,WAAW;gBAAE,KAAK,CAAC,eAAe,EAAE,CAAA;YAEtE,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,KAAK,WAAW,IAAI,SAAS,CAAC,OAAO,CAAC,CAAA;YAC/G,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;YACvE,KAAK,IAAI;iBACN,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,WAAW,GAAG,EAAE,KAAK,CAAC,CAAA;YAC5E,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACZ,IAAI,IAAI,CAAC,OAAO,EAAE,WAAW,KAAK,WAAW;oBAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;gBAClE,mEAAmE;gBACnE,iDAAiD;gBACjD,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;YACzC,CAAC,CAAC,CAAA;QACN,CAAC,EAAE,sBAAsB,CAAC,CAAA;QAE1B,IAAI,CAAC,OAAO,GAAG,EAAC,WAAW,EAAE,OAAO,EAAC,CAAA;QACrC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,2EAA2E;IACpE,KAAK;QACV,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QACzB,MAAM,EAAC,WAAW,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC;CACF","sourcesContent":["export const MINIAPP_EXIT_NOTICE_MS = 1500\n\ninterface ExitGestureApp {\n packageName: string\n name: string\n running: boolean\n}\n\ninterface ExitGestureState {\n apps: ExitGestureApp[]\n foregroundedPackage: string | null\n clearForeground: () => void\n stop: (packageName: string) => Promise<void>\n}\n\nexport interface ExitGestureDependencies {\n getState: () => ExitGestureState\n getVisiblePackageName: () => string | null\n beginExitNotice: (packageName: string, text: string) => boolean\n finishExitNotice: (packageName: string) => void\n cancelExitNotice: (packageName: string) => void\n setTimeout: (callback: () => void, delay: number) => number\n clearTimeout: (timerId: number) => void\n}\n\n/** Owns the short double-tap → exit-notice → normal-stop handoff. */\nexport class MiniappExitGesture {\n private pending: {packageName: string; timerId: number} | null = null\n\n public constructor(private readonly deps: ExitGestureDependencies) {}\n\n /** Returns true when the system gesture was consumed by a visible miniapp exit. */\n public tryExitVisibleMiniapp(): boolean {\n // Swallow repeat taps while the notice is already counting down.\n if (this.pending) return true\n\n const packageName = this.deps.getVisiblePackageName()\n if (!packageName) return false\n\n const app = this.deps.getState().apps.find((candidate) => candidate.packageName === packageName)\n if (!app?.running) return false\n\n const name = app.name.trim() || packageName\n if (!this.deps.beginExitNotice(packageName, `Exiting ${name}…`)) return false\n\n const timerId = this.deps.setTimeout(() => {\n const state = this.deps.getState()\n if (state.foregroundedPackage === packageName) state.clearForeground()\n\n const stillRunning = state.apps.some((candidate) => candidate.packageName === packageName && candidate.running)\n const stop = stillRunning ? state.stop(packageName) : Promise.resolve()\n void stop\n .catch((error) => {\n console.error(`MiniappExitGesture: failed to stop ${packageName}:`, error)\n })\n .finally(() => {\n if (this.pending?.packageName === packageName) this.pending = null\n // LocalMiniappRuntime.onUnmount normally finishes the notice. This\n // fallback also handles a failed/no-op teardown.\n this.deps.finishExitNotice(packageName)\n })\n }, MINIAPP_EXIT_NOTICE_MS)\n\n this.pending = {packageName, timerId}\n return true\n }\n\n /** Cancel an in-flight handoff when the device-event router shuts down. */\n public reset(): void {\n if (!this.pending) return\n const {packageName, timerId} = this.pending\n this.pending = null\n this.deps.clearTimeout(timerId)\n this.deps.cancelExitNotice(packageName)\n }\n}\n"]}
1
+ {"version":3,"file":"MiniappExitGesture.js","sourceRoot":"","sources":["../../src/services/MiniappExitGesture.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAA;AA4B1C,qEAAqE;AACrE,MAAM,OAAO,kBAAkB;IAGO;IAF5B,OAAO,GAAkD,IAAI,CAAA;IAErE,YAAoC,IAA6B;QAA7B,SAAI,GAAJ,IAAI,CAAyB;IAAG,CAAC;IAErE,2EAA2E;IACpE,cAAc;QACnB,iEAAiE;QACjE,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QAE7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAA;QACpD,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,KAAK,WAAW,CAAC,CAAA;QAChG,IAAI,CAAC,GAAG,EAAE,OAAO,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO,KAAK,CAAA;QAEjD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,WAAW,CAAA;QAC3C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,WAAW,IAAI,GAAG,CAAC;YAAE,OAAO,KAAK,CAAA;QAE7E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAA;YAClC,IAAI,KAAK,CAAC,mBAAmB,KAAK,WAAW;gBAAE,KAAK,CAAC,eAAe,EAAE,CAAA;YAEtE,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,KAAK,WAAW,IAAI,SAAS,CAAC,OAAO,CAAC,CAAA;YAC/G,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;YACvE,KAAK,IAAI;iBACN,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,WAAW,GAAG,EAAE,KAAK,CAAC,CAAA;YAC5E,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACZ,IAAI,IAAI,CAAC,OAAO,EAAE,WAAW,KAAK,WAAW;oBAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;gBAClE,mEAAmE;gBACnE,iDAAiD;gBACjD,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;YACzC,CAAC,CAAC,CAAA;QACN,CAAC,EAAE,sBAAsB,CAAC,CAAA;QAE1B,IAAI,CAAC,OAAO,GAAG,EAAC,WAAW,EAAE,OAAO,EAAC,CAAA;QACrC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,2EAA2E;IACpE,KAAK;QACV,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QACzB,MAAM,EAAC,WAAW,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC;CACF","sourcesContent":["export const MINIAPP_EXIT_NOTICE_MS = 1500\n\ninterface ExitGestureApp {\n packageName: string\n name: string\n running: boolean\n /** Running only for a placed widget: the wearer is not in it, so there is nothing to leave. */\n widgetOnly?: boolean\n}\n\ninterface ExitGestureState {\n apps: ExitGestureApp[]\n foregroundedPackage: string | null\n clearForeground: () => void\n stop: (packageName: string) => Promise<void>\n}\n\nexport interface ExitGestureDependencies {\n getState: () => ExitGestureState\n /** The miniapp a double tap leaves right now, or null when the tap is the system's. */\n getTargetPackageName: () => string | null\n beginExitNotice: (packageName: string, text: string) => boolean\n finishExitNotice: (packageName: string) => void\n cancelExitNotice: (packageName: string) => void\n setTimeout: (callback: () => void, delay: number) => number\n clearTimeout: (timerId: number) => void\n}\n\n/** Owns the short double-tap → exit-notice → normal-stop handoff. */\nexport class MiniappExitGesture {\n private pending: {packageName: string; timerId: number} | null = null\n\n public constructor(private readonly deps: ExitGestureDependencies) {}\n\n /** Returns true when the system gesture was consumed by a miniapp exit. */\n public tryExitMiniapp(): boolean {\n // Swallow repeat taps while the notice is already counting down.\n if (this.pending) return true\n\n const packageName = this.deps.getTargetPackageName()\n if (!packageName) return false\n\n const app = this.deps.getState().apps.find((candidate) => candidate.packageName === packageName)\n if (!app?.running || app.widgetOnly) return false\n\n const name = app.name.trim() || packageName\n if (!this.deps.beginExitNotice(packageName, `Exiting ${name}…`)) return false\n\n const timerId = this.deps.setTimeout(() => {\n const state = this.deps.getState()\n if (state.foregroundedPackage === packageName) state.clearForeground()\n\n const stillRunning = state.apps.some((candidate) => candidate.packageName === packageName && candidate.running)\n const stop = stillRunning ? state.stop(packageName) : Promise.resolve()\n void stop\n .catch((error) => {\n console.error(`MiniappExitGesture: failed to stop ${packageName}:`, error)\n })\n .finally(() => {\n if (this.pending?.packageName === packageName) this.pending = null\n // LocalMiniappRuntime.onUnmount normally finishes the notice. This\n // fallback also handles a failed/no-op teardown.\n this.deps.finishExitNotice(packageName)\n })\n }, MINIAPP_EXIT_NOTICE_MS)\n\n this.pending = {packageName, timerId}\n return true\n }\n\n /** Cancel an in-flight handoff when the device-event router shuts down. */\n public reset(): void {\n if (!this.pending) return\n const {packageName, timerId} = this.pending\n this.pending = null\n this.deps.clearTimeout(timerId)\n this.deps.cancelExitNotice(packageName)\n }\n}\n"]}
@@ -2,14 +2,16 @@
2
2
  * Widget mode: a miniapp whose background context runs only because a host
3
3
  * (the dashboard) placed one of its widgets. It is running, but it has no
4
4
  * card in the app switcher, draws nothing of its own on the glasses and
5
- * hears no touch input; the Widgets pill lists it. Opening the app from the
6
- * home screen promotes it to a normal run; closing a placed app from the
7
- * tray demotes it back here instead of stopping it.
5
+ * hears no touch input; the Widgets pill lists it. Foregrounding the app on
6
+ * the phone promotes it to a normal run (the store's start/setForeground);
7
+ * closing a placed app from the tray demotes it back here instead of
8
+ * stopping it.
8
9
  *
9
10
  * The runtime is the writer (placements come and go through it); the apps
10
11
  * store projects `widgetOnly` from here beside `running`.
11
12
  */
12
- type Listener = () => void;
13
+ /** Called with the app whose mode changed and whether it is now widget-only. */
14
+ type Listener = (packageName: string, widgetOnly: boolean) => void;
13
15
  export declare const miniappWidgetMode: {
14
16
  /** Mark (or clear) widget mode. Clearing promotes: the app keeps running as a normal app. */
15
17
  setWidgetOnly(packageName: string, on: boolean): void;
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappWidgetMode.d.ts","sourceRoot":"","sources":["../../src/services/MiniappWidgetMode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAA;AAiB1B,eAAO,MAAM,iBAAiB;IAC5B,6FAA6F;+BAClE,MAAM,MAAM,OAAO,GAAG,IAAI;8BAM3B,MAAM,GAAG,OAAO;IAI1C,oEAAoE;2BAC7C,MAAM,MAAM,OAAO,GAAG,IAAI;0BAK3B,MAAM,GAAG,OAAO;cAI5B,MAAM,EAAE;kBAIJ,QAAQ,GAAG,MAAM,IAAI;CAMpC,CAAA"}
1
+ {"version":3,"file":"MiniappWidgetMode.d.ts","sourceRoot":"","sources":["../../src/services/MiniappWidgetMode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,gFAAgF;AAChF,KAAK,QAAQ,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,IAAI,CAAA;AAiBlE,eAAO,MAAM,iBAAiB;IAC5B,6FAA6F;+BAClE,MAAM,MAAM,OAAO,GAAG,IAAI;8BAM3B,MAAM,GAAG,OAAO;IAI1C,oEAAoE;2BAC7C,MAAM,MAAM,OAAO,GAAG,IAAI;0BAK3B,MAAM,GAAG,OAAO;cAI5B,MAAM,EAAE;kBAIJ,QAAQ,GAAG,MAAM,IAAI;CAMpC,CAAA"}
@@ -2,9 +2,10 @@
2
2
  * Widget mode: a miniapp whose background context runs only because a host
3
3
  * (the dashboard) placed one of its widgets. It is running, but it has no
4
4
  * card in the app switcher, draws nothing of its own on the glasses and
5
- * hears no touch input; the Widgets pill lists it. Opening the app from the
6
- * home screen promotes it to a normal run; closing a placed app from the
7
- * tray demotes it back here instead of stopping it.
5
+ * hears no touch input; the Widgets pill lists it. Foregrounding the app on
6
+ * the phone promotes it to a normal run (the store's start/setForeground);
7
+ * closing a placed app from the tray demotes it back here instead of
8
+ * stopping it.
8
9
  *
9
10
  * The runtime is the writer (placements come and go through it); the apps
10
11
  * store projects `widgetOnly` from here beside `running`.
@@ -13,10 +14,10 @@ const widgetOnly = new Set();
13
14
  /** Providers with at least one widget placed by some host. */
14
15
  const placed = new Set();
15
16
  const listeners = new Set();
16
- function notify() {
17
+ function notify(packageName, on) {
17
18
  for (const fn of listeners) {
18
19
  try {
19
- fn();
20
+ fn(packageName, on);
20
21
  }
21
22
  catch (e) {
22
23
  console.warn("MiniappWidgetMode: listener threw", e);
@@ -30,7 +31,7 @@ export const miniappWidgetMode = {
30
31
  if (on)
31
32
  widgetOnly.add(packageName);
32
33
  if (changed)
33
- notify();
34
+ notify(packageName, on);
34
35
  },
35
36
  isWidgetOnly(packageName) {
36
37
  return widgetOnly.has(packageName);
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappWidgetMode.js","sourceRoot":"","sources":["../../src/services/MiniappWidgetMode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAA;AACpC,8DAA8D;AAC9D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAA;AAChC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY,CAAA;AAErC,SAAS,MAAM;IACb,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,EAAE,EAAE,CAAA;QACN,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,CAAC,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,6FAA6F;IAC7F,aAAa,CAAC,WAAmB,EAAE,EAAW;QAC5C,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QAClF,IAAI,EAAE;YAAE,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACnC,IAAI,OAAO;YAAE,MAAM,EAAE,CAAA;IACvB,CAAC;IAED,YAAY,CAAC,WAAmB;QAC9B,OAAO,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACpC,CAAC;IAED,oEAAoE;IACpE,SAAS,CAAC,WAAmB,EAAE,EAAW;QACxC,IAAI,EAAE;YAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;;YAC1B,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IACjC,CAAC;IAED,QAAQ,CAAC,WAAmB;QAC1B,OAAO,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC/B,CAAC;IAED,SAAS,CAAC,EAAY;QACpB,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACjB,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACtB,CAAC,CAAA;IACH,CAAC;CACF,CAAA","sourcesContent":["/**\n * Widget mode: a miniapp whose background context runs only because a host\n * (the dashboard) placed one of its widgets. It is running, but it has no\n * card in the app switcher, draws nothing of its own on the glasses and\n * hears no touch input; the Widgets pill lists it. Opening the app from the\n * home screen promotes it to a normal run; closing a placed app from the\n * tray demotes it back here instead of stopping it.\n *\n * The runtime is the writer (placements come and go through it); the apps\n * store projects `widgetOnly` from here beside `running`.\n */\n\ntype Listener = () => void\n\nconst widgetOnly = new Set<string>()\n/** Providers with at least one widget placed by some host. */\nconst placed = new Set<string>()\nconst listeners = new Set<Listener>()\n\nfunction notify(): void {\n for (const fn of listeners) {\n try {\n fn()\n } catch (e) {\n console.warn(\"MiniappWidgetMode: listener threw\", e)\n }\n }\n}\n\nexport const miniappWidgetMode = {\n /** Mark (or clear) widget mode. Clearing promotes: the app keeps running as a normal app. */\n setWidgetOnly(packageName: string, on: boolean): void {\n const changed = on ? !widgetOnly.has(packageName) : widgetOnly.delete(packageName)\n if (on) widgetOnly.add(packageName)\n if (changed) notify()\n },\n\n isWidgetOnly(packageName: string): boolean {\n return widgetOnly.has(packageName)\n },\n\n /** Record whether any host has a widget of this provider placed. */\n setPlaced(packageName: string, on: boolean): void {\n if (on) placed.add(packageName)\n else placed.delete(packageName)\n },\n\n isPlaced(packageName: string): boolean {\n return placed.has(packageName)\n },\n\n getAll(): string[] {\n return Array.from(widgetOnly)\n },\n\n subscribe(fn: Listener): () => void {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\n}\n"]}
1
+ {"version":3,"file":"MiniappWidgetMode.js","sourceRoot":"","sources":["../../src/services/MiniappWidgetMode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAA;AACpC,8DAA8D;AAC9D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAA;AAChC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY,CAAA;AAErC,SAAS,MAAM,CAAC,WAAmB,EAAE,EAAW;IAC9C,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QACrB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,CAAC,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,6FAA6F;IAC7F,aAAa,CAAC,WAAmB,EAAE,EAAW;QAC5C,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QAClF,IAAI,EAAE;YAAE,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACnC,IAAI,OAAO;YAAE,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;IACtC,CAAC;IAED,YAAY,CAAC,WAAmB;QAC9B,OAAO,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACpC,CAAC;IAED,oEAAoE;IACpE,SAAS,CAAC,WAAmB,EAAE,EAAW;QACxC,IAAI,EAAE;YAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;;YAC1B,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IACjC,CAAC;IAED,QAAQ,CAAC,WAAmB;QAC1B,OAAO,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC/B,CAAC;IAED,SAAS,CAAC,EAAY;QACpB,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACjB,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACtB,CAAC,CAAA;IACH,CAAC;CACF,CAAA","sourcesContent":["/**\n * Widget mode: a miniapp whose background context runs only because a host\n * (the dashboard) placed one of its widgets. It is running, but it has no\n * card in the app switcher, draws nothing of its own on the glasses and\n * hears no touch input; the Widgets pill lists it. Foregrounding the app on\n * the phone promotes it to a normal run (the store's start/setForeground);\n * closing a placed app from the tray demotes it back here instead of\n * stopping it.\n *\n * The runtime is the writer (placements come and go through it); the apps\n * store projects `widgetOnly` from here beside `running`.\n */\n\n/** Called with the app whose mode changed and whether it is now widget-only. */\ntype Listener = (packageName: string, widgetOnly: boolean) => void\n\nconst widgetOnly = new Set<string>()\n/** Providers with at least one widget placed by some host. */\nconst placed = new Set<string>()\nconst listeners = new Set<Listener>()\n\nfunction notify(packageName: string, on: boolean): void {\n for (const fn of listeners) {\n try {\n fn(packageName, on)\n } catch (e) {\n console.warn(\"MiniappWidgetMode: listener threw\", e)\n }\n }\n}\n\nexport const miniappWidgetMode = {\n /** Mark (or clear) widget mode. Clearing promotes: the app keeps running as a normal app. */\n setWidgetOnly(packageName: string, on: boolean): void {\n const changed = on ? !widgetOnly.has(packageName) : widgetOnly.delete(packageName)\n if (on) widgetOnly.add(packageName)\n if (changed) notify(packageName, on)\n },\n\n isWidgetOnly(packageName: string): boolean {\n return widgetOnly.has(packageName)\n },\n\n /** Record whether any host has a widget of this provider placed. */\n setPlaced(packageName: string, on: boolean): void {\n if (on) placed.add(packageName)\n else placed.delete(packageName)\n },\n\n isPlaced(packageName: string): boolean {\n return placed.has(packageName)\n },\n\n getAll(): string[] {\n return Array.from(widgetOnly)\n },\n\n subscribe(fn: Listener): () => void {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\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":"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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 // 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,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"}