@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.
- package/build/engine.d.ts +9 -2
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +5 -0
- package/build/engine.js.map +1 -1
- package/build/facades/dev.js +1 -1
- package/build/facades/dev.js.map +1 -1
- package/build/facades/miniapps.d.ts +5 -5
- package/build/facades/miniapps.d.ts.map +1 -1
- package/build/facades/miniapps.js +5 -6
- package/build/facades/miniapps.js.map +1 -1
- package/build/facades/speech.d.ts +10 -0
- package/build/facades/speech.d.ts.map +1 -1
- package/build/facades/speech.js +10 -0
- package/build/facades/speech.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 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/services/AppRegistry.d.ts +1 -7
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +15 -26
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/DevServerBridge.d.ts +1 -1
- package/build/services/DevServerBridge.js +2 -2
- package/build/services/DevServerBridge.js.map +1 -1
- package/build/services/DeviceEventRouter.d.ts.map +1 -1
- package/build/services/DeviceEventRouter.js +2 -0
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/JSRouter.js +1 -1
- package/build/services/JSRouter.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +21 -6
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +130 -48
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/LocalTranslationService.d.ts +51 -0
- package/build/services/LocalTranslationService.d.ts.map +1 -0
- package/build/services/LocalTranslationService.js +141 -0
- package/build/services/LocalTranslationService.js.map +1 -0
- package/build/services/MiniappLauncher.d.ts +17 -23
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +30 -34
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/MiniappPauseCoordinator.d.ts +15 -0
- package/build/services/MiniappPauseCoordinator.d.ts.map +1 -0
- package/build/services/MiniappPauseCoordinator.js +93 -0
- package/build/services/MiniappPauseCoordinator.js.map +1 -0
- package/build/services/OfflineTranslationManager.d.ts +19 -0
- package/build/services/OfflineTranslationManager.d.ts.map +1 -0
- package/build/services/OfflineTranslationManager.js +35 -0
- package/build/services/OfflineTranslationManager.js.map +1 -0
- package/build/services/pages/OverlayPages.d.ts +5 -5
- package/build/services/pages/OverlayPages.d.ts.map +1 -1
- package/build/services/pages/OverlayPages.js +5 -5
- package/build/services/pages/OverlayPages.js.map +1 -1
- package/build/services/pages/overlayApps.d.ts +14 -11
- package/build/services/pages/overlayApps.d.ts.map +1 -1
- package/build/services/pages/overlayApps.js +21 -19
- package/build/services/pages/overlayApps.js.map +1 -1
- package/build/services/settingsBridge.d.ts +8 -8
- package/build/services/settingsBridge.d.ts.map +1 -1
- package/build/services/settingsBridge.js +12 -13
- package/build/services/settingsBridge.js.map +1 -1
- package/build/stores/apps.d.ts +0 -5
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +32 -52
- package/build/stores/apps.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +0 -10
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +3 -1
- package/build/types/applet.d.ts.map +1 -1
- package/build/types/applet.js.map +1 -1
- package/build/types/capabilities/even-realities-g1.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g1.js +1 -0
- package/build/types/capabilities/even-realities-g1.js.map +1 -1
- package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g2.js +1 -0
- package/build/types/capabilities/even-realities-g2.js.map +1 -1
- package/build/types/capabilities/mentra-display.d.ts.map +1 -1
- package/build/types/capabilities/mentra-display.js +1 -0
- package/build/types/capabilities/mentra-display.js.map +1 -1
- package/build/types/capabilities/nimo.d.ts.map +1 -1
- package/build/types/capabilities/nimo.js +1 -0
- package/build/types/capabilities/nimo.js.map +1 -1
- package/build/types/hardware.d.ts +9 -0
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js +2 -0
- package/build/types/hardware.js.map +1 -1
- package/build/utils/miniappGlobals.js +1 -1
- package/build/utils/miniappGlobals.js.map +1 -1
- package/package.json +7 -7
- package/src/engine.ts +5 -0
- package/src/facades/dev.ts +1 -1
- package/src/facades/miniapps.ts +7 -7
- package/src/facades/speech.ts +12 -0
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/index.ts +0 -2
- package/src/services/AppRegistry.ts +15 -27
- package/src/services/DevServerBridge.ts +2 -2
- package/src/services/DeviceEventRouter.ts +2 -0
- package/src/services/JSRouter.ts +1 -1
- package/src/services/LocalMiniappRuntime.ts +165 -66
- package/src/services/LocalTranslationService.ts +184 -0
- package/src/services/MiniappLauncher.ts +31 -34
- package/src/services/MiniappPauseCoordinator.ts +100 -0
- package/src/services/OfflineTranslationManager.ts +40 -0
- package/src/services/pages/OverlayPages.ts +5 -5
- package/src/services/pages/overlayApps.ts +19 -19
- package/src/services/settingsBridge.ts +12 -13
- package/src/stores/apps.ts +30 -58
- package/src/stores/settings.ts +0 -10
- package/src/types/applet.ts +3 -1
- package/src/types/capabilities/even-realities-g1.ts +1 -0
- package/src/types/capabilities/even-realities-g2.ts +1 -0
- package/src/types/capabilities/mentra-display.ts +1 -0
- package/src/types/capabilities/nimo.ts +1 -0
- package/src/types/hardware.ts +9 -0
- package/src/utils/miniappGlobals.ts +1 -1
- package/build/services/pages/activeDashboard.d.ts +0 -7
- package/build/services/pages/activeDashboard.d.ts.map +0 -1
- package/build/services/pages/activeDashboard.js +0 -21
- package/build/services/pages/activeDashboard.js.map +0 -1
- package/src/services/pages/activeDashboard.ts +0 -24
|
@@ -36,6 +36,9 @@ import { createAnthropicProvider, createGeminiProvider, createOpenAiProvider, }
|
|
|
36
36
|
import { normalizeAiTier, resolveAiTier } from "./AiTierResolution";
|
|
37
37
|
import { HttpError } from "@glassly/cloud-client";
|
|
38
38
|
import localDisplayManager from "./LocalDisplayManager";
|
|
39
|
+
import sttModelManager from "./STTModelManager";
|
|
40
|
+
import localTranslationService from "./LocalTranslationService";
|
|
41
|
+
import { offlineTranslationSupported } from "./OfflineTranslationManager";
|
|
39
42
|
import { dropGestureClaims, emptyGestureClaims, gestureClaimant, hasGestureClaim, setGestureClaims, } from "./gestureClaims";
|
|
40
43
|
import overlayPages from "./pages/OverlayPages";
|
|
41
44
|
import dashboardOpenTrace from "./debug/DashboardOpenTrace";
|
|
@@ -43,8 +46,7 @@ import { isAppForegrounded, isPhoneActive, onAppForegroundChange, onPhoneActivit
|
|
|
43
46
|
import { installedNotificationApps } from "./PhoneNotificationsSync";
|
|
44
47
|
import { DERIVED_SETTINGS_KEYS, SETTINGS_BRIDGE_KEYS, isDerivedSettingsKey, ringBatteryFrom, selectSettingsBridgeKeys, validateSettingsSet, } from "./settingsBridge";
|
|
45
48
|
import { mayUsePermission } from "./miniappPermissions";
|
|
46
|
-
import {
|
|
47
|
-
import { activeDashboard, addDashboardApp, displayViewsFor, isDashboardApp, isOverlayApp, onOverlayAppsChange, removeDashboardApp, setActiveDashboard, setOverlayViewer, } from "./pages/overlayApps";
|
|
49
|
+
import { activeDashboard, addDashboardApp, displayViewsFor, isDashboardApp, isOverlayApp, onOverlayAppsChange, removeDashboardApp, setOverlayViewer, } from "./pages/overlayApps";
|
|
48
50
|
import { pagesEligible } from "./pages/pageEligibility";
|
|
49
51
|
import nativeNavigationService from "./NativeNavigationService";
|
|
50
52
|
import headingService from "./HeadingService";
|
|
@@ -52,6 +54,7 @@ import glassesSensorService from "./GlassesSensorService";
|
|
|
52
54
|
import localSttFallbackCoordinator from "./LocalSttFallbackCoordinator";
|
|
53
55
|
import speakerLabelResolver from "./SpeakerLabelResolver";
|
|
54
56
|
import micStateCoordinator from "./MicStateCoordinator";
|
|
57
|
+
import { isMiniappsPaused, onMiniappsPauseChange } from "./MiniappPauseCoordinator";
|
|
55
58
|
import sonioxDirectService from "./soniox/SonioxDirectService";
|
|
56
59
|
import { BlobStore } from "./BlobStore";
|
|
57
60
|
import { CloudAudioSubscriptionSync } from "./CloudAudioSubscriptionSync";
|
|
@@ -84,6 +87,8 @@ import { LocalMiniappStorage } from "./LocalMiniappStorage";
|
|
|
84
87
|
import { captureDecisionFor } from "./MemoryCapture";
|
|
85
88
|
import { memoryStore } from "./memoryStoreInstance";
|
|
86
89
|
import { speakerProfileStore } from "./speakerProfileStoreInstance";
|
|
90
|
+
/** Streams that carry speech text and so have a cloud route and an on-device route. */
|
|
91
|
+
const isSpeechTextStream = (stream) => stream.startsWith("transcription:") || stream.startsWith("translation:");
|
|
87
92
|
/**
|
|
88
93
|
* Strictness ordering for `location_stream` rate values. Higher index =
|
|
89
94
|
* higher accuracy and higher power cost. The aggregate tier applied to
|
|
@@ -428,6 +433,8 @@ class LocalMiniappRuntime {
|
|
|
428
433
|
* Drives {@link waitForConnect}.
|
|
429
434
|
*/
|
|
430
435
|
handshookApps = new Set();
|
|
436
|
+
/** Links that arrived before the target finished its handshake; sent right after CONNECT_ACK. */
|
|
437
|
+
pendingLinks = new Map();
|
|
431
438
|
/** Pending {@link waitForConnect} resolvers, keyed by packageName. */
|
|
432
439
|
connectWaiters = new Map();
|
|
433
440
|
/** Ref-counted stream subscriptions: stream → set of packageNames. */
|
|
@@ -439,6 +446,7 @@ class LocalMiniappRuntime {
|
|
|
439
446
|
cloudStatusWired = false;
|
|
440
447
|
capabilitiesWired = false;
|
|
441
448
|
preferencesWired = false;
|
|
449
|
+
pauseWired = false;
|
|
442
450
|
settingsBridgeWired = false;
|
|
443
451
|
/** Last derived read-only values pushed on the settings stream, for change detection. */
|
|
444
452
|
derivedSettings = new Map();
|
|
@@ -845,6 +853,7 @@ class LocalMiniappRuntime {
|
|
|
845
853
|
this.ensureCloudStatusWired();
|
|
846
854
|
this.ensureCapabilitiesWired();
|
|
847
855
|
this.ensurePreferencesWired();
|
|
856
|
+
this.ensurePauseWired();
|
|
848
857
|
this.ensureSettingsBridgeWired();
|
|
849
858
|
this.ensurePingLoop();
|
|
850
859
|
// Show the system boot message ("Starting <name>…") on the glasses for the
|
|
@@ -961,6 +970,7 @@ class LocalMiniappRuntime {
|
|
|
961
970
|
// (e.g. a wake that's mid-handshake when the app is torn down). Done before
|
|
962
971
|
// the early-return so it runs even if the connectedApps entry is already gone.
|
|
963
972
|
this.handshookApps.delete(packageName);
|
|
973
|
+
this.pendingLinks.delete(packageName);
|
|
964
974
|
this.flushConnectWaiters(packageName, new Error(`${packageName} unregistered before connect`));
|
|
965
975
|
// Drop this app's transcription hints; the cloud subscription rebuild
|
|
966
976
|
// below (via replaceStreamSubscribers) re-derives the hint union without it.
|
|
@@ -1092,7 +1102,7 @@ class LocalMiniappRuntime {
|
|
|
1092
1102
|
// Console-tap forwarding. The miniapp's console.log/warn/etc is wrapped
|
|
1093
1103
|
// (via injected shim from miniappGlobals.ts) to post a `dev_log`
|
|
1094
1104
|
// envelope. We fan out to two destinations:
|
|
1095
|
-
// 1. DevServerBridge — forwards to the laptop's `glassly
|
|
1105
|
+
// 1. DevServerBridge — forwards to the laptop's `glassly dev`
|
|
1096
1106
|
// terminal. No-op when there's no sidecar (installed miniapps).
|
|
1097
1107
|
// 2. React Native console — surfaces the log in Metro / Xcode console
|
|
1098
1108
|
// / adb logcat so installed-miniapp errors are still inspectable
|
|
@@ -1485,12 +1495,10 @@ class LocalMiniappRuntime {
|
|
|
1485
1495
|
// Update lastPongAt so it doesn't time out right away
|
|
1486
1496
|
existing.lastPongAt = Date.now();
|
|
1487
1497
|
existing.unansweredPingRounds = 0;
|
|
1488
|
-
// The dashboard
|
|
1489
|
-
// capabilities carry the dashboard view from the start.
|
|
1490
|
-
if (existing.installedManifest?.type === "dashboard")
|
|
1498
|
+
// The connected dashboard holds the role and may take the overlay slot:
|
|
1499
|
+
// its capabilities carry the dashboard view from the start.
|
|
1500
|
+
if (existing.installedManifest?.type === "dashboard")
|
|
1491
1501
|
addDashboardApp(packageName);
|
|
1492
|
-
this.syncActiveDashboard();
|
|
1493
|
-
}
|
|
1494
1502
|
const capabilities = this.capabilitiesFor(packageName);
|
|
1495
1503
|
// Build the declared-permission record for the SDK's session.permissions
|
|
1496
1504
|
// module. Lower-cased to match v3's PermissionType union (microphone,
|
|
@@ -1517,6 +1525,8 @@ class LocalMiniappRuntime {
|
|
|
1517
1525
|
capabilities,
|
|
1518
1526
|
preferences: this.currentPreferences(),
|
|
1519
1527
|
visibility: this.currentVisibility(),
|
|
1528
|
+
paused: isMiniappsPaused(),
|
|
1529
|
+
...(isMiniappsPaused() ? { pauseReason: "glasses_disconnected" } : {}),
|
|
1520
1530
|
permissions: declaredPermissions,
|
|
1521
1531
|
...(existing.launchSource ? { launchSource: existing.launchSource } : {}),
|
|
1522
1532
|
...(initialAuth ? { auth: initialAuth } : {}),
|
|
@@ -1535,6 +1545,11 @@ class LocalMiniappRuntime {
|
|
|
1535
1545
|
// Handshake complete — unblock any launcher.waitForConnect() callers.
|
|
1536
1546
|
this.handshookApps.add(packageName);
|
|
1537
1547
|
this.flushConnectWaiters(packageName);
|
|
1548
|
+
const pendingLink = this.pendingLinks.get(packageName);
|
|
1549
|
+
if (pendingLink) {
|
|
1550
|
+
this.pendingLinks.delete(packageName);
|
|
1551
|
+
this.sendToMiniapp(packageName, { type: MiniappResponseType.LINK, params: pendingLink });
|
|
1552
|
+
}
|
|
1538
1553
|
}
|
|
1539
1554
|
async requestMiniappAuth(packageName, opts) {
|
|
1540
1555
|
const authPackageName = getDevAppSourcePackage(packageName) ?? packageName;
|
|
@@ -1791,15 +1806,15 @@ class LocalMiniappRuntime {
|
|
|
1791
1806
|
}
|
|
1792
1807
|
return "location_update";
|
|
1793
1808
|
}
|
|
1794
|
-
if (s.forceLocal === true && s.stream
|
|
1809
|
+
if (s.forceLocal === true && isSpeechTextStream(s.stream)) {
|
|
1795
1810
|
forceLocalTranscriptionStreams.add(s.stream);
|
|
1796
1811
|
}
|
|
1797
|
-
if (s.stream
|
|
1812
|
+
if (isSpeechTextStream(s.stream) && (s.forceLocal !== true || s.includeCloud === true)) {
|
|
1798
1813
|
cloudTranscriptionStreams.add(s.stream);
|
|
1799
1814
|
}
|
|
1800
1815
|
return s.stream;
|
|
1801
1816
|
}
|
|
1802
|
-
if (s
|
|
1817
|
+
if (isSpeechTextStream(s))
|
|
1803
1818
|
cloudTranscriptionStreams.add(s);
|
|
1804
1819
|
return s;
|
|
1805
1820
|
});
|
|
@@ -2197,8 +2212,6 @@ class LocalMiniappRuntime {
|
|
|
2197
2212
|
return localDisplayManager.foregroundAppOnScreen();
|
|
2198
2213
|
case "ring_battery":
|
|
2199
2214
|
return ringBatteryFrom(useGlassesStore.getState());
|
|
2200
|
-
case "active_dashboard":
|
|
2201
|
-
return activeDashboard();
|
|
2202
2215
|
}
|
|
2203
2216
|
}
|
|
2204
2217
|
handleSettingsGet(packageName, payload, requestId) {
|
|
@@ -4232,7 +4245,7 @@ class LocalMiniappRuntime {
|
|
|
4232
4245
|
const previousSubscriptions = app.subscriptions;
|
|
4233
4246
|
app.subscriptions = new Set(streams);
|
|
4234
4247
|
app.forceLocalTranscriptionStreams.clear();
|
|
4235
|
-
app.cloudTranscriptionStreams = new Set(streams.filter(
|
|
4248
|
+
app.cloudTranscriptionStreams = new Set(streams.filter(isSpeechTextStream));
|
|
4236
4249
|
this.replaceStreamSubscribers(packageName, previousSubscriptions, app.subscriptions);
|
|
4237
4250
|
this.recomputeMicRequirements();
|
|
4238
4251
|
this.updateCloudSubscriptions();
|
|
@@ -4250,6 +4263,14 @@ class LocalMiniappRuntime {
|
|
|
4250
4263
|
loudnessGateEnabled: typeof configuredLoudnessGate === "boolean" ? configuredLoudnessGate : null,
|
|
4251
4264
|
};
|
|
4252
4265
|
}
|
|
4266
|
+
/**
|
|
4267
|
+
* Subscriptions that hold mic and transcription demand right now. A paused
|
|
4268
|
+
* app keeps its subscriptions registered (they come back on resume) but
|
|
4269
|
+
* demands nothing while the glasses are away.
|
|
4270
|
+
*/
|
|
4271
|
+
activeStreamSubscribers() {
|
|
4272
|
+
return isMiniappsPaused() ? [] : this.streamSubscribers;
|
|
4273
|
+
}
|
|
4253
4274
|
recomputeMicRequirements() {
|
|
4254
4275
|
// Soniox-direct (user API key) consumes decoded PCM on the phone, so
|
|
4255
4276
|
// transcription/translation subscriptions request PCM instead of LC3 —
|
|
@@ -4257,7 +4278,7 @@ class LocalMiniappRuntime {
|
|
|
4257
4278
|
const sonioxDirect = sonioxDirectService.isEnabled();
|
|
4258
4279
|
let anyPcm = false;
|
|
4259
4280
|
let anyLc3 = false;
|
|
4260
|
-
for (const [stream, subscribers] of this.
|
|
4281
|
+
for (const [stream, subscribers] of this.activeStreamSubscribers()) {
|
|
4261
4282
|
if (subscribers.size === 0)
|
|
4262
4283
|
continue;
|
|
4263
4284
|
if (stream === "audio_chunk")
|
|
@@ -4289,13 +4310,25 @@ class LocalMiniappRuntime {
|
|
|
4289
4310
|
let transcriptionLang = null;
|
|
4290
4311
|
let forceLocalTranscriptionLang = null;
|
|
4291
4312
|
let hasForceLocalTranscription = false;
|
|
4292
|
-
|
|
4313
|
+
const localTranslations = [];
|
|
4314
|
+
for (const [stream, subscribers] of this.activeStreamSubscribers()) {
|
|
4293
4315
|
if (subscribers.size === 0)
|
|
4294
4316
|
continue;
|
|
4295
4317
|
// Only transcription / translation need cloud delivery. Location and
|
|
4296
4318
|
// notifications are sourced natively on the phone — no cloud hop.
|
|
4297
4319
|
if (stream.startsWith("translation:")) {
|
|
4298
|
-
|
|
4320
|
+
const routes = summarizeTranscriptionRoutes(subscribers, (packageName) => this.appTranscriptionRoutesForSubscription(packageName, stream));
|
|
4321
|
+
if (routes.hasCloudSubscriber)
|
|
4322
|
+
cloudStreams.add(stream);
|
|
4323
|
+
const [, source, target, ...rest] = stream.split(":");
|
|
4324
|
+
if (target && target !== "*" && rest.length === 0) {
|
|
4325
|
+
localTranslations.push({
|
|
4326
|
+
source,
|
|
4327
|
+
target,
|
|
4328
|
+
cloud: routes.hasCloudSubscriber,
|
|
4329
|
+
forceLocal: routes.hasForceLocalSubscriber,
|
|
4330
|
+
});
|
|
4331
|
+
}
|
|
4299
4332
|
}
|
|
4300
4333
|
if (stream.startsWith("transcription:")) {
|
|
4301
4334
|
const language = stream.substring("transcription:".length);
|
|
@@ -4311,7 +4344,14 @@ class LocalMiniappRuntime {
|
|
|
4311
4344
|
cloudStreams.add(stream);
|
|
4312
4345
|
}
|
|
4313
4346
|
}
|
|
4314
|
-
|
|
4347
|
+
// On-device translation is fed by on-device transcripts, so a translation
|
|
4348
|
+
// subscription it can serve keeps local STT eligible exactly like a
|
|
4349
|
+
// transcription subscription does.
|
|
4350
|
+
const translatesLocally = offlineTranslationSupported() ? localTranslations : [];
|
|
4351
|
+
localTranslationService.setSubscriptions(translatesLocally);
|
|
4352
|
+
const hasForceLocalTranslation = translatesLocally.some((sub) => sub.forceLocal);
|
|
4353
|
+
const localSourceLang = translatesLocally.length > 0 ? sttModelManager.getBcp47ForLanguage() : null;
|
|
4354
|
+
localSttFallbackCoordinator.onSubscriptionChange(transcriptionLang !== null || localSourceLang !== null, forceLocalTranscriptionLang ?? transcriptionLang ?? localSourceLang, hasForceLocalTranscription || hasForceLocalTranslation);
|
|
4315
4355
|
// Mirror the same set as typed AudioSubscription[]. When Soniox-direct is
|
|
4316
4356
|
// enabled (user API key set), the DIRECT service serves the whole set and
|
|
4317
4357
|
// the cloud gets an EMPTY one — pushed as a real PUT, not skipped, so
|
|
@@ -4437,6 +4477,11 @@ class LocalMiniappRuntime {
|
|
|
4437
4477
|
markTranscriptSourceRecent("direct");
|
|
4438
4478
|
this.handleTranslationData(d);
|
|
4439
4479
|
});
|
|
4480
|
+
localTranslationService.attach({
|
|
4481
|
+
deliver: (stream, data) => this.forwardEvent(stream, data, "local"),
|
|
4482
|
+
primaryUp: () => this.transcriptionPrimaryUp(),
|
|
4483
|
+
onPairUnavailable: () => localDisplayManager.showSystemNotice("Offline translation language not downloaded.\nGet it in Settings > Offline models."),
|
|
4484
|
+
});
|
|
4440
4485
|
// When the user sets/clears their Soniox key mid-session, re-partition
|
|
4441
4486
|
// subscriptions and mic requirements without waiting for a subscription
|
|
4442
4487
|
// change.
|
|
@@ -4533,7 +4578,7 @@ class LocalMiniappRuntime {
|
|
|
4533
4578
|
didTranslate: true,
|
|
4534
4579
|
provider: d.provider,
|
|
4535
4580
|
confidence: d.confidence,
|
|
4536
|
-
});
|
|
4581
|
+
}, "cloud");
|
|
4537
4582
|
}
|
|
4538
4583
|
/** Re-partition audio routing after a Soniox-direct enable/disable flip. */
|
|
4539
4584
|
recomputeAudioRouting() {
|
|
@@ -4597,33 +4642,16 @@ class LocalMiniappRuntime {
|
|
|
4597
4642
|
useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.cfw_native_navigation.key), broadcast);
|
|
4598
4643
|
// The dashboard master switch flips the dashboard view.
|
|
4599
4644
|
useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.custom_dashboard_enabled.key), broadcast);
|
|
4600
|
-
// The
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
*/
|
|
4611
|
-
syncActiveDashboard() {
|
|
4612
|
-
const candidates = [...useAppStatusStore.getState().apps];
|
|
4613
|
-
// A dashboard can connect at launch before the apps store has listed it.
|
|
4614
|
-
for (const [packageName, app] of this.connectedApps) {
|
|
4615
|
-
if (candidates.some((candidate) => candidate.packageName === packageName))
|
|
4616
|
-
continue;
|
|
4617
|
-
candidates.push({ packageName, type: app.installedManifest?.type });
|
|
4618
|
-
}
|
|
4619
|
-
const next = activeDashboardAmong(candidates);
|
|
4620
|
-
const previous = activeDashboard();
|
|
4621
|
-
if (next === previous)
|
|
4622
|
-
return;
|
|
4623
|
-
if (previous !== null)
|
|
4624
|
-
overlayPages.hide(previous, "replaced");
|
|
4625
|
-
setActiveDashboard(next);
|
|
4626
|
-
this.broadcastCapabilities();
|
|
4645
|
+
// The role follows the connected dashboard: when it passes, every app
|
|
4646
|
+
// sees its views again.
|
|
4647
|
+
let role = activeDashboard();
|
|
4648
|
+
onOverlayAppsChange(() => {
|
|
4649
|
+
const next = activeDashboard();
|
|
4650
|
+
if (next === role)
|
|
4651
|
+
return;
|
|
4652
|
+
role = next;
|
|
4653
|
+
broadcast();
|
|
4654
|
+
});
|
|
4627
4655
|
}
|
|
4628
4656
|
/** Capabilities as `packageName` sees them: the model profile plus the views this app may draw. */
|
|
4629
4657
|
capabilitiesFor(packageName) {
|
|
@@ -4641,6 +4669,29 @@ class LocalMiniappRuntime {
|
|
|
4641
4669
|
});
|
|
4642
4670
|
}
|
|
4643
4671
|
}
|
|
4672
|
+
/**
|
|
4673
|
+
* Pause/resume (MiniappPauseCoordinator) lands as one push per handshook
|
|
4674
|
+
* app, and the mic and cloud transcription demand follow it.
|
|
4675
|
+
*/
|
|
4676
|
+
ensurePauseWired() {
|
|
4677
|
+
if (this.pauseWired)
|
|
4678
|
+
return;
|
|
4679
|
+
this.pauseWired = true;
|
|
4680
|
+
onMiniappsPauseChange((paused) => {
|
|
4681
|
+
this.broadcastPauseState(paused);
|
|
4682
|
+
this.recomputeMicRequirements();
|
|
4683
|
+
this.updateCloudSubscriptions();
|
|
4684
|
+
});
|
|
4685
|
+
}
|
|
4686
|
+
broadcastPauseState(paused) {
|
|
4687
|
+
for (const packageName of this.handshookApps) {
|
|
4688
|
+
this.sendToMiniapp(packageName, {
|
|
4689
|
+
type: MiniappResponseType.PAUSE_CHANGE,
|
|
4690
|
+
paused,
|
|
4691
|
+
...(paused ? { reason: "glasses_disconnected" } : {}),
|
|
4692
|
+
});
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4644
4695
|
/** Host user preferences miniapps follow by default (Settings > Appearance). */
|
|
4645
4696
|
currentPreferences() {
|
|
4646
4697
|
const settings = useSettingsStore.getState();
|
|
@@ -4698,6 +4749,11 @@ class LocalMiniappRuntime {
|
|
|
4698
4749
|
forwardEvent(streamType, data, transcriptionSource,
|
|
4699
4750
|
/** Deliver to this subscriber alone (the designated dashboard app's gestures while it shows). */
|
|
4700
4751
|
only) {
|
|
4752
|
+
// Paused miniapps (glasses disconnected) hear no streams. The resume push
|
|
4753
|
+
// lands before the reconnect's own glasses_connection event, so nothing
|
|
4754
|
+
// a resumed app needs is lost here.
|
|
4755
|
+
if (isMiniappsPaused())
|
|
4756
|
+
return;
|
|
4701
4757
|
// Local sherpa results enter the runtime only through here (DeviceEventRouter's
|
|
4702
4758
|
// local_transcription listener); cloud/direct are marked at their onTranscript
|
|
4703
4759
|
// wiring, which never passes "local".
|
|
@@ -4725,6 +4781,7 @@ class LocalMiniappRuntime {
|
|
|
4725
4781
|
// translation:<source>:<target> — exact (TranslationModule.fromTo)
|
|
4726
4782
|
// translation:auto — back-compat alias for *:*
|
|
4727
4783
|
const matchedSubs = new Set();
|
|
4784
|
+
let translationPatterns = [];
|
|
4728
4785
|
const exact = this.streamSubscribers.get(normalizedStream);
|
|
4729
4786
|
if (exact)
|
|
4730
4787
|
for (const p of exact)
|
|
@@ -4741,13 +4798,13 @@ class LocalMiniappRuntime {
|
|
|
4741
4798
|
// parts[0] = "translation", parts[1] = source, parts[2] = target
|
|
4742
4799
|
if (parts.length === 3) {
|
|
4743
4800
|
const [, source, target] = parts;
|
|
4744
|
-
|
|
4801
|
+
translationPatterns = [
|
|
4745
4802
|
"translation:auto", // legacy alias for *:*
|
|
4746
4803
|
"translation:*:*", // cloud v3 "all pairs"
|
|
4747
4804
|
`translation:*:${target}`, // cloud v3 "to <target>"
|
|
4748
4805
|
`translation:${source}:*`, // cloud v3 "from <source>"
|
|
4749
4806
|
];
|
|
4750
|
-
for (const pat of
|
|
4807
|
+
for (const pat of translationPatterns) {
|
|
4751
4808
|
const subs = this.streamSubscribers.get(pat);
|
|
4752
4809
|
if (subs)
|
|
4753
4810
|
for (const p of subs)
|
|
@@ -4820,6 +4877,13 @@ class LocalMiniappRuntime {
|
|
|
4820
4877
|
if (!transcriptionRoute)
|
|
4821
4878
|
continue;
|
|
4822
4879
|
}
|
|
4880
|
+
else if (normalizedStream.startsWith("translation:") && transcriptionSource) {
|
|
4881
|
+
const routes = this.appTranslationRoutesForEvent(packageName, [normalizedStream, ...translationPatterns]);
|
|
4882
|
+
transcriptionRoute =
|
|
4883
|
+
transcriptionDeliveryRoute(transcriptionSource, routes, this.transcriptionPrimaryUp()) ?? undefined;
|
|
4884
|
+
if (!transcriptionRoute)
|
|
4885
|
+
continue;
|
|
4886
|
+
}
|
|
4823
4887
|
if (TRANSCRIPT_TIMING_TELEMETRY && normalizedStream.startsWith("transcription:")) {
|
|
4824
4888
|
const transcript = data;
|
|
4825
4889
|
const now = Date.now();
|
|
@@ -4905,6 +4969,13 @@ class LocalMiniappRuntime {
|
|
|
4905
4969
|
forceLocal: app?.forceLocalTranscriptionStreams.has(stream) === true,
|
|
4906
4970
|
};
|
|
4907
4971
|
}
|
|
4972
|
+
appTranslationRoutesForEvent(packageName, patterns) {
|
|
4973
|
+
const app = this.connectedApps.get(packageName);
|
|
4974
|
+
return {
|
|
4975
|
+
cloud: patterns.some((pattern) => app?.cloudTranscriptionStreams.has(pattern) === true),
|
|
4976
|
+
forceLocal: patterns.some((pattern) => app?.forceLocalTranscriptionStreams.has(pattern) === true),
|
|
4977
|
+
};
|
|
4978
|
+
}
|
|
4908
4979
|
appTranscriptionRoutesForEvent(packageName, stream) {
|
|
4909
4980
|
const app = this.connectedApps.get(packageName);
|
|
4910
4981
|
const autoStream = `${MiniappStreamType.TRANSCRIPTION}:auto`;
|
|
@@ -5415,6 +5486,17 @@ class LocalMiniappRuntime {
|
|
|
5415
5486
|
...(error ? { error } : {}),
|
|
5416
5487
|
}, requestId);
|
|
5417
5488
|
}
|
|
5489
|
+
/**
|
|
5490
|
+
* Hand an external link's query parameters to a miniapp. A miniapp that is
|
|
5491
|
+
* still spawning gets them once its handshake completes.
|
|
5492
|
+
*/
|
|
5493
|
+
deliverLink(packageName, params) {
|
|
5494
|
+
if (!this.handshookApps.has(packageName)) {
|
|
5495
|
+
this.pendingLinks.set(packageName, params);
|
|
5496
|
+
return;
|
|
5497
|
+
}
|
|
5498
|
+
this.sendToMiniapp(packageName, { type: MiniappResponseType.LINK, params });
|
|
5499
|
+
}
|
|
5418
5500
|
/**
|
|
5419
5501
|
* Send a VISIBILITY_CHANGE push to a miniapp.
|
|
5420
5502
|
*/
|