@glassly/engine 0.3.1-dev.494 → 0.3.1-dev.523

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
@@ -1 +1 @@
1
- {"version":3,"file":"even-realities-g2.js","sourceRoot":"","sources":["../../../src/types/capabilities/even-realities-g2.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAiB;IAC3C,SAAS,EAAE,mBAAmB;IAE9B,kDAAkD;IAClD,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,2EAA2E;IAC3E,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE;QACP,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,OAAO;QACd,gBAAgB,EAAE,IAAI;QACtB,6EAA6E;QAC7E,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC;QACrC,WAAW,EAAE,EAAC,UAAU,EAAE,EAAE,EAAC;QAC7B,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,IAAI;QAEtB,mDAAmD;QACnD,KAAK,EAAE,GAAG;QACV,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,CAAC,EAAE,gDAAgD;QACpE,gBAAgB,EAAE,CAAC,EAAE,gCAAgC;QACrD,sEAAsE;QACtE,2EAA2E;QAC3E,2EAA2E;QAC3E,+BAA+B;QAC/B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,kCAAkC;QACpD,eAAe,EAAE,CAAC;QAClB,aAAa,EAAE,IAAI;KACpB;IAED,0EAA0E;IAC1E,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,KAAK;KACd;IAED,oDAAoD;IACpD,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,IAAI;IAEb,gCAAgC;IAChC,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IAET,qDAAqD;IACrD,SAAS,EAAE,IAAI;IACf,MAAM,EAAE;QACN,KAAK,EAAE,CAAC;QACR,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,CAAC;gBACnF,YAAY,EAAE,IAAI;aACnB;SACF;KACF;IAED,+CAA+C;IAC/C,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IAEX,qBAAqB;IACrB,KAAK,EAAE;QACL,kBAAkB,EAAE,KAAK;KAC1B;IAED,+CAA+C;IAC/C,OAAO,EAAE,KAAK;IAEd,4EAA4E;IAC5E,MAAM,EAAE,KAAK;IAEb,sEAAsE;IACtE,iBAAiB,EAAE,IAAI;IAEvB,0EAA0E;IAC1E,4EAA4E;IAC5E,kBAAkB,EAAE,IAAI;IAExB,wEAAwE;IACxE,sEAAsE;IACtE,mBAAmB,EAAE,IAAI;IAEzB,uEAAuE;IACvE,sBAAsB,EAAE,IAAI;IAC5B,8BAA8B,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;CAC9C,CAAA","sourcesContent":["/**\n * @fileoverview Even Realities G2 Hardware Capabilities\n *\n * Capability profile for the Even Realities G2 smart glasses model.\n * G2 uses the EvenHub protocol with protobuf-based commands.\n */\n\nimport type {Capabilities} from \"../hardware\"\n\n/**\n * Even Realities G2 capability profile\n */\nexport const evenRealitiesG2: Capabilities = {\n modelName: \"Even Realities G2\",\n\n // Camera capabilities - G2 does not have a camera\n hasCamera: false,\n camera: null,\n\n // Display capabilities - G2 has a green monochrome display (similar to G1)\n hasDisplay: true,\n display: {\n count: 2,\n isColor: false,\n color: \"green\",\n canDisplayBitmap: true,\n // Physical panel; stock firmware blits its 576×288 scene canvas into it 1:1.\n resolution: {width: 640, height: 480},\n fieldOfView: {horizontal: 25},\n maxTextLines: 8,\n adjustBrightness: true,\n\n // Scene display API — EvenHub retained containers.\n width: 576,\n height: 288,\n canPosition: true,\n maxTextElements: 6, // firmware text-container pool (rects share it)\n maxImageElements: 4, // firmware image-container pool\n // No maxImagePx: the SGC tiles larger images across multiple firmware\n // containers (each ≤200x100 — the hardware-verified per-container transfer\n // envelope). Pathological sizes needing more than the 4-container pool are\n // dropped SGC-side with a log.\n shapes: [\"rect\"], // bordered empty container ≈ rect\n intensityLevels: 2,\n partialUpdate: true,\n },\n\n // Microphone capabilities - G2 has one microphone (right side), LC3 codec\n hasMicrophone: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n\n // Speaker capabilities - G2 does not have a speaker\n hasSpeaker: false,\n speaker: null,\n\n // IMU capabilities - G2 has IMU\n hasIMU: true,\n imu: null,\n\n // Button capabilities - G2 has a capacitive touchbar\n hasButton: true,\n button: {\n count: 1,\n buttons: [\n {\n type: \"swipe1d\",\n events: [\"TAP\", \"DOUBLE_TAP\", \"TRIPLE_TAP\", \"PRESS_HOLD\", \"SWIPE_UP\", \"SWIPE_DOWN\"],\n isCapacitive: true,\n },\n ],\n },\n\n // Light capabilities - G2 does not have lights\n hasLight: false,\n light: null,\n\n // Power capabilities\n power: {\n hasExternalBattery: false,\n },\n\n // WiFi capabilities - G2 does not support WiFi\n hasWifi: false,\n\n // OTA capabilities - G2 firmware updates are not driven by the ASG OTA flow\n hasOta: false,\n\n // Glassly CFW is bundled for the G2 and flashed over BLE from the app\n hasCustomFirmware: true,\n\n // Dashboard - G2 renders Even Realities' native dashboard in firmware, so\n // Glassly does not manage the dashboard or expose dashboard settings for it\n hasNativeDashboard: true,\n\n // Navigation - firmware nav UI on service 0x08 (card, 176×176 mini map,\n // 576×188 overview map); magnetometer streams only while it is active\n hasNativeNavigation: true,\n\n // Firmware notification centre: popups + history, on-glass app filter.\n hasNativeNotifications: true,\n nativeNotificationPopupSeconds: [0, 5, 7, 10],\n}\n"]}
1
+ {"version":3,"file":"even-realities-g2.js","sourceRoot":"","sources":["../../../src/types/capabilities/even-realities-g2.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAiB;IAC3C,SAAS,EAAE,mBAAmB;IAE9B,kDAAkD;IAClD,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,2EAA2E;IAC3E,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE;QACP,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,OAAO;QACd,gBAAgB,EAAE,IAAI;QACtB,6EAA6E;QAC7E,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC;QACrC,WAAW,EAAE,EAAC,UAAU,EAAE,EAAE,EAAC;QAC7B,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,EAAC,MAAM,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAC,EAAE,KAAK,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAC,EAAC;QAEnE,mDAAmD;QACnD,KAAK,EAAE,GAAG;QACV,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,CAAC,EAAE,gDAAgD;QACpE,gBAAgB,EAAE,CAAC,EAAE,gCAAgC;QACrD,sEAAsE;QACtE,2EAA2E;QAC3E,2EAA2E;QAC3E,+BAA+B;QAC/B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,kCAAkC;QACpD,eAAe,EAAE,CAAC;QAClB,aAAa,EAAE,IAAI;KACpB;IAED,0EAA0E;IAC1E,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,KAAK;KACd;IAED,oDAAoD;IACpD,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,IAAI;IAEb,gCAAgC;IAChC,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IAET,qDAAqD;IACrD,SAAS,EAAE,IAAI;IACf,MAAM,EAAE;QACN,KAAK,EAAE,CAAC;QACR,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,CAAC;gBACnF,YAAY,EAAE,IAAI;aACnB;SACF;KACF;IAED,+CAA+C;IAC/C,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IAEX,qBAAqB;IACrB,KAAK,EAAE;QACL,kBAAkB,EAAE,KAAK;KAC1B;IAED,+CAA+C;IAC/C,OAAO,EAAE,KAAK;IAEd,4EAA4E;IAC5E,MAAM,EAAE,KAAK;IAEb,sEAAsE;IACtE,iBAAiB,EAAE,IAAI;IAEvB,0EAA0E;IAC1E,4EAA4E;IAC5E,kBAAkB,EAAE,IAAI;IAExB,wEAAwE;IACxE,sEAAsE;IACtE,mBAAmB,EAAE,IAAI;IAEzB,uEAAuE;IACvE,sBAAsB,EAAE,IAAI;IAC5B,8BAA8B,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;CAC9C,CAAA","sourcesContent":["/**\n * @fileoverview Even Realities G2 Hardware Capabilities\n *\n * Capability profile for the Even Realities G2 smart glasses model.\n * G2 uses the EvenHub protocol with protobuf-based commands.\n */\n\nimport type {Capabilities} from \"../hardware\"\n\n/**\n * Even Realities G2 capability profile\n */\nexport const evenRealitiesG2: Capabilities = {\n modelName: \"Even Realities G2\",\n\n // Camera capabilities - G2 does not have a camera\n hasCamera: false,\n camera: null,\n\n // Display capabilities - G2 has a green monochrome display (similar to G1)\n hasDisplay: true,\n display: {\n count: 2,\n isColor: false,\n color: \"green\",\n canDisplayBitmap: true,\n // Physical panel; stock firmware blits its 576×288 scene canvas into it 1:1.\n resolution: {width: 640, height: 480},\n fieldOfView: {horizontal: 25},\n maxTextLines: 8,\n adjustBrightness: true,\n positionRange: {height: {min: 0, max: 12}, depth: {min: 1, max: 3}},\n\n // Scene display API — EvenHub retained containers.\n width: 576,\n height: 288,\n canPosition: true,\n maxTextElements: 6, // firmware text-container pool (rects share it)\n maxImageElements: 4, // firmware image-container pool\n // No maxImagePx: the SGC tiles larger images across multiple firmware\n // containers (each ≤200x100 — the hardware-verified per-container transfer\n // envelope). Pathological sizes needing more than the 4-container pool are\n // dropped SGC-side with a log.\n shapes: [\"rect\"], // bordered empty container ≈ rect\n intensityLevels: 2,\n partialUpdate: true,\n },\n\n // Microphone capabilities - G2 has one microphone (right side), LC3 codec\n hasMicrophone: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n\n // Speaker capabilities - G2 does not have a speaker\n hasSpeaker: false,\n speaker: null,\n\n // IMU capabilities - G2 has IMU\n hasIMU: true,\n imu: null,\n\n // Button capabilities - G2 has a capacitive touchbar\n hasButton: true,\n button: {\n count: 1,\n buttons: [\n {\n type: \"swipe1d\",\n events: [\"TAP\", \"DOUBLE_TAP\", \"TRIPLE_TAP\", \"PRESS_HOLD\", \"SWIPE_UP\", \"SWIPE_DOWN\"],\n isCapacitive: true,\n },\n ],\n },\n\n // Light capabilities - G2 does not have lights\n hasLight: false,\n light: null,\n\n // Power capabilities\n power: {\n hasExternalBattery: false,\n },\n\n // WiFi capabilities - G2 does not support WiFi\n hasWifi: false,\n\n // OTA capabilities - G2 firmware updates are not driven by the ASG OTA flow\n hasOta: false,\n\n // Glassly CFW is bundled for the G2 and flashed over BLE from the app\n hasCustomFirmware: true,\n\n // Dashboard - G2 renders Even Realities' native dashboard in firmware, so\n // Glassly does not manage the dashboard or expose dashboard settings for it\n hasNativeDashboard: true,\n\n // Navigation - firmware nav UI on service 0x08 (card, 176×176 mini map,\n // 576×188 overview map); magnetometer streams only while it is active\n hasNativeNavigation: true,\n\n // Firmware notification centre: popups + history, on-glass app filter.\n hasNativeNotifications: true,\n nativeNotificationPopupSeconds: [0, 5, 7, 10],\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"mentra-display.d.ts","sourceRoot":"","sources":["../../../src/types/capabilities/mentra-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AAE7C;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,YAiE3B,CAAA"}
1
+ {"version":3,"file":"mentra-display.d.ts","sourceRoot":"","sources":["../../../src/types/capabilities/mentra-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AAE7C;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,YAkE3B,CAAA"}
@@ -23,6 +23,7 @@ export const mentraDisplay = {
23
23
  fieldOfView: { horizontal: 25 },
24
24
  maxTextLines: 5,
25
25
  adjustBrightness: true,
26
+ positionRange: { height: { min: 0, max: 8 }, depth: { min: 1, max: 4 } },
26
27
  // Scene display API — firmware canvas components on the 500x220 virtual
27
28
  // screen centered in the 640x480 A6N panel (mos_display_config.c).
28
29
  width: 500,
@@ -1 +1 @@
1
- {"version":3,"file":"mentra-display.js","sourceRoot":"","sources":["../../../src/types/capabilities/mentra-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,SAAS,EAAE,gBAAgB;IAE3B,8DAA8D;IAC9D,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,uEAAuE;IACvE,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE;QACP,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,OAAO;QACd,gBAAgB,EAAE,IAAI;QACtB,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC;QACrC,WAAW,EAAE,EAAC,UAAU,EAAE,EAAE,EAAC;QAC7B,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,IAAI;QAEtB,wEAAwE;QACxE,mEAAmE;QACnE,KAAK,EAAE,GAAG;QACV,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,CAAC,EAAE,iDAAiD;QACrE,gBAAgB,EAAE,CAAC,EAAE,iCAAiC;QACtD,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC,EAAE,gDAAgD;QACvF,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,qDAAqD;QACvE,eAAe,EAAE,CAAC,EAAE,oBAAoB;QACxC,aAAa,EAAE,IAAI;KACpB;IAED,0EAA0E;IAC1E,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,KAAK;KACd;IAED,gEAAgE;IAChE,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,IAAI;IAEb,wGAAwG;IACxG,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IAET,6DAA6D;IAC7D,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,2DAA2D;IAC3D,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IAEX,qEAAqE;IACrE,KAAK,EAAE;QACL,kBAAkB,EAAE,KAAK;KAC1B;IAED,2DAA2D;IAC3D,OAAO,EAAE,KAAK;IAEd,wFAAwF;IACxF,MAAM,EAAE,KAAK;CACd,CAAA","sourcesContent":["/**\n * @fileoverview Mentra Display Hardware Capabilities\n *\n * Capability profile for the Mentra Display smart glasses model.\n * Defines the hardware and software features available on this device.\n */\n\nimport type {Capabilities} from \"../hardware\"\n\n/**\n * Mentra Display capability profile\n */\nexport const mentraDisplay: Capabilities = {\n modelName: \"Mentra Display\",\n\n // Camera capabilities - Mentra Display does not have a camera\n hasCamera: false,\n camera: null,\n\n // Display capabilities - Mentra Display has a green monochrome display\n hasDisplay: true,\n display: {\n count: 2,\n isColor: false,\n color: \"green\",\n canDisplayBitmap: true,\n resolution: {width: 640, height: 480},\n fieldOfView: {horizontal: 25},\n maxTextLines: 5,\n adjustBrightness: true,\n\n // Scene display API — firmware canvas components on the 500x220 virtual\n // screen centered in the 640x480 A6N panel (mos_display_config.c).\n width: 500,\n height: 220,\n canPosition: true,\n maxTextElements: 6, // firmware TEXTBOX pool ids 1-6 (rects share it)\n maxImageElements: 4, // firmware BITMAP pool ids 10-13\n maxImagePx: {width: 200, height: 200}, // CANVAS_IMAGE_MAX_I1_BYTES / NAV_IMAGE_MAX_W/H\n shapes: [\"rect\"], // bordered empty TEXTBOX ≈ rect (no shape primitive)\n intensityLevels: 2, // 1-bpp panel today\n partialUpdate: true,\n },\n\n // Microphone capabilities - Mentra Display has one microphone without VAD\n hasMicrophone: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n\n // Speaker capabilities - Mentra Display does not have a speaker\n hasSpeaker: false,\n speaker: null,\n\n // IMU capabilities - Mentra Display has IMU for head-up/down detection but raw data not exposed to apps\n hasIMU: true,\n imu: null,\n\n // Button capabilities - Mentra Display does not have buttons\n hasButton: false,\n button: null,\n\n // Light capabilities - Mentra Display does not have lights\n hasLight: false,\n light: null,\n\n // Power capabilities - Mentra Display does not have external battery\n power: {\n hasExternalBattery: false,\n },\n\n // WiFi capabilities - Mentra Display does not support WiFi\n hasWifi: false,\n\n // OTA capabilities - Mentra Display firmware updates are not driven by the ASG OTA flow\n hasOta: false,\n}\n"]}
1
+ {"version":3,"file":"mentra-display.js","sourceRoot":"","sources":["../../../src/types/capabilities/mentra-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,SAAS,EAAE,gBAAgB;IAE3B,8DAA8D;IAC9D,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,uEAAuE;IACvE,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE;QACP,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,OAAO;QACd,gBAAgB,EAAE,IAAI;QACtB,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC;QACrC,WAAW,EAAE,EAAC,UAAU,EAAE,EAAE,EAAC;QAC7B,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,EAAC,MAAM,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAC,EAAE,KAAK,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAC,EAAC;QAElE,wEAAwE;QACxE,mEAAmE;QACnE,KAAK,EAAE,GAAG;QACV,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,CAAC,EAAE,iDAAiD;QACrE,gBAAgB,EAAE,CAAC,EAAE,iCAAiC;QACtD,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC,EAAE,gDAAgD;QACvF,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,qDAAqD;QACvE,eAAe,EAAE,CAAC,EAAE,oBAAoB;QACxC,aAAa,EAAE,IAAI;KACpB;IAED,0EAA0E;IAC1E,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,KAAK;KACd;IAED,gEAAgE;IAChE,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,IAAI;IAEb,wGAAwG;IACxG,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IAET,6DAA6D;IAC7D,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,2DAA2D;IAC3D,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IAEX,qEAAqE;IACrE,KAAK,EAAE;QACL,kBAAkB,EAAE,KAAK;KAC1B;IAED,2DAA2D;IAC3D,OAAO,EAAE,KAAK;IAEd,wFAAwF;IACxF,MAAM,EAAE,KAAK;CACd,CAAA","sourcesContent":["/**\n * @fileoverview Mentra Display Hardware Capabilities\n *\n * Capability profile for the Mentra Display smart glasses model.\n * Defines the hardware and software features available on this device.\n */\n\nimport type {Capabilities} from \"../hardware\"\n\n/**\n * Mentra Display capability profile\n */\nexport const mentraDisplay: Capabilities = {\n modelName: \"Mentra Display\",\n\n // Camera capabilities - Mentra Display does not have a camera\n hasCamera: false,\n camera: null,\n\n // Display capabilities - Mentra Display has a green monochrome display\n hasDisplay: true,\n display: {\n count: 2,\n isColor: false,\n color: \"green\",\n canDisplayBitmap: true,\n resolution: {width: 640, height: 480},\n fieldOfView: {horizontal: 25},\n maxTextLines: 5,\n adjustBrightness: true,\n positionRange: {height: {min: 0, max: 8}, depth: {min: 1, max: 4}},\n\n // Scene display API — firmware canvas components on the 500x220 virtual\n // screen centered in the 640x480 A6N panel (mos_display_config.c).\n width: 500,\n height: 220,\n canPosition: true,\n maxTextElements: 6, // firmware TEXTBOX pool ids 1-6 (rects share it)\n maxImageElements: 4, // firmware BITMAP pool ids 10-13\n maxImagePx: {width: 200, height: 200}, // CANVAS_IMAGE_MAX_I1_BYTES / NAV_IMAGE_MAX_W/H\n shapes: [\"rect\"], // bordered empty TEXTBOX ≈ rect (no shape primitive)\n intensityLevels: 2, // 1-bpp panel today\n partialUpdate: true,\n },\n\n // Microphone capabilities - Mentra Display has one microphone without VAD\n hasMicrophone: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n\n // Speaker capabilities - Mentra Display does not have a speaker\n hasSpeaker: false,\n speaker: null,\n\n // IMU capabilities - Mentra Display has IMU for head-up/down detection but raw data not exposed to apps\n hasIMU: true,\n imu: null,\n\n // Button capabilities - Mentra Display does not have buttons\n hasButton: false,\n button: null,\n\n // Light capabilities - Mentra Display does not have lights\n hasLight: false,\n light: null,\n\n // Power capabilities - Mentra Display does not have external battery\n power: {\n hasExternalBattery: false,\n },\n\n // WiFi capabilities - Mentra Display does not support WiFi\n hasWifi: false,\n\n // OTA capabilities - Mentra Display firmware updates are not driven by the ASG OTA flow\n hasOta: false,\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"nimo.d.ts","sourceRoot":"","sources":["../../../src/types/capabilities/nimo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AAE7C;;GAEG;AACH,eAAO,MAAM,IAAI,EAAE,YAsElB,CAAA"}
1
+ {"version":3,"file":"nimo.d.ts","sourceRoot":"","sources":["../../../src/types/capabilities/nimo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AAE7C;;GAEG;AACH,eAAO,MAAM,IAAI,EAAE,YAuElB,CAAA"}
@@ -27,6 +27,7 @@ export const nimo = {
27
27
  resolution: { width: 640, height: 400 },
28
28
  maxTextLines: 5,
29
29
  adjustBrightness: true,
30
+ positionRange: { height: { min: 0, max: 10 }, depth: { min: 0, max: 10 } },
30
31
  },
31
32
  // Microphone capabilities - Nimo streams Opus audio from both arms
32
33
  hasMicrophone: true,
@@ -1 +1 @@
1
- {"version":3,"file":"nimo.js","sourceRoot":"","sources":["../../../src/types/capabilities/nimo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,IAAI,GAAiB;IAChC,SAAS,EAAE,MAAM;IAEjB,oDAAoD;IACpD,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,sDAAsD;IACtD,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE;QACP,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,OAAO;QACd,gBAAgB,EAAE,IAAI;QACtB,0EAA0E;QAC1E,yEAAyE;QACzE,mDAAmD;QACnD,0DAA0D;QAC1D,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC;QACrC,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,IAAI;KACvB;IAED,mEAAmE;IACnE,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,KAAK;KACd;IAED,4DAA4D;IAC5D,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,IAAI;IAEb,mDAAmD;IACnD,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IAET,4DAA4D;IAC5D,SAAS,EAAE,IAAI;IACf,MAAM,EAAE;QACN,KAAK,EAAE,CAAC;QACR,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC;gBAC/C,YAAY,EAAE,IAAI;aACnB;YACD;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC;gBAC/C,YAAY,EAAE,IAAI;aACnB;SACF;KACF;IAED,iCAAiC;IACjC,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IAEX,qBAAqB;IACrB,KAAK,EAAE;QACL,kBAAkB,EAAE,KAAK;KAC1B;IAED,8BAA8B;IAC9B,OAAO,EAAE,KAAK;IAEd,8EAA8E;IAC9E,MAAM,EAAE,KAAK;CACd,CAAA","sourcesContent":["/**\n * @fileoverview Nimo Glasses Hardware Capabilities\n *\n * Capability profile for the Nimo smart glasses model.\n * Nimo uses a framed UART-over-BLE protocol with widget/app-based content\n * updates and a separate Opus microphone channel.\n */\n\nimport type {Capabilities} from \"../hardware\"\n\n/**\n * Nimo Glasses capability profile\n */\nexport const nimo: Capabilities = {\n modelName: \"NIMO\",\n\n // Camera capabilities - Nimo does not have a camera\n hasCamera: false,\n camera: null,\n\n // Display capabilities - monochrome binocular display\n hasDisplay: true,\n display: {\n count: 2,\n isColor: false,\n color: \"green\",\n canDisplayBitmap: true,\n // Bitmaps render into the nav mini-map widget (appId 0x01, 160x160 2bpp);\n // see displayBitmap in Nimo.kt / Nimo.swift. The resolution below is the\n // text-display placeholder, not the bitmap target.\n // TODO: placeholder - verify against hardware screen info\n resolution: {width: 640, height: 400},\n maxTextLines: 5,\n adjustBrightness: true,\n },\n\n // Microphone capabilities - Nimo streams Opus audio from both arms\n hasMicrophone: true,\n microphone: {\n count: 2,\n hasVAD: false,\n },\n\n // Speaker capabilities - no speaker channel in the protocol\n hasSpeaker: false,\n speaker: null,\n\n // IMU capabilities - head-up/head-down events only\n hasIMU: true,\n imu: null,\n\n // Button capabilities - capacitive touch areas on both arms\n hasButton: true,\n button: {\n count: 2,\n buttons: [\n {\n type: \"press\",\n events: [\"press\", \"double_press\", \"long_press\"],\n isCapacitive: true,\n },\n {\n type: \"press\",\n events: [\"press\", \"double_press\", \"long_press\"],\n isCapacitive: true,\n },\n ],\n },\n\n // Light capabilities - no lights\n hasLight: false,\n light: null,\n\n // Power capabilities\n power: {\n hasExternalBattery: false,\n },\n\n // WiFi capabilities - no WiFi\n hasWifi: false,\n\n // OTA capabilities - Nimo firmware updates are not driven by the ASG OTA flow\n hasOta: false,\n}\n"]}
1
+ {"version":3,"file":"nimo.js","sourceRoot":"","sources":["../../../src/types/capabilities/nimo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,IAAI,GAAiB;IAChC,SAAS,EAAE,MAAM;IAEjB,oDAAoD;IACpD,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IAEZ,sDAAsD;IACtD,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE;QACP,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,OAAO;QACd,gBAAgB,EAAE,IAAI;QACtB,0EAA0E;QAC1E,yEAAyE;QACzE,mDAAmD;QACnD,0DAA0D;QAC1D,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC;QACrC,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,EAAC,MAAM,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAC,EAAE,KAAK,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAC,EAAC;KACrE;IAED,mEAAmE;IACnE,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,KAAK;KACd;IAED,4DAA4D;IAC5D,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,IAAI;IAEb,mDAAmD;IACnD,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IAET,4DAA4D;IAC5D,SAAS,EAAE,IAAI;IACf,MAAM,EAAE;QACN,KAAK,EAAE,CAAC;QACR,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC;gBAC/C,YAAY,EAAE,IAAI;aACnB;YACD;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC;gBAC/C,YAAY,EAAE,IAAI;aACnB;SACF;KACF;IAED,iCAAiC;IACjC,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IAEX,qBAAqB;IACrB,KAAK,EAAE;QACL,kBAAkB,EAAE,KAAK;KAC1B;IAED,8BAA8B;IAC9B,OAAO,EAAE,KAAK;IAEd,8EAA8E;IAC9E,MAAM,EAAE,KAAK;CACd,CAAA","sourcesContent":["/**\n * @fileoverview Nimo Glasses Hardware Capabilities\n *\n * Capability profile for the Nimo smart glasses model.\n * Nimo uses a framed UART-over-BLE protocol with widget/app-based content\n * updates and a separate Opus microphone channel.\n */\n\nimport type {Capabilities} from \"../hardware\"\n\n/**\n * Nimo Glasses capability profile\n */\nexport const nimo: Capabilities = {\n modelName: \"NIMO\",\n\n // Camera capabilities - Nimo does not have a camera\n hasCamera: false,\n camera: null,\n\n // Display capabilities - monochrome binocular display\n hasDisplay: true,\n display: {\n count: 2,\n isColor: false,\n color: \"green\",\n canDisplayBitmap: true,\n // Bitmaps render into the nav mini-map widget (appId 0x01, 160x160 2bpp);\n // see displayBitmap in Nimo.kt / Nimo.swift. The resolution below is the\n // text-display placeholder, not the bitmap target.\n // TODO: placeholder - verify against hardware screen info\n resolution: {width: 640, height: 400},\n maxTextLines: 5,\n adjustBrightness: true,\n positionRange: {height: {min: 0, max: 10}, depth: {min: 0, max: 10}},\n },\n\n // Microphone capabilities - Nimo streams Opus audio from both arms\n hasMicrophone: true,\n microphone: {\n count: 2,\n hasVAD: false,\n },\n\n // Speaker capabilities - no speaker channel in the protocol\n hasSpeaker: false,\n speaker: null,\n\n // IMU capabilities - head-up/head-down events only\n hasIMU: true,\n imu: null,\n\n // Button capabilities - capacitive touch areas on both arms\n hasButton: true,\n button: {\n count: 2,\n buttons: [\n {\n type: \"press\",\n events: [\"press\", \"double_press\", \"long_press\"],\n isCapacitive: true,\n },\n {\n type: \"press\",\n events: [\"press\", \"double_press\", \"long_press\"],\n isCapacitive: true,\n },\n ],\n },\n\n // Light capabilities - no lights\n hasLight: false,\n light: null,\n\n // Power capabilities\n power: {\n hasExternalBattery: false,\n },\n\n // WiFi capabilities - no WiFi\n hasWifi: false,\n\n // OTA capabilities - Nimo firmware updates are not driven by the ASG OTA flow\n hasOta: false,\n}\n"]}
@@ -30,6 +30,10 @@ export interface CameraCapabilities {
30
30
  }[];
31
31
  };
32
32
  }
33
+ export interface LevelRange {
34
+ min: number;
35
+ max: number;
36
+ }
33
37
  /**
34
38
  * Display capabilities
35
39
  */
@@ -48,6 +52,11 @@ export interface DisplayCapabilities {
48
52
  };
49
53
  maxTextLines?: number;
50
54
  adjustBrightness?: boolean;
55
+ /** The `dashboard_height` / `dashboard_depth` steps the glasses accept, inclusive; absent ⇒ no position control. */
56
+ positionRange?: {
57
+ height: LevelRange;
58
+ depth: LevelRange;
59
+ };
51
60
  /** Public drawable canvas in px — the raw coordinate space for render() boxes. */
52
61
  width?: number;
53
62
  height?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"hardware.d.ts","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hardware.d.ts","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAC,eAAe,EAAC,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAC,eAAe,EAAC,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAC,aAAa,EAAC,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAC,gBAAgB,EAAC,MAAM,kCAAkC,CAAA;AACjE,OAAO,EAAC,IAAI,EAAC,MAAM,qBAAqB,CAAA;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,2BAA2B,CAAA;AAEnD,OAAO,EAAC,WAAW,EAAC,MAAM,SAAS,CAAA;AAGnC,YAAY,EAAC,mBAAmB,EAAC,MAAM,2BAA2B,CAAA;AAElE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAA;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE;QACL,SAAS,EAAE,OAAO,CAAA;QAClB,SAAS,EAAE,OAAO,CAAA;QAClB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;QAC/B,oBAAoB,CAAC,EAAE;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAC,EAAE,CAAA;KACzD,CAAA;CACF;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,UAAU,CAAC,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAA;IAC5C,WAAW,CAAC,EAAE;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAC,CAAA;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,oHAAoH;IACpH,aAAa,CAAC,EAAE;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAC,CAAA;IAKvD,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,sEAAsE;IACtE,UAAU,CAAC,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAA;IAC5C,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;IACvB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,KAAK,CAAC,EAAE,WAAW,EAAE,CAAA;CACtB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAA;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAE9G,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,EAAE,YAAY,EAA2E,CAAA;AAExH;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;QACrC,MAAM,EAAE,MAAM,EAAE,CAAA;QAChB,YAAY,CAAC,EAAE,OAAO,CAAA;KACvB,EAAE,CAAA;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE;QACP,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS,CAAA;QAChD,WAAW,EAAE,OAAO,CAAA;QACpB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,MAAM,GAAG,SAAS,CAAA;KAC/D,EAAE,CAAA;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,kBAAkB,EAAE,OAAO,CAAA;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IAGjB,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAGjC,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;IAGnC,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,sBAAsB,GAAG,IAAI,CAAA;IAGzC,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;IAGnC,MAAM,EAAE,OAAO,CAAA;IACf,GAAG,EAAE,eAAe,GAAG,IAAI,CAAA;IAG3B,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAGjC,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAG/B,KAAK,EAAE,iBAAiB,CAAA;IAGxB,OAAO,EAAE,OAAO,CAAA;IAGhB,MAAM,CAAC,EAAE,OAAO,CAAA;IAIhB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAO3B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAK5B,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAM7B,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAGhC,8BAA8B,CAAC,EAAE,MAAM,EAAE,CAAA;CAC1C;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA2B9D,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,YAAY,WAAW,KAAG,YAM9D,CAAA;AAGD,OAAO,EAAC,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAC,CAAA"}
@@ -33,6 +33,8 @@ export const HARDWARE_CAPABILITIES = {
33
33
  display: {
34
34
  ...evenRealitiesG1.display,
35
35
  canDisplayBitmap: false,
36
+ // The AR99 firmware takes no position command.
37
+ positionRange: undefined,
36
38
  },
37
39
  hasMicrophone: true,
38
40
  hasOta: true,
@@ -1 +1 @@
1
- {"version":3,"file":"hardware.js","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAC,eAAe,EAAC,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAC,eAAe,EAAC,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAC,aAAa,EAAC,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAC,gBAAgB,EAAC,MAAM,kCAAkC,CAAA;AACjE,OAAO,EAAC,IAAI,EAAC,MAAM,qBAAqB,CAAA;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,2BAA2B,CAAA;AACnD,OAAO,EAAC,IAAI,EAAC,MAAM,qBAAqB,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,SAAS,CAAA;AAoEnC,oEAAoE;AACpE,MAAM,CAAC,MAAM,kBAAkB,GAAmB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;AAmIxH;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAiC;IACjE,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,aAAa;IACxC,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,UAAU;IAClC,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,gBAAgB;IAC9C,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS;IAChC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;IACtB,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,mDAAmD;IACnF,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;IACtB,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;QAClB,GAAG,eAAe;QAClB,SAAS,EAAE,WAAW,CAAC,IAAI;QAC3B,OAAO,EAAE;YACP,GAAG,eAAe,CAAC,OAAO;YAC1B,gBAAgB,EAAE,KAAK;SACxB;QACD,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE;YACV,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,KAAK;SACd;KACF;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;CACvB,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAuB,EAAgB,EAAE;IAC5E,MAAM,SAAS,GAAG,UAAoB,CAAA;IACtC,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,qBAAqB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAA;AACzC,CAAC,CAAA;AAED,iCAAiC;AACjC,OAAO,EAAC,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAC,CAAA","sourcesContent":["/**\n * Hardware capability types\n */\n\nimport {evenRealitiesG1} from \"./capabilities/even-realities-g1\"\nimport {evenRealitiesG2} from \"./capabilities/even-realities-g2\"\nimport {mentraDisplay} from \"./capabilities/mentra-display\"\nimport {mentraLive} from \"./capabilities/mentra-live\"\nimport {simulatedGlasses} from \"./capabilities/simulated-glasses\"\nimport {nimo} from \"./capabilities/nimo\"\nimport {vuzixZ100} from \"./capabilities/vuzix-z100\"\nimport {none} from \"./capabilities/none\"\nimport {DeviceTypes} from \"./enums\"\n\n// Canonical def lives in @glassly/miniapp; re-exported for engine consumers.\nexport type {HardwareRequirement} from \"@glassly/miniapp/hardware\"\n\n/**\n * Camera capabilities\n */\nexport interface CameraCapabilities {\n resolution?: {width: number; height: number}\n hasHDR?: boolean\n hasFocus?: boolean\n video: {\n canRecord: boolean\n canStream: boolean\n supportedStreamTypes?: string[]\n supportedResolutions?: {width: number; height: number}[]\n }\n}\n\n/**\n * Display capabilities\n */\nexport interface DisplayCapabilities {\n count?: number\n isColor?: boolean\n color?: string // e.g., \"green\", \"full_color\", \"pallet\"\n canDisplayBitmap?: boolean\n resolution?: {width: number; height: number}\n fieldOfView?: {horizontal?: number; vertical?: number}\n maxTextLines?: number\n adjustBrightness?: boolean\n\n // --- Scene display API (display.render()) — typed capabilities ---\n // Pure DATA the host scene pipeline acts on generically; a device without\n // canPosition renders scenes via the host degrade path instead.\n /** Public drawable canvas in px — the raw coordinate space for render() boxes. */\n width?: number\n height?: number\n /** True ⇒ the device renders positioned elements (containers / canvas components). */\n canPosition?: boolean\n /** Element budgets. Rects share the text pool on container-based devices. */\n maxTextElements?: number\n maxImageElements?: number\n /** Per-image dimension cap (box-level), when the firmware has one. */\n maxImagePx?: {width: number; height: number}\n shapes?: DisplayShape[]\n /** True ⇒ the device animates element transitions itself. */\n animation?: boolean\n /** Geometric and SVG rotations, including duration-based transitions. */\n rotation?: boolean\n /** Per-element stereo `depth` (a lens disparity) is honored; otherwise elements draw flat. */\n depth?: boolean\n intensityLevels?: number\n\n partialUpdate?: boolean\n /**\n * The views a miniapp may draw right now: \"main\" for every app, \"dashboard\"\n * (the overlay slot) only for the designated system dashboard app while the\n * glasses are eligible. Per app; filled in by the miniapp runtime.\n */\n views?: DisplayView[]\n}\n\nexport type DisplayView = \"main\" | \"dashboard\"\n\nexport type DisplayShape = \"rect\" | \"circle\" | \"arc\" | \"pie\" | \"line\" | \"triangle\" | \"quad\" | \"bezier\" | \"svg\"\n\n/** Every vector shape the Glassly CFW rasterizes on the glasses. */\nexport const ALL_DISPLAY_SHAPES: DisplayShape[] = [\"rect\", \"circle\", \"arc\", \"pie\", \"line\", \"triangle\", \"quad\", \"bezier\"]\n\n/**\n * Microphone capabilities\n */\nexport interface MicrophoneCapabilities {\n count?: number\n hasVAD?: boolean // Voice Activity Detection\n}\n\n/**\n * Speaker capabilities\n */\nexport interface SpeakerCapabilities {\n count?: number\n isPrivate?: boolean // e.g., bone conduction\n}\n\n/**\n * IMU (Inertial Measurement Unit) capabilities\n */\nexport interface IMUCapabilities {\n axisCount?: number\n hasAccelerometer?: boolean\n hasCompass?: boolean\n hasGyroscope?: boolean\n}\n\n/**\n * Button capabilities\n */\nexport interface ButtonCapabilities {\n count?: number\n buttons?: {\n type: \"press\" | \"swipe1d\" | \"swipe2d\"\n events: string[] // e.g., \"press\", \"double_press\", \"long_press\", \"swipe_up\", \"swipe_down\"\n isCapacitive?: boolean\n }[]\n}\n\n/**\n * Light capabilities\n */\nexport interface LightCapabilities {\n count?: number\n lights?: {\n id: string // Unique identifier for the LED (e.g., \"privacy\", \"user_feedback\")\n purpose: \"privacy\" | \"user_feedback\" | \"general\" // LED purpose/function\n isFullColor: boolean\n color?: string // e.g., \"white\", \"rgb\"\n position?: \"front_facing\" | \"user_facing\" | \"side\" | \"unknown\" // LED physical position\n }[]\n}\n\n/**\n * Power capabilities\n */\nexport interface PowerCapabilities {\n hasExternalBattery: boolean // e.g., a case or puck\n}\n\n/**\n * Device hardware capabilities\n * Complete information about what hardware a device has\n */\nexport interface Capabilities {\n modelName: string\n\n // Camera capabilities\n hasCamera: boolean\n camera: CameraCapabilities | null\n\n // Display capabilities\n hasDisplay: boolean\n display: DisplayCapabilities | null\n\n // Microphone capabilities\n hasMicrophone: boolean\n microphone: MicrophoneCapabilities | null\n\n // Speaker capabilities\n hasSpeaker: boolean\n speaker: SpeakerCapabilities | null\n\n // IMU capabilities\n hasIMU: boolean\n imu: IMUCapabilities | null\n\n // Button capabilities\n hasButton: boolean\n button: ButtonCapabilities | null\n\n // Light capabilities\n hasLight: boolean\n light: LightCapabilities | null\n\n // Power capabilities\n power: PowerCapabilities\n\n // WiFi capability\n hasWifi: boolean\n\n // OTA capability\n hasOta?: boolean\n\n // True when the app ships a flashable Glassly custom firmware for this\n // model, so pairing can offer to install it and Settings exposes the flasher\n hasCustomFirmware?: boolean\n\n // Native dashboard capability\n // True when the device renders its own dashboard in firmware (e.g. Even\n // Realities' built-in dashboard on G2). Glassly does not manage or expose\n // dashboard settings for these devices. Absent/false means Glassly owns the\n // dashboard.\n hasNativeDashboard?: boolean\n\n // True when the device renders its own turn-by-turn nav UI in firmware\n // (G2 nav mode: instruction card + map slots + magnetometer compass); nav\n // apps feed it structured fields and map bitmaps instead of scene elements\n hasNativeNavigation?: boolean\n\n // True when the firmware presents phone notifications itself (popup +\n // history) and takes a GlassesNotificationConfig. How it gets the content\n // (reading the phone's stream, or an upload from the phone) is the driver's\n // business; the engine only decides who presents.\n hasNativeNotifications?: boolean\n // Popup durations the firmware accepts, in seconds (0 = firmware default).\n // Drivers snap other values; the settings UI offers exactly these.\n nativeNotificationPopupSeconds?: number[]\n}\n\n/**\n * Hardware capability profiles for supported glasses models\n * Key: model_name string (e.g., \"Even Realities G1\", \"Mentra Live\")\n * Value: Capabilities object defining device features\n */\nexport const HARDWARE_CAPABILITIES: Record<string, Capabilities> = {\n [evenRealitiesG1.modelName]: evenRealitiesG1,\n [evenRealitiesG2.modelName]: evenRealitiesG2,\n [mentraDisplay.modelName]: mentraDisplay,\n [mentraLive.modelName]: mentraLive,\n [simulatedGlasses.modelName]: simulatedGlasses,\n [vuzixZ100.modelName]: vuzixZ100,\n [nimo.modelName]: nimo,\n [DeviceTypes.MACH1]: vuzixZ100, // Mach1 uses same Vuzix Ultralite hardware as Z100\n [nimo.modelName]: nimo,\n [DeviceTypes.AR99]: {\n ...evenRealitiesG1,\n modelName: DeviceTypes.AR99,\n display: {\n ...evenRealitiesG1.display,\n canDisplayBitmap: false,\n },\n hasMicrophone: true,\n hasOta: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n },\n [none.modelName]: none,\n}\n\nexport const getModelCapabilities = (deviceType: DeviceTypes): Capabilities => {\n const modelName = deviceType as string\n if (!HARDWARE_CAPABILITIES[modelName]) {\n return HARDWARE_CAPABILITIES[DeviceTypes.NONE]\n }\n return HARDWARE_CAPABILITIES[modelName]\n}\n\n// export * from \"./capabilities\"\nexport {simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, nimo}\n"]}
1
+ {"version":3,"file":"hardware.js","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAC,eAAe,EAAC,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAC,eAAe,EAAC,MAAM,kCAAkC,CAAA;AAChE,OAAO,EAAC,aAAa,EAAC,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAC,gBAAgB,EAAC,MAAM,kCAAkC,CAAA;AACjE,OAAO,EAAC,IAAI,EAAC,MAAM,qBAAqB,CAAA;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,2BAA2B,CAAA;AACnD,OAAO,EAAC,IAAI,EAAC,MAAM,qBAAqB,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,SAAS,CAAA;AA2EnC,oEAAoE;AACpE,MAAM,CAAC,MAAM,kBAAkB,GAAmB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;AAmIxH;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAiC;IACjE,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,aAAa;IACxC,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,UAAU;IAClC,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,gBAAgB;IAC9C,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS;IAChC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;IACtB,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,mDAAmD;IACnF,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;IACtB,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;QAClB,GAAG,eAAe;QAClB,SAAS,EAAE,WAAW,CAAC,IAAI;QAC3B,OAAO,EAAE;YACP,GAAG,eAAe,CAAC,OAAO;YAC1B,gBAAgB,EAAE,KAAK;YACvB,+CAA+C;YAC/C,aAAa,EAAE,SAAS;SACzB;QACD,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE;YACV,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,KAAK;SACd;KACF;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;CACvB,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAuB,EAAgB,EAAE;IAC5E,MAAM,SAAS,GAAG,UAAoB,CAAA;IACtC,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,qBAAqB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAA;AACzC,CAAC,CAAA;AAED,iCAAiC;AACjC,OAAO,EAAC,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAC,CAAA","sourcesContent":["/**\n * Hardware capability types\n */\n\nimport {evenRealitiesG1} from \"./capabilities/even-realities-g1\"\nimport {evenRealitiesG2} from \"./capabilities/even-realities-g2\"\nimport {mentraDisplay} from \"./capabilities/mentra-display\"\nimport {mentraLive} from \"./capabilities/mentra-live\"\nimport {simulatedGlasses} from \"./capabilities/simulated-glasses\"\nimport {nimo} from \"./capabilities/nimo\"\nimport {vuzixZ100} from \"./capabilities/vuzix-z100\"\nimport {none} from \"./capabilities/none\"\nimport {DeviceTypes} from \"./enums\"\n\n// Canonical def lives in @glassly/miniapp; re-exported for engine consumers.\nexport type {HardwareRequirement} from \"@glassly/miniapp/hardware\"\n\n/**\n * Camera capabilities\n */\nexport interface CameraCapabilities {\n resolution?: {width: number; height: number}\n hasHDR?: boolean\n hasFocus?: boolean\n video: {\n canRecord: boolean\n canStream: boolean\n supportedStreamTypes?: string[]\n supportedResolutions?: {width: number; height: number}[]\n }\n}\n\nexport interface LevelRange {\n min: number\n max: number\n}\n\n/**\n * Display capabilities\n */\nexport interface DisplayCapabilities {\n count?: number\n isColor?: boolean\n color?: string // e.g., \"green\", \"full_color\", \"pallet\"\n canDisplayBitmap?: boolean\n resolution?: {width: number; height: number}\n fieldOfView?: {horizontal?: number; vertical?: number}\n maxTextLines?: number\n adjustBrightness?: boolean\n /** The `dashboard_height` / `dashboard_depth` steps the glasses accept, inclusive; absent ⇒ no position control. */\n positionRange?: {height: LevelRange; depth: LevelRange}\n\n // --- Scene display API (display.render()) — typed capabilities ---\n // Pure DATA the host scene pipeline acts on generically; a device without\n // canPosition renders scenes via the host degrade path instead.\n /** Public drawable canvas in px — the raw coordinate space for render() boxes. */\n width?: number\n height?: number\n /** True ⇒ the device renders positioned elements (containers / canvas components). */\n canPosition?: boolean\n /** Element budgets. Rects share the text pool on container-based devices. */\n maxTextElements?: number\n maxImageElements?: number\n /** Per-image dimension cap (box-level), when the firmware has one. */\n maxImagePx?: {width: number; height: number}\n shapes?: DisplayShape[]\n /** True ⇒ the device animates element transitions itself. */\n animation?: boolean\n /** Geometric and SVG rotations, including duration-based transitions. */\n rotation?: boolean\n /** Per-element stereo `depth` (a lens disparity) is honored; otherwise elements draw flat. */\n depth?: boolean\n intensityLevels?: number\n\n partialUpdate?: boolean\n /**\n * The views a miniapp may draw right now: \"main\" for every app, \"dashboard\"\n * (the overlay slot) only for the designated system dashboard app while the\n * glasses are eligible. Per app; filled in by the miniapp runtime.\n */\n views?: DisplayView[]\n}\n\nexport type DisplayView = \"main\" | \"dashboard\"\n\nexport type DisplayShape = \"rect\" | \"circle\" | \"arc\" | \"pie\" | \"line\" | \"triangle\" | \"quad\" | \"bezier\" | \"svg\"\n\n/** Every vector shape the Glassly CFW rasterizes on the glasses. */\nexport const ALL_DISPLAY_SHAPES: DisplayShape[] = [\"rect\", \"circle\", \"arc\", \"pie\", \"line\", \"triangle\", \"quad\", \"bezier\"]\n\n/**\n * Microphone capabilities\n */\nexport interface MicrophoneCapabilities {\n count?: number\n hasVAD?: boolean // Voice Activity Detection\n}\n\n/**\n * Speaker capabilities\n */\nexport interface SpeakerCapabilities {\n count?: number\n isPrivate?: boolean // e.g., bone conduction\n}\n\n/**\n * IMU (Inertial Measurement Unit) capabilities\n */\nexport interface IMUCapabilities {\n axisCount?: number\n hasAccelerometer?: boolean\n hasCompass?: boolean\n hasGyroscope?: boolean\n}\n\n/**\n * Button capabilities\n */\nexport interface ButtonCapabilities {\n count?: number\n buttons?: {\n type: \"press\" | \"swipe1d\" | \"swipe2d\"\n events: string[] // e.g., \"press\", \"double_press\", \"long_press\", \"swipe_up\", \"swipe_down\"\n isCapacitive?: boolean\n }[]\n}\n\n/**\n * Light capabilities\n */\nexport interface LightCapabilities {\n count?: number\n lights?: {\n id: string // Unique identifier for the LED (e.g., \"privacy\", \"user_feedback\")\n purpose: \"privacy\" | \"user_feedback\" | \"general\" // LED purpose/function\n isFullColor: boolean\n color?: string // e.g., \"white\", \"rgb\"\n position?: \"front_facing\" | \"user_facing\" | \"side\" | \"unknown\" // LED physical position\n }[]\n}\n\n/**\n * Power capabilities\n */\nexport interface PowerCapabilities {\n hasExternalBattery: boolean // e.g., a case or puck\n}\n\n/**\n * Device hardware capabilities\n * Complete information about what hardware a device has\n */\nexport interface Capabilities {\n modelName: string\n\n // Camera capabilities\n hasCamera: boolean\n camera: CameraCapabilities | null\n\n // Display capabilities\n hasDisplay: boolean\n display: DisplayCapabilities | null\n\n // Microphone capabilities\n hasMicrophone: boolean\n microphone: MicrophoneCapabilities | null\n\n // Speaker capabilities\n hasSpeaker: boolean\n speaker: SpeakerCapabilities | null\n\n // IMU capabilities\n hasIMU: boolean\n imu: IMUCapabilities | null\n\n // Button capabilities\n hasButton: boolean\n button: ButtonCapabilities | null\n\n // Light capabilities\n hasLight: boolean\n light: LightCapabilities | null\n\n // Power capabilities\n power: PowerCapabilities\n\n // WiFi capability\n hasWifi: boolean\n\n // OTA capability\n hasOta?: boolean\n\n // True when the app ships a flashable Glassly custom firmware for this\n // model, so pairing can offer to install it and Settings exposes the flasher\n hasCustomFirmware?: boolean\n\n // Native dashboard capability\n // True when the device renders its own dashboard in firmware (e.g. Even\n // Realities' built-in dashboard on G2). Glassly does not manage or expose\n // dashboard settings for these devices. Absent/false means Glassly owns the\n // dashboard.\n hasNativeDashboard?: boolean\n\n // True when the device renders its own turn-by-turn nav UI in firmware\n // (G2 nav mode: instruction card + map slots + magnetometer compass); nav\n // apps feed it structured fields and map bitmaps instead of scene elements\n hasNativeNavigation?: boolean\n\n // True when the firmware presents phone notifications itself (popup +\n // history) and takes a GlassesNotificationConfig. How it gets the content\n // (reading the phone's stream, or an upload from the phone) is the driver's\n // business; the engine only decides who presents.\n hasNativeNotifications?: boolean\n // Popup durations the firmware accepts, in seconds (0 = firmware default).\n // Drivers snap other values; the settings UI offers exactly these.\n nativeNotificationPopupSeconds?: number[]\n}\n\n/**\n * Hardware capability profiles for supported glasses models\n * Key: model_name string (e.g., \"Even Realities G1\", \"Mentra Live\")\n * Value: Capabilities object defining device features\n */\nexport const HARDWARE_CAPABILITIES: Record<string, Capabilities> = {\n [evenRealitiesG1.modelName]: evenRealitiesG1,\n [evenRealitiesG2.modelName]: evenRealitiesG2,\n [mentraDisplay.modelName]: mentraDisplay,\n [mentraLive.modelName]: mentraLive,\n [simulatedGlasses.modelName]: simulatedGlasses,\n [vuzixZ100.modelName]: vuzixZ100,\n [nimo.modelName]: nimo,\n [DeviceTypes.MACH1]: vuzixZ100, // Mach1 uses same Vuzix Ultralite hardware as Z100\n [nimo.modelName]: nimo,\n [DeviceTypes.AR99]: {\n ...evenRealitiesG1,\n modelName: DeviceTypes.AR99,\n display: {\n ...evenRealitiesG1.display,\n canDisplayBitmap: false,\n // The AR99 firmware takes no position command.\n positionRange: undefined,\n },\n hasMicrophone: true,\n hasOta: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n },\n [none.modelName]: none,\n}\n\nexport const getModelCapabilities = (deviceType: DeviceTypes): Capabilities => {\n const modelName = deviceType as string\n if (!HARDWARE_CAPABILITIES[modelName]) {\n return HARDWARE_CAPABILITIES[DeviceTypes.NONE]\n }\n return HARDWARE_CAPABILITIES[modelName]\n}\n\n// export * from \"./capabilities\"\nexport {simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, nimo}\n"]}
@@ -91,7 +91,7 @@ export function buildMiniappGlobalsScript(opts) {
91
91
  // Console-tap shim: wrap console.log/warn/error/info/debug so each call
92
92
  // also forwards a `dev_log` envelope back to the phone via
93
93
  // ReactNativeWebView.postMessage. Dev mode forwards to the laptop's
94
- // `glassly-miniapp dev` terminal; non-dev mode falls back to the React
94
+ // `glassly dev` terminal; non-dev mode falls back to the React
95
95
  // Native log stream (Metro / Xcode console / adb logcat) so installed-
96
96
  // miniapp errors are still inspectable when there's no laptop sidecar.
97
97
  //
@@ -1 +1 @@
1
- {"version":3,"file":"miniappGlobals.js","sourceRoot":"","sources":["../../src/utils/miniappGlobals.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * miniappGlobals — shared builder for the window.Glassly globals injected\n * into every miniapp WebView.\n *\n * Miniapps run on-device, and LocalMiniappView is the single injection path;\n * this util is the one place that builds the globals so every WebView gets the\n * same API surface.\n */\n\nimport {Dimensions, Platform} from \"react-native\"\n\nexport interface MiniappSafeArea {\n top: number\n bottom: number\n left: number\n right: number\n}\n\nexport interface CapsuleMenuRect {\n top: number\n right: number\n bottom: number\n left: number\n width: number\n height: number\n}\n\n/**\n * The capsule menu bounding rect in the WebView's own coordinate space.\n *\n * Keep in sync with CapsuleMenu.tsx:\n * - CapsuleButton height ≈ h-7.5 (30px), width ≈ 73px\n * - Positioned at right-2 (8px), top = theme.spacing.s2 (8px) below insets.top\n */\nconst CAPSULE_MENU_HEIGHT = 30\nconst CAPSULE_MENU_WIDTH = 73\nconst CAPSULE_MENU_PADDING = 8\n\n/**\n * @param topInsetOffset extra top offset to add when the WebView container does\n * NOT already pad by the safe-area inset (i.e. WebView fills edge-to-edge under\n * the status bar). Pass `insets.top` in that case, or 0 when the container\n * already applies top padding.\n */\nexport function getCapsuleMenuRect(topInsetOffset = 0): CapsuleMenuRect {\n const screenWidth = Dimensions.get(\"window\").width\n const top = topInsetOffset + CAPSULE_MENU_PADDING\n return {\n top,\n right: CAPSULE_MENU_PADDING,\n bottom: top + CAPSULE_MENU_HEIGHT,\n left: screenWidth - CAPSULE_MENU_PADDING - CAPSULE_MENU_WIDTH,\n width: CAPSULE_MENU_WIDTH,\n height: CAPSULE_MENU_HEIGHT,\n }\n}\n\nexport type MiniappColorScheme = \"light\" | \"dark\"\n\nexport interface BuildMiniappGlobalsOptions {\n packageName?: string\n capabilities?: string[]\n miniappLocal?: boolean\n miniappDeveloperMode?: boolean\n safeAreaInsets: MiniappSafeArea\n /**\n * True when the WebView container renders edge-to-edge (no top padding for\n * the status bar). In that case the capsule menu rect needs to shift down by\n * the top inset so its coords match where it's actually drawn on screen.\n */\n webviewFillsStatusBar?: boolean\n /** Current host color scheme. Miniapps may follow this to match the phone. */\n colorScheme?: MiniappColorScheme\n}\n\n/**\n * Returns the JS string to inject into a miniapp WebView before its content\n * loads. Sets window.Glassly with the standard fields miniapps read:\n *\n * window.Glassly = {\n * platform, packageName?, capabilities, safeAreaInsets, capsuleMenu,\n * miniappLocal?, miniappDeveloperMode?\n * }\n *\n * Also defines a stub window.receiveNativeMessage so the miniapp can safely\n * assign to it before the native bridge wires in.\n */\nexport function buildMiniappGlobalsScript(opts: BuildMiniappGlobalsOptions): string {\n const capsuleTopOffset = opts.webviewFillsStatusBar ? opts.safeAreaInsets.top : 0\n const globals: Record<string, unknown> = {\n platform: Platform.OS,\n capabilities: opts.capabilities ?? [\"share\", \"open_url\", \"copy_clipboard\", \"download\"],\n safeAreaInsets: opts.safeAreaInsets,\n capsuleMenu: getCapsuleMenuRect(capsuleTopOffset),\n }\n if (opts.packageName) globals.packageName = opts.packageName\n if (opts.miniappLocal) globals.miniappLocal = true\n if (opts.miniappDeveloperMode) globals.miniappDeveloperMode = true\n if (opts.colorScheme) globals.colorScheme = opts.colorScheme\n\n // CSS custom properties that mirror the capsule menu / safe-area data.\n // Miniapp CSS (or Tailwind arbitrary values) can read these without\n // touching JS: e.g. `style=\"margin-top: var(--glassly-capsule-top)\"`.\n const capsule = globals.capsuleMenu as CapsuleMenuRect\n const insets = opts.safeAreaInsets\n const capsuleCenter = capsule.top + capsule.height / 2\n const cssVars: Record<string, string> = {\n \"--glassly-safe-top\": `${insets.top}px`,\n \"--glassly-safe-bottom\": `${insets.bottom}px`,\n \"--glassly-safe-left\": `${insets.left}px`,\n \"--glassly-safe-right\": `${insets.right}px`,\n \"--glassly-capsule-top\": `${capsule.top}px`,\n \"--glassly-capsule-bottom\": `${capsule.bottom}px`,\n \"--glassly-capsule-left\": `${capsule.left}px`,\n \"--glassly-capsule-right\": `${capsule.right}px`,\n \"--glassly-capsule-width\": `${capsule.width}px`,\n \"--glassly-capsule-height\": `${capsule.height}px`,\n \"--glassly-capsule-center-y\": `${capsuleCenter}px`,\n // Right-side gutter to reserve so content doesn't slide under the\n // capsule: capsule width + 16px breathing room.\n \"--glassly-capsule-gutter\": `${capsule.width + 16}px`,\n }\n const cssVarsBlock = Object.entries(cssVars)\n .map(([k, v]) => `${k}: ${v};`)\n .join(\" \")\n\n // Console-tap shim: wrap console.log/warn/error/info/debug so each call\n // also forwards a `dev_log` envelope back to the phone via\n // ReactNativeWebView.postMessage. Dev mode forwards to the laptop's\n // `glassly-miniapp dev` terminal; non-dev mode falls back to the React\n // Native log stream (Metro / Xcode console / adb logcat) so installed-\n // miniapp errors are still inspectable when there's no laptop sidecar.\n //\n // Also tap window.onerror and window.onunhandledrejection so synchronous\n // throws and unhandled promise rejections surface even when the bundle\n // never gets a chance to call console.error itself.\n const consoleTapBlock = `\n (function() {\n try {\n var levels = [\"log\", \"warn\", \"error\", \"info\", \"debug\"];\n var seen = new WeakSet();\n function safeSerialize(arg) {\n if (arg instanceof Error) return {__error: true, message: arg.message, stack: arg.stack};\n if (arg === null || typeof arg !== \"object\") return arg;\n try {\n return JSON.parse(JSON.stringify(arg, function(k, v) {\n if (v !== null && typeof v === \"object\") {\n if (seen.has(v)) return \"[Circular]\";\n seen.add(v);\n }\n return v;\n }));\n } catch (e) {\n try { return String(arg); } catch (_) { return \"[unserializable]\"; }\n }\n }\n levels.forEach(function(level) {\n var original = console[level];\n if (typeof original !== \"function\") return;\n console[level] = function() {\n try { original.apply(console, arguments); } catch (_) {}\n try {\n var args = Array.prototype.slice.call(arguments).map(safeSerialize);\n if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === \"function\") {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n payload: {\n type: \"dev_log\",\n level: level,\n args: args,\n packageName: (window.Glassly && window.Glassly.packageName) || null,\n timestamp: Date.now()\n }\n }));\n }\n } catch (_) { /* swallow */ }\n };\n });\n } catch (_) { /* ignore */ }\n\n // Surface synchronous errors and unhandled promise rejections via the\n // same dev_log channel — many bundle-startup crashes never get to call\n // console.error themselves.\n try {\n window.addEventListener(\"error\", function(e) {\n try {\n if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === \"function\") {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n payload: {\n type: \"dev_log\",\n level: \"error\",\n args: [\"[window.error] \" + (e.message || \"unknown\"), {filename: e.filename, lineno: e.lineno, colno: e.colno, stack: e.error && e.error.stack}],\n packageName: (window.Glassly && window.Glassly.packageName) || null,\n timestamp: Date.now()\n }\n }));\n }\n } catch (_) {}\n });\n window.addEventListener(\"unhandledrejection\", function(e) {\n try {\n if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === \"function\") {\n var reason = e.reason;\n var serialized = reason instanceof Error\n ? {__error: true, message: reason.message, stack: reason.stack}\n : (typeof reason === \"object\" ? JSON.stringify(reason) : String(reason));\n window.ReactNativeWebView.postMessage(JSON.stringify({\n payload: {\n type: \"dev_log\",\n level: \"error\",\n args: [\"[unhandledrejection]\", serialized],\n packageName: (window.Glassly && window.Glassly.packageName) || null,\n timestamp: Date.now()\n }\n }));\n }\n } catch (_) {}\n });\n } catch (_) {}\n })();\n `\n\n // Lock zoom so miniapps feel like apps, not pages: force a non-scalable\n // viewport meta (overriding whatever the miniapp shipped) and disable\n // double-tap-zoom via CSS. Re-applies on DOMContentLoaded in case the\n // miniapp re-writes <head> during boot.\n return `\n window.Glassly = ${JSON.stringify(globals)};\n window.receiveNativeMessage = window.receiveNativeMessage || function() {};\n (function() {\n try {\n var styleEl = document.createElement(\"style\");\n styleEl.setAttribute(\"data-glassly-injected\", \"1\");\n styleEl.textContent =\n \":root { ${cssVarsBlock} }\" +\n \"html, body { touch-action: manipulation; -ms-content-zooming: none; }\";\n (document.head || document.documentElement).appendChild(styleEl);\n } catch (e) { /* ignore */ }\n\n function lockViewport() {\n try {\n var content = \"width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover\";\n var existing = document.querySelector('meta[name=\"viewport\"]');\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n var meta = document.createElement(\"meta\");\n meta.setAttribute(\"name\", \"viewport\");\n meta.setAttribute(\"content\", content);\n (document.head || document.documentElement).appendChild(meta);\n }\n } catch (e) { /* ignore */ }\n }\n lockViewport();\n document.addEventListener(\"DOMContentLoaded\", lockViewport);\n })();\n ${consoleTapBlock}\n true;\n `\n}\n"]}
1
+ {"version":3,"file":"miniappGlobals.js","sourceRoot":"","sources":["../../src/utils/miniappGlobals.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * miniappGlobals — shared builder for the window.Glassly globals injected\n * into every miniapp WebView.\n *\n * Miniapps run on-device, and LocalMiniappView is the single injection path;\n * this util is the one place that builds the globals so every WebView gets the\n * same API surface.\n */\n\nimport {Dimensions, Platform} from \"react-native\"\n\nexport interface MiniappSafeArea {\n top: number\n bottom: number\n left: number\n right: number\n}\n\nexport interface CapsuleMenuRect {\n top: number\n right: number\n bottom: number\n left: number\n width: number\n height: number\n}\n\n/**\n * The capsule menu bounding rect in the WebView's own coordinate space.\n *\n * Keep in sync with CapsuleMenu.tsx:\n * - CapsuleButton height ≈ h-7.5 (30px), width ≈ 73px\n * - Positioned at right-2 (8px), top = theme.spacing.s2 (8px) below insets.top\n */\nconst CAPSULE_MENU_HEIGHT = 30\nconst CAPSULE_MENU_WIDTH = 73\nconst CAPSULE_MENU_PADDING = 8\n\n/**\n * @param topInsetOffset extra top offset to add when the WebView container does\n * NOT already pad by the safe-area inset (i.e. WebView fills edge-to-edge under\n * the status bar). Pass `insets.top` in that case, or 0 when the container\n * already applies top padding.\n */\nexport function getCapsuleMenuRect(topInsetOffset = 0): CapsuleMenuRect {\n const screenWidth = Dimensions.get(\"window\").width\n const top = topInsetOffset + CAPSULE_MENU_PADDING\n return {\n top,\n right: CAPSULE_MENU_PADDING,\n bottom: top + CAPSULE_MENU_HEIGHT,\n left: screenWidth - CAPSULE_MENU_PADDING - CAPSULE_MENU_WIDTH,\n width: CAPSULE_MENU_WIDTH,\n height: CAPSULE_MENU_HEIGHT,\n }\n}\n\nexport type MiniappColorScheme = \"light\" | \"dark\"\n\nexport interface BuildMiniappGlobalsOptions {\n packageName?: string\n capabilities?: string[]\n miniappLocal?: boolean\n miniappDeveloperMode?: boolean\n safeAreaInsets: MiniappSafeArea\n /**\n * True when the WebView container renders edge-to-edge (no top padding for\n * the status bar). In that case the capsule menu rect needs to shift down by\n * the top inset so its coords match where it's actually drawn on screen.\n */\n webviewFillsStatusBar?: boolean\n /** Current host color scheme. Miniapps may follow this to match the phone. */\n colorScheme?: MiniappColorScheme\n}\n\n/**\n * Returns the JS string to inject into a miniapp WebView before its content\n * loads. Sets window.Glassly with the standard fields miniapps read:\n *\n * window.Glassly = {\n * platform, packageName?, capabilities, safeAreaInsets, capsuleMenu,\n * miniappLocal?, miniappDeveloperMode?\n * }\n *\n * Also defines a stub window.receiveNativeMessage so the miniapp can safely\n * assign to it before the native bridge wires in.\n */\nexport function buildMiniappGlobalsScript(opts: BuildMiniappGlobalsOptions): string {\n const capsuleTopOffset = opts.webviewFillsStatusBar ? opts.safeAreaInsets.top : 0\n const globals: Record<string, unknown> = {\n platform: Platform.OS,\n capabilities: opts.capabilities ?? [\"share\", \"open_url\", \"copy_clipboard\", \"download\"],\n safeAreaInsets: opts.safeAreaInsets,\n capsuleMenu: getCapsuleMenuRect(capsuleTopOffset),\n }\n if (opts.packageName) globals.packageName = opts.packageName\n if (opts.miniappLocal) globals.miniappLocal = true\n if (opts.miniappDeveloperMode) globals.miniappDeveloperMode = true\n if (opts.colorScheme) globals.colorScheme = opts.colorScheme\n\n // CSS custom properties that mirror the capsule menu / safe-area data.\n // Miniapp CSS (or Tailwind arbitrary values) can read these without\n // touching JS: e.g. `style=\"margin-top: var(--glassly-capsule-top)\"`.\n const capsule = globals.capsuleMenu as CapsuleMenuRect\n const insets = opts.safeAreaInsets\n const capsuleCenter = capsule.top + capsule.height / 2\n const cssVars: Record<string, string> = {\n \"--glassly-safe-top\": `${insets.top}px`,\n \"--glassly-safe-bottom\": `${insets.bottom}px`,\n \"--glassly-safe-left\": `${insets.left}px`,\n \"--glassly-safe-right\": `${insets.right}px`,\n \"--glassly-capsule-top\": `${capsule.top}px`,\n \"--glassly-capsule-bottom\": `${capsule.bottom}px`,\n \"--glassly-capsule-left\": `${capsule.left}px`,\n \"--glassly-capsule-right\": `${capsule.right}px`,\n \"--glassly-capsule-width\": `${capsule.width}px`,\n \"--glassly-capsule-height\": `${capsule.height}px`,\n \"--glassly-capsule-center-y\": `${capsuleCenter}px`,\n // Right-side gutter to reserve so content doesn't slide under the\n // capsule: capsule width + 16px breathing room.\n \"--glassly-capsule-gutter\": `${capsule.width + 16}px`,\n }\n const cssVarsBlock = Object.entries(cssVars)\n .map(([k, v]) => `${k}: ${v};`)\n .join(\" \")\n\n // Console-tap shim: wrap console.log/warn/error/info/debug so each call\n // also forwards a `dev_log` envelope back to the phone via\n // ReactNativeWebView.postMessage. Dev mode forwards to the laptop's\n // `glassly dev` terminal; non-dev mode falls back to the React\n // Native log stream (Metro / Xcode console / adb logcat) so installed-\n // miniapp errors are still inspectable when there's no laptop sidecar.\n //\n // Also tap window.onerror and window.onunhandledrejection so synchronous\n // throws and unhandled promise rejections surface even when the bundle\n // never gets a chance to call console.error itself.\n const consoleTapBlock = `\n (function() {\n try {\n var levels = [\"log\", \"warn\", \"error\", \"info\", \"debug\"];\n var seen = new WeakSet();\n function safeSerialize(arg) {\n if (arg instanceof Error) return {__error: true, message: arg.message, stack: arg.stack};\n if (arg === null || typeof arg !== \"object\") return arg;\n try {\n return JSON.parse(JSON.stringify(arg, function(k, v) {\n if (v !== null && typeof v === \"object\") {\n if (seen.has(v)) return \"[Circular]\";\n seen.add(v);\n }\n return v;\n }));\n } catch (e) {\n try { return String(arg); } catch (_) { return \"[unserializable]\"; }\n }\n }\n levels.forEach(function(level) {\n var original = console[level];\n if (typeof original !== \"function\") return;\n console[level] = function() {\n try { original.apply(console, arguments); } catch (_) {}\n try {\n var args = Array.prototype.slice.call(arguments).map(safeSerialize);\n if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === \"function\") {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n payload: {\n type: \"dev_log\",\n level: level,\n args: args,\n packageName: (window.Glassly && window.Glassly.packageName) || null,\n timestamp: Date.now()\n }\n }));\n }\n } catch (_) { /* swallow */ }\n };\n });\n } catch (_) { /* ignore */ }\n\n // Surface synchronous errors and unhandled promise rejections via the\n // same dev_log channel — many bundle-startup crashes never get to call\n // console.error themselves.\n try {\n window.addEventListener(\"error\", function(e) {\n try {\n if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === \"function\") {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n payload: {\n type: \"dev_log\",\n level: \"error\",\n args: [\"[window.error] \" + (e.message || \"unknown\"), {filename: e.filename, lineno: e.lineno, colno: e.colno, stack: e.error && e.error.stack}],\n packageName: (window.Glassly && window.Glassly.packageName) || null,\n timestamp: Date.now()\n }\n }));\n }\n } catch (_) {}\n });\n window.addEventListener(\"unhandledrejection\", function(e) {\n try {\n if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === \"function\") {\n var reason = e.reason;\n var serialized = reason instanceof Error\n ? {__error: true, message: reason.message, stack: reason.stack}\n : (typeof reason === \"object\" ? JSON.stringify(reason) : String(reason));\n window.ReactNativeWebView.postMessage(JSON.stringify({\n payload: {\n type: \"dev_log\",\n level: \"error\",\n args: [\"[unhandledrejection]\", serialized],\n packageName: (window.Glassly && window.Glassly.packageName) || null,\n timestamp: Date.now()\n }\n }));\n }\n } catch (_) {}\n });\n } catch (_) {}\n })();\n `\n\n // Lock zoom so miniapps feel like apps, not pages: force a non-scalable\n // viewport meta (overriding whatever the miniapp shipped) and disable\n // double-tap-zoom via CSS. Re-applies on DOMContentLoaded in case the\n // miniapp re-writes <head> during boot.\n return `\n window.Glassly = ${JSON.stringify(globals)};\n window.receiveNativeMessage = window.receiveNativeMessage || function() {};\n (function() {\n try {\n var styleEl = document.createElement(\"style\");\n styleEl.setAttribute(\"data-glassly-injected\", \"1\");\n styleEl.textContent =\n \":root { ${cssVarsBlock} }\" +\n \"html, body { touch-action: manipulation; -ms-content-zooming: none; }\";\n (document.head || document.documentElement).appendChild(styleEl);\n } catch (e) { /* ignore */ }\n\n function lockViewport() {\n try {\n var content = \"width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover\";\n var existing = document.querySelector('meta[name=\"viewport\"]');\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n var meta = document.createElement(\"meta\");\n meta.setAttribute(\"name\", \"viewport\");\n meta.setAttribute(\"content\", content);\n (document.head || document.documentElement).appendChild(meta);\n }\n } catch (e) { /* ignore */ }\n }\n lockViewport();\n document.addEventListener(\"DOMContentLoaded\", lockViewport);\n })();\n ${consoleTapBlock}\n true;\n `\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/engine",
3
- "version": "0.3.1-dev.494",
3
+ "version": "0.3.1-dev.523",
4
4
  "description": "Glassly Engine — miniapp registry, WebView bridge, and miniapp store",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -97,17 +97,17 @@
97
97
  "registry": "https://registry.npmjs.org/"
98
98
  },
99
99
  "dependencies": {
100
- "@glassly/bluetooth-sdk": "0.3.1-dev.494",
101
- "@glassly/cloud-client": "0.3.1-dev.494",
102
- "@glassly/cloud-protocol": "0.3.1-dev.494",
103
- "@glassly/crust": "0.3.1-dev.494",
104
- "@glassly/miniapp": "0.3.1-dev.494",
100
+ "@glassly/bluetooth-sdk": "0.3.1-dev.523",
101
+ "@glassly/cloud-client": "0.3.1-dev.523",
102
+ "@glassly/cloud-protocol": "0.3.1-dev.523",
103
+ "@glassly/crust": "0.3.1-dev.523",
104
+ "@glassly/miniapp": "0.3.1-dev.523",
105
105
  "buffer": "^6.0.3",
106
106
  "events": "^3.3.0",
107
107
  "react-native-marked": "8.1.1",
108
108
  "semver": "^7.7.4",
109
109
  "typesafe-ts": "^1.7.1",
110
- "@glassly/llm-providers": "0.3.1-dev.494"
110
+ "@glassly/llm-providers": "0.3.1-dev.523"
111
111
  },
112
112
  "devDependencies": {
113
113
  "@glassly/llm-providers": "workspace:*",
package/src/engine.ts CHANGED
@@ -12,6 +12,7 @@ import {configure, start as bootstrapStart, stop as bootstrapStop, updateUiSeams
12
12
  import {cloudClientService} from "./services/CloudClientService"
13
13
  import {hydrateDeviceStore, demoteOrphanedDefaultWearable} from "./services/DeviceStoreHydration"
14
14
  import {startPairedGlassesLatch, stopPairedGlassesLatch} from "./services/PairedGlassesLatch"
15
+ import {startMiniappPauseCoordinator, stopMiniappPauseCoordinator} from "./services/MiniappPauseCoordinator"
15
16
  import {startGlassesSettingsSync, stopGlassesSettingsSync} from "./services/GlassesSettingsSync"
16
17
  import {startGlassesStatusProjection, stopGlassesStatusProjection} from "./services/GlassesStatusProjection"
17
18
  import {startOtaService, stopOtaService} from "./services/OtaService"
@@ -102,6 +103,9 @@ export const engine = {
102
103
  }
103
104
  // After the settings load so an already-paired phone latches on this start.
104
105
  startPairedGlassesLatch()
106
+ // Pause local miniapps while the paired glasses stay disconnected. Reads
107
+ // the pairing identity, so after the settings load like the latch.
108
+ startMiniappPauseCoordinator()
105
109
  // Project the glasses' OTA events into the store for the engine.ota read surface.
106
110
  startOtaService()
107
111
  // Forward glasses mic_lc3 frames to the v2 cloud session so cloud transcription
@@ -160,6 +164,7 @@ export const engine = {
160
164
  }
161
165
  await safely("glasses settings sync", stopGlassesSettingsSync)
162
166
  await safely("paired glasses latch", stopPairedGlassesLatch)
167
+ await safely("miniapp pause coordinator", stopMiniappPauseCoordinator)
163
168
  await safely("glasses status projection", stopGlassesStatusProjection)
164
169
  await safely("device event router", stopDeviceEventRouter)
165
170
  await safely("page host", () => pageHost.stop())
@@ -158,7 +158,7 @@ export const dev = {
158
158
  // Persist the host that answered — not the stale input IP — so home-tile
159
159
  // relaunches skip a dead LAN address after mDNS/Metro failover.
160
160
  const resolvedUrl = result.resolvedUrl || devUrl
161
- // The `glassly-miniapp dev` server puts the hot-reload/log sidecar on the
161
+ // The `glassly dev` server puts the hot-reload/log sidecar on the
162
162
  // user port PLUS ONE — same convention as the first-party developer-URL
163
163
  // screen's deriveDevPort.
164
164
  let devPort: number | undefined
@@ -10,7 +10,6 @@
10
10
  */
11
11
  import {useAppStatusStore} from "../stores/apps"
12
12
  import localMiniappRuntime from "../services/LocalMiniappRuntime"
13
- import {activeDashboardAmong} from "../services/pages/activeDashboard"
14
13
  import type {ClientApp} from "../types"
15
14
 
16
15
  type AppsState = ReturnType<typeof useAppStatusStore.getState>
@@ -46,6 +45,9 @@ export const miniapps = {
46
45
  /** Bring a running miniapp to the foreground. */
47
46
  setForeground: (...args: Parameters<AppsState["setForeground"]>) =>
48
47
  useAppStatusStore.getState().setForeground(...args),
48
+ /** Hand an external link's query parameters to a miniapp (queued until it has connected). */
49
+ deliverLink: (packageName: string, params: Record<string, string>) =>
50
+ localMiniappRuntime.deliverLink(packageName, params),
49
51
  /** Clear the current foreground miniapp. */
50
52
  clearForeground: () => useAppStatusStore.getState().clearForeground(),
51
53
  /** Stop all running miniapps. */
@@ -63,11 +65,9 @@ export const miniapps = {
63
65
  /** Mark a miniapp Always On (starts at launch, stops only from the switcher pill), persisted for the next boot. */
64
66
  setAlwaysOn: (...args: Parameters<AppsState["setAlwaysOn"]>) => useAppStatusStore.getState().setAlwaysOn(...args),
65
67
 
66
- /** Installed miniapps that can hold the dashboard role (manifest type "dashboard"). */
68
+ /** Installed dashboard miniapps (manifest type "dashboard"); one runs at a time. */
67
69
  dashboards: (): ClientApp[] => useAppStatusStore.getState().apps.filter((app) => app.type === "dashboard"),
68
- /** The package that holds the dashboard role, or null when no dashboard is installed. */
69
- activeDashboard: (): string | null => activeDashboardAmong(useAppStatusStore.getState().apps),
70
- /** Give the dashboard role to an installed dashboard; the previous holder stops and the new one starts. */
71
- setActiveDashboard: (...args: Parameters<AppsState["setActiveDashboard"]>) =>
72
- useAppStatusStore.getState().setActiveDashboard(...args),
70
+ /** The running dashboard miniapp, or null when none is. */
71
+ runningDashboard: (): ClientApp | null =>
72
+ useAppStatusStore.getState().apps.find((app) => app.type === "dashboard" && app.running) ?? null,
73
73
  }
@@ -10,6 +10,7 @@ import ttsModelManager from "../services/TTSModelManager"
10
10
  import offlineSpeechModelService, {
11
11
  type DownloadStatus as OfflineModelDownloadStatus,
12
12
  } from "../services/OfflineSpeechModelService"
13
+ import {offlineTranslationManager} from "../services/OfflineTranslationManager"
13
14
  import sonioxDirectService from "../services/soniox/SonioxDirectService"
14
15
  import type {
15
16
  DownloadProgress as SttDownloadProgress,
@@ -51,6 +52,17 @@ export const speech = {
51
52
  validateKey: (...args: Parameters<typeof sonioxDirectService.validateApiKey>) =>
52
53
  sonioxDirectService.validateApiKey(...args),
53
54
  },
55
+ /** On-device translation language packs (iOS 26+; the OS owns the assets). */
56
+ translation: {
57
+ isSupported: () => offlineTranslationManager.isSupported(),
58
+ /** BCP-47 language the on-device transcriber produces, i.e. the translation source. */
59
+ sourceLanguage: () => sttModelManager.getBcp47ForLanguage(),
60
+ languages: (...args: Parameters<typeof offlineTranslationManager.languages>) =>
61
+ offlineTranslationManager.languages(...args),
62
+ status: (...args: Parameters<typeof offlineTranslationManager.status>) => offlineTranslationManager.status(...args),
63
+ download: (...args: Parameters<typeof offlineTranslationManager.download>) =>
64
+ offlineTranslationManager.download(...args),
65
+ },
54
66
  tts: {
55
67
  currentLanguage: () => ttsModelManager.getCurrentLanguage(),
56
68
  languages: () => ttsModelManager.getAvailableLanguages(),
@@ -12,9 +12,9 @@ export interface EngineReleaseMetadata {
12
12
  export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
13
13
  "schemaVersion": 1,
14
14
  "familyBaseVersion": "0.3.1",
15
- "releaseIdentity": "0.3.1-dev.494",
16
- "releaseSetId": "glassly-0.3.1-dev.494",
17
- "sourceCommit": "689c685fd1ce6985d90786549b8a20dd4cbac797",
15
+ "releaseIdentity": "0.3.1-dev.523",
16
+ "releaseSetId": "glassly-0.3.1-dev.523",
17
+ "sourceCommit": "fd7d340eb6cb031649e1c48104797a4568653339",
18
18
  "otaManifestUrl": null,
19
19
  "otaManifestSha256": null
20
20
  })
package/src/index.ts CHANGED
@@ -182,8 +182,6 @@ export {
182
182
  useUninstall,
183
183
  useActiveApps,
184
184
  useRunningAlwaysOnApps,
185
- useDashboards,
186
- useActiveDashboard,
187
185
  useActiveBackgroundApps,
188
186
  useBackgroundApps,
189
187
  useActiveForegroundApp,
@@ -171,8 +171,8 @@ async function downloadMiniAppZip(url: string): Promise<string> {
171
171
  throw "CREATE_DOWNLOAD_DIR_FAILED"
172
172
  }
173
173
 
174
- // Pre-delete any cached file with the same name. Both `glassly-miniapp dev`
175
- // and `glassly-miniapp release` serve their zip at /bundle.zip, so URLs
174
+ // Pre-delete any cached file with the same name. Both `glassly dev`
175
+ // and `glassly release` serve their zip at /bundle.zip, so URLs
176
176
  // collide on the cache filename. Without this delete, a stale dev-snapshot
177
177
  // (containing the project source tree) gets unzipped instead of the
178
178
  // release build → white screen because index.html points at TSX files.
@@ -314,6 +314,7 @@ class AppRegistry {
314
314
  // Offline apps live in a separate layer so they survive the disk-rebuild
315
315
  // path in getInstalledMiniapps (which reassigns cachedApps).
316
316
  private offlineApps: ClientApp[] = []
317
+ private offlineRunning = new Set<string>()
317
318
  /** Package names whose start() requires the on-device STT model (host-declared). */
318
319
  private sttModelRequired = new Set<string>()
319
320
  private refreshNeeded: boolean = true
@@ -810,8 +811,6 @@ class AppRegistry {
810
811
  actions,
811
812
  ...(isMiniappDev ? {isMiniappDev: true} : {}),
812
813
  ...(devUrl ? {devUrl} : {}),
813
- onStart: () => saveLocalAppRunningState(lmaInfo.packageName, true),
814
- onStop: () => saveLocalAppRunningState(lmaInfo.packageName, false),
815
814
  })
816
815
  }
817
816
 
@@ -865,19 +864,17 @@ class AppRegistry {
865
864
  isMiniappDev: true,
866
865
  devUrl: rec.devUrl,
867
866
  devPort: rec.devPort,
868
- onStart: () => saveLocalAppRunningState(rec.packageName, true),
869
- onStop: () => saveLocalAppRunningState(rec.packageName, false),
870
867
  }
871
868
  })
872
869
  }
873
870
 
874
871
  private projectOfflineApps(): ClientApp[] {
875
872
  // Offline apps don't go through miniappRunningRegistry (that's for
876
- // MiniappHost-mounted webviews). Their running flag is persisted to
877
- // MMKV by saveLocalAppRunningState — read it back here so refreshes
878
- // don't blow away the active flag.
873
+ // MiniappHost-mounted webviews). Their running flag lives in
874
+ // `offlineRunning` for this process only — read it back here so refreshes
875
+ // don't blow away the active flag, while a relaunch starts them stopped.
879
876
  return this.offlineApps.map((a) => {
880
- const running = getLocalAppRunningState(a.packageName)
877
+ const running = this.offlineRunning.has(a.packageName)
881
878
  const screenshot = getLocalAppScreenshot(a.packageName)
882
879
 
883
880
  return {...a, running, screenshot}
@@ -891,8 +888,14 @@ class AppRegistry {
891
888
  }
892
889
  this.offlineApps.push({
893
890
  ...app,
894
- onStart: () => saveLocalAppRunningState(app.packageName, true),
895
- onStop: () => saveLocalAppRunningState(app.packageName, false),
891
+ onStart: () => {
892
+ this.offlineRunning.add(app.packageName)
893
+ app.onStart?.()
894
+ },
895
+ onStop: () => {
896
+ this.offlineRunning.delete(app.packageName)
897
+ app.onStop?.()
898
+ },
896
899
  })
897
900
  this.refreshNeeded = true
898
901
  this.notify()
@@ -931,21 +934,6 @@ class AppRegistry {
931
934
  }
932
935
  }
933
936
 
934
- /**
935
- * Persist the running flag for a local miniapp (read on next cold boot so
936
- * autostart picks the right set of apps). Exported for hosts that want to
937
- * track running state independently.
938
- */
939
- export function saveLocalAppRunningState(packageName: string, status: boolean): void {
940
- storage.save(`${packageName}_running`, status)
941
- }
942
-
943
- export function getLocalAppRunningState(packageName: string): boolean {
944
- const res = storage.load<boolean>(`${packageName}_running`)
945
- if (res.is_ok()) return res.value
946
- return false
947
- }
948
-
949
937
  /** The wearer's Always On choice for a miniapp, beside the `_hidden` flag; null when they never chose. */
950
938
  export function getAlwaysOnOverride(packageName: string): boolean | null {
951
939
  const res = storage.load<boolean>(`${packageName}_always_on`)
@@ -1,7 +1,7 @@
1
1
  import {BgTimer} from "../utils/timers"
2
2
 
3
3
  /**
4
- * DevServerBridge — phone-side WebSocket connection to a `glassly-miniapp dev`
4
+ * DevServerBridge — phone-side WebSocket connection to a `glassly dev`
5
5
  * sidecar running on a developer's laptop.
6
6
  *
7
7
  * Multiplexed over a single WebSocket per dev miniapp:
@@ -119,7 +119,7 @@ class DevServerBridge {
119
119
  *
120
120
  * `source` distinguishes the two halves so the CLI can prefix lines
121
121
  * `[UI]` vs `[JSRuntime]`. Logs are silently dropped when no bridge
122
- * exists for the package (no `glassly-miniapp dev` running) and
122
+ * exists for the package (no `glassly dev` running) and
123
123
  * buffered in the per-bridge ring while disconnected.
124
124
  */
125
125
  public forwardLog(
@@ -23,6 +23,7 @@ import localMiniappRuntime from "./LocalMiniappRuntime"
23
23
  import glassesKeyboardInput from "./GlassesKeyboardInput"
24
24
  import glassesSensorService from "./GlassesSensorService"
25
25
  import localSttFallbackCoordinator from "./LocalSttFallbackCoordinator"
26
+ import localTranslationService from "./LocalTranslationService"
26
27
  import speakerLabelResolver from "./SpeakerLabelResolver"
27
28
  import {phonePhotoCoordinator} from "./PhonePhotoCoordinator"
28
29
  import {phoneStreamCoordinator} from "./PhoneStreamCoordinator"
@@ -278,6 +279,7 @@ export function startDeviceEventRouter(): void {
278
279
  speakerLabelResolver.annotateLocalTranscript(event),
279
280
  "local",
280
281
  )
282
+ localTranslationService.onLocalTranscript(event, lang)
281
283
  }),
282
284
  )
283
285
 
@@ -466,7 +466,7 @@ export class JSRouter {
466
466
  line = `${method}: <unserializable>`
467
467
  }
468
468
  this.logRing.push(packageName, line)
469
- // Forward to the `glassly-miniapp dev` sidecar (if one is connected
469
+ // Forward to the `glassly dev` sidecar (if one is connected
470
470
  // for this package). Source="background" so the CLI tags lines as
471
471
  // [JSRuntime]. The bridge silently drops when no dev server is up,
472
472
  // which makes this safe to call unconditionally in prod.