@glassly/engine 0.3.7-dev.974 → 0.3.8-beta.1013

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 (100) hide show
  1. package/build/engine.d.ts +10 -0
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/facades/ai.d.ts +3 -0
  4. package/build/facades/ai.d.ts.map +1 -1
  5. package/build/facades/ai.js +3 -0
  6. package/build/facades/ai.js.map +1 -1
  7. package/build/facades/glasses.d.ts +9 -1
  8. package/build/facades/glasses.d.ts.map +1 -1
  9. package/build/facades/glasses.js +13 -0
  10. package/build/facades/glasses.js.map +1 -1
  11. package/build/generated/releaseMetadata.js +4 -4
  12. package/build/generated/releaseMetadata.js.map +1 -1
  13. package/build/index.d.ts +2 -1
  14. package/build/index.d.ts.map +1 -1
  15. package/build/index.js.map +1 -1
  16. package/build/services/CloudClientService.d.ts +2 -1
  17. package/build/services/CloudClientService.d.ts.map +1 -1
  18. package/build/services/CloudClientService.js +8 -0
  19. package/build/services/CloudClientService.js.map +1 -1
  20. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  21. package/build/services/DeviceEventRouter.js +1 -0
  22. package/build/services/DeviceEventRouter.js.map +1 -1
  23. package/build/services/DisplayDiagnostics.d.ts.map +1 -1
  24. package/build/services/DisplayDiagnostics.js +4 -2
  25. package/build/services/DisplayDiagnostics.js.map +1 -1
  26. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  27. package/build/services/LocalDisplayManager.js +5 -2
  28. package/build/services/LocalDisplayManager.js.map +1 -1
  29. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  30. package/build/services/LocalMiniappRuntime.js +7 -2
  31. package/build/services/LocalMiniappRuntime.js.map +1 -1
  32. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  33. package/build/services/LocalSttFallbackCoordinator.js +2 -0
  34. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  35. package/build/services/LocalTranslationService.d.ts +2 -0
  36. package/build/services/LocalTranslationService.d.ts.map +1 -1
  37. package/build/services/LocalTranslationService.js +22 -0
  38. package/build/services/LocalTranslationService.js.map +1 -1
  39. package/build/services/OfflineActivity.d.ts +54 -0
  40. package/build/services/OfflineActivity.d.ts.map +1 -0
  41. package/build/services/OfflineActivity.js +86 -0
  42. package/build/services/OfflineActivity.js.map +1 -0
  43. package/build/services/STTModelManager.d.ts +1 -0
  44. package/build/services/STTModelManager.d.ts.map +1 -1
  45. package/build/services/STTModelManager.js +6 -0
  46. package/build/services/STTModelManager.js.map +1 -1
  47. package/build/services/controllers.d.ts.map +1 -1
  48. package/build/services/controllers.js +2 -0
  49. package/build/services/controllers.js.map +1 -1
  50. package/build/services/effectiveCapabilities.js +3 -1
  51. package/build/services/effectiveCapabilities.js.map +1 -1
  52. package/build/services/neuralBand/NeuralBandInput.d.ts +12 -1
  53. package/build/services/neuralBand/NeuralBandInput.d.ts.map +1 -1
  54. package/build/services/neuralBand/NeuralBandInput.js +27 -0
  55. package/build/services/neuralBand/NeuralBandInput.js.map +1 -1
  56. package/build/services/neuralBand/NeuralBandSettingsSync.d.ts.map +1 -1
  57. package/build/services/neuralBand/NeuralBandSettingsSync.js +6 -0
  58. package/build/services/neuralBand/NeuralBandSettingsSync.js.map +1 -1
  59. package/build/stores/appVisibility.d.ts.map +1 -1
  60. package/build/stores/appVisibility.js +0 -1
  61. package/build/stores/appVisibility.js.map +1 -1
  62. package/build/stores/apps.js +9 -9
  63. package/build/stores/apps.js.map +1 -1
  64. package/build/stores/settings.d.ts.map +1 -1
  65. package/build/stores/settings.js +26 -3
  66. package/build/stores/settings.js.map +1 -1
  67. package/build/types/capabilities/memomind-one.d.ts.map +1 -1
  68. package/build/types/capabilities/memomind-one.js +1 -0
  69. package/build/types/capabilities/memomind-one.js.map +1 -1
  70. package/build/types/capabilities/rayneo-io.d.ts.map +1 -1
  71. package/build/types/capabilities/rayneo-io.js +3 -2
  72. package/build/types/capabilities/rayneo-io.js.map +1 -1
  73. package/build/types/enums.d.ts +3 -1
  74. package/build/types/enums.d.ts.map +1 -1
  75. package/build/types/enums.js +2 -0
  76. package/build/types/enums.js.map +1 -1
  77. package/package.json +7 -7
  78. package/src/facades/ai.ts +4 -0
  79. package/src/facades/glasses.ts +17 -0
  80. package/src/generated/releaseMetadata.ts +4 -4
  81. package/src/index.ts +9 -0
  82. package/src/services/CloudClientService.ts +8 -0
  83. package/src/services/DeviceEventRouter.ts +1 -0
  84. package/src/services/DisplayDiagnostics.ts +4 -2
  85. package/src/services/LocalDisplayManager.ts +5 -2
  86. package/src/services/LocalMiniappRuntime.ts +6 -2
  87. package/src/services/LocalSttFallbackCoordinator.ts +4 -0
  88. package/src/services/LocalTranslationService.ts +20 -0
  89. package/src/services/OfflineActivity.ts +131 -0
  90. package/src/services/STTModelManager.ts +6 -0
  91. package/src/services/controllers.ts +2 -0
  92. package/src/services/effectiveCapabilities.ts +3 -1
  93. package/src/services/neuralBand/NeuralBandInput.ts +37 -1
  94. package/src/services/neuralBand/NeuralBandSettingsSync.ts +12 -0
  95. package/src/stores/appVisibility.ts +0 -1
  96. package/src/stores/apps.ts +11 -11
  97. package/src/stores/settings.ts +26 -3
  98. package/src/types/capabilities/memomind-one.ts +1 -0
  99. package/src/types/capabilities/rayneo-io.ts +3 -2
  100. package/src/types/enums.ts +2 -0
