@glassly/engine 0.3.7 → 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.
- package/README.md +4 -0
- package/build/engine.d.ts +18 -2
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +7 -4
- package/build/engine.js.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/facades/backup.d.ts +14 -0
- package/build/facades/backup.d.ts.map +1 -0
- package/build/facades/backup.js +22 -0
- package/build/facades/backup.js.map +1 -0
- package/build/facades/glasses.d.ts +13 -5
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +17 -4
- package/build/facades/glasses.js.map +1 -1
- package/build/generated/releaseMetadata.js +4 -4
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +3 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/services/AppRegistry.d.ts +2 -1
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +38 -8
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/AppStateBackup.d.ts +98 -0
- package/build/services/AppStateBackup.d.ts.map +1 -0
- package/build/services/AppStateBackup.js +224 -0
- package/build/services/AppStateBackup.js.map +1 -0
- package/build/services/BlobStore.d.ts.map +1 -1
- package/build/services/BlobStore.js +5 -12
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/CloudClientService.d.ts +3 -1
- package/build/services/CloudClientService.d.ts.map +1 -1
- package/build/services/CloudClientService.js +42 -2
- package/build/services/CloudClientService.js.map +1 -1
- package/build/services/DeviceEventRouter.d.ts.map +1 -1
- package/build/services/DeviceEventRouter.js +1 -0
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/DisplayDiagnostics.d.ts.map +1 -1
- package/build/services/DisplayDiagnostics.js +4 -2
- package/build/services/DisplayDiagnostics.js.map +1 -1
- package/build/services/DisplayProcessor.d.ts +2 -0
- package/build/services/DisplayProcessor.d.ts.map +1 -1
- package/build/services/DisplayProcessor.js +15 -1
- package/build/services/DisplayProcessor.js.map +1 -1
- package/build/services/GlassesTouchInput.d.ts.map +1 -1
- package/build/services/GlassesTouchInput.js +4 -2
- package/build/services/GlassesTouchInput.js.map +1 -1
- package/build/services/JSCrashloopReportService.d.ts.map +1 -1
- package/build/services/JSCrashloopReportService.js +5 -0
- package/build/services/JSCrashloopReportService.js.map +1 -1
- package/build/services/JSRouter.d.ts +11 -3
- package/build/services/JSRouter.d.ts.map +1 -1
- package/build/services/JSRouter.js +36 -6
- package/build/services/JSRouter.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +5 -2
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +13 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +61 -22
- 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/MiniappEngine.d.ts +9 -0
- package/build/services/MiniappEngine.d.ts.map +1 -1
- package/build/services/MiniappEngine.js +60 -27
- package/build/services/MiniappEngine.js.map +1 -1
- package/build/services/MiniappLauncher.d.ts +2 -0
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +6 -0
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/NavigationService.d.ts +1 -0
- package/build/services/NavigationService.d.ts.map +1 -1
- package/build/services/NavigationService.js +20 -6
- package/build/services/NavigationService.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/PhoneCalendarService.d.ts.map +1 -1
- package/build/services/PhoneCalendarService.js +8 -0
- package/build/services/PhoneCalendarService.js.map +1 -1
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +7 -3
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- 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.js +1 -1
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/appStateBackupFile.d.ts +32 -0
- package/build/services/appStateBackupFile.d.ts.map +1 -0
- package/build/services/appStateBackupFile.js +135 -0
- package/build/services/appStateBackupFile.js.map +1 -0
- package/build/services/controllers.d.ts.map +1 -1
- package/build/services/controllers.js +13 -3
- package/build/services/controllers.js.map +1 -1
- package/build/services/effectiveCapabilities.js +6 -1
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/services/neuralBand/NeuralBandInput.d.ts +12 -1
- package/build/services/neuralBand/NeuralBandInput.d.ts.map +1 -1
- package/build/services/neuralBand/NeuralBandInput.js +27 -0
- package/build/services/neuralBand/NeuralBandInput.js.map +1 -1
- package/build/services/neuralBand/NeuralBandSettingsSync.d.ts.map +1 -1
- package/build/services/neuralBand/NeuralBandSettingsSync.js +6 -0
- package/build/services/neuralBand/NeuralBandSettingsSync.js.map +1 -1
- package/build/services/pages/pageEligibility.d.ts.map +1 -1
- package/build/services/pages/pageEligibility.js +8 -1
- package/build/services/pages/pageEligibility.js.map +1 -1
- package/build/services/settingsBridge.d.ts.map +1 -1
- package/build/services/settingsBridge.js +1 -0
- package/build/services/settingsBridge.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/appVisibility.d.ts.map +1 -1
- package/build/stores/appVisibility.js +0 -1
- package/build/stores/appVisibility.js.map +1 -1
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +22 -10
- package/build/stores/apps.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +71 -6
- package/build/stores/settings.js.map +1 -1
- package/build/types/capabilities/even-realities-g1.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g1.js +3 -0
- package/build/types/capabilities/even-realities-g1.js.map +1 -1
- package/build/types/capabilities/memomind-one.d.ts +3 -0
- package/build/types/capabilities/memomind-one.d.ts.map +1 -0
- package/build/types/capabilities/memomind-one.js +42 -0
- package/build/types/capabilities/memomind-one.js.map +1 -0
- 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 +8 -2
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +6 -0
- package/build/types/enums.js.map +1 -1
- package/build/types/hardware.d.ts +3 -1
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js +3 -1
- package/build/types/hardware.js.map +1 -1
- package/build/utils/display/index.d.ts +2 -1
- package/build/utils/display/index.d.ts.map +1 -1
- package/build/utils/display/index.js +3 -2
- package/build/utils/display/index.js.map +1 -1
- package/build/utils/display/profiles/index.d.ts +2 -1
- package/build/utils/display/profiles/index.d.ts.map +1 -1
- package/build/utils/display/profiles/index.js +3 -2
- package/build/utils/display/profiles/index.js.map +1 -1
- package/build/utils/display/profiles/rayneo-io.d.ts +1 -1
- package/build/utils/display/profiles/rayneo-io.d.ts.map +1 -1
- package/build/utils/display/profiles/rayneo-io.js +1 -1
- package/build/utils/display/profiles/rayneo-io.js.map +1 -1
- package/build/utils/display/scene/index.d.ts +1 -1
- package/build/utils/display/scene/index.d.ts.map +1 -1
- package/build/utils/display/scene/index.js +1 -1
- package/build/utils/display/scene/index.js.map +1 -1
- package/build/utils/display/scene/process.d.ts +6 -0
- package/build/utils/display/scene/process.d.ts.map +1 -1
- package/build/utils/display/scene/process.js +22 -7
- package/build/utils/display/scene/process.js.map +1 -1
- package/build/utils/miniappGlobals.d.ts +13 -17
- package/build/utils/miniappGlobals.d.ts.map +1 -1
- package/build/utils/miniappGlobals.js +24 -56
- package/build/utils/miniappGlobals.js.map +1 -1
- package/package.json +7 -7
- package/src/engine.ts +7 -4
- package/src/facades/ai.ts +4 -0
- package/src/facades/backup.ts +28 -0
- package/src/facades/glasses.ts +22 -5
- package/src/generated/releaseMetadata.ts +4 -4
- package/src/index.ts +10 -0
- package/src/services/AppRegistry.ts +36 -9
- package/src/services/AppStateBackup.ts +283 -0
- package/src/services/BlobStore.ts +5 -11
- package/src/services/CloudClientService.ts +43 -4
- package/src/services/DeviceEventRouter.ts +1 -0
- package/src/services/DisplayDiagnostics.ts +4 -2
- package/src/services/DisplayProcessor.ts +20 -8
- package/src/services/GlassesTouchInput.ts +6 -2
- package/src/services/JSCrashloopReportService.ts +5 -0
- package/src/services/JSRouter.ts +37 -6
- package/src/services/LocalDisplayManager.ts +5 -2
- package/src/services/LocalMiniappRuntime.ts +61 -22
- package/src/services/LocalSttFallbackCoordinator.ts +4 -0
- package/src/services/LocalTranslationService.ts +20 -0
- package/src/services/MiniappEngine.ts +62 -27
- package/src/services/MiniappLauncher.ts +7 -0
- package/src/services/NavigationService.ts +28 -8
- package/src/services/OfflineActivity.ts +131 -0
- package/src/services/PhoneCalendarService.ts +7 -0
- package/src/services/PhoneNotificationsSync.ts +8 -3
- package/src/services/STTModelManager.ts +6 -0
- package/src/services/SceneRenderer.ts +1 -1
- package/src/services/appStateBackupFile.ts +171 -0
- package/src/services/controllers.ts +14 -3
- package/src/services/effectiveCapabilities.ts +6 -1
- package/src/services/neuralBand/NeuralBandInput.ts +37 -1
- package/src/services/neuralBand/NeuralBandSettingsSync.ts +12 -0
- package/src/services/pages/pageEligibility.ts +9 -1
- package/src/services/settingsBridge.ts +1 -0
- package/src/services/{appleWatch/AppleWatchLink.ts → watch/WatchLink.ts} +20 -20
- package/src/stores/appVisibility.ts +0 -1
- package/src/stores/apps.ts +22 -11
- package/src/stores/settings.ts +78 -10
- package/src/types/capabilities/even-realities-g1.ts +4 -0
- package/src/types/capabilities/memomind-one.ts +43 -0
- package/src/types/capabilities/rayneo-io.ts +3 -2
- package/src/types/enums.ts +6 -0
- package/src/types/hardware.ts +14 -1
- package/src/utils/display/index.ts +4 -2
- package/src/utils/display/profiles/index.ts +9 -2
- package/src/utils/display/profiles/rayneo-io.ts +6 -1
- package/src/utils/display/scene/index.ts +1 -1
- package/src/utils/display/scene/process.ts +22 -7
- package/src/utils/miniappGlobals.ts +32 -64
- package/build/services/appleWatch/AppleWatchLink.d.ts.map +0 -1
- package/build/services/appleWatch/AppleWatchLink.js.map +0 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App state backup files: the share-sheet export and file-picker restore
|
|
3
|
+
* behind Settings > Profile > Backup. What goes in the file is decided in
|
|
4
|
+
* AppStateBackup.ts; this side reads and writes the live stores.
|
|
5
|
+
*/
|
|
6
|
+
import { File, Paths } from "expo-file-system";
|
|
7
|
+
import { Platform } from "react-native";
|
|
8
|
+
import Share from "react-native-share";
|
|
9
|
+
import { SETTINGS, useSettingsStore } from "../stores/settings";
|
|
10
|
+
import { useAppStatusStore } from "../stores/apps";
|
|
11
|
+
import { storage } from "../utils/storage/storage";
|
|
12
|
+
import appRegistry, { saveAlwaysOnOverride, saveWidgetExpandChoice } from "./AppRegistry";
|
|
13
|
+
import { AppStateBackupError, collectAppStateBackup, miniappPrefs, miniappsWithStorage, parseAppStateBackup, restorableSettings, restoreMiniappStorage, } from "./AppStateBackup";
|
|
14
|
+
import { cloudClientService } from "./CloudClientService";
|
|
15
|
+
import { memoryStore } from "./memoryStoreInstance";
|
|
16
|
+
import { respawnMiniapp, runningMiniappPackages, stopMiniapp } from "./MiniappEngine";
|
|
17
|
+
const PICK_MIME_TYPES = Platform.OS === "android" ? ["application/json", "application/octet-stream"] : ["application/json"];
|
|
18
|
+
const backend = {
|
|
19
|
+
get(key) {
|
|
20
|
+
const result = storage.load(key);
|
|
21
|
+
return result.is_ok() ? result.value : null;
|
|
22
|
+
},
|
|
23
|
+
set(key, value) {
|
|
24
|
+
const result = storage.save(key, value);
|
|
25
|
+
if (result.is_error())
|
|
26
|
+
throw result.error;
|
|
27
|
+
},
|
|
28
|
+
keys: () => storage.getAllKeys(),
|
|
29
|
+
};
|
|
30
|
+
export async function exportAppStateFile() {
|
|
31
|
+
const userId = await cloudClientService.resolveUserId();
|
|
32
|
+
const installed = [];
|
|
33
|
+
for (const app of await appRegistry.getInstalledMiniapps()) {
|
|
34
|
+
const version = await appRegistry.getActiveVersion(app.packageName);
|
|
35
|
+
if (!version)
|
|
36
|
+
continue;
|
|
37
|
+
installed.push({
|
|
38
|
+
packageName: app.packageName,
|
|
39
|
+
version,
|
|
40
|
+
source: appRegistry.getReleaseIdentity(app.packageName, version)?.source,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const backup = collectAppStateBackup({
|
|
44
|
+
backend,
|
|
45
|
+
userId,
|
|
46
|
+
settings: Object.values(SETTINGS),
|
|
47
|
+
installed,
|
|
48
|
+
memory: await memoryStore.exportBackup(),
|
|
49
|
+
now: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
const filename = `glassly-backup-${localDate(backup.exportedAt)}.json`;
|
|
52
|
+
const file = new File(Paths.cache, filename);
|
|
53
|
+
file.writeSync(JSON.stringify(backup));
|
|
54
|
+
try {
|
|
55
|
+
await Share.open({ url: file.uri, type: "application/json", filename, title: "Glassly backup" });
|
|
56
|
+
return { success: true };
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
// react-native-share rejects when the user dismisses the sheet.
|
|
60
|
+
if (error?.message?.includes("User did not share"))
|
|
61
|
+
return { success: false, cancelled: true };
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Resolves null when the user cancels the picker. */
|
|
66
|
+
export async function importAppStateFile(opts = {}) {
|
|
67
|
+
const picked = await File.pickFileAsync({ mimeTypes: PICK_MIME_TYPES });
|
|
68
|
+
if (picked.canceled)
|
|
69
|
+
return null;
|
|
70
|
+
let data;
|
|
71
|
+
try {
|
|
72
|
+
data = JSON.parse(await picked.result.text());
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new AppStateBackupError("Not a Glassly app state backup");
|
|
76
|
+
}
|
|
77
|
+
const backup = parseAppStateBackup(data);
|
|
78
|
+
const userId = await cloudClientService.resolveUserId();
|
|
79
|
+
// First, so a memory section this build rejects fails the restore before anything is written.
|
|
80
|
+
const memory = backup.memory ? await memoryStore.importBackup(backup.memory) : null;
|
|
81
|
+
const { plain, indexed } = restorableSettings(backup, Object.values(SETTINGS));
|
|
82
|
+
const written = await useSettingsStore.getState().setManyLocally(plain);
|
|
83
|
+
if (written.is_error())
|
|
84
|
+
throw written.error;
|
|
85
|
+
// setManyLocally only takes registry keys; `key:index` values go to storage and the store directly.
|
|
86
|
+
for (const [key, value] of Object.entries(indexed))
|
|
87
|
+
backend.set(key, value);
|
|
88
|
+
if (Object.keys(indexed).length > 0) {
|
|
89
|
+
useSettingsStore.setState((state) => ({ settings: { ...state.settings, ...indexed } }));
|
|
90
|
+
}
|
|
91
|
+
// A running miniapp holds its old values in memory and could write them back over the
|
|
92
|
+
// restore, so the ones getting storage stop now and start again at the end.
|
|
93
|
+
const running = new Set(runningMiniappPackages());
|
|
94
|
+
const restarting = miniappsWithStorage(backup).filter((packageName) => running.has(packageName));
|
|
95
|
+
for (const packageName of restarting)
|
|
96
|
+
await stopMiniapp(packageName);
|
|
97
|
+
try {
|
|
98
|
+
const restored = restoreMiniappStorage(backend, userId, backup);
|
|
99
|
+
const apps = useAppStatusStore.getState();
|
|
100
|
+
for (const [packageName, record] of Object.entries(backup.miniapps)) {
|
|
101
|
+
const prefs = miniappPrefs(record);
|
|
102
|
+
if (prefs.hidden !== undefined)
|
|
103
|
+
apps.setHiddenStatus(packageName, prefs.hidden);
|
|
104
|
+
if (prefs.alwaysOn !== undefined)
|
|
105
|
+
saveAlwaysOnOverride(packageName, prefs.alwaysOn);
|
|
106
|
+
for (const [widgetId, expand] of Object.entries(prefs.widgetExpand)) {
|
|
107
|
+
saveWidgetExpandChoice(packageName, widgetId, expand);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
appRegistry.markRefreshNeeded();
|
|
111
|
+
await apps.refresh();
|
|
112
|
+
const installedNow = new Set((await appRegistry.getInstalledMiniapps()).map((app) => app.packageName));
|
|
113
|
+
const missing = Object.entries(backup.miniapps)
|
|
114
|
+
.filter(([packageName, record]) => record.installed && !installedNow.has(packageName))
|
|
115
|
+
.map(([packageName]) => packageName);
|
|
116
|
+
if (missing.length > 0)
|
|
117
|
+
await opts.reinstall?.(missing);
|
|
118
|
+
return {
|
|
119
|
+
settings: Object.keys(plain).length + Object.keys(indexed).length,
|
|
120
|
+
miniapps: restored.packages.length,
|
|
121
|
+
memory,
|
|
122
|
+
missing,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
for (const packageName of restarting)
|
|
127
|
+
await respawnMiniapp(packageName);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function localDate(ms) {
|
|
131
|
+
const d = new Date(ms);
|
|
132
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
133
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=appStateBackupFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appStateBackupFile.js","sourceRoot":"","sources":["../../src/services/appStateBackupFile.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * App state backup files: the share-sheet export and file-picker restore\n * behind Settings > Profile > Backup. What goes in the file is decided in\n * AppStateBackup.ts; this side reads and writes the live stores.\n */\n\nimport type {MemoryImportResult} from \"@glassly/miniapp\"\nimport {File, Paths} from \"expo-file-system\"\nimport {Platform} from \"react-native\"\nimport Share from \"react-native-share\"\n\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {storage} from \"../utils/storage/storage\"\nimport appRegistry, {saveAlwaysOnOverride, saveWidgetExpandChoice} from \"./AppRegistry\"\nimport {\n AppStateBackupError,\n collectAppStateBackup,\n miniappPrefs,\n miniappsWithStorage,\n parseAppStateBackup,\n restorableSettings,\n restoreMiniappStorage,\n type AppStateBackupBackend,\n} from \"./AppStateBackup\"\nimport {cloudClientService} from \"./CloudClientService\"\nimport {memoryStore} from \"./memoryStoreInstance\"\nimport {respawnMiniapp, runningMiniappPackages, stopMiniapp} from \"./MiniappEngine\"\n\nexport interface AppStateExportResult {\n success: boolean\n cancelled?: boolean\n}\n\nexport interface AppStateRestoreResult {\n /** Settings written. */\n settings: number\n /** Miniapps whose storage was restored. */\n miniapps: number\n memory: MemoryImportResult | null\n /** Installed when the backup was made but not on this phone now. */\n missing: string[]\n}\n\nexport interface AppStateRestoreOptions {\n /**\n * Install what the backup had installed and this phone lacks. Runs before\n * the restored miniapps start again, so the dashboard comes back with every\n * widget provider its saved layout names: it drops unknown widgets from the\n * layout the next time it saves it.\n */\n reinstall?: (packageNames: string[]) => Promise<void>\n}\n\nconst PICK_MIME_TYPES =\n Platform.OS === \"android\" ? [\"application/json\", \"application/octet-stream\"] : [\"application/json\"]\n\nconst backend: AppStateBackupBackend = {\n get(key) {\n const result = storage.load<unknown>(key)\n return result.is_ok() ? result.value : null\n },\n set(key, value) {\n const result = storage.save(key, value)\n if (result.is_error()) throw result.error\n },\n keys: () => storage.getAllKeys(),\n}\n\nexport async function exportAppStateFile(): Promise<AppStateExportResult> {\n const userId = await cloudClientService.resolveUserId()\n const installed: Array<{packageName: string; version: string; source?: string}> = []\n for (const app of await appRegistry.getInstalledMiniapps()) {\n const version = await appRegistry.getActiveVersion(app.packageName)\n if (!version) continue\n installed.push({\n packageName: app.packageName,\n version,\n source: appRegistry.getReleaseIdentity(app.packageName, version)?.source,\n })\n }\n const backup = collectAppStateBackup({\n backend,\n userId,\n settings: Object.values(SETTINGS),\n installed,\n memory: await memoryStore.exportBackup(),\n now: Date.now(),\n })\n\n const filename = `glassly-backup-${localDate(backup.exportedAt)}.json`\n const file = new File(Paths.cache, filename)\n file.writeSync(JSON.stringify(backup))\n try {\n await Share.open({url: file.uri, type: \"application/json\", filename, title: \"Glassly backup\"})\n return {success: true}\n } catch (error: any) {\n // react-native-share rejects when the user dismisses the sheet.\n if (error?.message?.includes(\"User did not share\")) return {success: false, cancelled: true}\n throw error\n }\n}\n\n/** Resolves null when the user cancels the picker. */\nexport async function importAppStateFile(opts: AppStateRestoreOptions = {}): Promise<AppStateRestoreResult | null> {\n const picked = await File.pickFileAsync({mimeTypes: PICK_MIME_TYPES})\n if (picked.canceled) return null\n let data: unknown\n try {\n data = JSON.parse(await picked.result.text())\n } catch {\n throw new AppStateBackupError(\"Not a Glassly app state backup\")\n }\n const backup = parseAppStateBackup(data)\n const userId = await cloudClientService.resolveUserId()\n\n // First, so a memory section this build rejects fails the restore before anything is written.\n const memory = backup.memory ? await memoryStore.importBackup(backup.memory) : null\n\n const {plain, indexed} = restorableSettings(backup, Object.values(SETTINGS))\n const written = await useSettingsStore.getState().setManyLocally(plain)\n if (written.is_error()) throw written.error\n // setManyLocally only takes registry keys; `key:index` values go to storage and the store directly.\n for (const [key, value] of Object.entries(indexed)) backend.set(key, value)\n if (Object.keys(indexed).length > 0) {\n useSettingsStore.setState((state) => ({settings: {...state.settings, ...indexed}}))\n }\n\n // A running miniapp holds its old values in memory and could write them back over the\n // restore, so the ones getting storage stop now and start again at the end.\n const running = new Set(runningMiniappPackages())\n const restarting = miniappsWithStorage(backup).filter((packageName) => running.has(packageName))\n for (const packageName of restarting) await stopMiniapp(packageName)\n\n try {\n const restored = restoreMiniappStorage(backend, userId, backup)\n\n const apps = useAppStatusStore.getState()\n for (const [packageName, record] of Object.entries(backup.miniapps)) {\n const prefs = miniappPrefs(record)\n if (prefs.hidden !== undefined) apps.setHiddenStatus(packageName, prefs.hidden)\n if (prefs.alwaysOn !== undefined) saveAlwaysOnOverride(packageName, prefs.alwaysOn)\n for (const [widgetId, expand] of Object.entries(prefs.widgetExpand)) {\n saveWidgetExpandChoice(packageName, widgetId, expand)\n }\n }\n appRegistry.markRefreshNeeded()\n await apps.refresh()\n\n const installedNow = new Set((await appRegistry.getInstalledMiniapps()).map((app) => app.packageName))\n const missing = Object.entries(backup.miniapps)\n .filter(([packageName, record]) => record.installed && !installedNow.has(packageName))\n .map(([packageName]) => packageName)\n if (missing.length > 0) await opts.reinstall?.(missing)\n\n return {\n settings: Object.keys(plain).length + Object.keys(indexed).length,\n miniapps: restored.packages.length,\n memory,\n missing,\n }\n } finally {\n for (const packageName of restarting) await respawnMiniapp(packageName)\n }\n}\n\nfunction localDate(ms: number): string {\n const d = new Date(ms)\n const pad = (n: number) => String(n).padStart(2, \"0\")\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controllers.d.ts","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAE9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,UAAU,CAAA;AAGhD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"controllers.d.ts","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAE9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,UAAU,CAAA;AAGhD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAoD7E,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAErE;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEpE;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,EAAE,CAUxE;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAAG,gBAAgB,EAAE,CAE3G;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAAG,MAAM,CAKlG"}
|
|
@@ -14,8 +14,8 @@ const NEURAL_BAND_SURFACE = {
|
|
|
14
14
|
"DIAL_DOWN",
|
|
15
15
|
],
|
|
16
16
|
};
|
|
17
|
-
/**
|
|
18
|
-
const
|
|
17
|
+
/** A watch trackpad's taps and four-way swipes, and the crown (Apple) or rotary bezel (Wear OS) as the dial. */
|
|
18
|
+
const WATCH_SURFACE = {
|
|
19
19
|
type: "swipe2d",
|
|
20
20
|
events: [
|
|
21
21
|
"TAP",
|
|
@@ -29,10 +29,20 @@ const APPLE_WATCH_SURFACE = {
|
|
|
29
29
|
"DIAL_DOWN",
|
|
30
30
|
],
|
|
31
31
|
};
|
|
32
|
+
/** The Gamebrick's D-pad is four absolute swipes; A is the press lifecycle (tap and hold) and B a double tap. */
|
|
33
|
+
const GAMEBRICK_SURFACE = {
|
|
34
|
+
type: "swipe2d",
|
|
35
|
+
events: ["TAP", "DOUBLE_TAP", "PRESS_HOLD", "SWIPE_UP", "SWIPE_DOWN", "SWIPE_LEFT", "SWIPE_RIGHT"],
|
|
36
|
+
};
|
|
32
37
|
// The R1 swipes up/down like the temples it stands in for, so it adds nothing.
|
|
33
38
|
const INPUT_SURFACES = {
|
|
34
39
|
[ControllerTypes.NEURAL_BAND]: [NEURAL_BAND_SURFACE],
|
|
35
|
-
[ControllerTypes.APPLE_WATCH]: [
|
|
40
|
+
[ControllerTypes.APPLE_WATCH]: [WATCH_SURFACE],
|
|
41
|
+
[ControllerTypes.WEAR_OS]: [WATCH_SURFACE],
|
|
42
|
+
[ControllerTypes.GAMEBRICK]: [GAMEBRICK_SURFACE],
|
|
43
|
+
[ControllerTypes.IINE_ANANKE]: [GAMEBRICK_SURFACE],
|
|
44
|
+
[ControllerTypes.ZERO2]: [GAMEBRICK_SURFACE],
|
|
45
|
+
[ControllerTypes.MICRO]: [GAMEBRICK_SURFACE],
|
|
36
46
|
[ControllerTypes.R1]: [],
|
|
37
47
|
};
|
|
38
48
|
/** The input surfaces a connected controller of `model` adds to the glasses'. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controllers.js","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"AAQA,OAAO,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAA;AAI9C,wDAAwD;AACxD,MAAM,mBAAmB,GAAiB;IACxC,IAAI,EAAE,SAAS;IACf,MAAM,EAAE;QACN,KAAK;QACL,YAAY;QACZ,YAAY;QACZ,UAAU;QACV,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,SAAS;QACT,WAAW;KACZ;CACF,CAAA;AAED,
|
|
1
|
+
{"version":3,"file":"controllers.js","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Controller registry read-model: the paired records (the native-authoritative\n * `controllers` setting) and the live driver statuses (glasses status\n * `controllers`), plus what each model adds to the input surfaces. Pure.\n */\nimport type {ControllerStatus, PairedController} from \"@glassly/bluetooth-sdk\"\n\nimport type {ButtonCapabilities} from \"../types\"\nimport {ControllerTypes} from \"../types/enums\"\n\nexport type InputSurface = NonNullable<ButtonCapabilities[\"buttons\"]>[number]\n\n/** The Neural Band's thumb swipes, pinches and dial. */\nconst NEURAL_BAND_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\n \"TAP\",\n \"DOUBLE_TAP\",\n \"PRESS_HOLD\",\n \"SWIPE_UP\",\n \"SWIPE_DOWN\",\n \"SWIPE_LEFT\",\n \"SWIPE_RIGHT\",\n \"DIAL_UP\",\n \"DIAL_DOWN\",\n ],\n}\n\n/** A watch trackpad's taps and four-way swipes, and the crown (Apple) or rotary bezel (Wear OS) as the dial. */\nconst WATCH_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\n \"TAP\",\n \"DOUBLE_TAP\",\n \"PRESS_HOLD\",\n \"SWIPE_UP\",\n \"SWIPE_DOWN\",\n \"SWIPE_LEFT\",\n \"SWIPE_RIGHT\",\n \"DIAL_UP\",\n \"DIAL_DOWN\",\n ],\n}\n\n/** The Gamebrick's D-pad is four absolute swipes; A is the press lifecycle (tap and hold) and B a double tap. */\nconst GAMEBRICK_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\"TAP\", \"DOUBLE_TAP\", \"PRESS_HOLD\", \"SWIPE_UP\", \"SWIPE_DOWN\", \"SWIPE_LEFT\", \"SWIPE_RIGHT\"],\n}\n\n// The R1 swipes up/down like the temples it stands in for, so it adds nothing.\nconst INPUT_SURFACES: Readonly<Record<string, readonly InputSurface[]>> = {\n [ControllerTypes.NEURAL_BAND]: [NEURAL_BAND_SURFACE],\n [ControllerTypes.APPLE_WATCH]: [WATCH_SURFACE],\n [ControllerTypes.WEAR_OS]: [WATCH_SURFACE],\n [ControllerTypes.GAMEBRICK]: [GAMEBRICK_SURFACE],\n [ControllerTypes.IINE_ANANKE]: [GAMEBRICK_SURFACE],\n [ControllerTypes.ZERO2]: [GAMEBRICK_SURFACE],\n [ControllerTypes.MICRO]: [GAMEBRICK_SURFACE],\n [ControllerTypes.R1]: [],\n}\n\n/** The input surfaces a connected controller of `model` adds to the glasses'. */\nexport function controllerInputSurfaces(model: string): InputSurface[] {\n return (INPUT_SURFACES[model] ?? []).map((surface) => ({...surface, events: [...surface.events]}))\n}\n\n/** The model swipes in two dimensions. */\nexport function hasInput2d(model: string): boolean {\n return (INPUT_SURFACES[model] ?? []).some((surface) => surface.type === \"swipe2d\")\n}\n\nexport function isControllerModel(model: string | undefined): boolean {\n return model !== undefined && (Object.values(ControllerTypes) as string[]).includes(model)\n}\n\n/** The `controllers` setting as records; malformed entries are dropped. */\nexport function pairedControllersFrom(value: unknown): PairedController[] {\n if (!Array.isArray(value)) return []\n const records: PairedController[] = []\n for (const entry of value) {\n if (!entry || typeof entry !== \"object\") continue\n const {id, model, name} = entry as Record<string, unknown>\n if (typeof id !== \"string\" || !id || typeof model !== \"string\") continue\n records.push({id, model, name: typeof name === \"string\" ? name : id})\n }\n return records\n}\n\n/** Delivering input: link up and the route or claim done. */\nexport function isControllerReady(status: ControllerStatus | undefined): boolean {\n return status?.connected === true && status.fullyBooted === true\n}\n\nexport function readyControllers(statuses: Record<string, ControllerStatus> | undefined): ControllerStatus[] {\n return Object.values(statuses ?? {}).filter(isControllerReady)\n}\n\n/** Change key over the controllers delivering input. */\nexport function readyControllersKey(statuses: Record<string, ControllerStatus> | undefined): string {\n return readyControllers(statuses)\n .map((status) => `${status.id}=${status.model}`)\n .sort()\n .join(\",\")\n}\n"]}
|
|
@@ -53,6 +53,8 @@ function glassesCapabilities(model) {
|
|
|
53
53
|
// Glassly CFW supports the complete vector contract, including SVG and rotation.
|
|
54
54
|
// Upstream g2flash publishes a raster canvas with sceneVector=false.
|
|
55
55
|
const vector = useGlassesStore.getState().sceneVector === true;
|
|
56
|
+
// Depth is a disparity between the lenses; one panel (MemoMind One) draws every element flat.
|
|
57
|
+
const stereo = (base.display.count ?? 1) >= 2;
|
|
56
58
|
return {
|
|
57
59
|
...(volumeGate ? withVolumeGate(base) : base),
|
|
58
60
|
hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),
|
|
@@ -60,11 +62,14 @@ function glassesCapabilities(model) {
|
|
|
60
62
|
...base.display,
|
|
61
63
|
width: canvas.width,
|
|
62
64
|
height: canvas.height,
|
|
65
|
+
// A Glassly CFW that publishes a canvas takes positioned elements, whatever
|
|
66
|
+
// the stock profile says (the G1's stock firmware degrades scenes to text walls).
|
|
67
|
+
canPosition: true,
|
|
63
68
|
maxTextElements: Math.max(base.display.maxTextElements ?? 0, vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_TEXT_ELEMENTS),
|
|
64
69
|
// Vector firmware draws an image as one cached slot, the same as a shape.
|
|
65
70
|
maxImageElements: Math.max(base.display.maxImageElements ?? 0, vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_IMAGE_ELEMENTS),
|
|
66
71
|
...(vector
|
|
67
|
-
? { shapes: [...ALL_DISPLAY_SHAPES, "svg"], animation: true, rotation: true, depth:
|
|
72
|
+
? { shapes: [...ALL_DISPLAY_SHAPES, "svg"], animation: true, rotation: true, depth: stereo }
|
|
68
73
|
: {}),
|
|
69
74
|
},
|
|
70
75
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effectiveCapabilities.js","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"effectiveCapabilities.js","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Effective device capabilities — the static model profile plus what the\n * connected firmware reports at runtime. The runtime override is the scene\n * canvas: Glassly CFW on the G2 rasterizes scenes phone-side straight into\n * the 640×480 panel, so the drawable canvas grows past stock's 576×288; on\n * the RayNeo iO it opens a 540×180 page that stock firmware has no\n * equivalent of. Any model whose profile declares `hasCustomFirmware` reads it.\n * Native publishes the canvas into the glasses status (0 = none). Because\n * that path is phone-side rasterization, the stock firmware's container\n * pools (6 text / 4 image) don't bound a CFW frame either — element caps are\n * lifted alongside the canvas, bounded only to keep frames sane. Firmware\n * that draws vector shapes keeps 128 scene slots, so shape-heavy frames\n * (a game board, a chart) get a larger shared text/shape budget.\n *\n * Every connected controller adds its model's input surfaces to\n * `button.buttons` (the Meta Neural Band a `swipe2d` entry), so a miniapp can\n * tell four-way swipes from the temples' and the R1 ring's up/down.\n */\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {ALL_DISPLAY_SHAPES, DeviceTypes, getModelCapabilities, type Capabilities} from \"../types\"\nimport {controllerInputSurfaces, readyControllers, type InputSurface} from \"./controllers\"\nimport {resolveNativeNavigation} from \"./nativeNavigationGate\"\n\nconst CFW_MAX_TEXT_ELEMENTS = 24\nconst CFW_MAX_IMAGE_ELEMENTS = 16\nconst CFW_VECTOR_MAX_ELEMENTS = 120\n\nexport interface SceneCanvasSize {\n width: number\n height: number\n}\n\nexport function runtimeSceneCanvas(): SceneCanvasSize | null {\n const {sceneCanvasWidth, sceneCanvasHeight} = useGlassesStore.getState()\n if (!sceneCanvasWidth || !sceneCanvasHeight || sceneCanvasWidth <= 0 || sceneCanvasHeight <= 0) return null\n return {width: sceneCanvasWidth, height: sceneCanvasHeight}\n}\n\nfunction forceNativeNavigationOnCfw(): boolean {\n return useSettingsStore.getState().getSetting(SETTINGS.cfw_native_navigation.key) === true\n}\n\n/** The connected firmware gates microphone streaming on volume (G2 Glassly CFW revision 40+). */\nexport function runtimeVolumeGate(): boolean {\n return useGlassesStore.getState().volumeGate === true\n}\n\nexport function getEffectiveCapabilities(model: DeviceTypes): Capabilities {\n return withControllerInput(glassesCapabilities(model))\n}\n\nfunction glassesCapabilities(model: DeviceTypes): Capabilities {\n const base = getModelCapabilities(model)\n // Only models with a Glassly CFW publish a canvas or a volume gate; never\n // let a stale value reshape another model.\n const canvas = base.hasCustomFirmware ? runtimeSceneCanvas() : null\n const volumeGate = base.hasCustomFirmware && base.hasMicrophone && runtimeVolumeGate()\n if (!canvas) return volumeGate ? withVolumeGate(base) : base\n if (!base.display) return volumeGate ? withVolumeGate(base) : base\n // Glassly CFW supports the complete vector contract, including SVG and rotation.\n // Upstream g2flash publishes a raster canvas with sceneVector=false.\n const vector = useGlassesStore.getState().sceneVector === true\n // Depth is a disparity between the lenses; one panel (MemoMind One) draws every element flat.\n const stereo = (base.display.count ?? 1) >= 2\n return {\n ...(volumeGate ? withVolumeGate(base) : base),\n hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),\n display: {\n ...base.display,\n width: canvas.width,\n height: canvas.height,\n // A Glassly CFW that publishes a canvas takes positioned elements, whatever\n // the stock profile says (the G1's stock firmware degrades scenes to text walls).\n canPosition: true,\n maxTextElements: Math.max(\n base.display.maxTextElements ?? 0,\n vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_TEXT_ELEMENTS,\n ),\n // Vector firmware draws an image as one cached slot, the same as a shape.\n maxImageElements: Math.max(\n base.display.maxImageElements ?? 0,\n vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_IMAGE_ELEMENTS,\n ),\n ...(vector\n ? {shapes: [...ALL_DISPLAY_SHAPES, \"svg\" as const], animation: true, rotation: true, depth: stereo}\n : {}),\n },\n }\n}\n\nfunction withControllerInput(caps: Capabilities): Capabilities {\n const buttons = caps.button?.buttons ?? []\n const types = new Set(buttons.map((surface) => surface.type))\n const added: InputSurface[] = []\n for (const status of readyControllers(useGlassesStore.getState().controllers)) {\n for (const surface of controllerInputSurfaces(status.model)) {\n if (types.has(surface.type)) continue\n types.add(surface.type)\n added.push(surface)\n }\n }\n if (added.length === 0) return caps\n return {\n ...caps,\n hasButton: true,\n button: {\n ...caps.button,\n count: (caps.button?.count ?? buttons.length) + added.length,\n buttons: [...buttons, ...added],\n },\n }\n}\n\nfunction withVolumeGate(base: Capabilities): Capabilities {\n return {...base, microphone: {...(base.microphone ?? {}), volumeGate: true}}\n}\n"]}
|
|
@@ -6,7 +6,7 @@
|
|
|
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 { NeuralBandClaimEvent, NeuralBandFirmwareEvent, NeuralBandHandwritingEvent, NeuralBandPointerEvent } from "@glassly/bluetooth-sdk";
|
|
10
10
|
import type { KeyboardEventPayload } from "../GlassesKeyboardInput";
|
|
11
11
|
export interface NeuralBandClaimStatus {
|
|
12
12
|
stage: "idle" | "progress" | "completed" | "failed";
|
|
@@ -27,6 +27,9 @@ export interface NeuralBandHandwritingUpdate {
|
|
|
27
27
|
id: string;
|
|
28
28
|
status: NeuralBandHandwritingStatus;
|
|
29
29
|
}
|
|
30
|
+
export type NeuralBandFirmwareStatus = Omit<NeuralBandFirmwareEvent, "type" | "controllerId"> & {
|
|
31
|
+
known: boolean;
|
|
32
|
+
};
|
|
30
33
|
export interface NeuralBandInputOptions {
|
|
31
34
|
forward?: (stream: string, payload: unknown) => void;
|
|
32
35
|
now?: () => number;
|
|
@@ -37,8 +40,10 @@ export declare class NeuralBandInput {
|
|
|
37
40
|
private latestClaim;
|
|
38
41
|
private claims;
|
|
39
42
|
private handwriting;
|
|
43
|
+
private firmware;
|
|
40
44
|
private claimListeners;
|
|
41
45
|
private handwritingListeners;
|
|
46
|
+
private firmwareListeners;
|
|
42
47
|
private readonly forward;
|
|
43
48
|
private readonly now;
|
|
44
49
|
constructor(options?: NeuralBandInputOptions);
|
|
@@ -47,10 +52,16 @@ export declare class NeuralBandInput {
|
|
|
47
52
|
onClaimStatus(cb: (status: NeuralBandClaimStatus) => void): () => void;
|
|
48
53
|
handwritingStatus(controllerId: string): NeuralBandHandwritingStatus;
|
|
49
54
|
onHandwritingStatus(cb: (update: NeuralBandHandwritingUpdate) => void): () => void;
|
|
55
|
+
firmwareStatus(controllerId: string): NeuralBandFirmwareStatus;
|
|
56
|
+
onFirmwareStatus(cb: (update: {
|
|
57
|
+
id: string;
|
|
58
|
+
status: NeuralBandFirmwareStatus;
|
|
59
|
+
}) => void): () => void;
|
|
50
60
|
/** A new pairing attempt starts from a clean slate. */
|
|
51
61
|
resetClaim(): void;
|
|
52
62
|
/** Drop an unpaired band's state. */
|
|
53
63
|
forget(controllerId: string): void;
|
|
64
|
+
handleFirmware(event: NeuralBandFirmwareEvent): void;
|
|
54
65
|
handleClaim(event: NeuralBandClaimEvent): void;
|
|
55
66
|
handleHandwriting(event: NeuralBandHandwritingEvent): void;
|
|
56
67
|
handlePointer(event: NeuralBandPointerEvent): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NeuralBandInput.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandInput.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"NeuralBandInput.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandInput.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,12 +32,24 @@ function codeFor(char) {
|
|
|
32
32
|
}
|
|
33
33
|
const IDLE_CLAIM = { stage: "idle", sessionExpired: false };
|
|
34
34
|
const IDLE_HANDWRITING = { phase: "idle", message: "", text: "" };
|
|
35
|
+
const IDLE_FIRMWARE = {
|
|
36
|
+
known: false,
|
|
37
|
+
connected: false,
|
|
38
|
+
hasMetaSession: false,
|
|
39
|
+
checked: false,
|
|
40
|
+
updateAvailable: false,
|
|
41
|
+
phase: "idle",
|
|
42
|
+
progress: 0,
|
|
43
|
+
message: "",
|
|
44
|
+
};
|
|
35
45
|
export class NeuralBandInput {
|
|
36
46
|
latestClaim = IDLE_CLAIM;
|
|
37
47
|
claims = new Map();
|
|
38
48
|
handwriting = new Map();
|
|
49
|
+
firmware = new Map();
|
|
39
50
|
claimListeners = new Set();
|
|
40
51
|
handwritingListeners = new Set();
|
|
52
|
+
firmwareListeners = new Set();
|
|
41
53
|
forward;
|
|
42
54
|
now;
|
|
43
55
|
constructor(options = {}) {
|
|
@@ -61,6 +73,13 @@ export class NeuralBandInput {
|
|
|
61
73
|
this.handwritingListeners.add(cb);
|
|
62
74
|
return () => this.handwritingListeners.delete(cb);
|
|
63
75
|
}
|
|
76
|
+
firmwareStatus(controllerId) {
|
|
77
|
+
return this.firmware.get(controllerId) ?? IDLE_FIRMWARE;
|
|
78
|
+
}
|
|
79
|
+
onFirmwareStatus(cb) {
|
|
80
|
+
this.firmwareListeners.add(cb);
|
|
81
|
+
return () => this.firmwareListeners.delete(cb);
|
|
82
|
+
}
|
|
64
83
|
/** A new pairing attempt starts from a clean slate. */
|
|
65
84
|
resetClaim() {
|
|
66
85
|
this.latestClaim = IDLE_CLAIM;
|
|
@@ -71,6 +90,14 @@ export class NeuralBandInput {
|
|
|
71
90
|
forget(controllerId) {
|
|
72
91
|
this.claims.delete(controllerId);
|
|
73
92
|
this.handwriting.delete(controllerId);
|
|
93
|
+
this.firmware.delete(controllerId);
|
|
94
|
+
}
|
|
95
|
+
handleFirmware(event) {
|
|
96
|
+
const { type: _type, controllerId, ...rest } = event;
|
|
97
|
+
const status = { ...rest, known: true };
|
|
98
|
+
this.firmware.set(controllerId, status);
|
|
99
|
+
for (const cb of this.firmwareListeners)
|
|
100
|
+
cb({ id: controllerId, status });
|
|
74
101
|
}
|
|
75
102
|
handleClaim(event) {
|
|
76
103
|
const status = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NeuralBandInput.js","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandInput.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Meta Neural Band's own event streams, past the gestures that ride `touch_event`:\n * claim progress (for the pairing screens), the handwriting model (typed into the\n * miniapp keyboard stream as synthesized key presses) and the air mouse (forwarded\n * to miniapps on the pointer stream). State is per band (`controllerId`); the\n * latest claim is also kept on its own, since the pairing screen runs before\n * the band's id reaches JS.\n */\nimport type {NeuralBandClaimEvent, NeuralBandHandwritingEvent, NeuralBandPointerEvent} from \"@glassly/bluetooth-sdk\"\nimport {MiniappStreamType} from \"@glassly/miniapp\"\n\nimport type {KeyboardEventPayload} from \"../GlassesKeyboardInput\"\nimport localMiniappRuntime from \"../LocalMiniappRuntime\"\n\nexport interface NeuralBandClaimStatus {\n stage: \"idle\" | \"progress\" | \"completed\" | \"failed\"\n message?: string\n sessionExpired: boolean\n bandName?: string\n /** The band being claimed; absent until a claim event arrives. */\n controllerId?: string\n}\n\nexport interface NeuralBandHandwritingStatus {\n phase: \"idle\" | \"preparing\" | \"ready\" | \"restoring\" | \"finished\"\n message: string\n problem?: string\n /** The last text the band produced since handwriting was switched on. */\n text: string\n}\n\nexport interface NeuralBandHandwritingUpdate {\n id: string\n status: NeuralBandHandwritingStatus\n}\n\nexport interface NeuralBandInputOptions {\n forward?: (stream: string, payload: unknown) => void\n now?: () => number\n}\n\nconst MODIFIERS = {shift: false, ctrl: false, alt: false, meta: false}\n\n/** One handwriting change as the key presses that would have typed it. */\nexport function keyEventsForTextChange(previous: string, next: string, timestamp: number): KeyboardEventPayload[] {\n let common = 0\n while (common < previous.length && common < next.length && previous[common] === next[common]) common++\n const events: KeyboardEventPayload[] = []\n const press = (key: string, code: string, text?: string) => {\n events.push({phase: \"down\", key, code, modifiers: MODIFIERS, text, repeat: false, timestamp})\n events.push({phase: \"up\", key, code, modifiers: MODIFIERS, repeat: false, timestamp})\n }\n for (let i = common; i < previous.length; i++) press(\"Backspace\", \"Backspace\")\n for (const char of Array.from(next.slice(common))) {\n if (char === \"\\n\") press(\"Enter\", \"Enter\")\n else if (char === \" \") press(\" \", \"Space\", \" \")\n else press(char, codeFor(char), char)\n }\n return events\n}\n\nfunction codeFor(char: string): string {\n if (/^[a-z]$/i.test(char)) return `Key${char.toUpperCase()}`\n if (/^[0-9]$/.test(char)) return `Digit${char}`\n return \"Unidentified\"\n}\n\nconst IDLE_CLAIM: NeuralBandClaimStatus = {stage: \"idle\", sessionExpired: false}\nconst IDLE_HANDWRITING: NeuralBandHandwritingStatus = {phase: \"idle\", message: \"\", text: \"\"}\n\nexport class NeuralBandInput {\n private latestClaim: NeuralBandClaimStatus = IDLE_CLAIM\n private claims = new Map<string, NeuralBandClaimStatus>()\n private handwriting = new Map<string, NeuralBandHandwritingStatus>()\n private claimListeners = new Set<(status: NeuralBandClaimStatus) => void>()\n private handwritingListeners = new Set<(update: NeuralBandHandwritingUpdate) => void>()\n private readonly forward: (stream: string, payload: unknown) => void\n private readonly now: () => number\n\n constructor(options: NeuralBandInputOptions = {}) {\n this.forward = options.forward ?? ((stream, payload) => localMiniappRuntime.forwardEvent(stream, payload))\n this.now = options.now ?? (() => Date.now())\n }\n\n /** One band's claim, or the latest claim of any band without an id. */\n claimStatus(controllerId?: string): NeuralBandClaimStatus {\n if (controllerId === undefined) return this.latestClaim\n return this.claims.get(controllerId) ?? IDLE_CLAIM\n }\n\n onClaimStatus(cb: (status: NeuralBandClaimStatus) => void): () => void {\n this.claimListeners.add(cb)\n return () => this.claimListeners.delete(cb)\n }\n\n handwritingStatus(controllerId: string): NeuralBandHandwritingStatus {\n return this.handwriting.get(controllerId) ?? IDLE_HANDWRITING\n }\n\n onHandwritingStatus(cb: (update: NeuralBandHandwritingUpdate) => void): () => void {\n this.handwritingListeners.add(cb)\n return () => this.handwritingListeners.delete(cb)\n }\n\n /** A new pairing attempt starts from a clean slate. */\n resetClaim(): void {\n this.latestClaim = IDLE_CLAIM\n for (const cb of this.claimListeners) cb(IDLE_CLAIM)\n }\n\n /** Drop an unpaired band's state. */\n forget(controllerId: string): void {\n this.claims.delete(controllerId)\n this.handwriting.delete(controllerId)\n }\n\n handleClaim(event: NeuralBandClaimEvent): void {\n const status: NeuralBandClaimStatus = {\n stage: event.stage,\n message: event.message,\n sessionExpired: event.sessionExpired === true,\n bandName: event.bandName,\n controllerId: event.controllerId,\n }\n if (event.controllerId) this.claims.set(event.controllerId, status)\n this.latestClaim = status\n for (const cb of this.claimListeners) cb(status)\n }\n\n handleHandwriting(event: NeuralBandHandwritingEvent): void {\n const id = event.controllerId\n const current = this.handwritingStatus(id)\n if (event.kind === \"state\") {\n const text = event.phase === \"preparing\" ? \"\" : current.text\n this.setHandwriting(id, {phase: event.phase, message: event.message, problem: event.problem, text})\n return\n }\n if (event.text !== current.text) {\n for (const key of keyEventsForTextChange(current.text, event.text, this.now())) {\n this.forward(MiniappStreamType.KEYBOARD_EVENT, key)\n }\n }\n this.setHandwriting(id, {...current, text: event.text})\n }\n\n handlePointer(event: NeuralBandPointerEvent): void {\n if (event.kind === \"move\") {\n this.forward(MiniappStreamType.POINTER_EVENT, {\n kind: \"move\",\n dx: event.dx,\n dy: event.dy,\n timestamp: event.timestamp,\n controllerId: event.controllerId,\n })\n } else {\n this.forward(MiniappStreamType.POINTER_EVENT, {\n kind: \"buttons\",\n buttons: event.buttons,\n timestamp: event.timestamp,\n controllerId: event.controllerId,\n })\n }\n }\n\n private setHandwriting(id: string, status: NeuralBandHandwritingStatus): void {\n this.handwriting.set(id, status)\n for (const cb of this.handwritingListeners) cb({id, status})\n }\n}\n\nconst neuralBandInput = new NeuralBandInput()\nexport default neuralBandInput\n"]}
|
|
1
|
+
{"version":3,"file":"NeuralBandInput.js","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandInput.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Meta Neural Band's own event streams, past the gestures that ride `touch_event`:\n * claim progress (for the pairing screens), the handwriting model (typed into the\n * miniapp keyboard stream as synthesized key presses) and the air mouse (forwarded\n * to miniapps on the pointer stream). State is per band (`controllerId`); the\n * latest claim is also kept on its own, since the pairing screen runs before\n * the band's id reaches JS.\n */\nimport type {\n NeuralBandClaimEvent,\n NeuralBandFirmwareEvent,\n NeuralBandHandwritingEvent,\n NeuralBandPointerEvent,\n} from \"@glassly/bluetooth-sdk\"\nimport {MiniappStreamType} from \"@glassly/miniapp\"\n\nimport type {KeyboardEventPayload} from \"../GlassesKeyboardInput\"\nimport localMiniappRuntime from \"../LocalMiniappRuntime\"\n\nexport interface NeuralBandClaimStatus {\n stage: \"idle\" | \"progress\" | \"completed\" | \"failed\"\n message?: string\n sessionExpired: boolean\n bandName?: string\n /** The band being claimed; absent until a claim event arrives. */\n controllerId?: string\n}\n\nexport interface NeuralBandHandwritingStatus {\n phase: \"idle\" | \"preparing\" | \"ready\" | \"restoring\" | \"finished\"\n message: string\n problem?: string\n /** The last text the band produced since handwriting was switched on. */\n text: string\n}\n\nexport interface NeuralBandHandwritingUpdate {\n id: string\n status: NeuralBandHandwritingStatus\n}\n\nexport type NeuralBandFirmwareStatus = Omit<NeuralBandFirmwareEvent, \"type\" | \"controllerId\"> & {known: boolean}\n\nexport interface NeuralBandInputOptions {\n forward?: (stream: string, payload: unknown) => void\n now?: () => number\n}\n\nconst MODIFIERS = {shift: false, ctrl: false, alt: false, meta: false}\n\n/** One handwriting change as the key presses that would have typed it. */\nexport function keyEventsForTextChange(previous: string, next: string, timestamp: number): KeyboardEventPayload[] {\n let common = 0\n while (common < previous.length && common < next.length && previous[common] === next[common]) common++\n const events: KeyboardEventPayload[] = []\n const press = (key: string, code: string, text?: string) => {\n events.push({phase: \"down\", key, code, modifiers: MODIFIERS, text, repeat: false, timestamp})\n events.push({phase: \"up\", key, code, modifiers: MODIFIERS, repeat: false, timestamp})\n }\n for (let i = common; i < previous.length; i++) press(\"Backspace\", \"Backspace\")\n for (const char of Array.from(next.slice(common))) {\n if (char === \"\\n\") press(\"Enter\", \"Enter\")\n else if (char === \" \") press(\" \", \"Space\", \" \")\n else press(char, codeFor(char), char)\n }\n return events\n}\n\nfunction codeFor(char: string): string {\n if (/^[a-z]$/i.test(char)) return `Key${char.toUpperCase()}`\n if (/^[0-9]$/.test(char)) return `Digit${char}`\n return \"Unidentified\"\n}\n\nconst IDLE_CLAIM: NeuralBandClaimStatus = {stage: \"idle\", sessionExpired: false}\nconst IDLE_HANDWRITING: NeuralBandHandwritingStatus = {phase: \"idle\", message: \"\", text: \"\"}\nconst IDLE_FIRMWARE: NeuralBandFirmwareStatus = {\n known: false,\n connected: false,\n hasMetaSession: false,\n checked: false,\n updateAvailable: false,\n phase: \"idle\",\n progress: 0,\n message: \"\",\n}\n\nexport class NeuralBandInput {\n private latestClaim: NeuralBandClaimStatus = IDLE_CLAIM\n private claims = new Map<string, NeuralBandClaimStatus>()\n private handwriting = new Map<string, NeuralBandHandwritingStatus>()\n private firmware = new Map<string, NeuralBandFirmwareStatus>()\n private claimListeners = new Set<(status: NeuralBandClaimStatus) => void>()\n private handwritingListeners = new Set<(update: NeuralBandHandwritingUpdate) => void>()\n private firmwareListeners = new Set<(update: {id: string; status: NeuralBandFirmwareStatus}) => void>()\n private readonly forward: (stream: string, payload: unknown) => void\n private readonly now: () => number\n\n constructor(options: NeuralBandInputOptions = {}) {\n this.forward = options.forward ?? ((stream, payload) => localMiniappRuntime.forwardEvent(stream, payload))\n this.now = options.now ?? (() => Date.now())\n }\n\n /** One band's claim, or the latest claim of any band without an id. */\n claimStatus(controllerId?: string): NeuralBandClaimStatus {\n if (controllerId === undefined) return this.latestClaim\n return this.claims.get(controllerId) ?? IDLE_CLAIM\n }\n\n onClaimStatus(cb: (status: NeuralBandClaimStatus) => void): () => void {\n this.claimListeners.add(cb)\n return () => this.claimListeners.delete(cb)\n }\n\n handwritingStatus(controllerId: string): NeuralBandHandwritingStatus {\n return this.handwriting.get(controllerId) ?? IDLE_HANDWRITING\n }\n\n onHandwritingStatus(cb: (update: NeuralBandHandwritingUpdate) => void): () => void {\n this.handwritingListeners.add(cb)\n return () => this.handwritingListeners.delete(cb)\n }\n\n firmwareStatus(controllerId: string): NeuralBandFirmwareStatus {\n return this.firmware.get(controllerId) ?? IDLE_FIRMWARE\n }\n\n onFirmwareStatus(cb: (update: {id: string; status: NeuralBandFirmwareStatus}) => void): () => void {\n this.firmwareListeners.add(cb)\n return () => this.firmwareListeners.delete(cb)\n }\n\n /** A new pairing attempt starts from a clean slate. */\n resetClaim(): void {\n this.latestClaim = IDLE_CLAIM\n for (const cb of this.claimListeners) cb(IDLE_CLAIM)\n }\n\n /** Drop an unpaired band's state. */\n forget(controllerId: string): void {\n this.claims.delete(controllerId)\n this.handwriting.delete(controllerId)\n this.firmware.delete(controllerId)\n }\n\n handleFirmware(event: NeuralBandFirmwareEvent): void {\n const {type: _type, controllerId, ...rest} = event\n const status: NeuralBandFirmwareStatus = {...rest, known: true}\n this.firmware.set(controllerId, status)\n for (const cb of this.firmwareListeners) cb({id: controllerId, status})\n }\n\n handleClaim(event: NeuralBandClaimEvent): void {\n const status: NeuralBandClaimStatus = {\n stage: event.stage,\n message: event.message,\n sessionExpired: event.sessionExpired === true,\n bandName: event.bandName,\n controllerId: event.controllerId,\n }\n if (event.controllerId) this.claims.set(event.controllerId, status)\n this.latestClaim = status\n for (const cb of this.claimListeners) cb(status)\n }\n\n handleHandwriting(event: NeuralBandHandwritingEvent): void {\n const id = event.controllerId\n const current = this.handwritingStatus(id)\n if (event.kind === \"state\") {\n const text = event.phase === \"preparing\" ? \"\" : current.text\n this.setHandwriting(id, {phase: event.phase, message: event.message, problem: event.problem, text})\n return\n }\n if (event.text !== current.text) {\n for (const key of keyEventsForTextChange(current.text, event.text, this.now())) {\n this.forward(MiniappStreamType.KEYBOARD_EVENT, key)\n }\n }\n this.setHandwriting(id, {...current, text: event.text})\n }\n\n handlePointer(event: NeuralBandPointerEvent): void {\n if (event.kind === \"move\") {\n this.forward(MiniappStreamType.POINTER_EVENT, {\n kind: \"move\",\n dx: event.dx,\n dy: event.dy,\n timestamp: event.timestamp,\n controllerId: event.controllerId,\n })\n } else {\n this.forward(MiniappStreamType.POINTER_EVENT, {\n kind: \"buttons\",\n buttons: event.buttons,\n timestamp: event.timestamp,\n controllerId: event.controllerId,\n })\n }\n }\n\n private setHandwriting(id: string, status: NeuralBandHandwritingStatus): void {\n this.handwriting.set(id, status)\n for (const cb of this.handwritingListeners) cb({id, status})\n }\n}\n\nconst neuralBandInput = new NeuralBandInput()\nexport default neuralBandInput\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NeuralBandSettingsSync.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandSettingsSync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NeuralBandSettingsSync.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandSettingsSync.ts"],"names":[],"mappings":"AAyCA,wBAAgB,2BAA2B,IAAI,IAAI,CA8BlD;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
|
|
@@ -22,6 +22,10 @@ function pushHand(id) {
|
|
|
22
22
|
const hand = useSettingsStore.getState().getSetting(SETTINGS.neural_band_hand.key) === "left" ? "left" : "right";
|
|
23
23
|
void BluetoothSdk.neuralBandSetHand(id, hand).catch((err) => console.warn("NeuralBandSettingsSync: hand push failed:", err));
|
|
24
24
|
}
|
|
25
|
+
function pushVibration(id) {
|
|
26
|
+
const enabled = useSettingsStore.getState().getSetting(SETTINGS.neural_band_vibration.key) !== false;
|
|
27
|
+
void BluetoothSdk.neuralBandSetGestureHaptics(id, enabled).catch((err) => console.warn("NeuralBandSettingsSync: vibration push failed:", err));
|
|
28
|
+
}
|
|
25
29
|
export function startNeuralBandSettingsSync() {
|
|
26
30
|
if (unsubscribers.length > 0)
|
|
27
31
|
return;
|
|
@@ -34,11 +38,13 @@ export function startNeuralBandSettingsSync() {
|
|
|
34
38
|
continue;
|
|
35
39
|
pushAirMouse(id);
|
|
36
40
|
pushHand(id);
|
|
41
|
+
pushVibration(id);
|
|
37
42
|
}
|
|
38
43
|
connected = next;
|
|
39
44
|
}),
|
|
40
45
|
useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.neural_band_air_mouse.key), () => connectedBandIds().forEach(pushAirMouse)),
|
|
41
46
|
useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.neural_band_hand.key), () => connectedBandIds().forEach(pushHand)),
|
|
47
|
+
useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.neural_band_vibration.key), () => connectedBandIds().forEach(pushVibration)),
|
|
42
48
|
];
|
|
43
49
|
}
|
|
44
50
|
export function stopNeuralBandSettingsSync() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NeuralBandSettingsSync.js","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandSettingsSync.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,YAAY,MAAM,iCAAiC,CAAA;AAE1D,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,gBAAgB,EAAC,MAAM,gBAAgB,CAAA;AAE/C,IAAI,aAAa,GAAmB,EAAE,CAAA;AAEtC,SAAS,gBAAgB;IACvB,OAAO,gBAAgB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC;SAC5D,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,eAAe,CAAC,WAAW,CAAC;SAChE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAC/B,CAAC;AAED,SAAS,YAAY,CAAC,EAAU;IAC9B,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAA;IACnG,KAAK,YAAY,CAAC,oBAAoB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAChE,OAAO,CAAC,IAAI,CAAC,gDAAgD,EAAE,GAAG,CAAC,CACpE,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,EAAU;IAC1B,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;IAChH,KAAK,YAAY,CAAC,iBAAiB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAC1D,OAAO,CAAC,IAAI,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAC/D,CAAA;AACH,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QAAE,OAAM;IACpC,IAAI,SAAS,GAAG,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAA;IAC3C,aAAa,GAAG;QACd,eAAe,CAAC,SAAS,CACvB,GAAG,EAAE,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EACzC,GAAG,EAAE;YACH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAA;YACxC,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;gBACtB,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,SAAQ;gBAC/B,YAAY,CAAC,EAAE,CAAC,CAAA;gBAChB,QAAQ,CAAC,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"NeuralBandSettingsSync.js","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandSettingsSync.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pushes the Neural Band user settings (air mouse, wearing hand) to every\n * connected band: to each band as it connects, and to all of them whenever\n * either setting changes. The native side keeps nothing across connections.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\n\nimport {useGlassesStore} from \"../../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../../stores/settings\"\nimport {ControllerTypes} from \"../../types/enums\"\nimport {readyControllers} from \"../controllers\"\n\nlet unsubscribers: (() => void)[] = []\n\nfunction connectedBandIds(): string[] {\n return readyControllers(useGlassesStore.getState().controllers)\n .filter((status) => status.model === ControllerTypes.NEURAL_BAND)\n .map((status) => status.id)\n}\n\nfunction pushAirMouse(id: string): void {\n const enabled = useSettingsStore.getState().getSetting(SETTINGS.neural_band_air_mouse.key) === true\n void BluetoothSdk.neuralBandSetPointer(id, enabled).catch((err) =>\n console.warn(\"NeuralBandSettingsSync: air mouse push failed:\", err),\n )\n}\n\nfunction pushHand(id: string): void {\n const hand = useSettingsStore.getState().getSetting(SETTINGS.neural_band_hand.key) === \"left\" ? \"left\" : \"right\"\n void BluetoothSdk.neuralBandSetHand(id, hand).catch((err) =>\n console.warn(\"NeuralBandSettingsSync: hand push failed:\", err),\n )\n}\n\nfunction pushVibration(id: string): void {\n const enabled = useSettingsStore.getState().getSetting(SETTINGS.neural_band_vibration.key) !== false\n void BluetoothSdk.neuralBandSetGestureHaptics(id, enabled).catch((err) =>\n console.warn(\"NeuralBandSettingsSync: vibration push failed:\", err),\n )\n}\n\nexport function startNeuralBandSettingsSync(): void {\n if (unsubscribers.length > 0) return\n let connected = new Set(connectedBandIds())\n unsubscribers = [\n useGlassesStore.subscribe(\n () => connectedBandIds().sort().join(\",\"),\n () => {\n const next = new Set(connectedBandIds())\n for (const id of next) {\n if (connected.has(id)) continue\n pushAirMouse(id)\n pushHand(id)\n pushVibration(id)\n }\n connected = next\n },\n ),\n useSettingsStore.subscribe(\n (s) => s.getSetting(SETTINGS.neural_band_air_mouse.key),\n () => connectedBandIds().forEach(pushAirMouse),\n ),\n useSettingsStore.subscribe(\n (s) => s.getSetting(SETTINGS.neural_band_hand.key),\n () => connectedBandIds().forEach(pushHand),\n ),\n useSettingsStore.subscribe(\n (s) => s.getSetting(SETTINGS.neural_band_vibration.key),\n () => connectedBandIds().forEach(pushVibration),\n ),\n ]\n}\n\nexport function stopNeuralBandSettingsSync(): void {\n for (const unsubscribe of unsubscribers) unsubscribe()\n unsubscribers = []\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageEligibility.d.ts","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAaA,0GAA0G;AAC1G,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"pageEligibility.d.ts","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAaA,0GAA0G;AAC1G,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAKxF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,GAAG,OAAO,CAWjH;AAED,wBAAgB,aAAa,IAAI,OAAO,CAOvC"}
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { useGlassesStore } from "../../stores/glasses";
|
|
9
9
|
import { SETTINGS, useSettingsStore } from "../../stores/settings";
|
|
10
|
+
import { DeviceTypes } from "../../types/enums";
|
|
10
11
|
import { getModelCapabilities } from "../../types/hardware";
|
|
11
12
|
import { isGlassesConnected } from "../GlassesReadiness";
|
|
12
13
|
/** The SGC appends this marker after the firmware advertises Glassly CFW capabilities (G2, RayNeo iO). */
|
|
13
14
|
export function isCfwFirmwareVersion(firmwareVersion) {
|
|
14
|
-
return firmwareVersion?.toLowerCase().includes("glassly cfw") === true
|
|
15
|
+
return (firmwareVersion?.toLowerCase().includes("glassly cfw") === true ||
|
|
16
|
+
/^GLASSLYCFW\/[0-9]+$/.test(firmwareVersion ?? ""));
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Only models that ship a Glassly custom firmware, and only while they run it.
|
|
@@ -21,6 +23,11 @@ export function isCfwFirmwareVersion(firmwareVersion) {
|
|
|
21
23
|
export function isCustomDashboardEligible(info) {
|
|
22
24
|
if (!info.model)
|
|
23
25
|
return false;
|
|
26
|
+
if (info.model === DeviceTypes.MEMOMIND_ONE) {
|
|
27
|
+
const match = /^GLASSLYCFW\/([0-9]+)$/.exec(info.firmwareVersion ?? "");
|
|
28
|
+
const revision = match ? Number(match[1]) : 0;
|
|
29
|
+
return Number.isSafeInteger(revision) && revision >= 10;
|
|
30
|
+
}
|
|
24
31
|
return (getModelCapabilities(info.model).hasCustomFirmware === true &&
|
|
25
32
|
isCfwFirmwareVersion(info.firmwareVersion));
|
|
26
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageEligibility.js","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"pageEligibility.js","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAC,oBAAoB,EAAC,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAC,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AAEtD,0GAA0G;AAC1G,MAAM,UAAU,oBAAoB,CAAC,eAA0C;IAC7E,OAAO,CACL,eAAe,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,IAAI;QAC/D,sBAAsB,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CACnD,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAA8D;IACtG,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAC7B,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,YAAY,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAA;QACvE,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7C,OAAO,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,EAAE,CAAA;IACzD,CAAC;IACD,OAAO,CACL,oBAAoB,CAAC,IAAI,CAAC,KAAoB,CAAC,CAAC,iBAAiB,KAAK,IAAI;QAC1E,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,CAC3C,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,wBAAwB,CAAC,GAAG,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAA;IACzG,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IACxC,OAAO,CACL,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC;QACpC,yBAAyB,CAAC,EAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAC,CAAC,CAC9F,CAAA;AACH,CAAC","sourcesContent":["/**\n * Whether the dashboard view may show right now: connected Glassly CFW\n * glasses with the dashboard enabled. The page host reads it before showing\n * a page and hides the showing page when it turns false; the miniapp runtime\n * folds it into the designated app's `display.views` capability. Read at\n * decision time, never cached.\n */\nimport {useGlassesStore} from \"../../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../../stores/settings\"\nimport {DeviceTypes} from \"../../types/enums\"\nimport {getModelCapabilities} from \"../../types/hardware\"\nimport {isGlassesConnected} from \"../GlassesReadiness\"\n\n/** The SGC appends this marker after the firmware advertises Glassly CFW capabilities (G2, RayNeo iO). */\nexport function isCfwFirmwareVersion(firmwareVersion: string | null | undefined): boolean {\n return (\n firmwareVersion?.toLowerCase().includes(\"glassly cfw\") === true ||\n /^GLASSLYCFW\\/[0-9]+$/.test(firmwareVersion ?? \"\")\n )\n}\n\n/**\n * Only models that ship a Glassly custom firmware, and only while they run it.\n * Stock firmware on those models and every other model (NIMO, G1, ...) are\n * intentionally excluded.\n */\nexport function isCustomDashboardEligible(info: {model?: string | null; firmwareVersion?: string | null}): boolean {\n if (!info.model) return false\n if (info.model === DeviceTypes.MEMOMIND_ONE) {\n const match = /^GLASSLYCFW\\/([0-9]+)$/.exec(info.firmwareVersion ?? \"\")\n const revision = match ? Number(match[1]) : 0\n return Number.isSafeInteger(revision) && revision >= 10\n }\n return (\n getModelCapabilities(info.model as DeviceTypes).hasCustomFirmware === true &&\n isCfwFirmwareVersion(info.firmwareVersion)\n )\n}\n\nexport function pagesEligible(): boolean {\n if (useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_enabled.key) === false) return false\n const state = useGlassesStore.getState()\n return (\n isGlassesConnected(state.connection) &&\n isCustomDashboardEligible({model: state.deviceModel, firmwareVersion: state.firmwareVersion})\n )\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settingsBridge.d.ts","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAK9E,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"settingsBridge.d.ts","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAK9E,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAwB/E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,sEAAuE,CAAA;AACzG,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAG7E;AAED,wDAAwD;AACxD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,EAAE,CAIrE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAC,GACvC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAA;AAE5E,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,qBAAqB,CAY3F;AAED,wEAAwE;AACxE,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,wBAAwB;IACxB,SAAS,EAAE,OAAO,CAAA;IAClB,6BAA6B;IAC7B,OAAO,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAC,CAAA;IAC3C,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,gGAAgG;AAChG,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,OAAO,CAAA;CACtB;AAcD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,EACtD,OAAO,EAAE,WAAW,GACnB,sBAAsB,EAAE,CAqB1B"}
|