@glassly/engine 0.3.8-beta.984 → 0.3.8-dev.1000
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.
- package/build/engine.d.ts +2 -0
- package/build/engine.d.ts.map +1 -1
- package/build/facades/ai.d.ts +3 -0
- package/build/facades/ai.d.ts.map +1 -1
- package/build/facades/ai.js +3 -0
- package/build/facades/ai.js.map +1 -1
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts +0 -2
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +5 -6
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +0 -6
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +5 -23
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.js +2 -0
- package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
- package/build/services/LocalTranslationService.d.ts +2 -0
- package/build/services/LocalTranslationService.d.ts.map +1 -1
- package/build/services/LocalTranslationService.js +22 -0
- package/build/services/LocalTranslationService.js.map +1 -1
- package/build/services/OfflineActivity.d.ts +54 -0
- package/build/services/OfflineActivity.d.ts.map +1 -0
- package/build/services/OfflineActivity.js +86 -0
- package/build/services/OfflineActivity.js.map +1 -0
- package/build/services/STTModelManager.d.ts +1 -0
- package/build/services/STTModelManager.d.ts.map +1 -1
- package/build/services/STTModelManager.js +6 -0
- package/build/services/STTModelManager.js.map +1 -1
- package/build/services/SceneRenderer.d.ts +0 -8
- package/build/services/SceneRenderer.d.ts.map +1 -1
- package/build/services/SceneRenderer.js +0 -28
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/controllers.d.ts.map +1 -1
- package/build/services/controllers.js +1 -0
- package/build/services/controllers.js.map +1 -1
- package/build/services/effectiveCapabilities.js +3 -1
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +7 -0
- package/build/stores/settings.js.map +1 -1
- package/build/types/capabilities/memomind-one.d.ts.map +1 -1
- package/build/types/capabilities/memomind-one.js +1 -0
- package/build/types/capabilities/memomind-one.js.map +1 -1
- package/build/types/capabilities/rayneo-io.d.ts.map +1 -1
- package/build/types/capabilities/rayneo-io.js +3 -2
- package/build/types/capabilities/rayneo-io.js.map +1 -1
- package/build/types/enums.d.ts +2 -1
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -0
- package/build/types/enums.js.map +1 -1
- package/package.json +7 -7
- package/src/facades/ai.ts +4 -0
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/index.ts +8 -0
- package/src/services/LocalDisplayManager.ts +5 -7
- package/src/services/LocalMiniappRuntime.ts +4 -24
- package/src/services/LocalSttFallbackCoordinator.ts +4 -0
- package/src/services/LocalTranslationService.ts +20 -0
- package/src/services/OfflineActivity.ts +131 -0
- package/src/services/STTModelManager.ts +6 -0
- package/src/services/SceneRenderer.ts +0 -28
- package/src/services/controllers.ts +1 -0
- package/src/services/effectiveCapabilities.ts +3 -1
- package/src/stores/settings.ts +7 -0
- package/src/types/capabilities/memomind-one.ts +1 -0
- package/src/types/capabilities/rayneo-io.ts +3 -2
- package/src/types/enums.ts +1 -0
package/build/types/enums.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ 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"
|
|
24
25
|
}
|
|
25
26
|
//# 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;
|
|
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;CACxB"}
|
package/build/types/enums.js
CHANGED
|
@@ -23,5 +23,6 @@ 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";
|
|
26
27
|
})(ControllerTypes || (ControllerTypes = {}));
|
|
27
28
|
//# sourceMappingURL=enums.js.map
|
package/build/types/enums.js.map
CHANGED
|
@@ -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,
|
|
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,eAQX;AARD,WAAY,eAAe;IACzB,2CAAwB,CAAA;IACxB,mDAAgC,CAAA;IAChC,8CAA2B,CAAA;IAC3B,4CAAyB,CAAA;IACzB,+CAA4B,CAAA;IAC5B,oDAAiC,CAAA;IACjC,0CAAuB,CAAA;AACzB,CAAC,EARW,eAAe,KAAf,eAAe,QAQ1B","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}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glassly/engine",
|
|
3
|
-
"version": "0.3.8-
|
|
3
|
+
"version": "0.3.8-dev.1000",
|
|
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.8-
|
|
101
|
-
"@glassly/cloud-client": "0.3.8-
|
|
102
|
-
"@glassly/cloud-protocol": "0.3.8-
|
|
103
|
-
"@glassly/crust": "0.3.8-
|
|
104
|
-
"@glassly/miniapp": "0.3.8-
|
|
100
|
+
"@glassly/bluetooth-sdk": "0.3.8-dev.1000",
|
|
101
|
+
"@glassly/cloud-client": "0.3.8-dev.1000",
|
|
102
|
+
"@glassly/cloud-protocol": "0.3.8-dev.1000",
|
|
103
|
+
"@glassly/crust": "0.3.8-dev.1000",
|
|
104
|
+
"@glassly/miniapp": "0.3.8-dev.1000",
|
|
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.8-
|
|
110
|
+
"@glassly/llm-providers": "0.3.8-dev.1000",
|
|
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
|
}
|
|
@@ -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.8",
|
|
15
|
-
"releaseIdentity": "0.3.8-
|
|
16
|
-
"releaseSetId": "glassly-0.3.8-
|
|
17
|
-
"sourceCommit": "
|
|
15
|
+
"releaseIdentity": "0.3.8-dev.1000",
|
|
16
|
+
"releaseSetId": "glassly-0.3.8-dev.1000",
|
|
17
|
+
"sourceCommit": "8a287c091f98f631f77fe37b89eb6004ffc8ea1a",
|
|
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
|
|
@@ -489,11 +489,6 @@ class LocalDisplayManager {
|
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
-
/** A frame for native to keep warm, never shown (SceneRenderer.prefetchScene). */
|
|
493
|
-
public prefetch(packageName: string, view: "main" | "dashboard", elements: SceneElementInput[]): boolean {
|
|
494
|
-
return sceneRenderer.prefetchScene(packageName, view, elements)
|
|
495
|
-
}
|
|
496
|
-
|
|
497
492
|
/**
|
|
498
493
|
* The overlay slot only; bypasses arbitration and main ownership state.
|
|
499
494
|
* Frames from an owner other than the slot's are dropped. Returns the scene
|
|
@@ -632,6 +627,9 @@ class LocalDisplayManager {
|
|
|
632
627
|
this.currentDisplay &&
|
|
633
628
|
this.currentDisplay.packageName === this.backgroundLock.packageName
|
|
634
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)
|
|
635
633
|
resolve?.({status: "blocked", reason: "a background app holds the display"})
|
|
636
634
|
return
|
|
637
635
|
}
|
|
@@ -979,8 +977,8 @@ class LocalDisplayManager {
|
|
|
979
977
|
}, delay)
|
|
980
978
|
}
|
|
981
979
|
} else {
|
|
982
|
-
// Retained state gone (app stopped
|
|
983
|
-
// 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.
|
|
984
982
|
this.sendNow(saved.packageName, {view: "main", scene: saved.scene, durationMs: remaining})
|
|
985
983
|
}
|
|
986
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,
|
|
@@ -1495,9 +1496,6 @@ class LocalMiniappRuntime {
|
|
|
1495
1496
|
case MiniappRequestType.SHOW_VIEW:
|
|
1496
1497
|
this.handleShowView(packageName, payload, requestId)
|
|
1497
1498
|
break
|
|
1498
|
-
case MiniappRequestType.PREFETCH:
|
|
1499
|
-
this.handlePrefetch(packageName, payload, requestId)
|
|
1500
|
-
break
|
|
1501
1499
|
case MiniappRequestType.SETTINGS_GET:
|
|
1502
1500
|
this.handleSettingsGet(packageName, payload, requestId)
|
|
1503
1501
|
break
|
|
@@ -2654,27 +2652,6 @@ class LocalMiniappRuntime {
|
|
|
2654
2652
|
)
|
|
2655
2653
|
}
|
|
2656
2654
|
|
|
2657
|
-
/**
|
|
2658
|
-
* display.prefetch: the active dashboard hands over the frame its view would
|
|
2659
|
-
* show if opened now. Native keeps its fonts and images on the glasses while
|
|
2660
|
-
* nothing is on screen; nothing is displayed or retained here.
|
|
2661
|
-
*/
|
|
2662
|
-
private handlePrefetch(packageName: string, payload: Record<string, unknown>, requestId?: string): void {
|
|
2663
|
-
if (payload.view !== "dashboard" || !Array.isArray(payload.elements)) {
|
|
2664
|
-
this.sendResult(packageName, requestId, false, undefined, {
|
|
2665
|
-
code: MiniappErrorCode.INVALID_ARGUMENT,
|
|
2666
|
-
message: 'prefetch takes the "dashboard" view and an elements array',
|
|
2667
|
-
})
|
|
2668
|
-
return
|
|
2669
|
-
}
|
|
2670
|
-
if (!this.connectedApps.has(packageName) || !isDashboardApp(packageName) || !isOverlayApp(packageName)) {
|
|
2671
|
-
this.sendResult(packageName, requestId, true, {status: "unsupported", reason: "not the active dashboard"})
|
|
2672
|
-
return
|
|
2673
|
-
}
|
|
2674
|
-
const sent = localDisplayManager.prefetch(packageName, "dashboard", payload.elements as NonNullable<DisplayPayload["scene"]>)
|
|
2675
|
-
this.sendResult(packageName, requestId, true, sent ? {status: "queued"} : {status: "unsupported", reason: "no scene display"})
|
|
2676
|
-
}
|
|
2677
|
-
|
|
2678
2655
|
/**
|
|
2679
2656
|
* Reject the request with PERMISSION_NOT_DECLARED unless the caller's
|
|
2680
2657
|
* manifest declares `permission`. System apps pass without declaring it.
|
|
@@ -4367,6 +4344,7 @@ class LocalMiniappRuntime {
|
|
|
4367
4344
|
return
|
|
4368
4345
|
}
|
|
4369
4346
|
|
|
4347
|
+
const endActivity = offlineActivity.beginLlm(reason, localLlmModelManager.model(modelId).label)
|
|
4370
4348
|
try {
|
|
4371
4349
|
// webSearch is a vendor server-side grant; the phone has none.
|
|
4372
4350
|
const {webSearch: _webSearch, ...localRequest} = request
|
|
@@ -4391,6 +4369,8 @@ class LocalMiniappRuntime {
|
|
|
4391
4369
|
undefined,
|
|
4392
4370
|
primaryError ?? {code: MiniappErrorCode.AI_LOCAL_UNAVAILABLE, message},
|
|
4393
4371
|
)
|
|
4372
|
+
} finally {
|
|
4373
|
+
endActivity()
|
|
4394
4374
|
}
|
|
4395
4375
|
}
|
|
4396
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
|
|
@@ -283,34 +283,6 @@ class SceneRenderer {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
/**
|
|
287
|
-
* Hand native the frame `elements` would make for (app, view) without
|
|
288
|
-
* showing it or touching the retained scene: drivers with an on-glass asset
|
|
289
|
-
* cache upload its fonts and images while the panel is idle. Every element
|
|
290
|
-
* is "created" and the frame carries seq 0 (no presentation report). Empty
|
|
291
|
-
* elements drop it. False when there is nothing to send it to.
|
|
292
|
-
*/
|
|
293
|
-
public prefetchScene(appId: string, view: "main" | "dashboard", elements: SceneElementInput[]): boolean {
|
|
294
|
-
const caps = this.currentCapabilities()
|
|
295
|
-
if (!caps?.canPosition) return false
|
|
296
|
-
const processed = processScene(resolveAnchors(elements, caps.width, caps.height), caps, displayProcessor.getSceneProfile())
|
|
297
|
-
let synthetic = 0
|
|
298
|
-
const {elements: created} = diffScene([], processed.elements, () => `~prefetch${synthetic++}`)
|
|
299
|
-
const frame: SceneFrame = {
|
|
300
|
-
appId,
|
|
301
|
-
view,
|
|
302
|
-
sceneEpoch: 0,
|
|
303
|
-
seq: 0,
|
|
304
|
-
elements: created,
|
|
305
|
-
removed: [],
|
|
306
|
-
canvas: {width: caps.width, height: caps.height},
|
|
307
|
-
}
|
|
308
|
-
void Promise.resolve(BluetoothSdk.displayEvent({view, scene: frame, prefetch: true})).catch((err) => {
|
|
309
|
-
console.error(`${LOG_TAG}: native prefetch failed:`, err)
|
|
310
|
-
})
|
|
311
|
-
return true
|
|
312
|
-
}
|
|
313
|
-
|
|
314
286
|
/**
|
|
315
287
|
* Re-emit an app's retained scene (reconnect / arbitration restore). Replay
|
|
316
288
|
* is always create-based — the device rebuilds from scratch (spec §4).
|
|
@@ -55,6 +55,7 @@ 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],
|
|
58
59
|
[ControllerTypes.R1]: [],
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -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:
|
|
86
|
+
? {shapes: [...ALL_DISPLAY_SHAPES, "svg" as const], animation: true, rotation: true, depth: stereo}
|
|
85
87
|
: {}),
|
|
86
88
|
},
|
|
87
89
|
}
|
package/src/stores/settings.ts
CHANGED
|
@@ -930,6 +930,13 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
930
930
|
saveOnServer: false,
|
|
931
931
|
persist: true,
|
|
932
932
|
},
|
|
933
|
+
home_offline_card: {
|
|
934
|
+
key: "home_offline_card",
|
|
935
|
+
defaultValue: () => true,
|
|
936
|
+
writable: true,
|
|
937
|
+
saveOnServer: false,
|
|
938
|
+
persist: true,
|
|
939
|
+
},
|
|
933
940
|
// Gesture-dots debug indicator: flash a glyph on the glasses when a touch
|
|
934
941
|
// gesture is received while the display is empty. Works on any
|
|
935
942
|
// scene-capable device, not only CFW. Device-local debug knob, default off.
|
|
@@ -19,10 +19,11 @@ export const rayneoIO: Capabilities = {
|
|
|
19
19
|
hasCamera: false,
|
|
20
20
|
camera: null,
|
|
21
21
|
|
|
22
|
-
// Display capabilities -
|
|
22
|
+
// Display capabilities - a monochrome panel per eye; the Glassly CFW keeps
|
|
23
|
+
// a scene per lens, so element depth is a real disparity here
|
|
23
24
|
hasDisplay: true,
|
|
24
25
|
display: {
|
|
25
|
-
count:
|
|
26
|
+
count: 2,
|
|
26
27
|
isColor: false,
|
|
27
28
|
color: "green",
|
|
28
29
|
// Bitmaps and positioned elements need the Glassly CFW; stock firmware
|