@@ -20,6 +20,8 @@ export declare enum ControllerTypes {
20
20
  APPLE_WATCH = "Apple Watch",
21
21
  WEAR_OS = "Wear OS Watch",
22
22
  GAMEBRICK = "IINE Gamebrick",
23
- IINE_ANANKE = "IINE Ananke Gen 2"
23
+ IINE_ANANKE = "IINE Ananke Gen 2",
24
+ ZERO2 = "8BitDo Zero 2",
25
+ MICRO = "8BitDo Micro"
24
26
  }
25
27
  //# sourceMappingURL=enums.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,SAAS,sBAAsB;IAC/B,IAAI,SAAS;IACb,EAAE,sBAAsB;IACxB,EAAE,sBAAsB;IACxB,IAAI,gBAAgB;IACpB,KAAK,iBAAiB;IACtB,IAAI,eAAe;IACnB,GAAG,mBAAmB;IACtB,IAAI,SAAS;IACb,KAAK,oBAAoB;IACzB,SAAS,cAAc;IACvB,YAAY,iBAAiB;CAC9B;AAED,oBAAY,eAAe;IACzB,EAAE,sBAAsB;IACxB,WAAW,qBAAqB;IAChC,WAAW,gBAAgB;IAC3B,OAAO,kBAAkB;IACzB,SAAS,mBAAmB;IAC5B,WAAW,sBAAsB;CAClC"}
1
+ {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,SAAS,sBAAsB;IAC/B,IAAI,SAAS;IACb,EAAE,sBAAsB;IACxB,EAAE,sBAAsB;IACxB,IAAI,gBAAgB;IACpB,KAAK,iBAAiB;IACtB,IAAI,eAAe;IACnB,GAAG,mBAAmB;IACtB,IAAI,SAAS;IACb,KAAK,oBAAoB;IACzB,SAAS,cAAc;IACvB,YAAY,iBAAiB;CAC9B;AAED,oBAAY,eAAe;IACzB,EAAE,sBAAsB;IACxB,WAAW,qBAAqB;IAChC,WAAW,gBAAgB;IAC3B,OAAO,kBAAkB;IACzB,SAAS,mBAAmB;IAC5B,WAAW,sBAAsB;IACjC,KAAK,kBAAkB;IACvB,KAAK,iBAAiB;CACvB"}
@@ -23,5 +23,7 @@ export var ControllerTypes;
23
23
  ControllerTypes["WEAR_OS"] = "Wear OS Watch";
24
24
  ControllerTypes["GAMEBRICK"] = "IINE Gamebrick";
25
25
  ControllerTypes["IINE_ANANKE"] = "IINE Ananke Gen 2";
26
+ ControllerTypes["ZERO2"] = "8BitDo Zero 2";
27
+ ControllerTypes["MICRO"] = "8BitDo Micro";
26
28
  })(ControllerTypes || (ControllerTypes = {}));
27
29
  //# sourceMappingURL=enums.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,MAAM,CAAN,IAAY,WAcX;AAdD,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,8CAA+B,CAAA;IAC/B,4BAAa,CAAA;IACb,uCAAwB,CAAA;IACxB,uCAAwB,CAAA;IACxB,mCAAoB,CAAA;IACpB,qCAAsB,CAAA;IACtB,kCAAmB,CAAA;IACnB,qCAAsB,CAAA;IACtB,4BAAa,CAAA;IACb,wCAAyB,CAAA;IACzB,sCAAuB,CAAA;IACvB,4CAA6B,CAAA;AAC/B,CAAC,EAdW,WAAW,KAAX,WAAW,QActB;AAED,MAAM,CAAN,IAAY,eAOX;AAPD,WAAY,eAAe;IACzB,2CAAwB,CAAA;IACxB,mDAAgC,CAAA;IAChC,8CAA2B,CAAA;IAC3B,4CAAyB,CAAA;IACzB,+CAA4B,CAAA;IAC5B,oDAAiC,CAAA;AACnC,CAAC,EAPW,eAAe,KAAf,eAAe,QAO1B","sourcesContent":["export {HardwareType, HardwareRequirementLevel} from \"@glassly/miniapp/hardware\"\n\nexport enum DeviceTypes {\n NONE = \"None\",\n SIMULATED = \"Simulated Glasses\",\n AR99 = \"AR99\",\n G1 = \"Even Realities G1\",\n G2 = \"Even Realities G2\",\n LIVE = \"Mentra Live\",\n MACH1 = \"Mentra Mach1\",\n Z100 = \"Vuzix Z100\",\n NEX = \"Mentra Display\",\n NIMO = \"NIMO\",\n FRAME = \"Brilliant Frame\",\n RAYNEO_IO = \"RayNeo iO\",\n MEMOMIND_ONE = \"MemoMind One\",\n}\n\nexport enum ControllerTypes {\n R1 = \"Even Realities R1\",\n NEURAL_BAND = \"Meta Neural Band\",\n APPLE_WATCH = \"Apple Watch\",\n WEAR_OS = \"Wear OS Watch\",\n GAMEBRICK = \"IINE Gamebrick\",\n IINE_ANANKE = \"IINE Ananke Gen 2\",\n}\n"]}
