@glassly/engine 0.4.0 → 0.4.1-dev.1137
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 +3 -1
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +6 -4
- package/build/engine.js.map +1 -1
- package/build/facades/display.d.ts +1 -1
- package/build/facades/display.d.ts.map +1 -1
- package/build/facades/display.js +1 -1
- package/build/facades/display.js.map +1 -1
- package/build/facades/glasses.js +1 -1
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/permissions.d.ts.map +1 -1
- package/build/facades/permissions.js +0 -2
- package/build/facades/permissions.js.map +1 -1
- package/build/facades/phoneNotifications.d.ts +7 -0
- package/build/facades/phoneNotifications.d.ts.map +1 -1
- package/build/facades/phoneNotifications.js +6 -0
- package/build/facades/phoneNotifications.js.map +1 -1
- package/build/facades/providerKeys.d.ts +1 -1
- package/build/facades/providerKeys.js +1 -1
- package/build/facades/providerKeys.js.map +1 -1
- package/build/facades/reports.d.ts +15 -0
- package/build/facades/reports.d.ts.map +1 -1
- package/build/facades/reports.js +37 -0
- package/build/facades/reports.js.map +1 -1
- package/build/facades/settings.js +1 -1
- package/build/facades/settings.js.map +1 -1
- package/build/generated/releaseMetadata.js +4 -4
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts.map +1 -1
- package/build/react/useMentraLiveOta.js.map +1 -1
- package/build/runtime/config.d.ts.map +1 -1
- package/build/runtime/config.js.map +1 -1
- package/build/services/AiTierResolution.d.ts +1 -1
- package/build/services/AiTierResolution.d.ts.map +1 -1
- package/build/services/AiTierResolution.js.map +1 -1
- package/build/services/AppRegistry.d.ts +12 -1
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +33 -0
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/AutomaticReportResult.d.ts.map +1 -1
- package/build/services/AutomaticReportResult.js.map +1 -1
- package/build/services/CloudClientService.d.ts +7 -5
- package/build/services/CloudClientService.d.ts.map +1 -1
- package/build/services/CloudClientService.js +17 -14
- package/build/services/CloudClientService.js.map +1 -1
- package/build/services/DisplayProcessor.d.ts.map +1 -1
- package/build/services/DisplayProcessor.js +2 -1
- package/build/services/DisplayProcessor.js.map +1 -1
- package/build/services/GestureDotsService.js +1 -1
- package/build/services/GestureDotsService.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts.map +1 -1
- package/build/services/HotspotOtaTransport.js +0 -1
- package/build/services/HotspotOtaTransport.js.map +1 -1
- package/build/services/JSRouter.js +2 -2
- package/build/services/JSRouter.js.map +1 -1
- package/build/services/LocalLlmModelManager.d.ts.map +1 -1
- package/build/services/LocalLlmModelManager.js +4 -3
- package/build/services/LocalLlmModelManager.js.map +1 -1
- package/build/services/LocalLlmProvider.d.ts.map +1 -1
- package/build/services/LocalLlmProvider.js +5 -6
- package/build/services/LocalLlmProvider.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +10 -3
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +142 -30
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
- package/build/services/NativeNavigationService.d.ts.map +1 -1
- package/build/services/NativeNavigationService.js.map +1 -1
- package/build/services/NavigationHandlers.d.ts +1 -1
- package/build/services/NavigationHandlers.js +2 -2
- package/build/services/NavigationHandlers.js.map +1 -1
- package/build/services/NavigationService.d.ts +2 -2
- package/build/services/NavigationService.d.ts.map +1 -1
- package/build/services/NavigationService.js +11 -11
- package/build/services/NavigationService.js.map +1 -1
- package/build/services/PairingIdentity.d.ts.map +1 -1
- package/build/services/PairingIdentity.js.map +1 -1
- package/build/services/PhoneCalendarService.d.ts.map +1 -1
- package/build/services/PhoneCalendarService.js.map +1 -1
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +2 -1
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/ProviderKeyValidation.d.ts +1 -1
- package/build/services/ProviderKeyValidation.js +1 -1
- package/build/services/ProviderKeyValidation.js.map +1 -1
- package/build/services/STTModelManager.d.ts.map +1 -1
- package/build/services/STTModelManager.js +1 -6
- package/build/services/STTModelManager.js.map +1 -1
- package/build/services/SupportProfileSync.d.ts.map +1 -1
- package/build/services/SupportProfileSync.js +3 -0
- package/build/services/SupportProfileSync.js.map +1 -1
- package/build/services/TranscriptionRouting.d.ts +3 -0
- package/build/services/TranscriptionRouting.d.ts.map +1 -1
- package/build/services/TranscriptionRouting.js +14 -0
- package/build/services/TranscriptionRouting.js.map +1 -1
- package/build/services/UIRouter.d.ts.map +1 -1
- package/build/services/UIRouter.js.map +1 -1
- package/build/services/controllers.d.ts.map +1 -1
- package/build/services/controllers.js +1 -0
- package/build/services/controllers.js.map +1 -1
- package/build/services/effectiveCapabilities.js +3 -2
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/services/gestureDotGlyphs.d.ts.map +1 -1
- package/build/services/gestureDotGlyphs.js.map +1 -1
- package/build/services/manifestActions.d.ts.map +1 -1
- package/build/services/manifestActions.js.map +1 -1
- package/build/services/neuralBand/MetaAuth.d.ts +1 -0
- package/build/services/neuralBand/MetaAuth.d.ts.map +1 -1
- package/build/services/neuralBand/MetaAuth.js +36 -8
- package/build/services/neuralBand/MetaAuth.js.map +1 -1
- package/build/services/notificationAppPayload.d.ts +14 -0
- package/build/services/notificationAppPayload.d.ts.map +1 -0
- package/build/services/notificationAppPayload.js +15 -0
- package/build/services/notificationAppPayload.js.map +1 -0
- package/build/services/notifications/miniappNotifications.d.ts +31 -0
- package/build/services/notifications/miniappNotifications.d.ts.map +1 -0
- package/build/services/notifications/miniappNotifications.js +24 -0
- package/build/services/notifications/miniappNotifications.js.map +1 -0
- package/build/services/pages/pageEligibility.d.ts +1 -2
- package/build/services/pages/pageEligibility.d.ts.map +1 -1
- package/build/services/pages/pageEligibility.js +4 -2
- package/build/services/pages/pageEligibility.js.map +1 -1
- package/build/services/settingsBridge.d.ts.map +1 -1
- package/build/services/settingsBridge.js +0 -2
- package/build/services/settingsBridge.js.map +1 -1
- package/build/services/uiShim.d.ts.map +1 -1
- package/build/services/uiShim.js +14 -0
- package/build/services/uiShim.js.map +1 -1
- package/build/stores/appVisibility.d.ts.map +1 -1
- package/build/stores/appVisibility.js.map +1 -1
- package/build/stores/gallerySync.d.ts.map +1 -1
- package/build/stores/gallerySync.js.map +1 -1
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js +1 -0
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +27 -28
- 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 +4 -0
- package/build/types/capabilities/even-realities-g1.js.map +1 -1
- package/build/types/capabilities/nimo.d.ts.map +1 -1
- package/build/types/capabilities/nimo.js +21 -7
- package/build/types/capabilities/nimo.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/build/utils/display/index.d.ts +1 -0
- package/build/utils/display/index.d.ts.map +1 -1
- package/build/utils/display/index.js +2 -2
- package/build/utils/display/index.js.map +1 -1
- package/build/utils/display/profiles/index.d.ts +1 -0
- package/build/utils/display/profiles/index.d.ts.map +1 -1
- package/build/utils/display/profiles/index.js +1 -0
- package/build/utils/display/profiles/index.js.map +1 -1
- package/build/utils/display/profiles/nimo.d.ts +2 -0
- package/build/utils/display/profiles/nimo.d.ts.map +1 -0
- package/build/utils/display/profiles/nimo.js +3 -0
- package/build/utils/display/profiles/nimo.js.map +1 -0
- package/build/utils/display/scene/svgPath.js.map +1 -1
- package/build/utils/memory/search.d.ts.map +1 -1
- package/build/utils/memory/search.js +17 -7
- package/build/utils/memory/search.js.map +1 -1
- package/package.json +15 -15
- package/src/engine.ts +9 -7
- package/src/facades/display.ts +1 -1
- package/src/facades/glasses.ts +1 -1
- package/src/facades/permissions.ts +0 -2
- package/src/facades/phoneNotifications.ts +10 -0
- package/src/facades/providerKeys.ts +1 -1
- package/src/facades/reports.ts +41 -0
- package/src/facades/settings.ts +1 -1
- package/src/generated/releaseMetadata.ts +4 -4
- package/src/index.ts +2 -0
- package/src/react/useMentraLiveOta.ts +1 -4
- package/src/runtime/config.ts +1 -6
- package/src/services/AiTierResolution.ts +1 -1
- package/src/services/AppRegistry.ts +38 -1
- package/src/services/AutomaticReportResult.ts +1 -3
- package/src/services/CloudClientService.ts +17 -11
- package/src/services/DisplayProcessor.ts +2 -0
- package/src/services/GestureDotsService.ts +1 -1
- package/src/services/HotspotOtaTransport.ts +1 -5
- package/src/services/JSRouter.ts +2 -2
- package/src/services/LocalLlmModelManager.ts +4 -3
- package/src/services/LocalLlmProvider.ts +6 -7
- package/src/services/LocalMiniappRuntime.ts +170 -40
- package/src/services/LocalSttFallbackCoordinator.ts +1 -5
- package/src/services/NativeNavigationService.ts +1 -2
- package/src/services/NavigationHandlers.ts +2 -2
- package/src/services/NavigationService.ts +12 -11
- package/src/services/PairingIdentity.ts +1 -3
- package/src/services/PhoneCalendarService.ts +1 -3
- package/src/services/PhoneNotificationsSync.ts +2 -1
- package/src/services/PhoneStreamCoordinator.ts +1 -2
- package/src/services/ProviderKeyValidation.ts +1 -1
- package/src/services/STTModelManager.ts +1 -6
- package/src/services/SupportProfileSync.ts +2 -0
- package/src/services/TranscriptionRouting.ts +18 -0
- package/src/services/UIRouter.ts +3 -9
- package/src/services/controllers.ts +1 -0
- package/src/services/effectiveCapabilities.ts +3 -2
- package/src/services/gestureDotGlyphs.ts +10 -5
- package/src/services/manifestActions.ts +2 -1
- package/src/services/neuralBand/MetaAuth.ts +37 -8
- package/src/services/notificationAppPayload.ts +15 -0
- package/src/services/notifications/miniappNotifications.ts +53 -0
- package/src/services/pages/pageEligibility.ts +3 -2
- package/src/services/settingsBridge.ts +1 -4
- package/src/services/uiShim.ts +14 -0
- package/src/stores/appVisibility.ts +2 -5
- package/src/stores/gallerySync.ts +1 -7
- package/src/stores/glasses.ts +1 -0
- package/src/stores/settings.ts +27 -28
- package/src/types/capabilities/even-realities-g1.ts +5 -0
- package/src/types/capabilities/nimo.ts +23 -7
- package/src/types/enums.ts +1 -0
- package/src/utils/display/index.ts +2 -2
- package/src/utils/display/profiles/index.ts +2 -0
- package/src/utils/display/profiles/nimo.ts +2 -0
- package/src/utils/display/scene/svgPath.ts +1 -1
- package/src/utils/memory/search.ts +17 -7
|
@@ -83,6 +83,8 @@ function armDebounce(generation: number): void {
|
|
|
83
83
|
|
|
84
84
|
async function sendCurrentSnapshot(force: boolean, generation = syncGeneration): Promise<void> {
|
|
85
85
|
if (!unsubscribe || generation !== syncGeneration) return
|
|
86
|
+
// Every send would fail into the retry backoff with no client to send through.
|
|
87
|
+
if (cloudClientService.isCloudDisabled()) return
|
|
86
88
|
if (sendingGeneration === generation) {
|
|
87
89
|
const fingerprint = snapshotFingerprint(buildSnapshot())
|
|
88
90
|
if (fingerprint !== lastSentFingerprint) queuedFingerprint = fingerprint
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
import type {TranscriptionSubscription} from "@glassly/cloud-protocol"
|
|
2
|
+
|
|
3
|
+
/** Match a provider result to a logical miniapp subscription. */
|
|
4
|
+
export function matchesTranscriptionSubscription(
|
|
5
|
+
stream: string,
|
|
6
|
+
resolvedStream: string,
|
|
7
|
+
subscription: TranscriptionSubscription,
|
|
8
|
+
): boolean {
|
|
9
|
+
const prefix = "transcription:"
|
|
10
|
+
if (!stream.startsWith(prefix) || !resolvedStream.startsWith(prefix)) return false
|
|
11
|
+
if (subscription.language.mode === "specific") return stream === `${prefix}${subscription.language.code}`
|
|
12
|
+
if (stream === `${prefix}auto`) return true
|
|
13
|
+
// Providers detect ISO language codes; miniapps request regional BCP-47 tags.
|
|
14
|
+
// Unknown language stays auto-only until the recognizer identifies it.
|
|
15
|
+
const detected = resolvedStream.slice(prefix.length).split("-")[0].toLowerCase()
|
|
16
|
+
return detected !== "auto" && detected !== "" && stream.slice(prefix.length).split("-")[0].toLowerCase() === detected
|
|
17
|
+
}
|
|
18
|
+
|
|
1
19
|
export interface TranscriptionRouteSummary {
|
|
2
20
|
hasCloudSubscriber: boolean
|
|
3
21
|
hasForceLocalSubscriber: boolean
|
package/src/services/UIRouter.ts
CHANGED
|
@@ -193,9 +193,7 @@ export class UIRouter {
|
|
|
193
193
|
const cancel = {type: "cancel", requestId: uiSendPayload.requestId}
|
|
194
194
|
const literal = JSON.stringify(cancel)
|
|
195
195
|
const escaped = JSON.stringify(literal)
|
|
196
|
-
binding.inject(
|
|
197
|
-
`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`,
|
|
198
|
-
)
|
|
196
|
+
binding.inject(`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`)
|
|
199
197
|
return
|
|
200
198
|
}
|
|
201
199
|
const outbound: Record<string, unknown> = {
|
|
@@ -207,9 +205,7 @@ export class UIRouter {
|
|
|
207
205
|
if (typeof uiSendPayload.requestId === "string") outbound.requestId = uiSendPayload.requestId
|
|
208
206
|
const literal = JSON.stringify(outbound)
|
|
209
207
|
const escaped = JSON.stringify(literal)
|
|
210
|
-
binding.inject(
|
|
211
|
-
`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`,
|
|
212
|
-
)
|
|
208
|
+
binding.inject(`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`)
|
|
213
209
|
}
|
|
214
210
|
|
|
215
211
|
/**
|
|
@@ -222,9 +218,7 @@ export class UIRouter {
|
|
|
222
218
|
if (!binding) return
|
|
223
219
|
const literal = JSON.stringify(frame)
|
|
224
220
|
const escaped = JSON.stringify(literal)
|
|
225
|
-
binding.inject(
|
|
226
|
-
`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`,
|
|
227
|
-
)
|
|
221
|
+
binding.inject(`if (window.__host && window.__host.recv) window.__host.recv(JSON.parse(${escaped})); true;`)
|
|
228
222
|
}
|
|
229
223
|
|
|
230
224
|
/**
|
|
@@ -57,6 +57,7 @@ const INPUT_SURFACES: Readonly<Record<string, readonly InputSurface[]>> = {
|
|
|
57
57
|
[ControllerTypes.IINE_ANANKE]: [GAMEBRICK_SURFACE],
|
|
58
58
|
[ControllerTypes.ZERO2]: [GAMEBRICK_SURFACE],
|
|
59
59
|
[ControllerTypes.MICRO]: [GAMEBRICK_SURFACE],
|
|
60
|
+
[ControllerTypes.CRKD_ATOM_PLUS]: [GAMEBRICK_SURFACE],
|
|
60
61
|
[ControllerTypes.R1]: [],
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -61,8 +61,9 @@ function glassesCapabilities(model: DeviceTypes): Capabilities {
|
|
|
61
61
|
// Glassly CFW supports the complete vector contract, including SVG and rotation.
|
|
62
62
|
// Upstream g2flash publishes a raster canvas with sceneVector=false.
|
|
63
63
|
const vector = useGlassesStore.getState().sceneVector === true
|
|
64
|
-
// Depth is a disparity between the
|
|
65
|
-
|
|
64
|
+
// Depth is a disparity between the eyes: a lens per eye, or firmware that
|
|
65
|
+
// presents a separate raster to each eye's panel (MemoMind One CFW 22+).
|
|
66
|
+
const stereo = (base.display.count ?? 1) >= 2 || useGlassesStore.getState().sceneStereo === true
|
|
66
67
|
return {
|
|
67
68
|
...(volumeGate ? withVolumeGate(base) : base),
|
|
68
69
|
hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),
|
|
@@ -4,18 +4,23 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const GESTURE_DOT_GLYPH_SIZE = 24
|
|
6
6
|
|
|
7
|
-
export type GestureDotGlyph =
|
|
7
|
+
export type GestureDotGlyph =
|
|
8
|
+
"dot" | "double" | "ring" | "doubleRing" | "up" | "down" | "left" | "right" | "hold" | "holdRing" | "blank"
|
|
8
9
|
|
|
9
10
|
export const GESTURE_DOT_GLYPHS: Record<GestureDotGlyph, string> = {
|
|
10
11
|
dot: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAALklEQVR4nM2QsQ0AAAjC/P/pmrhqZ+xIF6DqBQyWbwO3gduAmKjQuj7QL/ETEzQmMptl7Q9NBAAAAABJRU5ErkJggg==",
|
|
11
|
-
double:
|
|
12
|
+
double:
|
|
13
|
+
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAG0lEQVR4nGNgGD7gPxjgYkN4yOyBlcDp3GEBAB3sd4lPaQxDAAAAAElFTkSuQmCC",
|
|
12
14
|
ring: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAANElEQVR4nGNgGBTgPxjgEseUgQphyCD4qDL/kSX+Y9WApoWqEjgsx+lc3B7EHSS4A3EgAAC+cEu1JTNREQAAAABJRU5ErkJggg==",
|
|
13
|
-
doubleRing:
|
|
15
|
+
doubleRing:
|
|
16
|
+
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAIklEQVR4nGNgGD7gPxjgYkN4yGwGmBA6SV0JHJbjdO6wAADBMlep8zl7TAAAAABJRU5ErkJggg==",
|
|
14
17
|
up: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAMElEQVR4nGNgoCv4////f1zi2GT+/4ciTHE4RhdHIlDFUUhkcTQKQx9WBw8PCVoBAIcbOcf3xeeBAAAAAElFTkSuQmCC",
|
|
15
18
|
down: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAALUlEQVR4nGNgoC/4/3+4SkBFUCkkNjKJIoMg0GRgGEMGgrC4CQSwuxanR2gEAO1/Ocfgp8uvAAAAAElFTkSuQmCC",
|
|
16
19
|
left: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAI0lEQVR4nGNgoCv4j0sch8RAiTP8RwPESOAwajDI4JKgGQAAKO05x7DqJxIAAAAASUVORK5CYII=",
|
|
17
|
-
right:
|
|
20
|
+
right:
|
|
21
|
+
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAIElEQVR4nGNgoDf4j1NiMMn8RwOEJYizZKDEcQc7bQAAS7w5x+ro2lsAAAAASUVORK5CYII=",
|
|
18
22
|
hold: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAR0lEQVR4nGNgIAj+gwF2UUy5//+xyyDzcLGRedjMhTEwXAKl0Z0Lof7DKbgMhBpQCZzOxedBHEGCMxAJBTXhKGRAA9hFMQEANr7jHQU9fO0AAAAASUVORK5CYII=",
|
|
19
|
-
holdRing:
|
|
23
|
+
holdRing:
|
|
24
|
+
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAQ0lEQVR4nGNgIAj+gwF2UUy5//+xyyDzcLGRedjMhTEwXAKlMd2IVQNMaEAlcDoXnwdxBAnOQCQU1ISjEIvjsYhiAgAo9KdZzePY3AAAAABJRU5ErkJggg==",
|
|
20
25
|
blank: "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAAAAADFHGIkAAAAD0lEQVR4nGNgGAWjgPoAAAJYAAGgxYwDAAAAAElFTkSuQmCC",
|
|
21
26
|
}
|
|
@@ -80,7 +80,8 @@ export function normalizeManifestActions(raw: unknown): DeclaredAction[] {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const WIDGET_SIZES: readonly DeclaredWidgetSize[] = ["XS", "XSM", "SM", "MD", "LG"]
|
|
83
|
-
const isWidgetSize = (value: unknown): value is DeclaredWidgetSize =>
|
|
83
|
+
const isWidgetSize = (value: unknown): value is DeclaredWidgetSize =>
|
|
84
|
+
(WIDGET_SIZES as readonly unknown[]).includes(value)
|
|
84
85
|
|
|
85
86
|
/**
|
|
86
87
|
* Normalize manifest widgets for installed and dev-sideloaded bundles: an
|
|
@@ -41,17 +41,45 @@ export function isLoginCallback(url: string): boolean {
|
|
|
41
41
|
return url.toLowerCase().startsWith("fb-viewapp:") || url.includes("frl_login")
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function queryPairs(url: string): [string, string][] {
|
|
45
|
+
const withoutFragment = url.includes("#") ? url.slice(0, url.indexOf("#")) : url
|
|
46
|
+
const query = withoutFragment.includes("?") ? withoutFragment.slice(withoutFragment.indexOf("?") + 1) : ""
|
|
47
|
+
const pairs: [string, string][] = []
|
|
48
|
+
for (const pair of query.split("&")) {
|
|
49
|
+
if (!pair) continue
|
|
50
|
+
const eq = pair.indexOf("=")
|
|
51
|
+
pairs.push(eq === -1 ? [pair, ""] : [pair.slice(0, eq), pair.slice(eq + 1)])
|
|
52
|
+
}
|
|
53
|
+
return pairs
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function decodeValue(value: string): string {
|
|
57
|
+
try {
|
|
58
|
+
return decodeURIComponent(value)
|
|
59
|
+
} catch {
|
|
60
|
+
return value
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
export function callbackQuery(url: string): {token?: string; blob?: string} {
|
|
45
|
-
const query = url.includes("?") ? url.slice(url.indexOf("?") + 1) : ""
|
|
46
65
|
const out: {token?: string; blob?: string} = {}
|
|
47
|
-
for (const
|
|
48
|
-
|
|
49
|
-
if (key === "
|
|
50
|
-
if (key === "blob") out.blob = decodeURIComponent(value)
|
|
66
|
+
for (const [key, value] of queryPairs(url)) {
|
|
67
|
+
if (key === "token") out.token = decodeValue(value)
|
|
68
|
+
if (key === "blob") out.blob = decodeValue(value)
|
|
51
69
|
}
|
|
52
70
|
return out
|
|
53
71
|
}
|
|
54
72
|
|
|
73
|
+
export function callbackShape(url: string): string {
|
|
74
|
+
const scheme = url.includes(":") ? url.slice(0, url.indexOf(":")) : "-"
|
|
75
|
+
const afterScheme = url.slice(scheme.length + 1)
|
|
76
|
+
const pathEnd = afterScheme.search(/[?#]/)
|
|
77
|
+
const path = pathEnd === -1 ? afterScheme : afterScheme.slice(0, pathEnd)
|
|
78
|
+
const keys = queryPairs(url).map(([key, value]) => `${key}:${value.length}`)
|
|
79
|
+
const fragment = url.includes("#") ? ` fragment=${url.slice(url.indexOf("#") + 1).length}` : ""
|
|
80
|
+
return `${scheme}:${path} query=[${keys.join(",")}]${fragment}`
|
|
81
|
+
}
|
|
82
|
+
|
|
55
83
|
/** The first 16 hex digits of the request token's SHA-256, which the callback must present. */
|
|
56
84
|
export function expectedCallbackToken(nativeSsoToken: string): string {
|
|
57
85
|
return sha256Hex(nativeSsoToken).slice(0, 16)
|
|
@@ -99,11 +127,12 @@ async function post(url: string, headers: Record<string, string>, body: string):
|
|
|
99
127
|
} catch {
|
|
100
128
|
json = null
|
|
101
129
|
}
|
|
130
|
+
const error = json?.error as {code?: number; error_subcode?: number} | null | undefined
|
|
102
131
|
if (response.status !== 200 || json === null) {
|
|
103
|
-
|
|
132
|
+
const code = error ? `, code ${error.code ?? "-"}` : ""
|
|
133
|
+
throw new MetaAuthError(`Meta sign-in failed (HTTP ${response.status}${code}). Try again.`)
|
|
104
134
|
}
|
|
105
|
-
if (
|
|
106
|
-
const error = json.error as {code?: number; error_subcode?: number} | null
|
|
135
|
+
if (error !== undefined) {
|
|
107
136
|
throw new MetaAuthError(
|
|
108
137
|
`Meta sign-in failed (code ${error?.code ?? "-"}, subcode ${error?.error_subcode ?? "-"}). Try again.`,
|
|
109
138
|
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Keep installed-app icons from exhausting the miniapp's 32 MiB JS heap.
|
|
2
|
+
* Icons are optional; clients already render a fallback when one is absent.
|
|
3
|
+
* Bound both individual icons and their total before JSON crosses the bridge.
|
|
4
|
+
*/
|
|
5
|
+
export function notificationAppPayload(
|
|
6
|
+
apps: Array<{packageName: string; appName: string; icon: string | null}>,
|
|
7
|
+
): Array<{packageName: string; appName: string; icon: string | null}> {
|
|
8
|
+
let remaining = 128 * 1024
|
|
9
|
+
return apps.map(({packageName, appName, icon}) => {
|
|
10
|
+
const size = icon?.length ?? 0
|
|
11
|
+
const keep = size <= 8 * 1024 && size <= remaining
|
|
12
|
+
if (keep) remaining -= size
|
|
13
|
+
return {packageName, appName, icon: keep ? icon : null}
|
|
14
|
+
})
|
|
15
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notifications miniapps post (`session.phone.notifications.post`) on their
|
|
3
|
+
* way into the host's phone-notification path. The runtime validates and
|
|
4
|
+
* emits here; the host subscribes through `engine.phoneNotifications` and
|
|
5
|
+
* feeds them to the same fan-out as the phone's own, with `system: false`
|
|
6
|
+
* and `source` set, so the dashboard, other readers and presentation treat
|
|
7
|
+
* them alike.
|
|
8
|
+
*/
|
|
9
|
+
export interface MiniappNotificationPosted {
|
|
10
|
+
type: "posted"
|
|
11
|
+
notificationId: string
|
|
12
|
+
source: string
|
|
13
|
+
app: string
|
|
14
|
+
title: string
|
|
15
|
+
content: string
|
|
16
|
+
timestamp: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MiniappNotificationDismissed {
|
|
20
|
+
type: "dismissed"
|
|
21
|
+
notificationId: string
|
|
22
|
+
source: string
|
|
23
|
+
timestamp: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type MiniappNotificationEvent = MiniappNotificationPosted | MiniappNotificationDismissed
|
|
27
|
+
|
|
28
|
+
export const MINIAPP_NOTIFICATION_TITLE_MAX = 200
|
|
29
|
+
export const MINIAPP_NOTIFICATION_CONTENT_MAX = 2_000
|
|
30
|
+
export const MINIAPP_NOTIFICATION_ID_MAX = 128
|
|
31
|
+
|
|
32
|
+
const listeners = new Set<(event: MiniappNotificationEvent) => void>()
|
|
33
|
+
|
|
34
|
+
export function miniappNotificationId(source: string, id: string): string {
|
|
35
|
+
return `${source}:${id}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function emitMiniappNotification(event: MiniappNotificationEvent): void {
|
|
39
|
+
for (const listener of listeners) {
|
|
40
|
+
try {
|
|
41
|
+
listener(event)
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.warn(`miniappNotifications: listener threw: ${(err as Error)?.message ?? err}`)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function onMiniappNotification(listener: (event: MiniappNotificationEvent) => void): () => void {
|
|
49
|
+
listeners.add(listener)
|
|
50
|
+
return () => {
|
|
51
|
+
listeners.delete(listener)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -21,11 +21,12 @@ export function isCfwFirmwareVersion(firmwareVersion: string | null | undefined)
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Only models that ship a Glassly custom firmware, and only while they run it.
|
|
24
|
-
* Stock firmware
|
|
25
|
-
* intentionally excluded.
|
|
24
|
+
* Stock firmware and every other model are intentionally excluded.
|
|
26
25
|
*/
|
|
27
26
|
export function isCustomDashboardEligible(info: {model?: string | null; firmwareVersion?: string | null}): boolean {
|
|
28
27
|
if (!info.model) return false
|
|
28
|
+
// The NIMO drivers draw no scenes through its CFW yet; the dashboard would land on the text page.
|
|
29
|
+
if (info.model === DeviceTypes.NIMO) return false
|
|
29
30
|
if (info.model === DeviceTypes.MEMOMIND_ONE) {
|
|
30
31
|
const match = /^GLASSLYCFW\/([0-9]+)$/.exec(info.firmwareVersion ?? "")
|
|
31
32
|
const revision = match ? Number(match[1]) : 0
|
|
@@ -23,7 +23,6 @@ export type SettingsBridgeAccess = "read" | "write"
|
|
|
23
23
|
export const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>> = {
|
|
24
24
|
// The dashboard's master switch is a phone-side debug knob.
|
|
25
25
|
custom_dashboard_enabled: "read",
|
|
26
|
-
contextual_dashboard: "write",
|
|
27
26
|
head_up_angle: "write",
|
|
28
27
|
// Where the glasses draw; each model's steps are its display.positionRange.
|
|
29
28
|
dashboard_depth: "write",
|
|
@@ -33,7 +32,6 @@ export const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>
|
|
|
33
32
|
auto_brightness: "write",
|
|
34
33
|
// Per paired model: the store resolves the indexed key.
|
|
35
34
|
preferred_mic: "write",
|
|
36
|
-
twelve_hour_time: "write",
|
|
37
35
|
menu_apps: "write",
|
|
38
36
|
notifications_enabled: "write",
|
|
39
37
|
notifications_popups: "write",
|
|
@@ -82,8 +80,7 @@ export function selectSettingsBridgeKeys(requested: unknown): string[] {
|
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
export type SettingsSetValidation =
|
|
85
|
-
|
|
86
|
-
| {ok: false; code: "INVALID_ARGUMENT" | "NOT_PERMITTED"; message: string}
|
|
83
|
+
{ok: true; key: string; value: unknown} | {ok: false; code: "INVALID_ARGUMENT" | "NOT_PERMITTED"; message: string}
|
|
87
84
|
|
|
88
85
|
/** A SETTINGS_SET payload: a writable key and a JSON value (undefined is not a value). */
|
|
89
86
|
export function validateSettingsSet(payload: Record<string, unknown>): SettingsSetValidation {
|
package/src/services/uiShim.ts
CHANGED
|
@@ -304,6 +304,13 @@ export function buildUiShim(options: UiShimOptions): string {
|
|
|
304
304
|
postEnvelope({ type: 'set_background', color: color });
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
var backHandler = null;
|
|
308
|
+
|
|
309
|
+
function setBackActionFn(handler) {
|
|
310
|
+
backHandler = typeof handler === 'function' ? handler : null;
|
|
311
|
+
postEnvelope({ type: 'set_back_action', shown: backHandler !== null });
|
|
312
|
+
}
|
|
313
|
+
|
|
307
314
|
function fireChannel(channel, payload) {
|
|
308
315
|
var list = channelHandlers[channel];
|
|
309
316
|
if (!list || list.length === 0) {
|
|
@@ -348,6 +355,12 @@ export function buildUiShim(options: UiShimOptions): string {
|
|
|
348
355
|
}
|
|
349
356
|
return;
|
|
350
357
|
}
|
|
358
|
+
if (type === 'back') {
|
|
359
|
+
if (backHandler) {
|
|
360
|
+
try { backHandler(); } catch (e) {}
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
351
364
|
if (type === 'cancel' && typeof envelope.requestId === 'string') {
|
|
352
365
|
// Background-side cancel (reserved; currently unused). If we had a
|
|
353
366
|
// future bidirectional RPC this would abort an in-flight UI handler.
|
|
@@ -366,6 +379,7 @@ export function buildUiShim(options: UiShimOptions): string {
|
|
|
366
379
|
onClose: onClose,
|
|
367
380
|
ready: readyFn,
|
|
368
381
|
setBackgroundColor: setBackgroundColorFn,
|
|
382
|
+
setBackAction: setBackActionFn,
|
|
369
383
|
/** @internal — packageName for diagnostic logs only. */
|
|
370
384
|
_packageName: packageName,
|
|
371
385
|
};
|
|
@@ -44,8 +44,5 @@ export const isGatedByDevTools = (packageName: string, devToolsEnabled: boolean)
|
|
|
44
44
|
export const PAIRED_GLASSES_PACKAGES: ReadonlySet<string> = new Set(["com.glassly.store"])
|
|
45
45
|
|
|
46
46
|
/** True when the app waits on a pairing that hasn't happened and dev tools are off. */
|
|
47
|
-
export const isGatedUntilPaired = (
|
|
48
|
-
packageName
|
|
49
|
-
devToolsEnabled: boolean,
|
|
50
|
-
hasPairedGlasses: boolean,
|
|
51
|
-
): boolean => !devToolsEnabled && !hasPairedGlasses && PAIRED_GLASSES_PACKAGES.has(packageName)
|
|
47
|
+
export const isGatedUntilPaired = (packageName: string, devToolsEnabled: boolean, hasPairedGlasses: boolean): boolean =>
|
|
48
|
+
!devToolsEnabled && !hasPairedGlasses && PAIRED_GLASSES_PACKAGES.has(packageName)
|
|
@@ -29,13 +29,7 @@ export interface PhotoInfo {
|
|
|
29
29
|
|
|
30
30
|
// Sync state machine states
|
|
31
31
|
export type SyncState =
|
|
32
|
-
| "
|
|
33
|
-
| "requesting_hotspot"
|
|
34
|
-
| "connecting_wifi"
|
|
35
|
-
| "syncing"
|
|
36
|
-
| "complete"
|
|
37
|
-
| "error"
|
|
38
|
-
| "cancelled"
|
|
32
|
+
"idle" | "requesting_hotspot" | "connecting_wifi" | "syncing" | "complete" | "error" | "cancelled"
|
|
39
33
|
|
|
40
34
|
export interface HotspotInfo {
|
|
41
35
|
ssid: string
|
package/src/stores/glasses.ts
CHANGED
package/src/stores/settings.ts
CHANGED
|
@@ -132,13 +132,6 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
132
132
|
saveOnServer: true,
|
|
133
133
|
persist: true,
|
|
134
134
|
},
|
|
135
|
-
memomind_accessory_picker: {
|
|
136
|
-
key: "memomind_accessory_picker",
|
|
137
|
-
defaultValue: () => false,
|
|
138
|
-
writable: true,
|
|
139
|
-
saveOnServer: false,
|
|
140
|
-
persist: true,
|
|
141
|
-
},
|
|
142
135
|
debug_console: {
|
|
143
136
|
key: "debug_console",
|
|
144
137
|
defaultValue: () => false,
|
|
@@ -333,7 +326,7 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
333
326
|
saveOnServer: false,
|
|
334
327
|
persist: true,
|
|
335
328
|
},
|
|
336
|
-
// User-supplied AI/voice provider keys (Settings > API Keys). Not consumed
|
|
329
|
+
// User-supplied AI/voice provider keys (Settings > Privacy > API Keys). Not consumed
|
|
337
330
|
// by the app itself — they exist so miniapps declaring the API_KEYS manifest
|
|
338
331
|
// permission can read them via session.keys.get() (see
|
|
339
332
|
// MINIAPP_READABLE_API_KEYS in LocalMiniappRuntime; mapbox is also readable
|
|
@@ -715,13 +708,6 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
715
708
|
saveOnServer: true,
|
|
716
709
|
persist: true,
|
|
717
710
|
},
|
|
718
|
-
twelve_hour_time: {
|
|
719
|
-
key: "twelve_hour_time",
|
|
720
|
-
defaultValue: () => true,
|
|
721
|
-
writable: true,
|
|
722
|
-
saveOnServer: true,
|
|
723
|
-
persist: true,
|
|
724
|
-
},
|
|
725
711
|
enforce_local_transcription: {
|
|
726
712
|
key: "enforce_local_transcription",
|
|
727
713
|
defaultValue: () => false,
|
|
@@ -762,20 +748,14 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
762
748
|
saveOnServer: true,
|
|
763
749
|
persist: true,
|
|
764
750
|
},
|
|
751
|
+
// Session-only: Position settings holds it on for stock G1, and a persisted
|
|
752
|
+
// true (app killed on that screen) blanks every glasses' display at boot.
|
|
765
753
|
screen_disabled: {
|
|
766
754
|
key: "screen_disabled",
|
|
767
755
|
defaultValue: () => false,
|
|
768
756
|
writable: true,
|
|
769
757
|
saveOnServer: false,
|
|
770
|
-
persist:
|
|
771
|
-
},
|
|
772
|
-
// glasses settings:
|
|
773
|
-
contextual_dashboard: {
|
|
774
|
-
key: "contextual_dashboard",
|
|
775
|
-
defaultValue: () => false,
|
|
776
|
-
writable: true,
|
|
777
|
-
saveOnServer: true,
|
|
778
|
-
persist: true,
|
|
758
|
+
persist: false,
|
|
779
759
|
},
|
|
780
760
|
// Debug escape hatch: G2 scene rendering through the Glassly CFW
|
|
781
761
|
// direct-framebuffer path (rasterized mode-3 frames into the carrier).
|
|
@@ -948,6 +928,13 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
948
928
|
saveOnServer: false,
|
|
949
929
|
persist: true,
|
|
950
930
|
},
|
|
931
|
+
debug_mirror_rail: {
|
|
932
|
+
key: "debug_mirror_rail",
|
|
933
|
+
defaultValue: () => false,
|
|
934
|
+
writable: true,
|
|
935
|
+
saveOnServer: false,
|
|
936
|
+
persist: true,
|
|
937
|
+
},
|
|
951
938
|
home_offline_card: {
|
|
952
939
|
key: "home_offline_card",
|
|
953
940
|
defaultValue: () => true,
|
|
@@ -955,6 +942,13 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
955
942
|
saveOnServer: false,
|
|
956
943
|
persist: true,
|
|
957
944
|
},
|
|
945
|
+
home_tips_seen: {
|
|
946
|
+
key: "home_tips_seen",
|
|
947
|
+
defaultValue: (): string[] => [],
|
|
948
|
+
writable: true,
|
|
949
|
+
saveOnServer: false,
|
|
950
|
+
persist: true,
|
|
951
|
+
},
|
|
958
952
|
// Gesture-dots debug indicator: flash a glyph on the glasses when a touch
|
|
959
953
|
// gesture is received while the display is empty. Works on any
|
|
960
954
|
// scene-capable device, not only CFW. Device-local debug knob, default off.
|
|
@@ -988,6 +982,13 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
988
982
|
saveOnServer: true,
|
|
989
983
|
persist: true,
|
|
990
984
|
},
|
|
985
|
+
dashboard_size: {
|
|
986
|
+
key: "dashboard_size",
|
|
987
|
+
defaultValue: () => 340,
|
|
988
|
+
writable: true,
|
|
989
|
+
saveOnServer: true,
|
|
990
|
+
persist: true,
|
|
991
|
+
},
|
|
991
992
|
menu_apps: {
|
|
992
993
|
key: "menu_apps",
|
|
993
994
|
defaultValue: () => null,
|
|
@@ -1252,22 +1253,20 @@ export const BLUETOOTH_SETTING_KEYS: string[] = [
|
|
|
1252
1253
|
SETTINGS.screen_disabled.key,
|
|
1253
1254
|
SETTINGS.auth_email.key,
|
|
1254
1255
|
SETTINGS.core_token.key,
|
|
1255
|
-
// glasses settings
|
|
1256
|
-
// no longer reads it):
|
|
1256
|
+
// glasses settings:
|
|
1257
1257
|
SETTINGS.head_up_angle.key,
|
|
1258
1258
|
SETTINGS.brightness.key,
|
|
1259
1259
|
SETTINGS.auto_brightness.key,
|
|
1260
1260
|
SETTINGS.dashboard_height.key,
|
|
1261
1261
|
SETTINGS.dashboard_depth.key,
|
|
1262
|
+
SETTINGS.dashboard_size.key,
|
|
1262
1263
|
SETTINGS.menu_apps.key,
|
|
1263
1264
|
SETTINGS.calendar_events.key,
|
|
1264
1265
|
SETTINGS.cfw_scene_renderer.key,
|
|
1265
1266
|
SETTINGS.cfw_fast_ble.key,
|
|
1266
1267
|
SETTINGS.perf_logs.key,
|
|
1267
|
-
SETTINGS.memomind_accessory_picker.key,
|
|
1268
1268
|
SETTINGS.ring_via_glasses.key,
|
|
1269
1269
|
SETTINGS.display_max_fps.key,
|
|
1270
|
-
SETTINGS.twelve_hour_time.key,
|
|
1271
1270
|
SETTINGS.metric_system.key,
|
|
1272
1271
|
// button:
|
|
1273
1272
|
SETTINGS.button_photo_size.key,
|
|
@@ -72,4 +72,9 @@ export const evenRealitiesG1: Capabilities = {
|
|
|
72
72
|
// The app flashes the bundled Glassly CFW over MCUmgr SMP (see
|
|
73
73
|
// bluetooth-sdk/ios/Source/Resources/G1Firmware/README.md).
|
|
74
74
|
hasCustomFirmware: true,
|
|
75
|
+
|
|
76
|
+
// On iOS the Glassly CFW (G1CFW/51) relays the notifications the phone shares
|
|
77
|
+
// with the glasses over ANCS; the dashboard presents them.
|
|
78
|
+
hasNativeNotifications: false,
|
|
79
|
+
hasNotificationRelay: true,
|
|
75
80
|
}
|
|
@@ -18,21 +18,34 @@ export const nimo: Capabilities = {
|
|
|
18
18
|
hasCamera: false,
|
|
19
19
|
camera: null,
|
|
20
20
|
|
|
21
|
-
// Display capabilities - monochrome binocular display
|
|
21
|
+
// Display capabilities - monochrome binocular display. Firmware 0.1.1.1+
|
|
22
|
+
// has a phone-drawn page (app 0xFD): a 500x220 canvas of positioned text,
|
|
23
|
+
// rectangles, circles, lines and bitmaps that the drivers render scene
|
|
24
|
+
// frames onto (NimoDynamicLayout.kt / .swift). Older firmware shows the
|
|
25
|
+
// frame's text on the ASR note page instead.
|
|
22
26
|
hasDisplay: true,
|
|
23
27
|
display: {
|
|
24
28
|
count: 2,
|
|
25
29
|
isColor: false,
|
|
26
30
|
color: "green",
|
|
27
31
|
canDisplayBitmap: true,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// text-display placeholder, not the bitmap target.
|
|
31
|
-
// TODO: placeholder - verify against hardware screen info
|
|
32
|
-
resolution: {width: 640, height: 400},
|
|
33
|
-
maxTextLines: 5,
|
|
32
|
+
resolution: {width: 540, height: 280},
|
|
33
|
+
maxTextLines: 10,
|
|
34
34
|
adjustBrightness: true,
|
|
35
35
|
positionRange: {height: {min: 0, max: 10}, depth: {min: 0, max: 10}},
|
|
36
|
+
|
|
37
|
+
// Scene display API - the Dynamic Layout canvas.
|
|
38
|
+
width: 500,
|
|
39
|
+
height: 220,
|
|
40
|
+
canPosition: true,
|
|
41
|
+
// One page holds 64 objects; the text pool is shared with rectangles.
|
|
42
|
+
maxTextElements: 32,
|
|
43
|
+
maxImageElements: 4,
|
|
44
|
+
maxImagePx: {width: 500, height: 220},
|
|
45
|
+
shapes: ["rect", "circle", "line"],
|
|
46
|
+
intensityLevels: 16,
|
|
47
|
+
// Every frame replaces the page; the firmware has no per-object update.
|
|
48
|
+
partialUpdate: false,
|
|
36
49
|
},
|
|
37
50
|
|
|
38
51
|
// Microphone capabilities - Nimo streams Opus audio from both arms
|
|
@@ -82,4 +95,7 @@ export const nimo: Capabilities = {
|
|
|
82
95
|
|
|
83
96
|
// OTA capabilities - Nimo firmware updates are not driven by the ASG OTA flow
|
|
84
97
|
hasOta: false,
|
|
98
|
+
|
|
99
|
+
// The drivers flash a bundled Glassly CFW over the GATT OTA characteristics.
|
|
100
|
+
hasCustomFirmware: true,
|
|
85
101
|
}
|
package/src/types/enums.ts
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
// =============================================================================
|
|
37
37
|
|
|
38
38
|
// Import for factory functions
|
|
39
|
-
import {G1_PROFILE_LEGACY, G2_PROFILE, Z100_PROFILE, NEX_PROFILE} from "./profiles"
|
|
39
|
+
import {G1_PROFILE_LEGACY, G2_PROFILE, Z100_PROFILE, NEX_PROFILE, G1_PROFILE} from "./profiles"
|
|
40
40
|
|
|
41
41
|
// =============================================================================
|
|
42
42
|
// Convenience factory functions
|
|
@@ -46,7 +46,6 @@ import {TextMeasurer} from "./measurer"
|
|
|
46
46
|
import {TextWrapper} from "./wrapper"
|
|
47
47
|
import {DisplayHelpers} from "./helpers"
|
|
48
48
|
import {ColumnComposer} from "./composer"
|
|
49
|
-
import {G1_PROFILE} from "./profiles"
|
|
50
49
|
import type {DisplayProfile} from "./profiles"
|
|
51
50
|
import type {WrapOptions} from "./wrapper"
|
|
52
51
|
|
|
@@ -276,3 +275,4 @@ export function createNexToolkit(): {
|
|
|
276
275
|
}
|
|
277
276
|
|
|
278
277
|
export {MEMOMIND_ONE_PROFILE} from "./profiles"
|
|
278
|
+
export {NIMO_PROFILE} from "./profiles"
|
|
@@ -128,7 +128,7 @@ export function compileSvgPath(d: string): {commands: number[]; edges: number} {
|
|
|
128
128
|
curve(a, depth + 1)
|
|
129
129
|
curve(b, depth + 1)
|
|
130
130
|
}
|
|
131
|
-
for (let i = 0; i < commands.length;
|
|
131
|
+
for (let i = 0; i < commands.length;) {
|
|
132
132
|
const op = commands[i++],
|
|
133
133
|
pairs = op === 4 ? 0 : op === 3 ? 3 : op === 2 ? 2 : 1
|
|
134
134
|
if (++count > 1024) throw new Error("SVG path exceeds 1024 commands")
|