@glassly/engine 0.3.7-dev.870 → 0.3.7-dev.874
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 -2
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +4 -4
- package/build/engine.js.map +1 -1
- package/build/facades/glasses.d.ts +4 -4
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +4 -4
- package/build/facades/glasses.js.map +1 -1
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/controllers.d.ts.map +1 -1
- package/build/services/controllers.js +4 -3
- package/build/services/controllers.js.map +1 -1
- package/build/services/{appleWatch/AppleWatchLink.d.ts → watch/WatchLink.d.ts} +8 -7
- package/build/services/watch/WatchLink.d.ts.map +1 -0
- package/build/services/{appleWatch/AppleWatchLink.js → watch/WatchLink.js} +14 -15
- package/build/services/watch/WatchLink.js.map +1 -0
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +28 -3
- package/build/stores/settings.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/engine.ts +4 -4
- package/src/facades/glasses.ts +5 -5
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/services/controllers.ts +4 -3
- package/src/services/{appleWatch/AppleWatchLink.ts → watch/WatchLink.ts} +20 -20
- package/src/stores/settings.ts +35 -7
- package/src/types/enums.ts +1 -0
- package/build/services/appleWatch/AppleWatchLink.d.ts.map +0 -1
- package/build/services/appleWatch/AppleWatchLink.js.map +0 -1
package/src/engine.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {startPhoneActivityTracking, stopPhoneActivityTracking} from "./services/
|
|
|
25
25
|
import {startPhoneNotificationsSync, stopPhoneNotificationsSync} from "./services/PhoneNotificationsSync"
|
|
26
26
|
import {startSpeakerIdSync, stopSpeakerIdSync} from "./services/SpeakerIdSync"
|
|
27
27
|
import {startNeuralBandSettingsSync, stopNeuralBandSettingsSync} from "./services/neuralBand/NeuralBandSettingsSync"
|
|
28
|
-
import {
|
|
28
|
+
import {startWatchLink, stopWatchLink} from "./services/watch/WatchLink"
|
|
29
29
|
import {startCaptionsTesterReportService, stopCaptionsTesterReportService} from "./services/CaptionsTesterReportService"
|
|
30
30
|
import {startJSCrashloopReportService, stopJSCrashloopReportService} from "./services/JSCrashloopReportService"
|
|
31
31
|
import {ensureMiniappEngine, stopMiniappEngine} from "./services/MiniappEngine"
|
|
@@ -146,8 +146,8 @@ export const engine = {
|
|
|
146
146
|
startSpeakerIdSync()
|
|
147
147
|
// Meta Neural Band: push the air mouse and wearing-hand settings to the band on connect.
|
|
148
148
|
startNeuralBandSettingsSync()
|
|
149
|
-
// Apple Watch
|
|
150
|
-
|
|
149
|
+
// Watch remote (Apple Watch on iOS, Wear OS on Android): status and mirror frames out, wrist commands in.
|
|
150
|
+
startWatchLink()
|
|
151
151
|
// Android internal/e2e: laptop captions tester can broadcast a failure intent;
|
|
152
152
|
// engine owns turning that into a Cloud report.
|
|
153
153
|
startCaptionsTesterReportService()
|
|
@@ -192,7 +192,7 @@ export const engine = {
|
|
|
192
192
|
await safely("phone activity tracking", stopPhoneActivityTracking)
|
|
193
193
|
await safely("speaker id sync", stopSpeakerIdSync)
|
|
194
194
|
await safely("neural band settings sync", stopNeuralBandSettingsSync)
|
|
195
|
-
await safely("
|
|
195
|
+
await safely("watch link", stopWatchLink)
|
|
196
196
|
await safely("captions tester report service", stopCaptionsTesterReportService)
|
|
197
197
|
await safely("js crashloop report service", stopJSCrashloopReportService)
|
|
198
198
|
await safely("miniapp engine", stopMiniappEngine)
|
package/src/facades/glasses.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// standalone build); jest moduleNameMapper + tsconfig both resolve it.
|
|
13
13
|
import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
|
|
14
14
|
import type {
|
|
15
|
-
|
|
15
|
+
WatchAvailability,
|
|
16
16
|
ButtonPressEvent,
|
|
17
17
|
ConnectOptions,
|
|
18
18
|
ControllerStatus,
|
|
@@ -346,10 +346,10 @@ export const glasses = {
|
|
|
346
346
|
setHand: (id: string, hand: "left" | "right"): Promise<void> => BluetoothSdk.neuralBandSetHand(id, hand),
|
|
347
347
|
},
|
|
348
348
|
|
|
349
|
-
// --- Apple Watch (iOS)
|
|
350
|
-
|
|
351
|
-
/** Whether this
|
|
352
|
-
availability: (): Promise<
|
|
349
|
+
// --- watch: an Apple Watch over WatchConnectivity (iOS), a Wear OS watch over the Data Layer (Android) ---
|
|
350
|
+
watch: {
|
|
351
|
+
/** Whether this phone has a paired watch and the Glassly watch app is on it. */
|
|
352
|
+
availability: (): Promise<WatchAvailability> => BluetoothSdk.watchAvailability(),
|
|
353
353
|
},
|
|
354
354
|
|
|
355
355
|
// --- audio (glasses media-volume, own-app playback hint, live mic level) ---
|
|
@@ -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.7",
|
|
15
|
-
"releaseIdentity": "0.3.7-dev.
|
|
16
|
-
"releaseSetId": "glassly-0.3.7-dev.
|
|
17
|
-
"sourceCommit": "
|
|
15
|
+
"releaseIdentity": "0.3.7-dev.874",
|
|
16
|
+
"releaseSetId": "glassly-0.3.7-dev.874",
|
|
17
|
+
"sourceCommit": "1d02e138714d57d40eeafb5eca71dbb30684e69a",
|
|
18
18
|
"otaManifestUrl": null,
|
|
19
19
|
"otaManifestSha256": null
|
|
20
20
|
})
|
|
@@ -26,8 +26,8 @@ const NEURAL_BAND_SURFACE: InputSurface = {
|
|
|
26
26
|
],
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
const
|
|
29
|
+
/** A watch trackpad's taps and four-way swipes, and the crown (Apple) or rotary bezel (Wear OS) as the dial. */
|
|
30
|
+
const WATCH_SURFACE: InputSurface = {
|
|
31
31
|
type: "swipe2d",
|
|
32
32
|
events: [
|
|
33
33
|
"TAP",
|
|
@@ -45,7 +45,8 @@ const APPLE_WATCH_SURFACE: InputSurface = {
|
|
|
45
45
|
// The R1 swipes up/down like the temples it stands in for, so it adds nothing.
|
|
46
46
|
const INPUT_SURFACES: Readonly<Record<string, readonly InputSurface[]>> = {
|
|
47
47
|
[ControllerTypes.NEURAL_BAND]: [NEURAL_BAND_SURFACE],
|
|
48
|
-
[ControllerTypes.APPLE_WATCH]: [
|
|
48
|
+
[ControllerTypes.APPLE_WATCH]: [WATCH_SURFACE],
|
|
49
|
+
[ControllerTypes.WEAR_OS]: [WATCH_SURFACE],
|
|
49
50
|
[ControllerTypes.R1]: [],
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The watch remote's phone half, for the Apple Watch (iOS, WatchConnectivity) and a Wear OS
|
|
3
|
+
* watch (Android, Wearable Data Layer) alike: the watch app's status (glasses, the miniapp in
|
|
3
4
|
* front, launchable miniapps, whether Assist takes questions, the mirror setting), mirror frames
|
|
4
5
|
* while the setting is on, and the commands the watch sends back. Native adds whether the active
|
|
5
6
|
* watch is paired and keeps the newest frame for when the watch app opens. Gestures ride
|
|
6
7
|
* `touch_event` like any controller's.
|
|
7
8
|
*/
|
|
8
|
-
import type {
|
|
9
|
+
import type {WatchCommandEvent} from "@glassly/bluetooth-sdk"
|
|
9
10
|
import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
|
|
10
11
|
import type {MirrorSnapshot} from "@glassly/miniapp"
|
|
11
12
|
import {MiniappStreamType} from "@glassly/miniapp"
|
|
12
|
-
import {Platform} from "react-native"
|
|
13
13
|
|
|
14
14
|
import {useAppStatusStore} from "../../stores/apps"
|
|
15
15
|
import {useGlassesStore} from "../../stores/glasses"
|
|
@@ -23,7 +23,7 @@ import {keyEventsForTextChange} from "../neuralBand/NeuralBandInput"
|
|
|
23
23
|
|
|
24
24
|
export const ASSIST_PACKAGE = "com.glassly.assistant"
|
|
25
25
|
export const ASSIST_ACTION = "ask"
|
|
26
|
-
/** WatchConnectivity messages top out near 64 KB; frames past this are skipped, not truncated. */
|
|
26
|
+
/** WatchConnectivity messages top out near 64 KB (Data Layer messages at 100 KB); frames past this are skipped, not truncated. */
|
|
27
27
|
const MAX_FRAME_BYTES = 48_000
|
|
28
28
|
const MAX_IMAGE_CHARS = 16_000
|
|
29
29
|
const MAX_APPS = 24
|
|
@@ -142,7 +142,7 @@ export function mirrorFrame(snapshot: MirrorSnapshot): Record<string, unknown> |
|
|
|
142
142
|
return JSON.stringify(frame).length > MAX_FRAME_BYTES ? null : frame
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
export interface
|
|
145
|
+
export interface WatchCommandDeps {
|
|
146
146
|
apps: () => readonly ClientApp[]
|
|
147
147
|
start: (app: ClientApp) => Promise<unknown>
|
|
148
148
|
stop: (packageName: string) => Promise<unknown>
|
|
@@ -151,7 +151,7 @@ export interface AppleWatchCommandDeps {
|
|
|
151
151
|
now: () => number
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
const defaultDeps:
|
|
154
|
+
const defaultDeps: WatchCommandDeps = {
|
|
155
155
|
apps: () => useAppStatusStore.getState().apps,
|
|
156
156
|
start: (app) => useAppStatusStore.getState().start(app),
|
|
157
157
|
stop: (packageName) => useAppStatusStore.getState().stop(packageName),
|
|
@@ -160,9 +160,9 @@ const defaultDeps: AppleWatchCommandDeps = {
|
|
|
160
160
|
now: () => Date.now(),
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
export async function
|
|
164
|
-
event:
|
|
165
|
-
deps:
|
|
163
|
+
export async function handleWatchCommand(
|
|
164
|
+
event: WatchCommandEvent,
|
|
165
|
+
deps: WatchCommandDeps = defaultDeps,
|
|
166
166
|
): Promise<void> {
|
|
167
167
|
switch (event.command) {
|
|
168
168
|
case "launch": {
|
|
@@ -170,7 +170,7 @@ export async function handleAppleWatchCommand(
|
|
|
170
170
|
? deps.apps().find((entry) => entry.packageName === event.packageName)
|
|
171
171
|
: undefined
|
|
172
172
|
if (!app) {
|
|
173
|
-
console.warn(`
|
|
173
|
+
console.warn(`Watch: launch of unknown miniapp ${event.packageName}`)
|
|
174
174
|
return
|
|
175
175
|
}
|
|
176
176
|
await deps.start(app)
|
|
@@ -206,7 +206,7 @@ function currentState(): Record<string, unknown> {
|
|
|
206
206
|
},
|
|
207
207
|
apps: useAppStatusStore.getState().apps,
|
|
208
208
|
assist: localMiniappRuntime.hostActionAvailable(ASSIST_PACKAGE, ASSIST_ACTION),
|
|
209
|
-
mirror: useSettingsStore.getState().getSetting(SETTINGS.
|
|
209
|
+
mirror: useSettingsStore.getState().getSetting(SETTINGS.watch_mirror.key) === true,
|
|
210
210
|
})
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -226,7 +226,7 @@ function syncMirror(enabled: boolean): void {
|
|
|
226
226
|
const send = (snapshot: MirrorSnapshot) => {
|
|
227
227
|
lastSentAt = Date.now()
|
|
228
228
|
void BluetoothSdk.setWatchMirror(mirrorFrame(snapshot)).catch((err) =>
|
|
229
|
-
console.warn("
|
|
229
|
+
console.warn("Watch: mirror push failed:", err),
|
|
230
230
|
)
|
|
231
231
|
}
|
|
232
232
|
const unsubscribe = mirrorService.subscribe((snapshot) => {
|
|
@@ -252,8 +252,8 @@ function syncMirror(enabled: boolean): void {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
export function
|
|
256
|
-
if (
|
|
255
|
+
export function startWatchLink(): void {
|
|
256
|
+
if (stops.length > 0) return
|
|
257
257
|
let lastKey = ""
|
|
258
258
|
let timer: ReturnType<typeof BgTimer.setTimeout> | null = null
|
|
259
259
|
const push = () => {
|
|
@@ -262,21 +262,21 @@ export function startAppleWatchLink(): void {
|
|
|
262
262
|
const key = JSON.stringify(state)
|
|
263
263
|
if (key === lastKey) return
|
|
264
264
|
lastKey = key
|
|
265
|
-
void BluetoothSdk.setWatchState(state).catch((err) => console.warn("
|
|
265
|
+
void BluetoothSdk.setWatchState(state).catch((err) => console.warn("Watch: state push failed:", err))
|
|
266
266
|
}
|
|
267
267
|
const schedule = () => {
|
|
268
268
|
timer ??= BgTimer.setTimeout(push, STATE_DEBOUNCE_MS)
|
|
269
269
|
}
|
|
270
|
-
const mirrorEnabled = () => useSettingsStore.getState().getSetting(SETTINGS.
|
|
271
|
-
const commands = BluetoothSdk.addListener("
|
|
272
|
-
|
|
270
|
+
const mirrorEnabled = () => useSettingsStore.getState().getSetting(SETTINGS.watch_mirror.key) === true
|
|
271
|
+
const commands = BluetoothSdk.addListener("watch_command", (event) => {
|
|
272
|
+
handleWatchCommand(event).catch((err) => console.warn(`Watch: ${event.command} failed:`, err))
|
|
273
273
|
})
|
|
274
274
|
stops = [
|
|
275
275
|
() => commands.remove(),
|
|
276
276
|
useGlassesStore.subscribe(schedule),
|
|
277
277
|
useAppStatusStore.subscribe(schedule),
|
|
278
278
|
useSettingsStore.subscribe(
|
|
279
|
-
(s) => s.getSetting(SETTINGS.
|
|
279
|
+
(s) => s.getSetting(SETTINGS.watch_mirror.key),
|
|
280
280
|
() => {
|
|
281
281
|
syncMirror(mirrorEnabled())
|
|
282
282
|
schedule()
|
|
@@ -291,7 +291,7 @@ export function startAppleWatchLink(): void {
|
|
|
291
291
|
schedule()
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
export function
|
|
294
|
+
export function stopWatchLink(): void {
|
|
295
295
|
stops.splice(0).forEach((stop) => stop())
|
|
296
296
|
syncMirror(false)
|
|
297
297
|
}
|
package/src/stores/settings.ts
CHANGED
|
@@ -841,12 +841,21 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
841
841
|
saveOnServer: false,
|
|
842
842
|
persist: true,
|
|
843
843
|
},
|
|
844
|
-
// Apple Watch: draw the glasses' current frame behind the watch
|
|
845
|
-
// cross
|
|
844
|
+
// Watch (Apple Watch, Wear OS): draw the glasses' current frame behind the watch
|
|
845
|
+
// trackpad. Frames cross the watch link while the watch app is up, so it stays off
|
|
846
|
+
// unless asked for.
|
|
847
|
+
watch_mirror: {
|
|
848
|
+
key: "watch_mirror",
|
|
849
|
+
defaultValue: () => false,
|
|
850
|
+
writable: true,
|
|
851
|
+
saveOnServer: false,
|
|
852
|
+
persist: true,
|
|
853
|
+
},
|
|
854
|
+
// Deprecated: the setting above under its Apple-only name; read once to migrate.
|
|
846
855
|
apple_watch_mirror: {
|
|
847
856
|
key: "apple_watch_mirror",
|
|
848
857
|
defaultValue: () => false,
|
|
849
|
-
writable:
|
|
858
|
+
writable: false,
|
|
850
859
|
saveOnServer: false,
|
|
851
860
|
persist: true,
|
|
852
861
|
},
|
|
@@ -1276,10 +1285,13 @@ export interface SettingsState {
|
|
|
1276
1285
|
}
|
|
1277
1286
|
|
|
1278
1287
|
const getDefaultSettings = () =>
|
|
1279
|
-
Object.keys(SETTINGS).reduce(
|
|
1280
|
-
acc
|
|
1281
|
-
|
|
1282
|
-
|
|
1288
|
+
Object.keys(SETTINGS).reduce(
|
|
1289
|
+
(acc, key) => {
|
|
1290
|
+
acc[key] = SETTINGS[key].defaultValue()
|
|
1291
|
+
return acc
|
|
1292
|
+
},
|
|
1293
|
+
{} as Record<string, any>,
|
|
1294
|
+
)
|
|
1283
1295
|
|
|
1284
1296
|
// Single-flight for loadAllSettings: the host fires it at module load and
|
|
1285
1297
|
// engine.start()'s device-store hydration awaits it — without the memo the
|
|
@@ -1619,6 +1631,22 @@ export const useSettingsStore = create<SettingsState>()(
|
|
|
1619
1631
|
}
|
|
1620
1632
|
if (menuMigrated) storage.save(MENU_SETTINGS_MIGRATION_KEY, true)
|
|
1621
1633
|
}
|
|
1634
|
+
|
|
1635
|
+
// The watch mirror switch lost its Apple-only name when Wear OS joined: carry a
|
|
1636
|
+
// turned-on `apple_watch_mirror` over to `watch_mirror` once.
|
|
1637
|
+
const WATCH_MIRROR_MIGRATION_KEY = "migration:watch_mirror_v1"
|
|
1638
|
+
const watchMirrorMigrationDone = storage.load<boolean>(WATCH_MIRROR_MIGRATION_KEY)
|
|
1639
|
+
if (watchMirrorMigrationDone.is_error() || !watchMirrorMigrationDone.value) {
|
|
1640
|
+
let watchMirrorMigrated = true
|
|
1641
|
+
if (get().getSetting(SETTINGS.apple_watch_mirror.key) === true) {
|
|
1642
|
+
const result = await get().setSetting(SETTINGS.watch_mirror.key, true, false)
|
|
1643
|
+
if (result.is_error()) {
|
|
1644
|
+
console.log("SETTINGS: watch_mirror migration failed:", result.error)
|
|
1645
|
+
watchMirrorMigrated = false
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
if (watchMirrorMigrated) storage.save(WATCH_MIRROR_MIGRATION_KEY, true)
|
|
1649
|
+
}
|
|
1622
1650
|
})
|
|
1623
1651
|
loadAllSettingsInFlight = inFlight
|
|
1624
1652
|
void Promise.resolve(inFlight).then((result) => {
|
package/src/types/enums.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AppleWatchLink.d.ts","sourceRoot":"","sources":["../../../src/services/appleWatch/AppleWatchLink.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,wBAAwB,CAAA;AAElE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAOpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAA;AAO1C,eAAO,MAAM,cAAc,0BAA0B,CAAA;AACrD,eAAO,MAAM,aAAa,QAAQ,CAAA;AAQlC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAA;IAC7D,IAAI,EAAE,SAAS,SAAS,EAAE,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,GAAG;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,EAAE,CAMhG;AAED,wBAAgB,UAAU,CAAC,EAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAC,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUpG;AAOD,+FAA+F;AAC/F,wBAAgB,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CA4EpF;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,SAAS,SAAS,EAAE,CAAA;IAChC,KAAK,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAChD,GAAG,EAAE,MAAM,MAAM,CAAA;CAClB;AAWD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,sBAAsB,EAC7B,IAAI,GAAE,qBAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AA6DD,wBAAgB,mBAAmB,IAAI,IAAI,CAqC1C;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AppleWatchLink.js","sourceRoot":"","sources":["../../../src/services/appleWatch/AppleWatchLink.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Apple Watch remote's phone half: the watch app's status context (glasses, the miniapp in\n * front, launchable miniapps, whether Assist takes questions, the mirror setting), mirror frames\n * while the setting is on, and the commands the watch sends back. Native adds whether the active\n * watch is paired and keeps the newest frame for when the watch app opens. Gestures ride\n * `touch_event` like any controller's.\n */\nimport type {AppleWatchCommandEvent} from \"@glassly/bluetooth-sdk\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {MirrorSnapshot} from \"@glassly/miniapp\"\nimport {MiniappStreamType} from \"@glassly/miniapp\"\nimport {Platform} from \"react-native\"\n\nimport {useAppStatusStore} from \"../../stores/apps\"\nimport {useGlassesStore} from \"../../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../../stores/settings\"\nimport type {ClientApp} from \"../../types\"\nimport {BgTimer} from \"../../utils/timers\"\nimport {isGlassesConnected} from \"../GlassesReadiness\"\nimport localMiniappRuntime from \"../LocalMiniappRuntime\"\nimport {mirrorService} from \"../MirrorService\"\nimport {keyEventsForTextChange} from \"../neuralBand/NeuralBandInput\"\n\nexport const ASSIST_PACKAGE = \"com.glassly.assistant\"\nexport const ASSIST_ACTION = \"ask\"\n/** WatchConnectivity messages top out near 64 KB; frames past this are skipped, not truncated. */\nconst MAX_FRAME_BYTES = 48_000\nconst MAX_IMAGE_CHARS = 16_000\nconst MAX_APPS = 24\nconst STATE_DEBOUNCE_MS = 250\nconst MIRROR_INTERVAL_MS = 500\n\nexport interface WatchStateInput {\n glasses: {connected: boolean; model: string; battery: number}\n apps: readonly ClientApp[]\n assist: boolean\n mirror: boolean\n}\n\n/** Miniapps the watch can open: shown, compatible, and drawing on the glasses. */\nexport function launchableApps(apps: readonly ClientApp[]): {packageName: string; name: string}[] {\n return apps\n .filter((app) => app.type === \"standard\" && !app.hidden && app.compatibility?.isCompatible !== false)\n .filter((app) => app.packageName && !app.packageName.includes(\"@empty\"))\n .slice(0, MAX_APPS)\n .map((app) => ({packageName: app.packageName, name: app.name}))\n}\n\nexport function watchState({glasses, apps, assist, mirror}: WatchStateInput): Record<string, unknown> {\n const front = apps.find((app) => app.foregrounded && app.running && app.type === \"standard\")\n const state: Record<string, unknown> = {\n glasses: {connected: glasses.connected, name: glasses.model || \"Glasses\", battery: glasses.battery},\n apps: launchableApps(apps),\n assist,\n mirror,\n }\n if (front) state.app = {packageName: front.packageName, name: front.name}\n return state\n}\n\ntype WatchElement = Record<string, string | number | boolean | number[]>\n\nconst level = (color: unknown, fallback = 15) =>\n typeof color === \"number\" ? Math.max(0, Math.min(15, color)) : fallback\n\n/** The glasses frame in the watch's drawing vocabulary; null when there is nothing to draw. */\nexport function mirrorFrame(snapshot: MirrorSnapshot): Record<string, unknown> | null {\n const canvas = snapshot.canvas\n if (!canvas) return null\n const content = snapshot.content\n if (content.kind === \"bitmap\") {\n if (content.data.length > MAX_FRAME_BYTES) return null\n return {width: canvas.width, height: canvas.height, bitmap: content.data, elements: []}\n }\n if (content.kind !== \"scene\") return null\n const elements: WatchElement[] = []\n for (const element of content.elements) {\n const {x, y, w, h} = element.box\n const style = element.style ?? {}\n switch (element.type) {\n case \"text\":\n if (!element.text) break\n elements.push({\n type: \"text\",\n x,\n y,\n w,\n h,\n text: element.text,\n size: style.fontSize ?? 20,\n color: level(style.color),\n })\n break\n case \"rect\":\n elements.push({\n type: \"rect\",\n x,\n y,\n w,\n h,\n fill: style.fill === true,\n border: style.border ?? 1,\n radius: style.radius ?? 0,\n color: level(style.color),\n })\n break\n case \"circle\":\n elements.push({\n type: \"circle\",\n x,\n y,\n w,\n h,\n fill: style.fill === true,\n border: style.border ?? 1,\n color: level(style.color),\n })\n break\n case \"line\":\n case \"triangle\":\n case \"quad\": {\n const points = (element.points ?? []).flatMap((point) => [point.x, point.y])\n if (points.length < 4) break\n const closed = element.type !== \"line\"\n elements.push({\n type: \"line\",\n points,\n width: style.width ?? style.border ?? 1,\n color: level(style.color),\n closed,\n fill: closed && style.fill === true,\n })\n break\n }\n case \"image\":\n if (element.data && element.data.length <= MAX_IMAGE_CHARS)\n elements.push({type: \"image\", x, y, w, h, data: element.data})\n break\n }\n }\n const frame = {width: canvas.width, height: canvas.height, elements}\n return JSON.stringify(frame).length > MAX_FRAME_BYTES ? null : frame\n}\n\nexport interface AppleWatchCommandDeps {\n apps: () => readonly ClientApp[]\n start: (app: ClientApp) => Promise<unknown>\n stop: (packageName: string) => Promise<unknown>\n ask: (query: string) => Promise<unknown>\n type: (stream: string, payload: unknown) => void\n now: () => number\n}\n\nconst defaultDeps: AppleWatchCommandDeps = {\n apps: () => useAppStatusStore.getState().apps,\n start: (app) => useAppStatusStore.getState().start(app),\n stop: (packageName) => useAppStatusStore.getState().stop(packageName),\n ask: (query) => localMiniappRuntime.invokeActionFromHost(ASSIST_PACKAGE, ASSIST_ACTION, {query}),\n type: (stream, payload) => localMiniappRuntime.forwardEvent(stream, payload),\n now: () => Date.now(),\n}\n\nexport async function handleAppleWatchCommand(\n event: AppleWatchCommandEvent,\n deps: AppleWatchCommandDeps = defaultDeps,\n): Promise<void> {\n switch (event.command) {\n case \"launch\": {\n const app = launchableApps(deps.apps()).some((entry) => entry.packageName === event.packageName)\n ? deps.apps().find((entry) => entry.packageName === event.packageName)\n : undefined\n if (!app) {\n console.warn(`AppleWatch: launch of unknown miniapp ${event.packageName}`)\n return\n }\n await deps.start(app)\n return\n }\n case \"stop\": {\n const front = deps.apps().find((app) => app.foregrounded && app.running && app.type === \"standard\")\n if (front) await deps.stop(front.packageName)\n return\n }\n case \"assist\": {\n const query = event.text.trim()\n if (query) await deps.ask(query)\n return\n }\n case \"text\":\n for (const key of keyEventsForTextChange(\"\", event.text, deps.now()))\n deps.type(MiniappStreamType.KEYBOARD_EVENT, key)\n return\n }\n}\n\nlet stops: (() => void)[] = []\nlet stopMirror: (() => void) | null = null\n\nfunction currentState(): Record<string, unknown> {\n const glasses = useGlassesStore.getState()\n return watchState({\n glasses: {\n connected: isGlassesConnected(glasses.connection),\n model: glasses.deviceModel,\n battery: glasses.batteryLevel,\n },\n apps: useAppStatusStore.getState().apps,\n assist: localMiniappRuntime.hostActionAvailable(ASSIST_PACKAGE, ASSIST_ACTION),\n mirror: useSettingsStore.getState().getSetting(SETTINGS.apple_watch_mirror.key) === true,\n })\n}\n\nfunction syncMirror(enabled: boolean): void {\n if (!enabled) {\n if (stopMirror) {\n stopMirror()\n stopMirror = null\n void BluetoothSdk.setWatchMirror(null).catch(() => {})\n }\n return\n }\n if (stopMirror) return\n let lastSentAt = 0\n let pending: MirrorSnapshot | null = null\n let timer: ReturnType<typeof BgTimer.setTimeout> | null = null\n const send = (snapshot: MirrorSnapshot) => {\n lastSentAt = Date.now()\n void BluetoothSdk.setWatchMirror(mirrorFrame(snapshot)).catch((err) =>\n console.warn(\"AppleWatch: mirror push failed:\", err),\n )\n }\n const unsubscribe = mirrorService.subscribe((snapshot) => {\n const wait = lastSentAt + MIRROR_INTERVAL_MS - Date.now()\n if (wait <= 0 && !timer) {\n send(snapshot)\n return\n }\n pending = snapshot\n timer ??= BgTimer.setTimeout(\n () => {\n timer = null\n if (pending) send(pending)\n pending = null\n },\n Math.max(wait, 0),\n )\n })\n stopMirror = () => {\n unsubscribe()\n if (timer) BgTimer.clearTimeout(timer)\n timer = null\n }\n}\n\nexport function startAppleWatchLink(): void {\n if (Platform.OS !== \"ios\" || stops.length > 0) return\n let lastKey = \"\"\n let timer: ReturnType<typeof BgTimer.setTimeout> | null = null\n const push = () => {\n timer = null\n const state = currentState()\n const key = JSON.stringify(state)\n if (key === lastKey) return\n lastKey = key\n void BluetoothSdk.setWatchState(state).catch((err) => console.warn(\"AppleWatch: state push failed:\", err))\n }\n const schedule = () => {\n timer ??= BgTimer.setTimeout(push, STATE_DEBOUNCE_MS)\n }\n const mirrorEnabled = () => useSettingsStore.getState().getSetting(SETTINGS.apple_watch_mirror.key) === true\n const commands = BluetoothSdk.addListener(\"apple_watch_command\", (event) => {\n handleAppleWatchCommand(event).catch((err) => console.warn(`AppleWatch: ${event.command} failed:`, err))\n })\n stops = [\n () => commands.remove(),\n useGlassesStore.subscribe(schedule),\n useAppStatusStore.subscribe(schedule),\n useSettingsStore.subscribe(\n (s) => s.getSetting(SETTINGS.apple_watch_mirror.key),\n () => {\n syncMirror(mirrorEnabled())\n schedule()\n },\n ),\n () => {\n if (timer) BgTimer.clearTimeout(timer)\n timer = null\n },\n ]\n syncMirror(mirrorEnabled())\n schedule()\n}\n\nexport function stopAppleWatchLink(): void {\n stops.splice(0).forEach((stop) => stop())\n syncMirror(false)\n}\n"]}
|