1
+ {"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,MAAM,CAAN,IAAY,WAcX;AAdD,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,8CAA+B,CAAA;IAC/B,4BAAa,CAAA;IACb,uCAAwB,CAAA;IACxB,uCAAwB,CAAA;IACxB,mCAAoB,CAAA;IACpB,qCAAsB,CAAA;IACtB,kCAAmB,CAAA;IACnB,qCAAsB,CAAA;IACtB,4BAAa,CAAA;IACb,wCAAyB,CAAA;IACzB,sCAAuB,CAAA;IACvB,4CAA6B,CAAA;AAC/B,CAAC,EAdW,WAAW,KAAX,WAAW,QActB;AAED,MAAM,CAAN,IAAY,eASX;AATD,WAAY,eAAe;IACzB,2CAAwB,CAAA;IACxB,mDAAgC,CAAA;IAChC,8CAA2B,CAAA;IAC3B,4CAAyB,CAAA;IACzB,+CAA4B,CAAA;IAC5B,oDAAiC,CAAA;IACjC,0CAAuB,CAAA;IACvB,yCAAsB,CAAA;AACxB,CAAC,EATW,eAAe,KAAf,eAAe,QAS1B","sourcesContent":["export {HardwareType, HardwareRequirementLevel} from \"@glassly/miniapp/hardware\"\n\nexport enum DeviceTypes {\n NONE = \"None\",\n SIMULATED = \"Simulated Glasses\",\n AR99 = \"AR99\",\n G1 = \"Even Realities G1\",\n G2 = \"Even Realities G2\",\n LIVE = \"Mentra Live\",\n MACH1 = \"Mentra Mach1\",\n Z100 = \"Vuzix Z100\",\n NEX = \"Mentra Display\",\n NIMO = \"NIMO\",\n FRAME = \"Brilliant Frame\",\n RAYNEO_IO = \"RayNeo iO\",\n MEMOMIND_ONE = \"MemoMind One\",\n}\n\nexport enum ControllerTypes {\n R1 = \"Even Realities R1\",\n NEURAL_BAND = \"Meta Neural Band\",\n APPLE_WATCH = \"Apple Watch\",\n WEAR_OS = \"Wear OS Watch\",\n GAMEBRICK = \"IINE Gamebrick\",\n IINE_ANANKE = \"IINE Ananke Gen 2\",\n ZERO2 = \"8BitDo Zero 2\",\n MICRO = \"8BitDo Micro\",\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/engine",
3
- "version": "0.3.7-dev.974",
3
+ "version": "0.3.8-beta.1013",
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.7-dev.974",
101
- "@glassly/cloud-client": "0.3.7-dev.974",
102
- "@glassly/cloud-protocol": "0.3.7-dev.974",
103
- "@glassly/crust": "0.3.7-dev.974",
104
- "@glassly/miniapp": "0.3.7-dev.974",
100
+ "@glassly/bluetooth-sdk": "0.3.8-beta.1013",
101
+ "@glassly/cloud-client": "0.3.8-beta.1013",
102
+ "@glassly/cloud-protocol": "0.3.8-beta.1013",
103
+ "@glassly/crust": "0.3.8-beta.1013",
104
+ "@glassly/miniapp": "0.3.8-beta.1013",
105
105
  "buffer": "^6.0.3",
106
106
  "events": "^3.3.0",
107
107
  "react-native-marked": "8.3.2",
108
108
  "semver": "^7.7.4",
109
109
  "typesafe-ts": "^1.7.1",
110
- "@glassly/llm-providers": "0.3.7-dev.974",
110
+ "@glassly/llm-providers": "0.3.8-beta.1013",
111
111
  "@huggingface/jinja": "^0.5.9",
112
112
  "jsonrepair": "^3.12.0"
113
113
  },
package/src/facades/ai.ts CHANGED
@@ -12,6 +12,7 @@ import localLlmModelManager, {
12
12
  type LocalLlmModelId,
13
13
  } from "../services/LocalLlmModelManager"
14
14
  import localLlmProvider from "../services/LocalLlmProvider"
15
+ import offlineActivity, {type OfflineActivity} from "../services/OfflineActivity"
15
16
 
16
17
  export const ai = {
17
18
  local: {
@@ -40,4 +41,7 @@ export const ai = {
40
41
  unload: (): Promise<void> => localLlmProvider.unload(),
41
42
  formatBytes: (bytes: number): string => LocalLlmModelManager.formatBytes(bytes),
42
43
  },
44
+ offlineActivity: (): OfflineActivity => offlineActivity.current(),
45
+ onOfflineActivity: (listener: (activity: OfflineActivity) => void): (() => void) =>
46
+ offlineActivity.subscribe(listener),
43
47
  }
@@ -19,6 +19,7 @@ import type {
19
19
  Device,
20
20
  KeyboardAdvertEvent,
21
21
  KeyboardPairingEvent,
22
+ NeuralBandFirmwareEvent,
22
23
  PairedController,
23
24
  TouchEvent,
24
25
  } from "@glassly/bluetooth-sdk"
@@ -39,6 +40,7 @@ import {pushAllBluetoothSettings} from "../services/GlassesSettingsSync"
39
40
  import {isControllerReady, pairedControllersFrom, readyControllers} from "../services/controllers"
40
41
  import neuralBandInput, {
41
42
  type NeuralBandClaimStatus,
43
+ type NeuralBandFirmwareStatus,
42
44
  type NeuralBandHandwritingStatus,
43
45
  type NeuralBandHandwritingUpdate,
44
46
  } from "../services/neuralBand/NeuralBandInput"
@@ -344,6 +346,21 @@ export const glasses = {
344
346
  /** The band's air mouse; movement reaches miniapps on the pointer stream. */
345
347
  setPointer: (id: string, enabled: boolean): Promise<void> => BluetoothSdk.neuralBandSetPointer(id, enabled),
346
348
  setHand: (id: string, hand: "left" | "right"): Promise<void> => BluetoothSdk.neuralBandSetHand(id, hand),
349
+ firmwareStatus: (id: string): NeuralBandFirmwareStatus => ({...neuralBandInput.firmwareStatus(id)}),
350
+ onFirmwareStatus: (cb: (update: {id: string; status: NeuralBandFirmwareStatus}) => void): (() => void) =>
351
+ neuralBandInput.onFirmwareStatus(cb),
352
+ refreshFirmware: async (id: string): Promise<NeuralBandFirmwareStatus> => {
353
+ const info = await BluetoothSdk.neuralBandFirmwareInfo(id)
354
+ neuralBandInput.handleFirmware({
355
+ ...(info as object),
356
+ type: "neural_band_firmware",
357
+ controllerId: id,
358
+ } as NeuralBandFirmwareEvent)
359
+ return neuralBandInput.firmwareStatus(id)
360
+ },
361
+ checkFirmware: (id: string): Promise<void> => BluetoothSdk.neuralBandCheckFirmware(id),
362
+ updateFirmware: (id: string, mode: "update" | "cancel" | "key" | "send" | "trial" | "install"): Promise<void> =>
363
+ BluetoothSdk.neuralBandUpdateFirmware(id, mode),
347
364
  },
348
365
 
349
366
  // --- watch: an Apple Watch over WatchConnectivity (iOS), a Wear OS watch over the Data Layer (Android) ---
@@ -11,10 +11,10 @@ export interface EngineReleaseMetadata {
11
11
 
12
12
  export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
13
13
  "schemaVersion": 1,
14
- "familyBaseVersion": "0.3.7",
15
- "releaseIdentity": "0.3.7-dev.974",
16
- "releaseSetId": "glassly-0.3.7-dev.974",
17
- "sourceCommit": "b5a2910045d3f4f16014e78df052f9010dea6a99",
14
+ "familyBaseVersion": "0.3.8",
15
+ "releaseIdentity": "0.3.8-beta.1013",
16
+ "releaseSetId": "glassly-0.3.8-beta.1013",
17
+ "sourceCommit": "35a6df924b5f3a9f3b15e5f25d4a263c6d0b82ee",
18
18
  "otaManifestUrl": null,
19
19
  "otaManifestSha256": null
20
20
  })
package/src/index.ts CHANGED
@@ -64,6 +64,14 @@ export type {
64
64
  LocalLlmModelId,
65
65
  } from "./services/LocalLlmModelManager"
66
66
  export {DEFAULT_LOCAL_LLM_MODEL, isLocalLlmModelId} from "./services/LocalLlmModelManager"
67
+ export type {
68
+ OfflineActivity,
69
+ OfflineActivityReason,
70
+ OfflineLlmActivity,
71
+ OfflineTranscriptionActivity,
72
+ OfflineTranslationActivity,
73
+ OfflineTranslationPair,
74
+ } from "./services/OfflineActivity"
67
75
 
68
76
  // Shared user memory timeline (engine.memory / Settings > Memory). Entry and
69
77
  // session shapes are canonical in @glassly/miniapp (the session.memory
@@ -231,6 +239,7 @@ export {
231
239
  } from "./services/neuralBand/MetaAuth"
232
240
  export type {
233
241
  NeuralBandClaimStatus,
242
+ NeuralBandFirmwareStatus,
234
243
  NeuralBandHandwritingStatus,
235
244
  NeuralBandHandwritingUpdate,
236
245
  } from "./services/neuralBand/NeuralBandInput"
@@ -19,6 +19,7 @@ import type {
19
19
  RuntimeSnapshot,
20
20
  StoreCatalogResponse,
21
21
  StoreListing,
22
+ StoreReleasesResponse,
22
23
  } from "@glassly/cloud-client/react-native"
23
24
  import type {SubjectTokenType} from "@glassly/cloud-client"
24
25
  import {AppState, Platform} from "react-native"
@@ -778,6 +779,13 @@ export const cloudClientService = {
778
779
  return c.core.store.get(packageName)
779
780
  },
780
781
 
782
+ async getStoreReleases(packageName: string): Promise<StoreReleasesResponse> {
783
+ if (!client) this.init()
784
+ const c = client
785
+ if (!c?.core) throw new Error("cloud client core is unavailable")
786
+ return c.core.store.releases(packageName)
787
+ },
788
+
781
789
  /** Bytes of a store icon or screenshot; these routes need the user Bearer. */
782
790
  async fetchStoreAsset(url: string): Promise<Uint8Array> {
783
791
  if (!client) this.init()
@@ -245,6 +245,7 @@ export function startDeviceEventRouter(): void {
245
245
  subs.push(BluetoothSdk.addListener("neural_band_claim", (event) => neuralBandInput.handleClaim(event)))
246
246
  subs.push(BluetoothSdk.addListener("neural_band_handwriting", (event) => neuralBandInput.handleHandwriting(event)))
247
247
  subs.push(BluetoothSdk.addListener("neural_band_pointer", (event) => neuralBandInput.handlePointer(event)))
248
+ subs.push(BluetoothSdk.addListener("neural_band_firmware", (event) => neuralBandInput.handleFirmware(event)))
248
249
  subs.push({
249
250
  remove: useGlassesStore.subscribe(
250
251
  (s) => s.keyboardConnected === true,
@@ -4,6 +4,7 @@ import type {DisplayDiagnostic} from "@glassly/miniapp"
4
4
  // messages, packet bytes, or arbitrary application content to another miniapp.
5
5
  const fields = new Set([
6
6
  "v",
7
+ "device",
7
8
  "run",
8
9
  "id",
9
10
  "t",
@@ -72,10 +73,11 @@ export function collectDisplayDiagnostics(
72
73
  const seen = new Set<string>()
73
74
  for (let i = entries.length - 1; i >= 0 && records.length < 180; i--) {
74
75
  const message = entries[i].message
75
- const offset = message.indexOf("G2PERF ")
76
+ const prefix = message.includes("MEMOMINDPERF ") ? "MEMOMINDPERF " : "G2PERF "
77
+ const offset = message.indexOf(prefix)
76
78
  if (offset < 0 || message.length > 8192) continue
77
79
  try {
78
- const raw = JSON.parse(message.slice(offset + 7))
80
+ const raw = JSON.parse(message.slice(offset + prefix.length))
79
81
  if (
80
82
  !raw ||
81
83
  raw.v !== 1 ||
@@ -627,6 +627,9 @@ class LocalDisplayManager {
627
627
  this.currentDisplay &&
628
628
  this.currentDisplay.packageName === this.backgroundLock.packageName
629
629
  if (bgHoldsAndDisplays) {
630
+ // The retained scene is now older than the saved frame; forget it so
631
+ // the restore paints this frame rather than replaying the stale one.
632
+ sceneRenderer.clearApp(packageName)
630
633
  resolve?.({status: "blocked", reason: "a background app holds the display"})
631
634
  return
632
635
  }
@@ -974,8 +977,8 @@ class LocalDisplayManager {
974
977
  }, delay)
975
978
  }
976
979
  } else {
977
- // Retained state gone (app stopped since) — re-render the saved
978
- // elements as a fresh scene instead.
980
+ // Retained state gone (app stopped, or its newer frame was blocked) —
981
+ // re-render the saved elements as a fresh scene instead.
979
982
  this.sendNow(saved.packageName, {view: "main", scene: saved.scene, durationMs: remaining})
980
983
  }
981
984
  return
@@ -57,6 +57,7 @@ import type {AiTier, AiTierProvider, SavedLlmKeys} from "./AiTierResolution"
57
57
  import {decideLlmRoute, localLlmReason, type LocalLlmReason} from "./LlmRouting"
58
58
  import localLlmModelManager from "./LocalLlmModelManager"
59
59
  import localLlmProvider from "./LocalLlmProvider"
60
+ import offlineActivity from "./OfflineActivity"
60
61
  import {HttpError} from "@glassly/cloud-client"
61
62
  import type {
62
63
  QuotaStatusPayload,
@@ -1680,11 +1681,11 @@ class LocalMiniappRuntime {
1680
1681
  this.handleOpenUrl(packageName, payload)
1681
1682
  break
1682
1683
  case MiniappRequestType.DISPLAY_DIAGNOSTICS: {
1683
- // The records are parsed out of the G2PERF lines, which the SDK only logs while g2_perf_logs is on.
1684
+ // The records are parsed out of the G2PERF lines, which the SDK only logs while perf_logs is on.
1684
1685
  const settings = useSettingsStore.getState()
1685
1686
  const enabled =
1686
1687
  settings.getSetting(SETTINGS.debug_console.key) === true &&
1687
- settings.getSetting(SETTINGS.g2_perf_logs.key) === true
1688
+ settings.getSetting(SETTINGS.perf_logs.key) === true
1688
1689
  this.sendResult(packageName, requestId, true, {
1689
1690
  enabled,
1690
1691
  generatedAt: Date.now(),
@@ -4343,6 +4344,7 @@ class LocalMiniappRuntime {
4343
4344
  return
4344
4345
  }
4345
4346
 
4347
+ const endActivity = offlineActivity.beginLlm(reason, localLlmModelManager.model(modelId).label)
4346
4348
  try {
4347
4349
  // webSearch is a vendor server-side grant; the phone has none.
4348
4350
  const {webSearch: _webSearch, ...localRequest} = request
@@ -4367,6 +4369,8 @@ class LocalMiniappRuntime {
4367
4369
  undefined,
4368
4370
  primaryError ?? {code: MiniappErrorCode.AI_LOCAL_UNAVAILABLE, message},
4369
4371
  )
4372
+ } finally {
4373
+ endActivity()
4370
4374
  }
4371
4375
  }
4372
4376
 
@@ -2,6 +2,7 @@ import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
2
2
  import {ISLAND_SETTINGS_KEYS} from "../runtime/config"
3
3
  import {useSettingsStore} from "../stores/settings"
4
4
  import {cloudClientService} from "./CloudClientService"
5
+ import offlineActivity from "./OfflineActivity"
5
6
  import sttModelManager from "./STTModelManager"
6
7
 
7
8
  /**
@@ -223,6 +224,9 @@ class LocalSttFallbackCoordinator {
223
224
 
224
225
  private publishReason(): void {
225
226
  const reason = this.localActive ? this.currentReason() : ""
227
+ offlineActivity.setTranscription(
228
+ this.localActive ? {reason: this.currentReason(), model: sttModelManager.currentModelLabel()} : null,
229
+ )
226
230
  const store = useSettingsStore.getState()
227
231
  if (store.getSetting(ISLAND_SETTINGS_KEYS.localSttFallbackReason) !== reason) {
228
232
  store.setSetting(ISLAND_SETTINGS_KEYS.localSttFallbackReason, reason)
@@ -1,3 +1,4 @@
1
+ import offlineActivity, {type ServedTranslationPair} from "./OfflineActivity"
1
2
  import {offlineTranslationManager} from "./OfflineTranslationManager"
2
3
 
3
4
  /** One concrete-target `translation:<source>:<target>` stream and who listens to it. */
@@ -67,6 +68,7 @@ export class LocalTranslationService {
67
68
 
68
69
  attach(host: Host): void {
69
70
  this.host = host
71
+ offlineActivity.setTranslationSource(() => this.servedPairs())
70
72
  }
71
73
 
72
74
  setSubscriptions(subscriptions: LocalTranslationSubscription[]): void {
@@ -75,6 +77,24 @@ export class LocalTranslationService {
75
77
  for (const key of this.lanes.keys()) {
76
78
  if (!live.has(key.substring(key.indexOf(">") + 1))) this.lanes.delete(key)
77
79
  }
80
+ offlineActivity.refresh()
81
+ }
82
+
83
+ servedPairs(): ServedTranslationPair[] {
84
+ if (!this.host || !offlineTranslationManager.isSupported()) return []
85
+ const pairs = new Map<string, ServedTranslationPair>()
86
+ let primaryUp: boolean | null = null
87
+ for (const sub of this.subscriptions) {
88
+ if (baseLanguage(sub.source) === baseLanguage(sub.target)) continue
89
+ if (!sub.forceLocal) {
90
+ primaryUp ??= this.host.primaryUp()
91
+ if (primaryUp || !sub.cloud) continue
92
+ }
93
+ const key = `${sub.source}>${sub.target}`
94
+ const existing = pairs.get(key)
95
+ pairs.set(key, {source: sub.source, target: sub.target, forceLocal: sub.forceLocal || !!existing?.forceLocal})
96
+ }
97
+ return [...pairs.values()]
78
98
  }
79
99
 
80
100
  onLocalTranscript(transcript: LocalTranscript, sourceLanguage: string): void {
@@ -0,0 +1,131 @@
1
+ /**
2
+ * What the phone is running on-device right now (transcription, translation,
3
+ * LLM) and why, for the home offline card. Producers push in; it imports
4
+ * nothing at runtime so their test stubs stay unaffected.
5
+ */
6
+ import type {LocalLlmReason} from "./LlmRouting"
7
+ import type {LocalSttFallbackReason} from "./LocalSttFallbackCoordinator"
8
+
9
+ export type OfflineActivityReason = LocalSttFallbackReason | LocalLlmReason
10
+
11
+ export interface OfflineTranscriptionActivity {
12
+ reason: LocalSttFallbackReason
13
+ model: string
14
+ }
15
+
16
+ export interface OfflineTranslationPair {
17
+ source: string
18
+ target: string
19
+ }
20
+
21
+ export interface OfflineTranslationActivity {
22
+ reason: OfflineActivityReason
23
+ pairs: OfflineTranslationPair[]
24
+ }
25
+
26
+ export interface OfflineLlmActivity {
27
+ reason: LocalLlmReason
28
+ model: string
29
+ requests: number
30
+ }
31
+
32
+ export interface OfflineActivity {
33
+ transcription: OfflineTranscriptionActivity | null
34
+ translation: OfflineTranslationActivity | null
35
+ llm: OfflineLlmActivity | null
36
+ }
37
+
38
+ export interface ServedTranslationPair extends OfflineTranslationPair {
39
+ forceLocal: boolean
40
+ }
41
+
42
+ const EMPTY: OfflineActivity = {transcription: null, translation: null, llm: null}
43
+
44
+ class OfflineActivityTracker {
45
+ private transcription: OfflineTranscriptionActivity | null = null
46
+ private llmRuns: Array<{reason: LocalLlmReason; model: string}> = []
47
+ private translationSource: (() => ServedTranslationPair[]) | null = null
48
+ private snapshot: OfflineActivity = EMPTY
49
+ private snapshotKey = JSON.stringify(EMPTY)
50
+ private listeners = new Set<(activity: OfflineActivity) => void>()
51
+
52
+ current(): OfflineActivity {
53
+ return this.snapshot
54
+ }
55
+
56
+ subscribe(listener: (activity: OfflineActivity) => void): () => void {
57
+ this.listeners.add(listener)
58
+ return () => {
59
+ this.listeners.delete(listener)
60
+ }
61
+ }
62
+
63
+ setTranscription(transcription: OfflineTranscriptionActivity | null): void {
64
+ this.transcription = transcription
65
+ this.refresh()
66
+ }
67
+
68
+ beginLlm(reason: LocalLlmReason, model: string): () => void {
69
+ const run = {reason, model}
70
+ this.llmRuns.push(run)
71
+ this.refresh()
72
+ let ended = false
73
+ return () => {
74
+ if (ended) return
75
+ ended = true
76
+ this.llmRuns = this.llmRuns.filter((r) => r !== run)
77
+ this.refresh()
78
+ }
79
+ }
80
+
81
+ setTranslationSource(source: (() => ServedTranslationPair[]) | null): void {
82
+ this.translationSource = source
83
+ this.refresh()
84
+ }
85
+
86
+ refresh(): void {
87
+ const next: OfflineActivity = {
88
+ transcription: this.transcription,
89
+ translation: this.translation(),
90
+ llm: this.llm(),
91
+ }
92
+ const key = JSON.stringify(next)
93
+ if (key === this.snapshotKey) return
94
+ this.snapshotKey = key
95
+ this.snapshot = next
96
+ for (const listener of this.listeners) {
97
+ try {
98
+ listener(next)
99
+ } catch (error) {
100
+ console.warn("OfflineActivity: listener failed:", error)
101
+ }
102
+ }
103
+ }
104
+
105
+ reset(): void {
106
+ this.transcription = null
107
+ this.llmRuns = []
108
+ this.translationSource = null
109
+ this.refresh()
110
+ }
111
+
112
+ private translation(): OfflineTranslationActivity | null {
113
+ if (!this.transcription || !this.translationSource) return null
114
+ const pairs = this.translationSource()
115
+ if (pairs.length === 0) return null
116
+ return {
117
+ reason: pairs.some((p) => p.forceLocal) ? "force_local" : this.transcription.reason,
118
+ pairs: pairs.map(({source, target}) => ({source, target})),
119
+ }
120
+ }
121
+
122
+ private llm(): OfflineLlmActivity | null {
123
+ const latest = this.llmRuns[this.llmRuns.length - 1]
124
+ if (!latest) return null
125
+ return {reason: latest.reason, model: latest.model, requests: this.llmRuns.length}
126
+ }
127
+ }
128
+
129
+ export {OfflineActivityTracker}
130
+ const offlineActivity = new OfflineActivityTracker()
131
+ export default offlineActivity
@@ -228,6 +228,12 @@ class STTModelManager {
228
228
  return this.currentLanguage
229
229
  }
230
230
 
231
+ currentModelLabel(): string {
232
+ const language = this.config(this.currentLanguage)
233
+ if (!language) return this.currentLanguage
234
+ return language.type === "apple" ? `Apple · ${language.displayName}` : language.displayName
235
+ }
236
+
231
237
  setCurrentLanguage(code: string): void {
232
238
  if (this.config(code)) {
233
239
  this.currentLanguage = code
@@ -55,6 +55,8 @@ const INPUT_SURFACES: Readonly<Record<string, readonly InputSurface[]>> = {
55
55
  [ControllerTypes.WEAR_OS]: [WATCH_SURFACE],
56
56
  [ControllerTypes.GAMEBRICK]: [GAMEBRICK_SURFACE],
57
57
  [ControllerTypes.IINE_ANANKE]: [GAMEBRICK_SURFACE],
58
+ [ControllerTypes.ZERO2]: [GAMEBRICK_SURFACE],
59
+ [ControllerTypes.MICRO]: [GAMEBRICK_SURFACE],
58
60
  [ControllerTypes.R1]: [],
59
61
  }
60
62
 
@@ -61,6 +61,8 @@ function glassesCapabilities(model: DeviceTypes): Capabilities {
61
61
  // Glassly CFW supports the complete vector contract, including SVG and rotation.
62
62
  // Upstream g2flash publishes a raster canvas with sceneVector=false.
63
63
  const vector = useGlassesStore.getState().sceneVector === true
64
+ // Depth is a disparity between the lenses; one panel (MemoMind One) draws every element flat.
65
+ const stereo = (base.display.count ?? 1) >= 2
64
66
  return {
65
67
  ...(volumeGate ? withVolumeGate(base) : base),
66
68
  hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),
@@ -81,7 +83,7 @@ function glassesCapabilities(model: DeviceTypes): Capabilities {
81
83
  vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_IMAGE_ELEMENTS,
82
84
  ),
83
85
  ...(vector
84
- ? {shapes: [...ALL_DISPLAY_SHAPES, "svg" as const], animation: true, rotation: true, depth: true}
86
+ ? {shapes: [...ALL_DISPLAY_SHAPES, "svg" as const], animation: true, rotation: true, depth: stereo}
85
87
  : {}),
86
88
  },
87
89
  }
@@ -6,7 +6,12 @@
6
6
  * latest claim is also kept on its own, since the pairing screen runs before
7
7
  * the band's id reaches JS.
8
8
  */
9
- import type {NeuralBandClaimEvent, NeuralBandHandwritingEvent, NeuralBandPointerEvent} from "@glassly/bluetooth-sdk"
9
+ import type {
10
+ NeuralBandClaimEvent,
11
+ NeuralBandFirmwareEvent,
12
+ NeuralBandHandwritingEvent,
13
+ NeuralBandPointerEvent,
14
+ } from "@glassly/bluetooth-sdk"
10
15
  import {MiniappStreamType} from "@glassly/miniapp"
11
16
 
12
17
  import type {KeyboardEventPayload} from "../GlassesKeyboardInput"
@@ -34,6 +39,8 @@ export interface NeuralBandHandwritingUpdate {
34
39
  status: NeuralBandHandwritingStatus
35
40
  }
36
41
 
42
+ export type NeuralBandFirmwareStatus = Omit<NeuralBandFirmwareEvent, "type" | "controllerId"> & {known: boolean}
43
+
37
44
  export interface NeuralBandInputOptions {
38
45
  forward?: (stream: string, payload: unknown) => void
39
46
  now?: () => number
@@ -67,13 +74,25 @@ function codeFor(char: string): string {
67
74
 
68
75
  const IDLE_CLAIM: NeuralBandClaimStatus = {stage: "idle", sessionExpired: false}
69
76
  const IDLE_HANDWRITING: NeuralBandHandwritingStatus = {phase: "idle", message: "", text: ""}
77
+ const IDLE_FIRMWARE: NeuralBandFirmwareStatus = {
78
+ known: false,
79
+ connected: false,
80
+ hasMetaSession: false,
81
+ checked: false,
82
+ updateAvailable: false,
83
+ phase: "idle",
84
+ progress: 0,
85
+ message: "",
86
+ }
70
87
 
71
88
  export class NeuralBandInput {
72
89
  private latestClaim: NeuralBandClaimStatus = IDLE_CLAIM
73
90
  private claims = new Map<string, NeuralBandClaimStatus>()
74
91
  private handwriting = new Map<string, NeuralBandHandwritingStatus>()
92
+ private firmware = new Map<string, NeuralBandFirmwareStatus>()
75
93
  private claimListeners = new Set<(status: NeuralBandClaimStatus) => void>()
76
94
  private handwritingListeners = new Set<(update: NeuralBandHandwritingUpdate) => void>()
95
+ private firmwareListeners = new Set<(update: {id: string; status: NeuralBandFirmwareStatus}) => void>()
77
96
  private readonly forward: (stream: string, payload: unknown) => void
78
97
  private readonly now: () => number
79
98
 
@@ -102,6 +121,15 @@ export class NeuralBandInput {
102
121
  return () => this.handwritingListeners.delete(cb)
103
122
  }
104
123
 
124
+ firmwareStatus(controllerId: string): NeuralBandFirmwareStatus {
125
+ return this.firmware.get(controllerId) ?? IDLE_FIRMWARE
126
+ }
127
+
128
+ onFirmwareStatus(cb: (update: {id: string; status: NeuralBandFirmwareStatus}) => void): () => void {
129
+ this.firmwareListeners.add(cb)
130
+ return () => this.firmwareListeners.delete(cb)
131
+ }
132
+
105
133
  /** A new pairing attempt starts from a clean slate. */
106
134
  resetClaim(): void {
107
135
  this.latestClaim = IDLE_CLAIM
@@ -112,6 +140,14 @@ export class NeuralBandInput {
112
140
  forget(controllerId: string): void {
113
141
  this.claims.delete(controllerId)
114
142
  this.handwriting.delete(controllerId)
143
+ this.firmware.delete(controllerId)
144
+ }
145
+
146
+ handleFirmware(event: NeuralBandFirmwareEvent): void {
147
+ const {type: _type, controllerId, ...rest} = event
148
+ const status: NeuralBandFirmwareStatus = {...rest, known: true}
149
+ this.firmware.set(controllerId, status)
150
+ for (const cb of this.firmwareListeners) cb({id: controllerId, status})
115
151
  }
116
152
 
117
153
  handleClaim(event: NeuralBandClaimEvent): void {
@@ -32,6 +32,13 @@ function pushHand(id: string): void {
32
32
  )
33
33
  }
34
34
 
35
+ function pushVibration(id: string): void {
36
+ const enabled = useSettingsStore.getState().getSetting(SETTINGS.neural_band_vibration.key) !== false
37
+ void BluetoothSdk.neuralBandSetGestureHaptics(id, enabled).catch((err) =>
38
+ console.warn("NeuralBandSettingsSync: vibration push failed:", err),
39
+ )
40
+ }
41
+
35
42
  export function startNeuralBandSettingsSync(): void {
36
43
  if (unsubscribers.length > 0) return
37
44
  let connected = new Set(connectedBandIds())
@@ -44,6 +51,7 @@ export function startNeuralBandSettingsSync(): void {
44
51
  if (connected.has(id)) continue
45
52
  pushAirMouse(id)
46
53
  pushHand(id)
54
+ pushVibration(id)
47
55
  }
48
56
  connected = next
49
57
  },
@@ -56,6 +64,10 @@ export function startNeuralBandSettingsSync(): void {
56
64
  (s) => s.getSetting(SETTINGS.neural_band_hand.key),
57
65
  () => connectedBandIds().forEach(pushHand),
58
66
  ),
67
+ useSettingsStore.subscribe(
68
+ (s) => s.getSetting(SETTINGS.neural_band_vibration.key),
69
+ () => connectedBandIds().forEach(pushVibration),
70
+ ),
59
71
  ]
60
72
  }
61
73
 
@@ -28,7 +28,6 @@ export const DEV_TOOLS_PACKAGES: ReadonlySet<string> = new Set([
28
28
  "com.glassly.example",
29
29
  "com.glassly.tetris",
30
30
  "com.glassly.compass",
31
- "com.glassly.showcase",
32
31
  ])
33
32
 
34
33
  /** True when the app is dev-only and developer tools are off. */