@mentra/engine 3.2.0-dev.255 → 3.2.0-dev.261
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/facades/displayMirror.js +1 -1
- package/build/facades/displayMirror.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/DisplayProcessor.d.ts +1 -1
- package/build/services/DisplayProcessor.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts +0 -4
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +2 -7
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +7 -21
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +9 -93
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MentraJSRouter.d.ts +5 -9
- package/build/services/MentraJSRouter.d.ts.map +1 -1
- package/build/services/MentraJSRouter.js +5 -9
- package/build/services/MentraJSRouter.js.map +1 -1
- package/build/services/MentraUIRouter.d.ts +9 -12
- package/build/services/MentraUIRouter.d.ts.map +1 -1
- package/build/services/MentraUIRouter.js +9 -12
- package/build/services/MentraUIRouter.js.map +1 -1
- package/build/services/MiniappLauncher.d.ts +6 -9
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +6 -9
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/PhoneLocationService.d.ts +2 -2
- package/build/services/PhoneLocationService.d.ts.map +1 -1
- package/build/services/PhoneLocationService.js +5 -9
- package/build/services/PhoneLocationService.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts +1 -2
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js +1 -2
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/slimStreamStatus.d.ts +4 -1
- package/build/services/slimStreamStatus.d.ts.map +1 -1
- package/build/services/slimStreamStatus.js +4 -1
- package/build/services/slimStreamStatus.js.map +1 -1
- package/build/stores/apps.js +2 -2
- package/build/stores/apps.js.map +1 -1
- package/build/utils/miniappGlobals.d.ts +3 -5
- package/build/utils/miniappGlobals.d.ts.map +1 -1
- package/build/utils/miniappGlobals.js +3 -5
- package/build/utils/miniappGlobals.js.map +1 -1
- package/package.json +8 -8
- package/src/facades/displayMirror.ts +1 -1
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/services/DisplayProcessor.ts +1 -1
- package/src/services/LocalDisplayManager.ts +2 -7
- package/src/services/LocalMiniappRuntime.ts +9 -103
- package/src/services/MentraJSRouter.ts +5 -9
- package/src/services/MentraUIRouter.ts +9 -12
- package/src/services/MiniappLauncher.ts +6 -9
- package/src/services/PhoneLocationService.ts +5 -9
- package/src/services/PhoneStreamCoordinator.ts +1 -2
- package/src/services/slimStreamStatus.ts +4 -1
- package/src/stores/apps.ts +2 -2
- package/src/utils/miniappGlobals.ts +3 -5
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* glasses display mirror — the typed read facade over the (now engine-owned)
|
|
3
3
|
* display store, for a phone-side preview of the glasses screen
|
|
4
4
|
* (`engine.display.mirror`). The store is fed by the display paths
|
|
5
|
-
* (LocalDisplayManager +
|
|
5
|
+
* (LocalDisplayManager + SceneRenderer) via `setDisplayEvent`; this facade is the
|
|
6
6
|
* read side: `current()` (snapshot) + `onMirror(cb)` (subscribe). The raw store is
|
|
7
7
|
* also exposed as `engine.displayStore` (the Mentra-app escape hatch).
|
|
8
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"displayMirror.js","sourceRoot":"","sources":["../../src/facades/displayMirror.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AAIjD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,0EAA0E;IAC1E,OAAO;QACL,+EAA+E;QAC/E,OAAO,EAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAC,CAAA;IACrD,CAAC;IAED,mFAAmF;IACnF,QAAQ,CAAC,EAAuC;QAC9C,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED,kEAAkE;IAClE,IAAI;QACF,OAAO,eAAe,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAA;IACxC,CAAC;IACD,oEAAoE;IACpE,OAAO,CAAC,IAA0B;QAChC,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,CAAC;CACF,CAAA","sourcesContent":["/**\n * glasses display mirror — the typed read facade over the (now engine-owned)\n * display store, for a phone-side preview of the glasses screen\n * (`engine.display.mirror`). The store is fed by the display paths\n * (LocalDisplayManager +
|
|
1
|
+
{"version":3,"file":"displayMirror.js","sourceRoot":"","sources":["../../src/facades/displayMirror.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AAIjD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,0EAA0E;IAC1E,OAAO;QACL,+EAA+E;QAC/E,OAAO,EAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAC,CAAA;IACrD,CAAC;IAED,mFAAmF;IACnF,QAAQ,CAAC,EAAuC;QAC9C,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED,kEAAkE;IAClE,IAAI;QACF,OAAO,eAAe,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAA;IACxC,CAAC;IACD,oEAAoE;IACpE,OAAO,CAAC,IAA0B;QAChC,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,CAAC;CACF,CAAA","sourcesContent":["/**\n * glasses display mirror — the typed read facade over the (now engine-owned)\n * display store, for a phone-side preview of the glasses screen\n * (`engine.display.mirror`). The store is fed by the display paths\n * (LocalDisplayManager + SceneRenderer) via `setDisplayEvent`; this facade is the\n * read side: `current()` (snapshot) + `onMirror(cb)` (subscribe). The raw store is\n * also exposed as `engine.displayStore` (the Mentra-app escape hatch).\n */\nimport {useDisplayStore} from \"../stores/display\"\n\nexport type DisplayMirrorEvent = Record<string, unknown> & {view?: string; layout?: any}\n\nexport const displayMirror = {\n /** The display event currently shown on the previewed view (snapshot). */\n current(): DisplayMirrorEvent {\n // Copy: the snapshot must not hand callers a mutable reference into the store.\n return {...useDisplayStore.getState().currentEvent}\n },\n\n /** Subscribe to changes of the previewed display event; returns an unsubscribe. */\n onMirror(cb: (event: DisplayMirrorEvent) => void): () => void {\n return useDisplayStore.subscribe((s) => s.currentEvent, cb)\n },\n\n /** Which view is previewed — the main screen or the dashboard. */\n view(): string {\n return useDisplayStore.getState().view\n },\n /** Switch the previewed view between `\"main\"` and `\"dashboard\"`. */\n setView(view: \"main\" | \"dashboard\"): void {\n useDisplayStore.getState().setView(view)\n },\n}\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const ENGINE_RELEASE_METADATA = Object.freeze({
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"familyBaseVersion": "3.2.0",
|
|
4
|
-
"releaseIdentity": "3.2.0-dev.
|
|
5
|
-
"releaseSetId": "mentra-3.2.0-dev.
|
|
6
|
-
"sourceCommit": "
|
|
7
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
8
|
-
"otaManifestSha256": "
|
|
4
|
+
"releaseIdentity": "3.2.0-dev.261",
|
|
5
|
+
"releaseSetId": "mentra-3.2.0-dev.261",
|
|
6
|
+
"sourceCommit": "ebd405ff0f311035faccac6e19b0a5a38b3a1483",
|
|
7
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.261.json",
|
|
8
|
+
"otaManifestSha256": "e9d88a258f276ee9ddc5396e9c5745f91f924449b802248e6d37dfe9bb8bb3ee"
|
|
9
9
|
});
|
|
10
10
|
//# sourceMappingURL=releaseMetadata.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.
|
|
1
|
+
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.261\",\n \"releaseSetId\": \"mentra-3.2.0-dev.261\",\n \"sourceCommit\": \"ebd405ff0f311035faccac6e19b0a5a38b3a1483\",\n \"otaManifestUrl\": \"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.261.json\",\n \"otaManifestSha256\": \"e9d88a258f276ee9ddc5396e9c5745f91f924449b802248e6d37dfe9bb8bb3ee\"\n})\n"]}
|
|
@@ -22,7 +22,7 @@ export type DeviceModel = "g1" | "g2" | "z100" | "nex" | "mach1" | "mentra-live"
|
|
|
22
22
|
*/
|
|
23
23
|
export type DisplayLayoutType = "text_wall" | "text_line" | "text_rows" | "reference_card" | "double_text_wall" | "bitmap_view";
|
|
24
24
|
/**
|
|
25
|
-
* Raw display event
|
|
25
|
+
* Raw local display event before device-specific processing
|
|
26
26
|
*/
|
|
27
27
|
export interface DisplayEvent {
|
|
28
28
|
view: "main" | "dashboard";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DisplayProcessor.js","sourceRoot":"","sources":["../../src/services/DisplayProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DisplayProcessor\n *\n * Processes display events with pixel-accurate text wrapping using the same\n * Engine display utilities. This ensures the mobile preview matches exactly\n * what is shown on the glasses.\n *\n * Key responsibilities:\n * 1. Intercept display events before sending to native SGC\n * 2. Wrap text using the correct device profile (G1, etc.)\n * 3. Ensure GlassesDisplayMirror shows exactly what glasses will show\n *\n * @see notes/miniapp-display-api-redesign.md for the current display design\n */\n\nimport {\n createDisplayToolkit,\n G1_PROFILE,\n G2_PROFILE,\n Z100_PROFILE,\n NEX_PROFILE,\n TextMeasurer,\n TextWrapper,\n ColumnComposer,\n type DisplayProfile,\n type WrapOptions,\n type BreakMode,\n} from \"../utils/display\"\n\nimport {ISLAND_SETTINGS_KEYS} from \"../runtime/config\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Supported device models for display processing\n */\nexport type DeviceModel = \"g1\" | \"g2\" | \"z100\" | \"nex\" | \"mach1\" | \"mentra-live\" | \"simulated\" | \"unknown\"\n\n/**\n * Display event types that we process\n */\nexport type DisplayLayoutType =\n | \"text_wall\"\n | \"text_line\"\n | \"text_rows\"\n | \"reference_card\"\n | \"double_text_wall\"\n | \"bitmap_view\"\n\n/**\n * Raw display event from the cloud/WebSocket\n */\nexport interface DisplayEvent {\n view: \"main\" | \"dashboard\"\n layoutType?: DisplayLayoutType\n layout?: {\n layoutType: DisplayLayoutType\n text?: string | string[] // string for text_wall/text_line, string[] for text_rows\n title?: string\n topText?: string\n bottomText?: string\n breakMode?: BreakMode\n data?: string // For bitmap_view\n x?: number // bitmap_view container position/size (forwarded to native, used by G2)\n y?: number\n width?: number\n height?: number\n [key: string]: unknown\n }\n text?: string | string[]\n title?: string\n topText?: string\n bottomText?: string\n breakMode?: BreakMode\n [key: string]: unknown\n}\n\n/**\n * Processed display event with guaranteed wrapped text\n */\nexport interface ProcessedDisplayEvent extends DisplayEvent {\n /** Marks this event as processed */\n _processed: true\n /** The device profile used for processing */\n _profile: string\n /** Pre-split lines for text_wall/text_line (for easy rendering) */\n _lines?: string[]\n /** Original layout type before processing (e.g., double_text_wall -> text_wall) */\n _originalLayoutType?: DisplayLayoutType\n /** Column composition metadata for double_text_wall */\n _composedColumns?: {\n leftLines: string[]\n rightLines: string[]\n config: {\n leftColumnWidthPx: number\n rightColumnStartPx: number\n rightColumnWidthPx: number\n maxLines: number\n leftMarginSpaces?: number\n }\n }\n}\n\n/**\n * Options for the DisplayProcessor\n */\nexport interface DisplayProcessorOptions {\n /** Default break mode for text wrapping */\n breakMode?: BreakMode\n /** Whether to log processing details */\n debug?: boolean\n}\n\n// =============================================================================\n// Placeholder Replacement\n// =============================================================================\n\n/**\n * Placeholder values that can be replaced in display text.\n * These match the placeholders used in CoreManager.kt/CoreManager.swift\n */\ninterface PlaceholderValues {\n /** Current time in 12-hour format (e.g., \"2:30 PM\") */\n TIME12: string\n /** Current time in 24-hour format (e.g., \"14:30\") */\n TIME24: string\n /** Current date (e.g., \"1/22\") */\n DATE: string\n /** Glasses battery level (e.g., \"85%\" or \"\" if unknown) */\n GBATT: string\n /** Connection status */\n CONNECTION_STATUS: string\n}\n\n/**\n * Get current placeholder values from device state\n */\nfunction getPlaceholderValues(): PlaceholderValues {\n const now = new Date()\n\n // Format time in 12-hour format\n const hours12 = now.getHours() % 12 || 12\n const minutes = now.getMinutes().toString().padStart(2, \"0\")\n const ampm = now.getHours() >= 12 ? \"PM\" : \"AM\"\n const TIME12 = `${hours12}:${minutes} ${ampm}`\n\n // Format time in 24-hour format\n const hours24 = now.getHours().toString().padStart(2, \"0\")\n const TIME24 = `${hours24}:${minutes}`\n\n // Format date\n const month = now.getMonth() + 1\n const day = now.getDate()\n const DATE = `${month}/${day}`\n\n // Battery + connection read from the engine glasses store directly (was a host\n // glassesStatus hook).\n const batteryLevel = useGlassesStore.getState().batteryLevel ?? -1\n const GBATT = batteryLevel === -1 ? \"\" : `${batteryLevel}%`\n\n // Connection status\n const connected = isGlassesConnected(useGlassesStore.getState().connection)\n const CONNECTION_STATUS = connected ? \"Connected\" : \"\"\n\n return {TIME12, TIME24, DATE, GBATT, CONNECTION_STATUS}\n}\n\n/**\n * Replace placeholders in text with actual values.\n * Placeholders are in format $NAME$ (e.g., $GBATT$, $TIME12$)\n *\n * @param text - Text containing placeholders\n * @returns Text with placeholders replaced\n */\nfunction replacePlaceholders(text: string): string {\n if (!text || !text.includes(\"$\")) {\n return text\n }\n\n const values = getPlaceholderValues()\n\n return text\n .replace(/\\$TIME12\\$/g, values.TIME12)\n .replace(/\\$TIME24\\$/g, values.TIME24)\n .replace(/\\$DATE\\$/g, values.DATE)\n .replace(/\\$GBATT\\$/g, values.GBATT)\n .replace(/\\$CONNECTION_STATUS\\$/g, values.CONNECTION_STATUS)\n .replace(/\\$no_datetime\\$/g, `${values.DATE}, ${values.TIME12}`)\n}\n\nfunction isBreakMode(value: unknown): value is BreakMode {\n return value === \"character\" || value === \"character-no-hyphen\" || value === \"word\" || value === \"strict-word\"\n}\n\n// =============================================================================\n// Device Profile Mapping\n// =============================================================================\n\n/**\n * Map device model names to display profiles\n */\nconst DEVICE_PROFILES: Record<DeviceModel, DisplayProfile> = {\n \"g1\": G1_PROFILE,\n \"g2\": G2_PROFILE,\n \"z100\": Z100_PROFILE,\n \"nex\": NEX_PROFILE,\n \"mach1\": Z100_PROFILE, // Mach1 uses same hardware as Vuzix Z100\n \"mentra-live\": G1_PROFILE, // Mentra Live has no display, uses G1 as fallback\n \"simulated\": G1_PROFILE, // Simulated uses G1 profile\n \"unknown\": G1_PROFILE, // Default to G1\n}\n\n/**\n * Normalize various model name strings to our DeviceModel type\n */\nfunction normalizeModelName(modelName: string | null | undefined): DeviceModel {\n if (!modelName) return \"unknown\"\n\n const lower = modelName.toLowerCase()\n\n if (lower.includes(\"g2\") || lower.includes(\"even realities g2\")) {\n return \"g2\"\n }\n if (lower.includes(\"g1\") || lower.includes(\"even realities\")) {\n return \"g1\"\n }\n if (lower.includes(\"z100\") || lower.includes(\"vuzix\")) {\n return \"z100\"\n }\n if (lower.includes(\"nex\") || lower.includes(\"mentra display\")) {\n return \"nex\"\n }\n if (lower.includes(\"mach1\") || lower.includes(\"mach 1\")) {\n return \"mach1\"\n }\n if (lower.includes(\"mentra live\") || lower.includes(\"mentra-live\")) {\n return \"mentra-live\"\n }\n if (lower.includes(\"simulated\") || lower.includes(\"simulator\")) {\n return \"simulated\"\n }\n\n return \"unknown\"\n}\n\n// =============================================================================\n// DisplayProcessor Class\n// =============================================================================\n\n/**\n * Processes display events with pixel-accurate text wrapping.\n *\n * Usage:\n * ```typescript\n * const processor = DisplayProcessor.getInstance()\n *\n * // When glasses connect\n * processor.setDeviceModel(\"Even Realities G1\")\n *\n * // Process display events\n * const processed = processor.processDisplayEvent(rawEvent)\n * ```\n */\nexport class DisplayProcessor {\n private static instance: DisplayProcessor | null = null\n\n private measurer: TextMeasurer\n private wrapper: TextWrapper\n private composer: ColumnComposer\n private profile: DisplayProfile\n private deviceModel: DeviceModel = \"unknown\"\n private options: DisplayProcessorOptions\n\n private runtimeAttached = false\n private unsubscribeGlassesStatus: (() => void) | null = null\n\n private constructor(options: DisplayProcessorOptions = {}) {\n this.options = {\n breakMode: \"character-no-hyphen\",\n debug: false,\n ...options,\n }\n\n // Initialize with default G1 profile\n const engine = createDisplayToolkit(G1_PROFILE, {\n breakMode: this.options.breakMode,\n })\n\n this.measurer = engine.measurer\n this.wrapper = engine.wrapper\n this.composer = new ColumnComposer(engine.profile, this.options.breakMode)\n this.profile = engine.profile\n\n // Engine stores may not be hydrated yet at module-load time; attachToRuntime()\n // is called again by the host after app services start.\n this.attachToRuntime()\n }\n\n /**\n * Read the current default wearable and subscribe to engine glasses-status changes.\n * Idempotent — safe to call again after app services start.\n */\n public attachToRuntime(): void {\n if (this.runtimeAttached) {\n return\n }\n\n // Seed the device profile from the saved default wearable, read off the engine\n // settings store directly (was a host settings hook).\n const defaultWearable = useSettingsStore.getState().getSetting(ISLAND_SETTINGS_KEYS.defaultWearable) as\n | string\n | undefined\n if (defaultWearable) {\n this.setDeviceModel(defaultWearable)\n console.log(`DISPLAY_PROCESSOR: Initialized DisplayProcessor with default wearable: ${defaultWearable}`)\n }\n\n // Track device-model changes off the engine glasses store directly (was a host\n // subscribeGlassesStatus hook).\n this.unsubscribeGlassesStatus = useGlassesStore.subscribe(\n (s) => s.deviceModel,\n (deviceModel) => {\n if (deviceModel) this.setDeviceModel(String(deviceModel))\n },\n )\n\n this.runtimeAttached = true\n }\n\n /** Detach runtime subscriptions installed by attachToRuntime(). */\n public detachFromRuntime(): void {\n this.unsubscribeGlassesStatus?.()\n this.unsubscribeGlassesStatus = null\n this.runtimeAttached = false\n }\n\n /**\n * Get the singleton instance\n */\n public static getInstance(options?: DisplayProcessorOptions): DisplayProcessor {\n if (!DisplayProcessor.instance) {\n DisplayProcessor.instance = new DisplayProcessor(options)\n }\n return DisplayProcessor.instance\n }\n\n /**\n * Reset the singleton (useful for testing)\n */\n public static resetInstance(): void {\n DisplayProcessor.instance?.detachFromRuntime()\n DisplayProcessor.instance = null\n }\n\n // ===========================================================================\n // Device Profile Management\n // ===========================================================================\n\n /**\n * Set the device model and update the display profile accordingly.\n * Call this when glasses connect.\n *\n * @param modelName - The model name from the connected glasses (e.g., \"Even Realities G1\")\n */\n public setDeviceModel(modelName: string | null | undefined): void {\n const normalizedModel = normalizeModelName(modelName)\n\n if (normalizedModel === this.deviceModel) {\n return // No change needed\n }\n\n this.deviceModel = normalizedModel\n const newProfile = DEVICE_PROFILES[normalizedModel]\n\n if (newProfile !== this.profile) {\n this.updateProfile(newProfile)\n }\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Device model set to: ${normalizedModel} (from: ${modelName})`)\n }\n }\n\n /**\n * Update the display profile and recreate engine\n */\n private updateProfile(newProfile: DisplayProfile): void {\n const engine = createDisplayToolkit(newProfile, {\n breakMode: this.options.breakMode,\n })\n\n this.measurer = engine.measurer\n this.wrapper = engine.wrapper\n this.composer = new ColumnComposer(newProfile, this.options.breakMode)\n this.profile = engine.profile\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Profile updated to: ${newProfile.id}`)\n }\n }\n\n /**\n * Get the current device model\n */\n public getDeviceModel(): DeviceModel {\n return this.deviceModel\n }\n\n /**\n * Get the current display profile\n */\n public getProfile(): DisplayProfile {\n return this.profile\n }\n\n // ===========================================================================\n // Display Event Processing\n // ===========================================================================\n\n /**\n * Process a display event, wrapping text as needed.\n *\n * @param event - Raw display event from WebSocket\n * @returns Processed event with wrapped text\n */\n public processDisplayEvent(event: DisplayEvent): ProcessedDisplayEvent {\n // Already processed? Return as-is\n if ((event as ProcessedDisplayEvent)._processed) {\n return event as ProcessedDisplayEvent\n }\n\n // Get layout type from either root or nested layout object\n const layoutType = event.layoutType || event.layout?.layoutType\n const layout = event.layout || event\n\n if (!layoutType) {\n // No layout type - pass through\n return {\n ...event,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n\n switch (layoutType) {\n case \"text_wall\":\n case \"text_line\":\n return this.processTextWall(event, layout)\n\n case \"text_rows\":\n return this.processTextRows(event, layout)\n\n case \"reference_card\":\n return this.processReferenceCard(event, layout)\n\n case \"double_text_wall\":\n return this.processDoubleTextWall(event, layout)\n\n case \"bitmap_view\":\n // Bitmap views don't need text processing\n return {\n ...event,\n _processed: true,\n _profile: this.profile.id,\n }\n\n default:\n // Unknown layout type - pass through\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Unknown layout type: ${layoutType}`)\n }\n return {\n ...event,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n }\n\n /**\n * Process text_wall or text_line layout\n */\n private processTextWall(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n // text_wall/text_line always have string text, not string[]\n const rawText = layout.text\n const text = typeof rawText === \"string\" ? rawText : \"\"\n\n // Replace placeholders BEFORE wrapping (so we measure actual content)\n const textWithPlaceholders = replacePlaceholders(text)\n const breakMode = this.getEventBreakMode(event, layout)\n\n // Wrap the text\n const lines = this.wrapText(textWithPlaceholders, breakMode ? {breakMode} : undefined)\n const wrappedText = lines.join(\"\\n\")\n\n // Update both root and nested layout if present\n const processedLayout = event.layout\n ? {\n ...event.layout,\n text: wrappedText,\n }\n : undefined\n\n return {\n ...event,\n // Store original text with placeholders for debugging\n _originalText: text,\n text: wrappedText,\n layout: processedLayout,\n _processed: true,\n _profile: this.profile.id,\n _lines: lines,\n }\n }\n\n /**\n * Process text_rows layout\n */\n private processTextRows(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n const textField = layout.text\n const rows: string[] = Array.isArray(textField) ? textField : []\n\n // Replace placeholders and wrap each row\n const breakMode = this.getEventBreakMode(event, layout)\n const wrappedRows = rows.map((row: string) => {\n const rowWithPlaceholders = replacePlaceholders(row)\n const lines = this.wrapText(rowWithPlaceholders, breakMode ? {breakMode} : undefined)\n return lines.join(\"\\n\")\n })\n\n const processedLayout = event.layout\n ? {\n ...event.layout,\n text: wrappedRows as string | string[],\n }\n : undefined\n\n return {\n ...event,\n text: wrappedRows as string | string[],\n layout: processedLayout,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n\n /**\n * Process reference_card layout\n */\n private processReferenceCard(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n const title = layout.title || \"\"\n // reference_card text is always string, not string[]\n const rawText = layout.text\n const text = typeof rawText === \"string\" ? rawText : \"\"\n\n // Replace placeholders BEFORE wrapping\n const titleWithPlaceholders = replacePlaceholders(title)\n const textWithPlaceholders = replacePlaceholders(text)\n const breakMode = this.getEventBreakMode(event, layout)\n\n // Wrap title and text separately\n // Title typically gets 1 line, text gets remaining lines\n const wrappedTitle = this.wrapText(titleWithPlaceholders, {maxLines: 1, ...(breakMode ? {breakMode} : {})})\n const wrappedText = this.wrapText(textWithPlaceholders, {\n maxLines: this.profile.maxLines - 1,\n ...(breakMode ? {breakMode} : {}),\n })\n\n const processedLayout = event.layout\n ? {\n ...event.layout,\n title: wrappedTitle.join(\"\\n\"),\n text: wrappedText.join(\"\\n\"),\n }\n : undefined\n\n return {\n ...event,\n title: wrappedTitle.join(\"\\n\"),\n text: wrappedText.join(\"\\n\"),\n layout: processedLayout,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n\n /**\n * Process double_text_wall layout\n *\n * Uses ColumnComposer to create a fully composed string with both columns\n * merged line-by-line with pixel-precise space alignment. This is the single\n * source of truth for double_text_wall composition, replacing duplicate\n * implementations in native iOS (G1Text.swift) and Android (G1Text.kt).\n *\n * The composed text is sent to native as a regular text_wall - native just\n * chunks and sends it without any re-wrapping or column composition.\n */\n private processDoubleTextWall(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n const leftText = layout.topText || \"\"\n const rightText = layout.bottomText || \"\"\n\n // Replace placeholders BEFORE composition (so we measure actual content)\n const leftTextWithPlaceholders = replacePlaceholders(leftText)\n const rightTextWithPlaceholders = replacePlaceholders(rightText)\n const breakMode = this.getEventBreakMode(event, layout)\n const composer =\n breakMode && breakMode !== this.options.breakMode ? new ColumnComposer(this.profile, breakMode) : this.composer\n\n // Use ColumnComposer for pixel-precise column composition\n const result = composer.composeDoubleTextWall(leftTextWithPlaceholders, rightTextWithPlaceholders)\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] double_text_wall composed:`)\n console.log(` Left lines: ${result.leftLines.length}`)\n console.log(` Right lines: ${result.rightLines.length}`)\n console.log(` Config: ${JSON.stringify(result.config)}`)\n }\n\n // The composed text is a single string with both columns merged\n // Native will receive this as a text_wall and just chunk & send it\n const processedLayout = event.layout\n ? {\n ...event.layout,\n // Keep original topText/bottomText for reference but add composed text\n topText: leftTextWithPlaceholders,\n bottomText: rightTextWithPlaceholders,\n // The composed text is what native should actually display\n _composedText: result.composedText,\n }\n : undefined\n\n return {\n ...event,\n // Override the layout type to text_wall since we've pre-composed the columns\n // This tells native to use sendTextWall instead of sendDoubleTextWall\n layoutType: \"text_wall\" as DisplayLayoutType,\n text: result.composedText,\n // Keep original values for debugging/preview (with placeholders replaced)\n topText: leftTextWithPlaceholders,\n bottomText: rightTextWithPlaceholders,\n // Store original values with placeholders for debugging\n _originalTopText: leftText,\n _originalBottomText: rightText,\n layout: processedLayout\n ? {\n ...processedLayout,\n layoutType: \"text_wall\" as DisplayLayoutType,\n text: result.composedText,\n }\n : undefined,\n _processed: true,\n _profile: this.profile.id,\n _originalLayoutType: \"double_text_wall\",\n _composedColumns: {\n leftLines: result.leftLines,\n rightLines: result.rightLines,\n config: result.config,\n },\n }\n }\n\n // ===========================================================================\n // Text Wrapping Utilities\n // ===========================================================================\n\n /**\n * Wrap text using the current profile\n *\n * @param text - Text to wrap\n * @param options - Optional override options\n * @returns Array of wrapped lines\n */\n public wrapText(text: string, options?: WrapOptions): string[] {\n if (!text) return [\"\"]\n\n const result = this.wrapper.wrap(text, options)\n return result.lines\n }\n\n private getEventBreakMode(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): BreakMode | undefined {\n const breakMode = layout.breakMode ?? event.breakMode\n return isBreakMode(breakMode) ? breakMode : undefined\n }\n\n /**\n * Measure the pixel width of text\n *\n * @param text - Text to measure\n * @returns Width in pixels\n */\n public measureText(text: string): number {\n return this.measurer.measureText(text)\n }\n\n /**\n * Check if text fits on a single line\n *\n * @param text - Text to check\n * @returns true if text fits without wrapping\n */\n public fitsOnSingleLine(text: string): boolean {\n return this.measurer.measureText(text) <= this.profile.displayWidthPx\n }\n\n /**\n * Get the display width in pixels\n */\n public getDisplayWidth(): number {\n return this.profile.displayWidthPx\n }\n\n /**\n * Get the maximum number of lines\n */\n public getMaxLines(): number {\n return this.profile.maxLines\n }\n\n /**\n * Get detailed text measurement\n */\n public measureTextDetailed(text: string) {\n return this.measurer.measureTextDetailed(text)\n }\n\n // ===========================================================================\n // Break Mode Control\n // ===========================================================================\n\n /**\n * Set the break mode for text wrapping\n *\n * @param breakMode - 'character' | 'word' | 'strict-word'\n */\n public setBreakMode(breakMode: BreakMode): void {\n if (this.options.breakMode === breakMode) return\n\n this.options.breakMode = breakMode\n this.updateProfile(this.profile) // Recreate wrapper with new break mode\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Break mode set to: ${breakMode}`)\n }\n }\n\n /**\n * Get the current break mode\n */\n public getBreakMode(): BreakMode {\n return this.options.breakMode || \"character\"\n }\n}\n\nexport const displayProcessor = DisplayProcessor.getInstance()\nexport default displayProcessor\n"]}
|
|
1
|
+
{"version":3,"file":"DisplayProcessor.js","sourceRoot":"","sources":["../../src/services/DisplayProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DisplayProcessor\n *\n * Processes display events with pixel-accurate text wrapping using the same\n * Engine display utilities. This ensures the mobile preview matches exactly\n * what is shown on the glasses.\n *\n * Key responsibilities:\n * 1. Intercept display events before sending to native SGC\n * 2. Wrap text using the correct device profile (G1, etc.)\n * 3. Ensure GlassesDisplayMirror shows exactly what glasses will show\n *\n * @see notes/miniapp-display-api-redesign.md for the current display design\n */\n\nimport {\n createDisplayToolkit,\n G1_PROFILE,\n G2_PROFILE,\n Z100_PROFILE,\n NEX_PROFILE,\n TextMeasurer,\n TextWrapper,\n ColumnComposer,\n type DisplayProfile,\n type WrapOptions,\n type BreakMode,\n} from \"../utils/display\"\n\nimport {ISLAND_SETTINGS_KEYS} from \"../runtime/config\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Supported device models for display processing\n */\nexport type DeviceModel = \"g1\" | \"g2\" | \"z100\" | \"nex\" | \"mach1\" | \"mentra-live\" | \"simulated\" | \"unknown\"\n\n/**\n * Display event types that we process\n */\nexport type DisplayLayoutType =\n | \"text_wall\"\n | \"text_line\"\n | \"text_rows\"\n | \"reference_card\"\n | \"double_text_wall\"\n | \"bitmap_view\"\n\n/**\n * Raw local display event before device-specific processing\n */\nexport interface DisplayEvent {\n view: \"main\" | \"dashboard\"\n layoutType?: DisplayLayoutType\n layout?: {\n layoutType: DisplayLayoutType\n text?: string | string[] // string for text_wall/text_line, string[] for text_rows\n title?: string\n topText?: string\n bottomText?: string\n breakMode?: BreakMode\n data?: string // For bitmap_view\n x?: number // bitmap_view container position/size (forwarded to native, used by G2)\n y?: number\n width?: number\n height?: number\n [key: string]: unknown\n }\n text?: string | string[]\n title?: string\n topText?: string\n bottomText?: string\n breakMode?: BreakMode\n [key: string]: unknown\n}\n\n/**\n * Processed display event with guaranteed wrapped text\n */\nexport interface ProcessedDisplayEvent extends DisplayEvent {\n /** Marks this event as processed */\n _processed: true\n /** The device profile used for processing */\n _profile: string\n /** Pre-split lines for text_wall/text_line (for easy rendering) */\n _lines?: string[]\n /** Original layout type before processing (e.g., double_text_wall -> text_wall) */\n _originalLayoutType?: DisplayLayoutType\n /** Column composition metadata for double_text_wall */\n _composedColumns?: {\n leftLines: string[]\n rightLines: string[]\n config: {\n leftColumnWidthPx: number\n rightColumnStartPx: number\n rightColumnWidthPx: number\n maxLines: number\n leftMarginSpaces?: number\n }\n }\n}\n\n/**\n * Options for the DisplayProcessor\n */\nexport interface DisplayProcessorOptions {\n /** Default break mode for text wrapping */\n breakMode?: BreakMode\n /** Whether to log processing details */\n debug?: boolean\n}\n\n// =============================================================================\n// Placeholder Replacement\n// =============================================================================\n\n/**\n * Placeholder values that can be replaced in display text.\n * These match the placeholders used in CoreManager.kt/CoreManager.swift\n */\ninterface PlaceholderValues {\n /** Current time in 12-hour format (e.g., \"2:30 PM\") */\n TIME12: string\n /** Current time in 24-hour format (e.g., \"14:30\") */\n TIME24: string\n /** Current date (e.g., \"1/22\") */\n DATE: string\n /** Glasses battery level (e.g., \"85%\" or \"\" if unknown) */\n GBATT: string\n /** Connection status */\n CONNECTION_STATUS: string\n}\n\n/**\n * Get current placeholder values from device state\n */\nfunction getPlaceholderValues(): PlaceholderValues {\n const now = new Date()\n\n // Format time in 12-hour format\n const hours12 = now.getHours() % 12 || 12\n const minutes = now.getMinutes().toString().padStart(2, \"0\")\n const ampm = now.getHours() >= 12 ? \"PM\" : \"AM\"\n const TIME12 = `${hours12}:${minutes} ${ampm}`\n\n // Format time in 24-hour format\n const hours24 = now.getHours().toString().padStart(2, \"0\")\n const TIME24 = `${hours24}:${minutes}`\n\n // Format date\n const month = now.getMonth() + 1\n const day = now.getDate()\n const DATE = `${month}/${day}`\n\n // Battery + connection read from the engine glasses store directly (was a host\n // glassesStatus hook).\n const batteryLevel = useGlassesStore.getState().batteryLevel ?? -1\n const GBATT = batteryLevel === -1 ? \"\" : `${batteryLevel}%`\n\n // Connection status\n const connected = isGlassesConnected(useGlassesStore.getState().connection)\n const CONNECTION_STATUS = connected ? \"Connected\" : \"\"\n\n return {TIME12, TIME24, DATE, GBATT, CONNECTION_STATUS}\n}\n\n/**\n * Replace placeholders in text with actual values.\n * Placeholders are in format $NAME$ (e.g., $GBATT$, $TIME12$)\n *\n * @param text - Text containing placeholders\n * @returns Text with placeholders replaced\n */\nfunction replacePlaceholders(text: string): string {\n if (!text || !text.includes(\"$\")) {\n return text\n }\n\n const values = getPlaceholderValues()\n\n return text\n .replace(/\\$TIME12\\$/g, values.TIME12)\n .replace(/\\$TIME24\\$/g, values.TIME24)\n .replace(/\\$DATE\\$/g, values.DATE)\n .replace(/\\$GBATT\\$/g, values.GBATT)\n .replace(/\\$CONNECTION_STATUS\\$/g, values.CONNECTION_STATUS)\n .replace(/\\$no_datetime\\$/g, `${values.DATE}, ${values.TIME12}`)\n}\n\nfunction isBreakMode(value: unknown): value is BreakMode {\n return value === \"character\" || value === \"character-no-hyphen\" || value === \"word\" || value === \"strict-word\"\n}\n\n// =============================================================================\n// Device Profile Mapping\n// =============================================================================\n\n/**\n * Map device model names to display profiles\n */\nconst DEVICE_PROFILES: Record<DeviceModel, DisplayProfile> = {\n \"g1\": G1_PROFILE,\n \"g2\": G2_PROFILE,\n \"z100\": Z100_PROFILE,\n \"nex\": NEX_PROFILE,\n \"mach1\": Z100_PROFILE, // Mach1 uses same hardware as Vuzix Z100\n \"mentra-live\": G1_PROFILE, // Mentra Live has no display, uses G1 as fallback\n \"simulated\": G1_PROFILE, // Simulated uses G1 profile\n \"unknown\": G1_PROFILE, // Default to G1\n}\n\n/**\n * Normalize various model name strings to our DeviceModel type\n */\nfunction normalizeModelName(modelName: string | null | undefined): DeviceModel {\n if (!modelName) return \"unknown\"\n\n const lower = modelName.toLowerCase()\n\n if (lower.includes(\"g2\") || lower.includes(\"even realities g2\")) {\n return \"g2\"\n }\n if (lower.includes(\"g1\") || lower.includes(\"even realities\")) {\n return \"g1\"\n }\n if (lower.includes(\"z100\") || lower.includes(\"vuzix\")) {\n return \"z100\"\n }\n if (lower.includes(\"nex\") || lower.includes(\"mentra display\")) {\n return \"nex\"\n }\n if (lower.includes(\"mach1\") || lower.includes(\"mach 1\")) {\n return \"mach1\"\n }\n if (lower.includes(\"mentra live\") || lower.includes(\"mentra-live\")) {\n return \"mentra-live\"\n }\n if (lower.includes(\"simulated\") || lower.includes(\"simulator\")) {\n return \"simulated\"\n }\n\n return \"unknown\"\n}\n\n// =============================================================================\n// DisplayProcessor Class\n// =============================================================================\n\n/**\n * Processes display events with pixel-accurate text wrapping.\n *\n * Usage:\n * ```typescript\n * const processor = DisplayProcessor.getInstance()\n *\n * // When glasses connect\n * processor.setDeviceModel(\"Even Realities G1\")\n *\n * // Process display events\n * const processed = processor.processDisplayEvent(rawEvent)\n * ```\n */\nexport class DisplayProcessor {\n private static instance: DisplayProcessor | null = null\n\n private measurer: TextMeasurer\n private wrapper: TextWrapper\n private composer: ColumnComposer\n private profile: DisplayProfile\n private deviceModel: DeviceModel = \"unknown\"\n private options: DisplayProcessorOptions\n\n private runtimeAttached = false\n private unsubscribeGlassesStatus: (() => void) | null = null\n\n private constructor(options: DisplayProcessorOptions = {}) {\n this.options = {\n breakMode: \"character-no-hyphen\",\n debug: false,\n ...options,\n }\n\n // Initialize with default G1 profile\n const engine = createDisplayToolkit(G1_PROFILE, {\n breakMode: this.options.breakMode,\n })\n\n this.measurer = engine.measurer\n this.wrapper = engine.wrapper\n this.composer = new ColumnComposer(engine.profile, this.options.breakMode)\n this.profile = engine.profile\n\n // Engine stores may not be hydrated yet at module-load time; attachToRuntime()\n // is called again by the host after app services start.\n this.attachToRuntime()\n }\n\n /**\n * Read the current default wearable and subscribe to engine glasses-status changes.\n * Idempotent — safe to call again after app services start.\n */\n public attachToRuntime(): void {\n if (this.runtimeAttached) {\n return\n }\n\n // Seed the device profile from the saved default wearable, read off the engine\n // settings store directly (was a host settings hook).\n const defaultWearable = useSettingsStore.getState().getSetting(ISLAND_SETTINGS_KEYS.defaultWearable) as\n | string\n | undefined\n if (defaultWearable) {\n this.setDeviceModel(defaultWearable)\n console.log(`DISPLAY_PROCESSOR: Initialized DisplayProcessor with default wearable: ${defaultWearable}`)\n }\n\n // Track device-model changes off the engine glasses store directly (was a host\n // subscribeGlassesStatus hook).\n this.unsubscribeGlassesStatus = useGlassesStore.subscribe(\n (s) => s.deviceModel,\n (deviceModel) => {\n if (deviceModel) this.setDeviceModel(String(deviceModel))\n },\n )\n\n this.runtimeAttached = true\n }\n\n /** Detach runtime subscriptions installed by attachToRuntime(). */\n public detachFromRuntime(): void {\n this.unsubscribeGlassesStatus?.()\n this.unsubscribeGlassesStatus = null\n this.runtimeAttached = false\n }\n\n /**\n * Get the singleton instance\n */\n public static getInstance(options?: DisplayProcessorOptions): DisplayProcessor {\n if (!DisplayProcessor.instance) {\n DisplayProcessor.instance = new DisplayProcessor(options)\n }\n return DisplayProcessor.instance\n }\n\n /**\n * Reset the singleton (useful for testing)\n */\n public static resetInstance(): void {\n DisplayProcessor.instance?.detachFromRuntime()\n DisplayProcessor.instance = null\n }\n\n // ===========================================================================\n // Device Profile Management\n // ===========================================================================\n\n /**\n * Set the device model and update the display profile accordingly.\n * Call this when glasses connect.\n *\n * @param modelName - The model name from the connected glasses (e.g., \"Even Realities G1\")\n */\n public setDeviceModel(modelName: string | null | undefined): void {\n const normalizedModel = normalizeModelName(modelName)\n\n if (normalizedModel === this.deviceModel) {\n return // No change needed\n }\n\n this.deviceModel = normalizedModel\n const newProfile = DEVICE_PROFILES[normalizedModel]\n\n if (newProfile !== this.profile) {\n this.updateProfile(newProfile)\n }\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Device model set to: ${normalizedModel} (from: ${modelName})`)\n }\n }\n\n /**\n * Update the display profile and recreate engine\n */\n private updateProfile(newProfile: DisplayProfile): void {\n const engine = createDisplayToolkit(newProfile, {\n breakMode: this.options.breakMode,\n })\n\n this.measurer = engine.measurer\n this.wrapper = engine.wrapper\n this.composer = new ColumnComposer(newProfile, this.options.breakMode)\n this.profile = engine.profile\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Profile updated to: ${newProfile.id}`)\n }\n }\n\n /**\n * Get the current device model\n */\n public getDeviceModel(): DeviceModel {\n return this.deviceModel\n }\n\n /**\n * Get the current display profile\n */\n public getProfile(): DisplayProfile {\n return this.profile\n }\n\n // ===========================================================================\n // Display Event Processing\n // ===========================================================================\n\n /**\n * Process a display event, wrapping text as needed.\n *\n * @param event - Raw display event from WebSocket\n * @returns Processed event with wrapped text\n */\n public processDisplayEvent(event: DisplayEvent): ProcessedDisplayEvent {\n // Already processed? Return as-is\n if ((event as ProcessedDisplayEvent)._processed) {\n return event as ProcessedDisplayEvent\n }\n\n // Get layout type from either root or nested layout object\n const layoutType = event.layoutType || event.layout?.layoutType\n const layout = event.layout || event\n\n if (!layoutType) {\n // No layout type - pass through\n return {\n ...event,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n\n switch (layoutType) {\n case \"text_wall\":\n case \"text_line\":\n return this.processTextWall(event, layout)\n\n case \"text_rows\":\n return this.processTextRows(event, layout)\n\n case \"reference_card\":\n return this.processReferenceCard(event, layout)\n\n case \"double_text_wall\":\n return this.processDoubleTextWall(event, layout)\n\n case \"bitmap_view\":\n // Bitmap views don't need text processing\n return {\n ...event,\n _processed: true,\n _profile: this.profile.id,\n }\n\n default:\n // Unknown layout type - pass through\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Unknown layout type: ${layoutType}`)\n }\n return {\n ...event,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n }\n\n /**\n * Process text_wall or text_line layout\n */\n private processTextWall(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n // text_wall/text_line always have string text, not string[]\n const rawText = layout.text\n const text = typeof rawText === \"string\" ? rawText : \"\"\n\n // Replace placeholders BEFORE wrapping (so we measure actual content)\n const textWithPlaceholders = replacePlaceholders(text)\n const breakMode = this.getEventBreakMode(event, layout)\n\n // Wrap the text\n const lines = this.wrapText(textWithPlaceholders, breakMode ? {breakMode} : undefined)\n const wrappedText = lines.join(\"\\n\")\n\n // Update both root and nested layout if present\n const processedLayout = event.layout\n ? {\n ...event.layout,\n text: wrappedText,\n }\n : undefined\n\n return {\n ...event,\n // Store original text with placeholders for debugging\n _originalText: text,\n text: wrappedText,\n layout: processedLayout,\n _processed: true,\n _profile: this.profile.id,\n _lines: lines,\n }\n }\n\n /**\n * Process text_rows layout\n */\n private processTextRows(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n const textField = layout.text\n const rows: string[] = Array.isArray(textField) ? textField : []\n\n // Replace placeholders and wrap each row\n const breakMode = this.getEventBreakMode(event, layout)\n const wrappedRows = rows.map((row: string) => {\n const rowWithPlaceholders = replacePlaceholders(row)\n const lines = this.wrapText(rowWithPlaceholders, breakMode ? {breakMode} : undefined)\n return lines.join(\"\\n\")\n })\n\n const processedLayout = event.layout\n ? {\n ...event.layout,\n text: wrappedRows as string | string[],\n }\n : undefined\n\n return {\n ...event,\n text: wrappedRows as string | string[],\n layout: processedLayout,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n\n /**\n * Process reference_card layout\n */\n private processReferenceCard(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n const title = layout.title || \"\"\n // reference_card text is always string, not string[]\n const rawText = layout.text\n const text = typeof rawText === \"string\" ? rawText : \"\"\n\n // Replace placeholders BEFORE wrapping\n const titleWithPlaceholders = replacePlaceholders(title)\n const textWithPlaceholders = replacePlaceholders(text)\n const breakMode = this.getEventBreakMode(event, layout)\n\n // Wrap title and text separately\n // Title typically gets 1 line, text gets remaining lines\n const wrappedTitle = this.wrapText(titleWithPlaceholders, {maxLines: 1, ...(breakMode ? {breakMode} : {})})\n const wrappedText = this.wrapText(textWithPlaceholders, {\n maxLines: this.profile.maxLines - 1,\n ...(breakMode ? {breakMode} : {}),\n })\n\n const processedLayout = event.layout\n ? {\n ...event.layout,\n title: wrappedTitle.join(\"\\n\"),\n text: wrappedText.join(\"\\n\"),\n }\n : undefined\n\n return {\n ...event,\n title: wrappedTitle.join(\"\\n\"),\n text: wrappedText.join(\"\\n\"),\n layout: processedLayout,\n _processed: true,\n _profile: this.profile.id,\n }\n }\n\n /**\n * Process double_text_wall layout\n *\n * Uses ColumnComposer to create a fully composed string with both columns\n * merged line-by-line with pixel-precise space alignment. This is the single\n * source of truth for double_text_wall composition, replacing duplicate\n * implementations in native iOS (G1Text.swift) and Android (G1Text.kt).\n *\n * The composed text is sent to native as a regular text_wall - native just\n * chunks and sends it without any re-wrapping or column composition.\n */\n private processDoubleTextWall(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): ProcessedDisplayEvent {\n const leftText = layout.topText || \"\"\n const rightText = layout.bottomText || \"\"\n\n // Replace placeholders BEFORE composition (so we measure actual content)\n const leftTextWithPlaceholders = replacePlaceholders(leftText)\n const rightTextWithPlaceholders = replacePlaceholders(rightText)\n const breakMode = this.getEventBreakMode(event, layout)\n const composer =\n breakMode && breakMode !== this.options.breakMode ? new ColumnComposer(this.profile, breakMode) : this.composer\n\n // Use ColumnComposer for pixel-precise column composition\n const result = composer.composeDoubleTextWall(leftTextWithPlaceholders, rightTextWithPlaceholders)\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] double_text_wall composed:`)\n console.log(` Left lines: ${result.leftLines.length}`)\n console.log(` Right lines: ${result.rightLines.length}`)\n console.log(` Config: ${JSON.stringify(result.config)}`)\n }\n\n // The composed text is a single string with both columns merged\n // Native will receive this as a text_wall and just chunk & send it\n const processedLayout = event.layout\n ? {\n ...event.layout,\n // Keep original topText/bottomText for reference but add composed text\n topText: leftTextWithPlaceholders,\n bottomText: rightTextWithPlaceholders,\n // The composed text is what native should actually display\n _composedText: result.composedText,\n }\n : undefined\n\n return {\n ...event,\n // Override the layout type to text_wall since we've pre-composed the columns\n // This tells native to use sendTextWall instead of sendDoubleTextWall\n layoutType: \"text_wall\" as DisplayLayoutType,\n text: result.composedText,\n // Keep original values for debugging/preview (with placeholders replaced)\n topText: leftTextWithPlaceholders,\n bottomText: rightTextWithPlaceholders,\n // Store original values with placeholders for debugging\n _originalTopText: leftText,\n _originalBottomText: rightText,\n layout: processedLayout\n ? {\n ...processedLayout,\n layoutType: \"text_wall\" as DisplayLayoutType,\n text: result.composedText,\n }\n : undefined,\n _processed: true,\n _profile: this.profile.id,\n _originalLayoutType: \"double_text_wall\",\n _composedColumns: {\n leftLines: result.leftLines,\n rightLines: result.rightLines,\n config: result.config,\n },\n }\n }\n\n // ===========================================================================\n // Text Wrapping Utilities\n // ===========================================================================\n\n /**\n * Wrap text using the current profile\n *\n * @param text - Text to wrap\n * @param options - Optional override options\n * @returns Array of wrapped lines\n */\n public wrapText(text: string, options?: WrapOptions): string[] {\n if (!text) return [\"\"]\n\n const result = this.wrapper.wrap(text, options)\n return result.lines\n }\n\n private getEventBreakMode(\n event: DisplayEvent,\n layout: DisplayEvent | NonNullable<DisplayEvent[\"layout\"]>,\n ): BreakMode | undefined {\n const breakMode = layout.breakMode ?? event.breakMode\n return isBreakMode(breakMode) ? breakMode : undefined\n }\n\n /**\n * Measure the pixel width of text\n *\n * @param text - Text to measure\n * @returns Width in pixels\n */\n public measureText(text: string): number {\n return this.measurer.measureText(text)\n }\n\n /**\n * Check if text fits on a single line\n *\n * @param text - Text to check\n * @returns true if text fits without wrapping\n */\n public fitsOnSingleLine(text: string): boolean {\n return this.measurer.measureText(text) <= this.profile.displayWidthPx\n }\n\n /**\n * Get the display width in pixels\n */\n public getDisplayWidth(): number {\n return this.profile.displayWidthPx\n }\n\n /**\n * Get the maximum number of lines\n */\n public getMaxLines(): number {\n return this.profile.maxLines\n }\n\n /**\n * Get detailed text measurement\n */\n public measureTextDetailed(text: string) {\n return this.measurer.measureTextDetailed(text)\n }\n\n // ===========================================================================\n // Break Mode Control\n // ===========================================================================\n\n /**\n * Set the break mode for text wrapping\n *\n * @param breakMode - 'character' | 'word' | 'strict-word'\n */\n public setBreakMode(breakMode: BreakMode): void {\n if (this.options.breakMode === breakMode) return\n\n this.options.breakMode = breakMode\n this.updateProfile(this.profile) // Recreate wrapper with new break mode\n\n if (this.options.debug) {\n console.log(`[DisplayProcessor] Break mode set to: ${breakMode}`)\n }\n }\n\n /**\n * Get the current break mode\n */\n public getBreakMode(): BreakMode {\n return this.options.breakMode || \"character\"\n }\n}\n\nexport const displayProcessor = DisplayProcessor.getInstance()\nexport default displayProcessor\n"]}
|
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
* - durationMs auto-clear
|
|
10
10
|
* - core-app vs background-app arbitration with a background lock
|
|
11
11
|
*
|
|
12
|
-
* Scope is deliberately local-only. Cloud-originated displays are untouched
|
|
13
|
-
* and may race with local displays during dev; that's acceptable per plan
|
|
14
|
-
* (agents/local-display-manager-plan.md).
|
|
15
|
-
*
|
|
16
12
|
* Display pacing is NOT done here. A JS-side ~300ms throttle used to live in
|
|
17
13
|
* this class, but JS timers (and therefore the throttle's trailing flush) do
|
|
18
14
|
* not fire on schedule while the iOS app is suspended in the background — so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalDisplayManager.d.ts","sourceRoot":"","sources":["../../src/services/LocalDisplayManager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"LocalDisplayManager.d.ts","sourceRoot":"","sources":["../../src/services/LocalDisplayManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAQH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,wBAAwB,CAAA;AAO7D,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAA;IACpC,kFAAkF;IAClF,MAAM,CAAC,EAAE;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAC,CAAA;IACrD,qEAAqE;IACrE,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAA;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAA;AAkD3E,cAAM,mBAAmB;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmC;IAE1D,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,cAAc,CAA8B;IAEpD,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,SAAS,CAAsF;IAEvG,OAAO,CAAC,aAAa,CAAsB;IAE3C,6DAA6D;IAC7D,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,mBAAmB,CAAQ;IACnC,OAAO,CAAC,wBAAwB,CAA4B;IAE5D,4BAA4B;IAC5B,OAAO,CAAC,GAAG,CAAiC;IAE5C,OAAO;WAEO,WAAW,IAAI,mBAAmB;IAWhD;;;;OAIG;IACI,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAyC9D;;;OAGG;IACI,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAexD,+DAA+D;IACxD,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IASvD;;OAEG;IACI,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IA8D3C;;;;;;;;OAQG;IACI,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI;IAuBpG;;;;;OAKG;IACI,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAQzC,OAAO,CAAC,gBAAgB;IA0ExB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,OAAO;IAmDf;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAsCjB,OAAO,CAAC,YAAY;IAkCpB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,OAAO;IAuCf;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAqCtB;;;;OAIG;IACI,eAAe,IAAI,IAAI;IAgB9B;;;;OAIG;IACI,aAAa,IAAI,IAAI;IAoB5B,OAAO,CAAC,gBAAgB;IAWxB,mCAAmC;IAC5B,cAAc,CAAC,EAAE,EAAE,MAAM,MAAM,GAAG,IAAI;IAI7C,+BAA+B;IACxB,aAAa,IAAI,IAAI;IAgB5B,6CAA6C;IACtC,YAAY,IAAI;QACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;QACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;QAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;QACxB,SAAS,EAAE,OAAO,CAAA;KACnB;CAQF;AAED,QAAA,MAAM,mBAAmB,qBAAoC,CAAA;AAC7D,eAAe,mBAAmB,CAAA;AAClC,OAAO,EAAC,mBAAmB,EAAC,CAAA"}
|
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
* - durationMs auto-clear
|
|
10
10
|
* - core-app vs background-app arbitration with a background lock
|
|
11
11
|
*
|
|
12
|
-
* Scope is deliberately local-only. Cloud-originated displays are untouched
|
|
13
|
-
* and may race with local displays during dev; that's acceptable per plan
|
|
14
|
-
* (agents/local-display-manager-plan.md).
|
|
15
|
-
*
|
|
16
12
|
* Display pacing is NOT done here. A JS-side ~300ms throttle used to live in
|
|
17
13
|
* this class, but JS timers (and therefore the throttle's trailing flush) do
|
|
18
14
|
* not fire on schedule while the iOS app is suspended in the background — so
|
|
@@ -37,9 +33,8 @@ import { BgTimer } from "../utils/timers";
|
|
|
37
33
|
// =============================================================================
|
|
38
34
|
const LOG_TAG = "LOCAL_DISPLAY";
|
|
39
35
|
const BOOT_DURATION_MS = 1500;
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// app wants the screen, we release.
|
|
36
|
+
// Background renders renew this lease. An expired lock is dropped when the
|
|
37
|
+
// next display request is arbitrated, allowing another app to take the screen.
|
|
43
38
|
const BACKGROUND_LOCK_TIMEOUT_MS = 10_000;
|
|
44
39
|
// Sentinel package name used as the "source" of the system boot message.
|
|
45
40
|
const SYSTEM_BOOT_PKG = "system.boot";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalDisplayManager.js","sourceRoot":"","sources":["../../src/services/LocalDisplayManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,YAAY,MAAM,gCAAgC,CAAA;AACzD,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,aAAa,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AAEjD,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AAmDvC,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,MAAM,OAAO,GAAG,eAAe,CAAA;AAC/B,MAAM,gBAAgB,GAAG,IAAI,CAAA;AAC7B,0EAA0E;AAC1E,2EAA2E;AAC3E,oCAAoC;AACpC,MAAM,0BAA0B,GAAG,MAAM,CAAA;AACzC,yEAAyE;AACzE,MAAM,eAAe,GAAG,aAAa,CAAA;AAErC,0EAA0E;AAC1E,SAAS,cAAc,CAAC,OAAuB;IAC7C,IAAI,OAAO,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAA;IACpD,OAAO,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,YAAY,CAAA;AACpD,CAAC;AAED,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF,MAAM,mBAAmB;IACf,MAAM,CAAC,QAAQ,GAA+B,IAAI,CAAA;IAElD,OAAO,GAAkB,IAAI,CAAA;IAC7B,cAAc,GAAyB,IAAI,CAAA;IAC3C,cAAc,GAAyB,IAAI,CAAA;IAC3C,cAAc,GAA0B,IAAI,CAAA;IAE5C,UAAU,GAAsB,IAAI,CAAA;IACpC,SAAS,GAA6E,IAAI,GAAG,EAAE,CAAA;IAE/F,aAAa,GAAkB,IAAI,CAAA;IAE3C,6DAA6D;IACrD,eAAe,GAAG,KAAK,CAAA;IACvB,mBAAmB,GAAG,KAAK,CAAA;IAC3B,wBAAwB,GAAwB,IAAI,CAAA;IAE5D,4BAA4B;IACpB,GAAG,GAAiB,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;IAE5C,gBAAuB,CAAC;IAEjB,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,CAAC;YAClC,mBAAmB,CAAC,QAAQ,GAAG,IAAI,mBAAmB,EAAE,CAAA;QAC1D,CAAC;QACD,OAAO,mBAAmB,CAAC,QAAQ,CAAA;IACrC,CAAC;IAED,8EAA8E;IAC9E,0CAA0C;IAC1C,8EAA8E;IAE9E;;;;OAIG;IACI,OAAO,CAAC,WAAmB,EAAE,WAAmB;QACrD,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,aAAa,WAAW,MAAM,WAAW,IAAI,CAAC,CAAA;QAEpE,yEAAyE;QACzE,4EAA4E;QAC5E,6EAA6E;QAC7E,wEAAwE;QACxE,0EAA0E;QAC1E,oEAAoE;QACpE,0EAA0E;QAC1E,0EAA0E;QAC1E,mCAAmC;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAA;QACjF,MAAM,OAAO,GAAG,SAAS,EAAE,WAAW,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;QAE7E,6CAA6C;QAC7C,IAAI,CAAC,UAAU,EAAE,CAAA;QAEjB,wEAAwE;QACxE,wEAAwE;QACxE,sEAAsE;QACtE,0CAA0C;QAC1C,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE;YAC5B,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,EAAC,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,WAAW,GAAG,EAAC;SACpE,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;YACtC,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC,EAAE,gBAAgB,CAAC,CAAA;QAEpB,IAAI,CAAC,UAAU,GAAG;YAChB,WAAW;YACX,WAAW;YACX,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,OAAO;YACP,OAAO;SACR,CAAA;QACD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;IAED;;;OAGG;IACI,eAAe,CAAC,WAA0B;QAC/C,IAAI,IAAI,CAAC,OAAO,KAAK,WAAW;YAAE,OAAM;QACxC,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,qBAAqB,WAAW,IAAI,MAAM,GAAG,CAAC,CAAA;QAEpE,IAAI,CAAC,OAAO,GAAG,WAAW,CAAA;QAE1B,mEAAmE;QACnE,qEAAqE;QACrE,8DAA8D;QAC9D,yDAAyD;QACzD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,+DAA+D;IACxD,qBAAqB;QAC1B,OAAO;YACL,kBAAkB,EAAE,IAAI,CAAC,OAAO;YAChC,yBAAyB,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,IAAI,IAAI;YACnE,yBAAyB,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,IAAI,IAAI;YACnE,kBAAkB,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,IAAI,IAAI;SACzD,CAAA;IACH,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,WAAmB;QAClC,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,eAAe,WAAW,GAAG,CAAC,CAAA;QAEpD,kEAAkE;QAClE,wEAAwE;QACxE,0BAA0B;QAC1B,IAAI,IAAI,CAAC,UAAU,EAAE,WAAW,KAAK,WAAW,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAA;YACvC,IAAI,CAAC,UAAU,EAAE,CAAA;YACjB,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,eAAe,EAAE,CAAC;gBACzD,iEAAiE;gBACjE,kEAAkE;gBAClE,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;gBACjF,MAAM,gBAAgB,GAAG,WAAW,KAAK,SAAS,IAAI,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;gBACzF,IAAI,OAAO,IAAI,CAAC,gBAAgB,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;oBACnG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;gBAC9B,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,SAAS,EAAE,CAAA;gBAClB,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAC,CAAC,CAAA;QACtF,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QAElC,oEAAoE;QACpE,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;QAEnC,uCAAuC;QACvC,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,WAAW,EAAE,CAAC;YACrD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC;QAED,sEAAsE;QACtE,sEAAsE;QACtE,wEAAwE;QACxE,mEAAmE;QACnE,uEAAuE;QACvE,sEAAsE;QACtE,oEAAoE;QACpE,uEAAuE;QACvE,wEAAwE;QACxE,IAAI,IAAI,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;YACjC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC;QAED,wEAAwE;QACxE,6DAA6D;QAC7D,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,WAAW,EAAE,CAAC;YACrD,IAAI,CAAC,gBAAgB,EAAE,CAAA;YACvB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;YAC1B,IAAI,CAAC,qBAAqB,EAAE,CAAA;YAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACzB,0CAA0C;gBAC1C,IAAI,CAAC,SAAS,EAAE,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,sEAAsE;IACtE,8EAA8E;IAE9E;;;;;;;;OAQG;IACI,OAAO,CAAC,WAAmB,EAAE,OAAuB,EAAE,OAAgC;QAC3F,yEAAyE;QACzE,+DAA+D;QAC/D,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;YAC3C,OAAM;QACR,CAAC;QAED,wEAAwE;QACxE,iEAAiE;QACjE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;YAClD,UAAU,EAAE,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,6BAA6B,EAAC,CAAC,CAAA;YACjF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,EAAC,OAAO,EAAE,OAAO,EAAC,CAAC,CAAA;YACnD,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;gBAChD,IAAI,CAAC,OAAO,EAAE,CAAA;YAChB,CAAC;YACD,OAAM;QACR,CAAC;QAED,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACtD,CAAC;IAED;;;;;OAKG;IACI,OAAO,CAAC,WAAmB;QAChC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC;IAED,8EAA8E;IAC9E,0BAA0B;IAC1B,8EAA8E;IAEtE,gBAAgB,CAAC,WAAmB,EAAE,OAAuB,EAAE,OAAgC;QACrG,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAEtB,6CAA6C;QAC7C,IAAI,IAAI,CAAC,cAAc,IAAI,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;YAC/D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC;QAED,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,uEAAuE;QACvE,gCAAgC;QAChC,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;YACzC,OAAM;QACR,CAAC;QAED,MAAM,MAAM,GAAG,WAAW,KAAK,IAAI,CAAC,OAAO,CAAA;QAE3C,IAAI,MAAM,EAAE,CAAC;YACX,gCAAgC;YAChC,IAAI,CAAC,cAAc,GAAG;gBACpB,WAAW;gBACX,cAAc,EAAE,EAAE,EAAE,oBAAoB;gBACxC,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;aAChE,CAAA;YAED,sEAAsE;YACtE,qEAAqE;YACrE,eAAe;YACf,MAAM,kBAAkB,GACtB,IAAI,CAAC,cAAc;gBACnB,IAAI,CAAC,cAAc;gBACnB,IAAI,CAAC,cAAc,CAAC,WAAW,KAAK,IAAI,CAAC,cAAc,CAAC,WAAW,CAAA;YACrE,IAAI,kBAAkB,EAAE,CAAC;gBACvB,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,oCAAoC,EAAC,CAAC,CAAA;gBAC5E,OAAM;YACR,CAAC;YAED,qEAAqE;YACrE,2BAA2B;YAC3B,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC/C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;YAC5B,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;YAC3C,OAAM;QACR,CAAC;QAED,sCAAsC;QACtC,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;YAC3E,gDAAgD;YAChD,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,0CAA0C,EAAC,CAAC,CAAA;YAClF,OAAM;QACR,CAAC;QAED,4BAA4B;QAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,GAAG;gBACpB,WAAW;gBACX,UAAU,EAAE,GAAG;gBACf,SAAS,EAAE,GAAG,GAAG,0BAA0B;gBAC3C,cAAc,EAAE,GAAG;aACpB,CAAA;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,cAAc,CAAC,cAAc,GAAG,GAAG,CAAA;YACxC,IAAI,CAAC,cAAc,CAAC,SAAS,GAAG,GAAG,GAAG,0BAA0B,CAAA;QAClE,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IAC7C,CAAC;IAED;;;;OAIG;IACK,cAAc,CAAC,WAAmB,EAAE,OAAgC;QAC1E,wEAAwE;QACxE,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;QAEnC,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,WAAW,EAAE,CAAC;YACrD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;YACjC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,WAAW,EAAE,CAAC;YACrD,IAAI,CAAC,gBAAgB,EAAE,CAAA;YACvB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;YAC1B,IAAI,CAAC,qBAAqB,EAAE,CAAA;YAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACzB,IAAI,CAAC,SAAS,EAAE,CAAA;YAClB,CAAC;QACH,CAAC;QACD,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC,CAAA;IAClC,CAAC;IAED,8EAA8E;IAC9E,mBAAmB;IACnB,8EAA8E;IAEtE,OAAO,CAAC,WAAmB,EAAE,OAAuB,EAAE,OAAgC;QAC5F,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAA;QAC7E,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAyB,CAAA;QAE1F,2EAA2E;QAC3E,yEAAyE;QACzE,iCAAiC;QACjC,IAAI,aAAa,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAA;QACzC,IAAI,CAAC,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,aAAa,CAAC,mBAAmB,EAAE,CAAA;YAChD,IAAI,IAAI,EAAE,WAAW,EAAE,CAAC;gBACtB,aAAa,GAAG,aAAa,CAAC,mBAAmB,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;YACzE,CAAC;QACH,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,CAAC,CAAA;QACtE,CAAC;aAAM,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1B,uEAAuE;YACvE,yEAAyE;YACzE,kEAAkE;YAClE,wEAAwE;YACxE,wBAAwB;YACxB,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;YAC9C,MAAM,QAAQ,GAA4B;gBACxC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAA;YACD,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBACrC,QAAQ,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAA;YAC1C,CAAC;YACD,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;YACnD,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC,CAAA;QAClC,CAAC;aAAM,CAAC;YACN,4DAA4D;YAC5D,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAC,CAAC,CAAA;YAC9D,OAAM;QACR,CAAC;QAED,0EAA0E;QAC1E,wEAAwE;QACxE,IAAI,WAAW,KAAK,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxD,IAAI,CAAC,cAAc,GAAG;gBACpB,WAAW;gBACX,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,cAAc;gBAClD,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,KAAK;gBAChC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,SAAS;aACzC,CAAA;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,SAAS,CACf,WAAmB,EACnB,IAA0B,EAC1B,QAA6B,EAC7B,SAAwB,EACxB,OAAgC;QAEhC,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAEnE,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAC,CAAC,CAAA;YAC9D,OAAM;QACR,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,EAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAC,EAAE,SAAS,CAAC,CAAA;YAC1E,CAAC;iBAAM,CAAC;gBACN,iEAAiE;gBACjE,gEAAgE;gBAChE,IAAI,CAAC,SAAS,EAAE,CAAA;YAClB,CAAC;YACD,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAC,CAAC,CAAA;YACpF,OAAM;QACR,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,EAAC,WAAW,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAC,CAAA;QACnF,IAAI,CAAC,gBAAgB,EAAE,CAAA;QACvB,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;YACjD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;gBAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;gBACzB,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;YAChC,CAAC,EAAE,KAAK,CAAC,CAAA;QACX,CAAC;QACD,OAAO,EAAE,CAAC,EAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAC,CAAC,CAAA;IACtF,CAAC;IAEO,YAAY,CAAC,WAAmB,EAAE,QAAiC,EAAE,SAAwB;QACnG,IAAI,cAAuC,CAAA;QAC3C,IAAI,CAAC;YACH,cAAc,GAAG,gBAAgB,CAAC,mBAAmB,CAAC,QAAe,CAAC,CAAA;QACxE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,4CAA4C,EAAE,GAAG,CAAC,CAAA;YAC1E,cAAc,GAAG,QAAQ,CAAA;QAC3B,CAAC;QAED,IAAI,CAAC;YACH,kFAAkF;YAClF,gEAAgE;YAChE,KAAK,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC5E,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,0BAA0B,EAAE,GAAG,CAAC,CAAA;YAC1D,CAAC,CAAC,CAAA;YACF,wEAAwE;YACxE,4CAA4C;YAC5C,eAAe,CAAC,QAAQ,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAA;QAC5E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,0BAA0B,EAAE,GAAG,CAAC,CAAA;QAC1D,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,EAAC,WAAW,EAAE,cAAc,EAAE,SAAS,EAAC,CAAA;QAE9D,IAAI,CAAC,gBAAgB,EAAE,CAAA;QACvB,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;YACjD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;gBAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;gBACzB,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;YAChC,CAAC,EAAE,KAAK,CAAC,CAAA;QACX,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,WAAmB;QACtC,+CAA+C;QAC/C,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;YAC5E,OAAM;QACR,CAAC;QAED,oDAAoD;QACpD,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,WAAW,EAAE,CAAC;YACrD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC1B,IAAI,CAAC,qBAAqB,EAAE,CAAA;QAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,IAAI,CAAC,SAAS,EAAE,CAAA;QAClB,CAAC;IACH,CAAC;IAEO,qBAAqB;QAC3B,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAM;QAChC,sEAAsE;QACtE,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;YAC1F,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;YAC1B,OAAM;QACR,CAAC;QACD,oCAAoC;QACpC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAC1C,CAAC;IAEO,SAAS;QACf,MAAM,UAAU,GAA4B;YAC1C,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,EAAC,UAAU,EAAE,YAAY,EAAC;SACnC,CAAA;QACD,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,UAAU,EAAE,IAAI,CAAC,CAAA;QACnD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC1B,oEAAoE;QACpE,kEAAkE;QAClE,8CAA8C;QAC9C,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;IAED,8EAA8E;IAC9E,mBAAmB;IACnB,8EAA8E;IAEtE,UAAU;QAChB,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAM;QAC5B,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;QAC7C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;IACxB,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAM;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAA;QACvC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;QAC7C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QAEtB,qEAAqE;QACrE,qDAAqD;QACrD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;QACnD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;QAEtB,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAC3F,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACvF,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YAClC,IAAI,GAAG,KAAK,IAAI,CAAC,OAAO;gBAAE,SAAQ;YAClC,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QAC1D,CAAC;QAED,0EAA0E;QAC1E,uEAAuE;QACvE,uEAAuE;QACvE,qEAAqE;QACrE,uEAAuE;QACvE,wEAAwE;QACxE,uEAAuE;QACvE,8CAA8C;QAC9C,IAAI,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,eAAe,EAAE,CAAC;YACzD,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;YAC5F,MAAM,gBAAgB,GAAG,UAAU,KAAK,SAAS,IAAI,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;YAC1F,IAAI,OAAO,IAAI,CAAC,gBAAgB,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;gBACnG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;YAC9B,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,SAAS,EAAE,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,cAAc,CAAC,KAAoB;QACzC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAElG,2EAA2E;QAC3E,wEAAwE;QACxE,6EAA6E;QAC7E,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;YACzE,IAAI,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC;gBACvD,IAAI,CAAC,cAAc,GAAG,EAAC,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAC,CAAA;gBACzG,IAAI,CAAC,gBAAgB,EAAE,CAAA;gBACvB,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;oBACvB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;oBACjD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;wBAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;wBACzB,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;oBACtC,CAAC,EAAE,KAAK,CAAC,CAAA;gBACX,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,gEAAgE;gBAChE,qCAAqC;gBACrC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,EAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAC,CAAC,CAAA;YAC5F,CAAC;YACD,OAAM;QACR,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE;YAC9B,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,MAAkC;YAC/D,UAAU,EAAE,SAAS;SACtB,CAAC,CAAA;IACJ,CAAC;IAED,8EAA8E;IAC9E,wEAAwE;IACxE,8EAA8E;IAE9E;;;;OAIG;IACI,eAAe;QACpB,IAAI,IAAI,CAAC,eAAe;YAAE,OAAM;QAChC,0EAA0E;QAC1E,+CAA+C;QAC/C,IAAI,CAAC,mBAAmB,GAAG,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;QACpF,IAAI,CAAC,wBAAwB,GAAG,eAAe,CAAC,SAAS,CAAC,GAAG,EAAE;YAC7D,MAAM,SAAS,GAAG,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;YAC3E,IAAI,SAAS,KAAK,IAAI,CAAC,mBAAmB;gBAAE,OAAM;YAClD,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAA;YACpC,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC,aAAa,EAAE,CAAA;YACtB,CAAC;QACH,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,eAAe,GAAG,IAAI,CAAA;IAC7B,CAAC;IAED;;;;OAIG;IACI,aAAa;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAA;QACnC,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC,SAAS;YAAE,OAAM;QAEzE,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,mBAAmB,OAAO,CAAC,WAAW,GAAG,CAAC,CAAA;QAChE,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC;gBAC1D,sEAAsE;gBACtE,IAAI,CAAC,SAAS,EAAE,CAAA;YAClB,CAAC;QACH,CAAC;aAAM,IAAI,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;YACzC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;QACnF,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,4BAA4B;IAC5B,8EAA8E;IAEtE,gBAAgB;QACtB,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YAChC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;YACxC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;QAC3B,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,aAAa;IACb,8EAA8E;IAE9E,mCAAmC;IAC5B,cAAc,CAAC,EAAgB;QACpC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAA;IACf,CAAC;IAED,+BAA+B;IACxB,aAAa;QAClB,IAAI,CAAC,UAAU,EAAE,CAAA;QACjB,IAAI,CAAC,gBAAgB,EAAE,CAAA;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC1B,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;QACtB,IAAI,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;QAC3B,IAAI,CAAC,wBAAwB,EAAE,EAAE,CAAA;QACjC,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAA;QACpC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAA;QAC5B,IAAI,CAAC,mBAAmB,GAAG,KAAK,CAAA;QAChC,aAAa,CAAC,aAAa,EAAE,CAAA;IAC/B,CAAC;IAED,6CAA6C;IACtC,YAAY;QAMjB,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,iBAAiB,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,IAAI,IAAI;YAC3D,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,IAAI,IAAI;YACnD,SAAS,EAAE,IAAI,CAAC,UAAU,KAAK,IAAI;SACpC,CAAA;IACH,CAAC;;AAGH,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,WAAW,EAAE,CAAA;AAC7D,eAAe,mBAAmB,CAAA;AAClC,OAAO,EAAC,mBAAmB,EAAC,CAAA","sourcesContent":["/**\n * LocalDisplayManager\n *\n * Phone-side arbiter for display requests originating from LOCAL miniapps\n * (the @mentra/miniapp SDK). Preserves the retired cloud display manager's\n * user-facing behavior for the features that matter on the phone:\n *\n * - boot message (\"Starting <AppName>…\") with a bounded window\n * - durationMs auto-clear\n * - core-app vs background-app arbitration with a background lock\n *\n * Scope is deliberately local-only. Cloud-originated displays are untouched\n * and may race with local displays during dev; that's acceptable per plan\n * (agents/local-display-manager-plan.md).\n *\n * Display pacing is NOT done here. A JS-side ~300ms throttle used to live in\n * this class, but JS timers (and therefore the throttle's trailing flush) do\n * not fire on schedule while the iOS app is suspended in the background — so\n * under streaming captions, updates accumulated and then flushed in a burst on\n * the next wake. Pacing + last-wins coalescing now live in the native per-device\n * queue (e.g. G2's event-driven EvenHub queue), which drains off the same BLE\n * events that wake the process. This class forwards every request straight\n * through; the native layer collapses redundant frames per display target.\n *\n * The remaining BgTimer use (boot window, durationMs expiry) is timing that\n * does not need sub-second background fidelity.\n */\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport displayProcessor from \"./DisplayProcessor\"\nimport {useDisplayStore} from \"../stores/display\"\nimport sceneRenderer from \"./SceneRenderer\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport type {SceneElementInput} from \"../utils/display/scene\"\nimport {BgTimer} from \"../utils/timers\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DisplayPayload {\n view?: \"main\" | \"dashboard\" | string\n /** Legacy layout (sugar / old bundles / cloud shapes). Either this or `scene`. */\n layout?: {layoutType: string; [key: string]: unknown}\n /** Scene elements from display.render(). Either this or `layout`. */\n scene?: SceneElementInput[]\n durationMs?: number\n}\n\n/** Outcome delivered to an awaiting render() call (REQUEST_RESULT payload). */\nexport interface DisplayRequestResult {\n status: \"displayed\" | \"blocked\"\n degraded?: boolean\n dropped?: string[]\n reason?: string\n}\n\nexport type DisplayRequestResolver = (result: DisplayRequestResult) => void\n\ninterface ActiveDisplay {\n packageName: string\n processedEvent: Record<string, unknown>\n /** Set when this display is a scene — restore goes through SceneRenderer replay. */\n scene?: SceneElementInput[]\n expiresAt: number | null\n}\n\ninterface BootingApp {\n packageName: string\n displayName: string\n startedAt: number\n timerId: number\n /** Whatever was on the glasses when this boot started, so a boot that times\n * out without the app rendering can restore it instead of blanking a frame\n * another (e.g. foreground) app owns. Null if the glasses were empty. */\n preBoot: ActiveDisplay | null\n}\n\ninterface BackgroundLock {\n packageName: string\n acquiredAt: number\n expiresAt: number\n lastActiveTime: number\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst LOG_TAG = \"LOCAL_DISPLAY\"\nconst BOOT_DURATION_MS = 1500\n// Mirrors cloud lease for a bg app holding the display. The bg app has to\n// keep driving the display to hold the lock; if it goes quiet and the core\n// app wants the screen, we release.\nconst BACKGROUND_LOCK_TIMEOUT_MS = 10_000\n// Sentinel package name used as the \"source\" of the system boot message.\nconst SYSTEM_BOOT_PKG = \"system.boot\"\n\n/** \"Render nothing\" — an empty scene or an explicit clear_view layout. */\nfunction isClearPayload(payload: DisplayPayload): boolean {\n if (payload.scene) return payload.scene.length === 0\n return payload.layout?.layoutType === \"clear_view\"\n}\n\n// =============================================================================\n// LocalDisplayManager\n// =============================================================================\n\nclass LocalDisplayManager {\n private static instance: LocalDisplayManager | null = null\n\n private coreApp: string | null = null\n private coreAppDisplay: ActiveDisplay | null = null\n private currentDisplay: ActiveDisplay | null = null\n private backgroundLock: BackgroundLock | null = null\n\n private bootingApp: BootingApp | null = null\n private bootQueue: Map<string, {payload: DisplayPayload; resolve?: DisplayRequestResolver}> = new Map()\n\n private expiryTimerId: number | null = null\n\n /** Reconnect-replay subscription state (attachToRuntime). */\n private runtimeAttached = false\n private glassesWasConnected = false\n private unsubscribeGlassesStatus: (() => void) | null = null\n\n /** Injectable for tests. */\n private now: () => number = () => Date.now()\n\n private constructor() {}\n\n public static getInstance(): LocalDisplayManager {\n if (!LocalDisplayManager.instance) {\n LocalDisplayManager.instance = new LocalDisplayManager()\n }\n return LocalDisplayManager.instance\n }\n\n // ===========================================================================\n // Lifecycle API (called from MiniappHost)\n // ===========================================================================\n\n /**\n * A new local miniapp just mounted. Start the boot window.\n * Calling this for a different pkg while another is booting cancels the\n * previous boot.\n */\n public onMount(packageName: string, displayName: string): void {\n console.log(`${LOG_TAG}: onMount(${packageName}, \"${displayName}\")`)\n\n // Snapshot whatever's on the glasses BEFORE the boot text overwrites it.\n // registerApp fires onMount for every spawn — including a background app or\n // a crash-respawn while another app is foreground — so a boot that times out\n // without rendering must put the prior frame back rather than blank it.\n // Never snapshot the boot text itself: back-to-back mounts (the autostart\n // restore loop, crash-respawn chains) land while the previous app's\n // \"Starting X…\" is still on the glasses, and endBoot would \"restore\" that\n // stale, expiry-less frame permanently. Across chained boots, inherit the\n // original pre-boot frame instead.\n const inherited = this.bootingApp ? this.bootingApp.preBoot : this.currentDisplay\n const preBoot = inherited?.packageName === SYSTEM_BOOT_PKG ? null : inherited\n\n // Cancel any in-flight boot for a prior app.\n this.cancelBoot()\n\n // Send the boot message directly (bypass arbitration — this is a system\n // display). Goes through sendNow so scene devices render it as a scene:\n // otherwise every app start would be a legacy→scene transition on the\n // glasses (which costs a blanking clear).\n this.sendNow(SYSTEM_BOOT_PKG, {\n view: \"main\",\n layout: {layoutType: \"text_wall\", text: `Starting ${displayName}…`},\n })\n\n const timerId = BgTimer.setTimeout(() => {\n this.endBoot()\n }, BOOT_DURATION_MS)\n\n this.bootingApp = {\n packageName,\n displayName,\n startedAt: this.now(),\n timerId,\n preBoot,\n }\n this.bootQueue.clear()\n }\n\n /**\n * Mark which miniapp is the \"core\" (foreground) app. Pass null when no local\n * miniapp is foreground.\n */\n public onCoreAppChange(packageName: string | null): void {\n if (this.coreApp === packageName) return\n console.log(`${LOG_TAG}: onCoreAppChange(${packageName ?? \"null\"})`)\n\n this.coreApp = packageName\n\n // No core app means no saved frame to restore on the next unmount.\n // Without this, the previous core's last frame can flicker back onto\n // the glasses when the user-initiated clear is followed by an\n // onUnmount that still finds a populated coreAppDisplay.\n if (packageName === null) {\n this.coreAppDisplay = null\n }\n }\n\n /** Report-safe ownership snapshot for incident diagnostics. */\n public getDiagnosticSnapshot(): Record<string, unknown> {\n return {\n coreAppPackageName: this.coreApp,\n currentDisplayPackageName: this.currentDisplay?.packageName ?? null,\n backgroundLockPackageName: this.backgroundLock?.packageName ?? null,\n bootingPackageName: this.bootingApp?.packageName ?? null,\n }\n }\n\n /**\n * A local miniapp is going away. Clean up all state associated with it.\n */\n public onUnmount(packageName: string): void {\n console.log(`${LOG_TAG}: onUnmount(${packageName})`)\n\n // If this was the booting app, cancel boot — and don't strand its\n // \"Starting X…\" text (a system frame with no expiry): put back what the\n // boot covered, or clear.\n if (this.bootingApp?.packageName === packageName) {\n const preBoot = this.bootingApp.preBoot\n this.cancelBoot()\n if (this.currentDisplay?.packageName === SYSTEM_BOOT_PKG) {\n // The preBoot owner may have forfeited its frame during the boot\n // window (a clear parked in the boot queue) — don't resurrect it.\n const ownerQueued = preBoot ? this.bootQueue.get(preBoot.packageName) : undefined\n const preBootForfeited = ownerQueued !== undefined && isClearPayload(ownerQueued.payload)\n if (preBoot && !preBootForfeited && (preBoot.expiresAt === null || preBoot.expiresAt > this.now())) {\n this.restoreDisplay(preBoot)\n } else {\n this.sendClear()\n }\n }\n }\n this.bootQueue.get(packageName)?.resolve?.({status: \"blocked\", reason: \"app stopped\"})\n this.bootQueue.delete(packageName)\n\n // Forget the app's retained scenes — nothing left to replay for it.\n sceneRenderer.clearApp(packageName)\n\n // Release bg lock if this app held it.\n if (this.backgroundLock?.packageName === packageName) {\n this.backgroundLock = null\n }\n\n // Drop the saved core frame if the core's context is going away — the\n // frame belongs to a dead context and must not be restored later. The\n // coreApp POINTER itself is NOT cleared here: it is derived state owned\n // by the apps-store reconciler. A liveness/crash respawn tears the\n // context down (this path) without any store change — the app is still\n // \"running\" — and clearing the pointer here would leave the respawned\n // foreground app arbitrating as background until an unrelated store\n // write happened to re-run the reconciler. Real stops release the slot\n // via the store (running → false → reconciler → onCoreAppChange(null)).\n if (this.coreApp === packageName) {\n this.coreAppDisplay = null\n }\n\n // If this app owned the current on-glasses display, clear it (and maybe\n // restore the core app's display if it still has time left).\n if (this.currentDisplay?.packageName === packageName) {\n this.clearExpiryTimer()\n this.currentDisplay = null\n this.tryRestoreCoreDisplay()\n if (!this.currentDisplay) {\n // Nothing to restore → clear the glasses.\n this.sendClear()\n }\n }\n }\n\n // ===========================================================================\n // Display request API (called from LocalMiniappRuntime.handleDisplay)\n // ===========================================================================\n\n /**\n * A miniapp requested a display. Routes through boot queue, arbitration,\n * and throttle. This is the only public entry point for miniapp display\n * traffic on the local path.\n *\n * `resolve`, when provided (render() requests), is guaranteed to be called\n * exactly once with the request's outcome — including for requests parked in\n * the boot queue (resolved when drained or superseded).\n */\n public request(packageName: string, payload: DisplayPayload, resolve?: DisplayRequestResolver): void {\n // Dashboard view: pass straight through (no throttle/arbitration). Local\n // dashboard rendering is currently a stub on the phone anyway.\n if (payload.view === \"dashboard\") {\n this.sendNow(packageName, payload, resolve)\n return\n }\n\n // During boot, any app's requests go into the queue. If the booting app\n // itself makes its first display call, end boot early and drain.\n if (this.bootingApp) {\n const superseded = this.bootQueue.get(packageName)\n superseded?.resolve?.({status: \"blocked\", reason: \"superseded by a newer frame\"})\n this.bootQueue.set(packageName, {payload, resolve})\n if (this.bootingApp.packageName === packageName) {\n this.endBoot()\n }\n return\n }\n\n this.arbitrateAndSend(packageName, payload, resolve)\n }\n\n /**\n * End a package's temporary display early. If it currently owns the main\n * view, release its background lock and restore the foreground miniapp's\n * saved frame. A dismissal for a package that is no longer visible is a\n * no-op.\n */\n public dismiss(packageName: string): void {\n this.handleExpiry(packageName)\n }\n\n // ===========================================================================\n // Internals — arbitration\n // ===========================================================================\n\n private arbitrateAndSend(packageName: string, payload: DisplayPayload, resolve?: DisplayRequestResolver): void {\n const now = this.now()\n\n // Expire a stale bg lock before arbitrating.\n if (this.backgroundLock && now > this.backgroundLock.expiresAt) {\n this.backgroundLock = null\n }\n\n // A clear (\"render nothing\") means \"I'm done with the display\" — it is a\n // forfeit, not a frame. A background app releases its lock and the core\n // app's still-valid display comes back; treating it as a render instead\n // would RENEW the lock with an empty frame and blank the glasses while\n // still blocking everyone else.\n if (isClearPayload(payload)) {\n this.handleAppClear(packageName, resolve)\n return\n }\n\n const isCore = packageName === this.coreApp\n\n if (isCore) {\n // Save so we can restore later.\n this.coreAppDisplay = {\n packageName,\n processedEvent: {}, // filled by sendNow\n scene: payload.scene,\n expiresAt: payload.durationMs ? now + payload.durationMs : null,\n }\n\n // If a bg app holds the lock AND is currently on the glasses, core is\n // blocked. The saved coreAppDisplay surfaces when the bg app expires\n // or unmounts.\n const bgHoldsAndDisplays =\n this.backgroundLock &&\n this.currentDisplay &&\n this.currentDisplay.packageName === this.backgroundLock.packageName\n if (bgHoldsAndDisplays) {\n resolve?.({status: \"blocked\", reason: \"a background app holds the display\"})\n return\n }\n\n // Core can display. If a bg lock exists but holder isn't actively on\n // the glasses, release it.\n if (this.backgroundLock && !bgHoldsAndDisplays) {\n this.backgroundLock = null\n }\n\n this.sendNow(packageName, payload, resolve)\n return\n }\n\n // Non-core: this is a background app.\n if (this.backgroundLock && this.backgroundLock.packageName !== packageName) {\n // Another bg app already holds the lock — drop.\n resolve?.({status: \"blocked\", reason: \"another background app holds the display\"})\n return\n }\n\n // Acquire or bump the lock.\n if (!this.backgroundLock) {\n this.backgroundLock = {\n packageName,\n acquiredAt: now,\n expiresAt: now + BACKGROUND_LOCK_TIMEOUT_MS,\n lastActiveTime: now,\n }\n } else {\n this.backgroundLock.lastActiveTime = now\n this.backgroundLock.expiresAt = now + BACKGROUND_LOCK_TIMEOUT_MS\n }\n\n this.sendNow(packageName, payload, resolve)\n }\n\n /**\n * An app cleared its display (\"render nothing\"). Same semantics as its\n * frame expiring: give up ownership, restore the core app's still-valid\n * display if there is one, otherwise clear the glasses.\n */\n private handleAppClear(packageName: string, resolve?: DisplayRequestResolver): void {\n // Forget the app's retained scene state — its next render starts fresh.\n sceneRenderer.clearApp(packageName)\n\n if (this.backgroundLock?.packageName === packageName) {\n this.backgroundLock = null\n }\n if (this.coreApp === packageName) {\n this.coreAppDisplay = null\n }\n\n if (this.currentDisplay?.packageName === packageName) {\n this.clearExpiryTimer()\n this.currentDisplay = null\n this.tryRestoreCoreDisplay()\n if (!this.currentDisplay) {\n this.sendClear()\n }\n }\n resolve?.({status: \"displayed\"})\n }\n\n // ===========================================================================\n // Internals — send\n // ===========================================================================\n\n private sendNow(packageName: string, payload: DisplayPayload, resolve?: DisplayRequestResolver): void {\n const expiresAt = payload.durationMs ? this.now() + payload.durationMs : null\n const view = (payload.view === \"dashboard\" ? \"dashboard\" : \"main\") as \"main\" | \"dashboard\"\n\n // Scene requests — and legacy layouts a positioning device can render as a\n // scene (sugar, old bundles) — go through the scene pipeline. Everything\n // else stays on the legacy path.\n let sceneElements = payload.scene ?? null\n if (!sceneElements && payload.layout) {\n const caps = sceneRenderer.currentCapabilities()\n if (caps?.canPosition) {\n sceneElements = sceneRenderer.convertLegacyLayout(payload.layout, caps)\n }\n }\n\n if (sceneElements) {\n this.sendScene(packageName, view, sceneElements, expiresAt, resolve)\n } else if (payload.layout) {\n // This layout bypasses the scene pipeline (clear_view, dashboard_card,\n // unknown types, or a non-positioning device). If this (app, view) had a\n // retained scene, the glasses no longer match it — reset the diff\n // baseline so the next render() repaints from empty instead of skipping\n // \"unchanged\" elements.\n sceneRenderer.resetBaseline(packageName, view)\n const rawEvent: Record<string, unknown> = {\n view: payload.view ?? \"main\",\n layout: payload.layout,\n }\n if (payload.durationMs !== undefined) {\n rawEvent.durationMs = payload.durationMs\n }\n this.sendToNative(packageName, rawEvent, expiresAt)\n resolve?.({status: \"displayed\"})\n } else {\n // A scene payload on a device with no display block at all.\n resolve?.({status: \"blocked\", reason: \"no display connected\"})\n return\n }\n\n // If core app fired, refresh the saved snapshot with the actual processed\n // event so restore uses the wrapped text (legacy) / the retained scene.\n if (packageName === this.coreApp && this.currentDisplay) {\n this.coreAppDisplay = {\n packageName,\n processedEvent: this.currentDisplay.processedEvent,\n scene: this.currentDisplay.scene,\n expiresAt: this.currentDisplay.expiresAt,\n }\n }\n }\n\n /**\n * Send a scene through SceneRenderer. On positioning devices this emits a\n * SceneFrame; on degrade devices SceneRenderer hands back a legacy layout\n * that rides the existing (unchanged) DisplayProcessor path.\n */\n private sendScene(\n packageName: string,\n view: \"main\" | \"dashboard\",\n elements: SceneElementInput[],\n expiresAt: number | null,\n resolve?: DisplayRequestResolver,\n ): void {\n const result = sceneRenderer.emitScene(packageName, view, elements)\n\n if (result.kind === \"no-display\") {\n resolve?.({status: \"blocked\", reason: \"no display connected\"})\n return\n }\n\n if (result.kind === \"legacy\") {\n if (result.layout) {\n this.sendToNative(packageName, {view, layout: result.layout}, expiresAt)\n } else {\n // Scene degraded to nothing (e.g. all-image scene on a text-only\n // device). render() replaces the frame, so nothing means clear.\n this.sendClear()\n }\n resolve?.({status: \"displayed\", degraded: result.degraded, dropped: result.dropped})\n return\n }\n\n this.currentDisplay = {packageName, processedEvent: {}, scene: elements, expiresAt}\n this.clearExpiryTimer()\n if (expiresAt !== null) {\n const delay = Math.max(0, expiresAt - this.now())\n this.expiryTimerId = BgTimer.setTimeout(() => {\n this.expiryTimerId = null\n this.handleExpiry(packageName)\n }, delay)\n }\n resolve?.({status: \"displayed\", degraded: result.degraded, dropped: result.dropped})\n }\n\n private sendToNative(packageName: string, rawEvent: Record<string, unknown>, expiresAt: number | null): void {\n let processedEvent: Record<string, unknown>\n try {\n processedEvent = displayProcessor.processDisplayEvent(rawEvent as any)\n } catch (err) {\n console.error(`${LOG_TAG}: DisplayProcessor error, using raw event:`, err)\n processedEvent = rawEvent\n }\n\n try {\n // The display output path is a direct btsdk call now (was a host sendDisplayEvent\n // hook) so a bare OEM renders to the glasses without wiring it.\n void Promise.resolve(BluetoothSdk.displayEvent(processedEvent)).catch((err) => {\n console.error(`${LOG_TAG}: native display failed:`, err)\n })\n // The mirror store also lives in engine, so display requests update the\n // phone-side preview without any host hook.\n useDisplayStore.getState().setDisplayEvent(JSON.stringify(processedEvent))\n } catch (err) {\n console.error(`${LOG_TAG}: native display failed:`, err)\n }\n\n this.currentDisplay = {packageName, processedEvent, expiresAt}\n\n this.clearExpiryTimer()\n if (expiresAt !== null) {\n const delay = Math.max(0, expiresAt - this.now())\n this.expiryTimerId = BgTimer.setTimeout(() => {\n this.expiryTimerId = null\n this.handleExpiry(packageName)\n }, delay)\n }\n }\n\n private handleExpiry(packageName: string): void {\n // Someone else is on the glasses now — ignore.\n if (!this.currentDisplay || this.currentDisplay.packageName !== packageName) {\n return\n }\n\n // Release the lock if it belonged to this (bg) app.\n if (this.backgroundLock?.packageName === packageName) {\n this.backgroundLock = null\n }\n\n this.currentDisplay = null\n this.tryRestoreCoreDisplay()\n if (!this.currentDisplay) {\n this.sendClear()\n }\n }\n\n private tryRestoreCoreDisplay(): void {\n if (!this.coreAppDisplay) return\n // If the core snapshot has a duration and it's already elapsed, skip.\n if (this.coreAppDisplay.expiresAt !== null && this.now() >= this.coreAppDisplay.expiresAt) {\n this.coreAppDisplay = null\n return\n }\n // Re-send the core's saved display.\n this.restoreDisplay(this.coreAppDisplay)\n }\n\n private sendClear(): void {\n const clearEvent: Record<string, unknown> = {\n view: \"main\",\n layout: {layoutType: \"clear_view\"},\n }\n this.sendToNative(\"system.clear\", clearEvent, null)\n this.currentDisplay = null\n // The glasses main view is now blank: every app's retained scene is\n // replay-only until it re-renders — diffing against it would skip\n // \"unchanged\" elements onto the blank screen.\n sceneRenderer.markViewStale(\"main\")\n }\n\n // ===========================================================================\n // Internals — boot\n // ===========================================================================\n\n private cancelBoot(): void {\n if (!this.bootingApp) return\n BgTimer.clearTimeout(this.bootingApp.timerId)\n this.bootingApp = null\n }\n\n private endBoot(): void {\n if (!this.bootingApp) return\n const preBoot = this.bootingApp.preBoot\n BgTimer.clearTimeout(this.bootingApp.timerId)\n this.bootingApp = null\n\n // Drain the queue: core first, then everyone else. Per-app last wins\n // because the map holds one payload per packageName.\n const queued = Array.from(this.bootQueue.entries())\n this.bootQueue.clear()\n\n const corePayload = this.coreApp ? queued.find(([pkg]) => pkg === this.coreApp) : undefined\n if (corePayload) {\n this.arbitrateAndSend(corePayload[0], corePayload[1].payload, corePayload[1].resolve)\n }\n for (const [pkg, entry] of queued) {\n if (pkg === this.coreApp) continue\n this.arbitrateAndSend(pkg, entry.payload, entry.resolve)\n }\n\n // If the boot text is still on the glasses — nothing was queued, or every\n // drained request lost arbitration — it must not strand: it's a system\n // frame with no expiry. Restore whatever was on the glasses before the\n // boot, else clear. Two reasons NOT to restore: the frame's duration\n // elapsed during the boot window, or its owner FORFEITED it during the\n // window (its final queued intent — the queue keeps last-wins per app —\n // was a clear, which the drain above processed as a forfeit; restoring\n // would resurrect content the clear removed).\n if (this.currentDisplay?.packageName === SYSTEM_BOOT_PKG) {\n const ownerEntry = preBoot ? queued.find(([pkg]) => pkg === preBoot.packageName) : undefined\n const preBootForfeited = ownerEntry !== undefined && isClearPayload(ownerEntry[1].payload)\n if (preBoot && !preBootForfeited && (preBoot.expiresAt === null || preBoot.expiresAt > this.now())) {\n this.restoreDisplay(preBoot)\n } else {\n this.sendClear()\n }\n }\n }\n\n /**\n * Re-push a previously-captured on-glasses frame (e.g. the display a boot\n * window overwrote). Mirrors tryRestoreCoreDisplay: the saved event is already\n * processed, and any remaining duration is recomputed from its expiry.\n */\n private restoreDisplay(saved: ActiveDisplay): void {\n const remaining = saved.expiresAt !== null ? Math.max(0, saved.expiresAt - this.now()) : undefined\n\n // Scene restores must REPLAY (create-based, epoch-bumped): re-emitting the\n // scene through the normal diff would come back all-\"unchanged\" and the\n // device would skip every element (spec §4 — replay is always create-based).\n if (saved.scene) {\n const expiresAt = remaining !== undefined ? this.now() + remaining : null\n if (sceneRenderer.replayApp(saved.packageName, \"main\")) {\n this.currentDisplay = {packageName: saved.packageName, processedEvent: {}, scene: saved.scene, expiresAt}\n this.clearExpiryTimer()\n if (expiresAt !== null) {\n const delay = Math.max(0, expiresAt - this.now())\n this.expiryTimerId = BgTimer.setTimeout(() => {\n this.expiryTimerId = null\n this.handleExpiry(saved.packageName)\n }, delay)\n }\n } else {\n // Retained state gone (app stopped since) — re-render the saved\n // elements as a fresh scene instead.\n this.sendNow(saved.packageName, {view: \"main\", scene: saved.scene, durationMs: remaining})\n }\n return\n }\n\n this.sendNow(saved.packageName, {\n view: \"main\",\n layout: saved.processedEvent.layout as DisplayPayload[\"layout\"],\n durationMs: remaining,\n })\n }\n\n // ===========================================================================\n // Reconnect replay (design doc §3.4.7 — recovery = replay, host-driven)\n // ===========================================================================\n\n /**\n * Subscribe to glasses connection state so the current owner's frame is\n * replayed after a reconnect. Safe to call repeatedly; attaches once. Wired\n * from MantleManager next to displayProcessor.attachToRuntime().\n */\n public attachToRuntime(): void {\n if (this.runtimeAttached) return\n // dev wired this through the (since-removed) glassesStatus runtime hooks;\n // engine reads its own glasses store directly.\n this.glassesWasConnected = isGlassesConnected(useGlassesStore.getState().connection)\n this.unsubscribeGlassesStatus = useGlassesStore.subscribe(() => {\n const connected = isGlassesConnected(useGlassesStore.getState().connection)\n if (connected === this.glassesWasConnected) return\n this.glassesWasConnected = connected\n if (connected) {\n this.replayCurrent()\n }\n })\n this.runtimeAttached = true\n }\n\n /**\n * Re-push the current on-glasses frame after a device recovery. Scenes replay\n * from SceneRenderer's retained state (create-based, epoch-bumped); legacy\n * frames re-send their processed event. Expired frames don't come back.\n */\n public replayCurrent(): void {\n const current = this.currentDisplay\n if (!current) return\n if (current.expiresAt !== null && this.now() >= current.expiresAt) return\n\n console.log(`${LOG_TAG}: replayCurrent(${current.packageName})`)\n if (current.scene) {\n if (!sceneRenderer.replayApp(current.packageName, \"main\")) {\n // Retained state gone (app stopped between disconnect and reconnect).\n this.sendClear()\n }\n } else if (current.processedEvent.layout) {\n this.sendToNative(current.packageName, current.processedEvent, current.expiresAt)\n }\n }\n\n // ===========================================================================\n // Internals — timer helpers\n // ===========================================================================\n\n private clearExpiryTimer(): void {\n if (this.expiryTimerId !== null) {\n BgTimer.clearTimeout(this.expiryTimerId)\n this.expiryTimerId = null\n }\n }\n\n // ===========================================================================\n // Test hooks\n // ===========================================================================\n\n /** Injected clock — tests only. */\n public _setNowForTest(fn: () => number): void {\n this.now = fn\n }\n\n /** Wipe state — tests only. */\n public _resetForTest(): void {\n this.cancelBoot()\n this.clearExpiryTimer()\n this.coreApp = null\n this.coreAppDisplay = null\n this.currentDisplay = null\n this.backgroundLock = null\n this.bootQueue.clear()\n this.now = () => Date.now()\n this.unsubscribeGlassesStatus?.()\n this.unsubscribeGlassesStatus = null\n this.runtimeAttached = false\n this.glassesWasConnected = false\n sceneRenderer._resetForTest()\n }\n\n /** Read-only state snapshot — tests only. */\n public _peekForTest(): {\n coreApp: string | null\n currentDisplayPkg: string | null\n bgLockPkg: string | null\n isBooting: boolean\n } {\n return {\n coreApp: this.coreApp,\n currentDisplayPkg: this.currentDisplay?.packageName ?? null,\n bgLockPkg: this.backgroundLock?.packageName ?? null,\n isBooting: this.bootingApp !== null,\n }\n }\n}\n\nconst localDisplayManager = LocalDisplayManager.getInstance()\nexport default localDisplayManager\nexport {LocalDisplayManager}\n"]}
|
|
1
|
+
{"version":3,"file":"LocalDisplayManager.js","sourceRoot":"","sources":["../../src/services/LocalDisplayManager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * LocalDisplayManager\n *\n * Phone-side arbiter for display requests originating from LOCAL miniapps\n * (the @mentra/miniapp SDK). Preserves the retired cloud display manager's\n * user-facing behavior for the features that matter on the phone:\n *\n * - boot message (\"Starting <AppName>…\") with a bounded window\n * - durationMs auto-clear\n * - core-app vs background-app arbitration with a background lock\n *\n * Display pacing is NOT done here. A JS-side ~300ms throttle used to live in\n * this class, but JS timers (and therefore the throttle's trailing flush) do\n * not fire on schedule while the iOS app is suspended in the background — so\n * under streaming captions, updates accumulated and then flushed in a burst on\n * the next wake. Pacing + last-wins coalescing now live in the native per-device\n * queue (e.g. G2's event-driven EvenHub queue), which drains off the same BLE\n * events that wake the process. This class forwards every request straight\n * through; the native layer collapses redundant frames per display target.\n *\n * The remaining BgTimer use (boot window, durationMs expiry) is timing that\n * does not need sub-second background fidelity.\n */\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport displayProcessor from \"./DisplayProcessor\"\nimport {useDisplayStore} from \"../stores/display\"\nimport sceneRenderer from \"./SceneRenderer\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport type {SceneElementInput} from \"../utils/display/scene\"\nimport {BgTimer} from \"../utils/timers\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DisplayPayload {\n view?: \"main\" | \"dashboard\" | string\n /** Legacy layout (sugar / old bundles / cloud shapes). Either this or `scene`. */\n layout?: {layoutType: string; [key: string]: unknown}\n /** Scene elements from display.render(). Either this or `layout`. */\n scene?: SceneElementInput[]\n durationMs?: number\n}\n\n/** Outcome delivered to an awaiting render() call (REQUEST_RESULT payload). */\nexport interface DisplayRequestResult {\n status: \"displayed\" | \"blocked\"\n degraded?: boolean\n dropped?: string[]\n reason?: string\n}\n\nexport type DisplayRequestResolver = (result: DisplayRequestResult) => void\n\ninterface ActiveDisplay {\n packageName: string\n processedEvent: Record<string, unknown>\n /** Set when this display is a scene — restore goes through SceneRenderer replay. */\n scene?: SceneElementInput[]\n expiresAt: number | null\n}\n\ninterface BootingApp {\n packageName: string\n displayName: string\n startedAt: number\n timerId: number\n /** Whatever was on the glasses when this boot started, so a boot that times\n * out without the app rendering can restore it instead of blanking a frame\n * another (e.g. foreground) app owns. Null if the glasses were empty. */\n preBoot: ActiveDisplay | null\n}\n\ninterface BackgroundLock {\n packageName: string\n acquiredAt: number\n expiresAt: number\n lastActiveTime: number\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst LOG_TAG = \"LOCAL_DISPLAY\"\nconst BOOT_DURATION_MS = 1500\n// Background renders renew this lease. An expired lock is dropped when the\n// next display request is arbitrated, allowing another app to take the screen.\nconst BACKGROUND_LOCK_TIMEOUT_MS = 10_000\n// Sentinel package name used as the \"source\" of the system boot message.\nconst SYSTEM_BOOT_PKG = \"system.boot\"\n\n/** \"Render nothing\" — an empty scene or an explicit clear_view layout. */\nfunction isClearPayload(payload: DisplayPayload): boolean {\n if (payload.scene) return payload.scene.length === 0\n return payload.layout?.layoutType === \"clear_view\"\n}\n\n// =============================================================================\n// LocalDisplayManager\n// =============================================================================\n\nclass LocalDisplayManager {\n private static instance: LocalDisplayManager | null = null\n\n private coreApp: string | null = null\n private coreAppDisplay: ActiveDisplay | null = null\n private currentDisplay: ActiveDisplay | null = null\n private backgroundLock: BackgroundLock | null = null\n\n private bootingApp: BootingApp | null = null\n private bootQueue: Map<string, {payload: DisplayPayload; resolve?: DisplayRequestResolver}> = new Map()\n\n private expiryTimerId: number | null = null\n\n /** Reconnect-replay subscription state (attachToRuntime). */\n private runtimeAttached = false\n private glassesWasConnected = false\n private unsubscribeGlassesStatus: (() => void) | null = null\n\n /** Injectable for tests. */\n private now: () => number = () => Date.now()\n\n private constructor() {}\n\n public static getInstance(): LocalDisplayManager {\n if (!LocalDisplayManager.instance) {\n LocalDisplayManager.instance = new LocalDisplayManager()\n }\n return LocalDisplayManager.instance\n }\n\n // ===========================================================================\n // Lifecycle API (called from MiniappHost)\n // ===========================================================================\n\n /**\n * A new local miniapp just mounted. Start the boot window.\n * Calling this for a different pkg while another is booting cancels the\n * previous boot.\n */\n public onMount(packageName: string, displayName: string): void {\n console.log(`${LOG_TAG}: onMount(${packageName}, \"${displayName}\")`)\n\n // Snapshot whatever's on the glasses BEFORE the boot text overwrites it.\n // registerApp fires onMount for every spawn — including a background app or\n // a crash-respawn while another app is foreground — so a boot that times out\n // without rendering must put the prior frame back rather than blank it.\n // Never snapshot the boot text itself: back-to-back mounts (the autostart\n // restore loop, crash-respawn chains) land while the previous app's\n // \"Starting X…\" is still on the glasses, and endBoot would \"restore\" that\n // stale, expiry-less frame permanently. Across chained boots, inherit the\n // original pre-boot frame instead.\n const inherited = this.bootingApp ? this.bootingApp.preBoot : this.currentDisplay\n const preBoot = inherited?.packageName === SYSTEM_BOOT_PKG ? null : inherited\n\n // Cancel any in-flight boot for a prior app.\n this.cancelBoot()\n\n // Send the boot message directly (bypass arbitration — this is a system\n // display). Goes through sendNow so scene devices render it as a scene:\n // otherwise every app start would be a legacy→scene transition on the\n // glasses (which costs a blanking clear).\n this.sendNow(SYSTEM_BOOT_PKG, {\n view: \"main\",\n layout: {layoutType: \"text_wall\", text: `Starting ${displayName}…`},\n })\n\n const timerId = BgTimer.setTimeout(() => {\n this.endBoot()\n }, BOOT_DURATION_MS)\n\n this.bootingApp = {\n packageName,\n displayName,\n startedAt: this.now(),\n timerId,\n preBoot,\n }\n this.bootQueue.clear()\n }\n\n /**\n * Mark which miniapp is the \"core\" (foreground) app. Pass null when no local\n * miniapp is foreground.\n */\n public onCoreAppChange(packageName: string | null): void {\n if (this.coreApp === packageName) return\n console.log(`${LOG_TAG}: onCoreAppChange(${packageName ?? \"null\"})`)\n\n this.coreApp = packageName\n\n // No core app means no saved frame to restore on the next unmount.\n // Without this, the previous core's last frame can flicker back onto\n // the glasses when the user-initiated clear is followed by an\n // onUnmount that still finds a populated coreAppDisplay.\n if (packageName === null) {\n this.coreAppDisplay = null\n }\n }\n\n /** Report-safe ownership snapshot for incident diagnostics. */\n public getDiagnosticSnapshot(): Record<string, unknown> {\n return {\n coreAppPackageName: this.coreApp,\n currentDisplayPackageName: this.currentDisplay?.packageName ?? null,\n backgroundLockPackageName: this.backgroundLock?.packageName ?? null,\n bootingPackageName: this.bootingApp?.packageName ?? null,\n }\n }\n\n /**\n * A local miniapp is going away. Clean up all state associated with it.\n */\n public onUnmount(packageName: string): void {\n console.log(`${LOG_TAG}: onUnmount(${packageName})`)\n\n // If this was the booting app, cancel boot — and don't strand its\n // \"Starting X…\" text (a system frame with no expiry): put back what the\n // boot covered, or clear.\n if (this.bootingApp?.packageName === packageName) {\n const preBoot = this.bootingApp.preBoot\n this.cancelBoot()\n if (this.currentDisplay?.packageName === SYSTEM_BOOT_PKG) {\n // The preBoot owner may have forfeited its frame during the boot\n // window (a clear parked in the boot queue) — don't resurrect it.\n const ownerQueued = preBoot ? this.bootQueue.get(preBoot.packageName) : undefined\n const preBootForfeited = ownerQueued !== undefined && isClearPayload(ownerQueued.payload)\n if (preBoot && !preBootForfeited && (preBoot.expiresAt === null || preBoot.expiresAt > this.now())) {\n this.restoreDisplay(preBoot)\n } else {\n this.sendClear()\n }\n }\n }\n this.bootQueue.get(packageName)?.resolve?.({status: \"blocked\", reason: \"app stopped\"})\n this.bootQueue.delete(packageName)\n\n // Forget the app's retained scenes — nothing left to replay for it.\n sceneRenderer.clearApp(packageName)\n\n // Release bg lock if this app held it.\n if (this.backgroundLock?.packageName === packageName) {\n this.backgroundLock = null\n }\n\n // Drop the saved core frame if the core's context is going away — the\n // frame belongs to a dead context and must not be restored later. The\n // coreApp POINTER itself is NOT cleared here: it is derived state owned\n // by the apps-store reconciler. A liveness/crash respawn tears the\n // context down (this path) without any store change — the app is still\n // \"running\" — and clearing the pointer here would leave the respawned\n // foreground app arbitrating as background until an unrelated store\n // write happened to re-run the reconciler. Real stops release the slot\n // via the store (running → false → reconciler → onCoreAppChange(null)).\n if (this.coreApp === packageName) {\n this.coreAppDisplay = null\n }\n\n // If this app owned the current on-glasses display, clear it (and maybe\n // restore the core app's display if it still has time left).\n if (this.currentDisplay?.packageName === packageName) {\n this.clearExpiryTimer()\n this.currentDisplay = null\n this.tryRestoreCoreDisplay()\n if (!this.currentDisplay) {\n // Nothing to restore → clear the glasses.\n this.sendClear()\n }\n }\n }\n\n // ===========================================================================\n // Display request API (called from LocalMiniappRuntime.handleDisplay)\n // ===========================================================================\n\n /**\n * A miniapp requested a display. Routes through boot queue, arbitration,\n * and throttle. This is the only public entry point for miniapp display\n * traffic on the local path.\n *\n * `resolve`, when provided (render() requests), is guaranteed to be called\n * exactly once with the request's outcome — including for requests parked in\n * the boot queue (resolved when drained or superseded).\n */\n public request(packageName: string, payload: DisplayPayload, resolve?: DisplayRequestResolver): void {\n // Dashboard view: pass straight through (no throttle/arbitration). Local\n // dashboard rendering is currently a stub on the phone anyway.\n if (payload.view === \"dashboard\") {\n this.sendNow(packageName, payload, resolve)\n return\n }\n\n // During boot, any app's requests go into the queue. If the booting app\n // itself makes its first display call, end boot early and drain.\n if (this.bootingApp) {\n const superseded = this.bootQueue.get(packageName)\n superseded?.resolve?.({status: \"blocked\", reason: \"superseded by a newer frame\"})\n this.bootQueue.set(packageName, {payload, resolve})\n if (this.bootingApp.packageName === packageName) {\n this.endBoot()\n }\n return\n }\n\n this.arbitrateAndSend(packageName, payload, resolve)\n }\n\n /**\n * End a package's temporary display early. If it currently owns the main\n * view, release its background lock and restore the foreground miniapp's\n * saved frame. A dismissal for a package that is no longer visible is a\n * no-op.\n */\n public dismiss(packageName: string): void {\n this.handleExpiry(packageName)\n }\n\n // ===========================================================================\n // Internals — arbitration\n // ===========================================================================\n\n private arbitrateAndSend(packageName: string, payload: DisplayPayload, resolve?: DisplayRequestResolver): void {\n const now = this.now()\n\n // Expire a stale bg lock before arbitrating.\n if (this.backgroundLock && now > this.backgroundLock.expiresAt) {\n this.backgroundLock = null\n }\n\n // A clear (\"render nothing\") means \"I'm done with the display\" — it is a\n // forfeit, not a frame. A background app releases its lock and the core\n // app's still-valid display comes back; treating it as a render instead\n // would RENEW the lock with an empty frame and blank the glasses while\n // still blocking everyone else.\n if (isClearPayload(payload)) {\n this.handleAppClear(packageName, resolve)\n return\n }\n\n const isCore = packageName === this.coreApp\n\n if (isCore) {\n // Save so we can restore later.\n this.coreAppDisplay = {\n packageName,\n processedEvent: {}, // filled by sendNow\n scene: payload.scene,\n expiresAt: payload.durationMs ? now + payload.durationMs : null,\n }\n\n // If a bg app holds the lock AND is currently on the glasses, core is\n // blocked. The saved coreAppDisplay surfaces when the bg app expires\n // or unmounts.\n const bgHoldsAndDisplays =\n this.backgroundLock &&\n this.currentDisplay &&\n this.currentDisplay.packageName === this.backgroundLock.packageName\n if (bgHoldsAndDisplays) {\n resolve?.({status: \"blocked\", reason: \"a background app holds the display\"})\n return\n }\n\n // Core can display. If a bg lock exists but holder isn't actively on\n // the glasses, release it.\n if (this.backgroundLock && !bgHoldsAndDisplays) {\n this.backgroundLock = null\n }\n\n this.sendNow(packageName, payload, resolve)\n return\n }\n\n // Non-core: this is a background app.\n if (this.backgroundLock && this.backgroundLock.packageName !== packageName) {\n // Another bg app already holds the lock — drop.\n resolve?.({status: \"blocked\", reason: \"another background app holds the display\"})\n return\n }\n\n // Acquire or bump the lock.\n if (!this.backgroundLock) {\n this.backgroundLock = {\n packageName,\n acquiredAt: now,\n expiresAt: now + BACKGROUND_LOCK_TIMEOUT_MS,\n lastActiveTime: now,\n }\n } else {\n this.backgroundLock.lastActiveTime = now\n this.backgroundLock.expiresAt = now + BACKGROUND_LOCK_TIMEOUT_MS\n }\n\n this.sendNow(packageName, payload, resolve)\n }\n\n /**\n * An app cleared its display (\"render nothing\"). Same semantics as its\n * frame expiring: give up ownership, restore the core app's still-valid\n * display if there is one, otherwise clear the glasses.\n */\n private handleAppClear(packageName: string, resolve?: DisplayRequestResolver): void {\n // Forget the app's retained scene state — its next render starts fresh.\n sceneRenderer.clearApp(packageName)\n\n if (this.backgroundLock?.packageName === packageName) {\n this.backgroundLock = null\n }\n if (this.coreApp === packageName) {\n this.coreAppDisplay = null\n }\n\n if (this.currentDisplay?.packageName === packageName) {\n this.clearExpiryTimer()\n this.currentDisplay = null\n this.tryRestoreCoreDisplay()\n if (!this.currentDisplay) {\n this.sendClear()\n }\n }\n resolve?.({status: \"displayed\"})\n }\n\n // ===========================================================================\n // Internals — send\n // ===========================================================================\n\n private sendNow(packageName: string, payload: DisplayPayload, resolve?: DisplayRequestResolver): void {\n const expiresAt = payload.durationMs ? this.now() + payload.durationMs : null\n const view = (payload.view === \"dashboard\" ? \"dashboard\" : \"main\") as \"main\" | \"dashboard\"\n\n // Scene requests — and legacy layouts a positioning device can render as a\n // scene (sugar, old bundles) — go through the scene pipeline. Everything\n // else stays on the legacy path.\n let sceneElements = payload.scene ?? null\n if (!sceneElements && payload.layout) {\n const caps = sceneRenderer.currentCapabilities()\n if (caps?.canPosition) {\n sceneElements = sceneRenderer.convertLegacyLayout(payload.layout, caps)\n }\n }\n\n if (sceneElements) {\n this.sendScene(packageName, view, sceneElements, expiresAt, resolve)\n } else if (payload.layout) {\n // This layout bypasses the scene pipeline (clear_view, dashboard_card,\n // unknown types, or a non-positioning device). If this (app, view) had a\n // retained scene, the glasses no longer match it — reset the diff\n // baseline so the next render() repaints from empty instead of skipping\n // \"unchanged\" elements.\n sceneRenderer.resetBaseline(packageName, view)\n const rawEvent: Record<string, unknown> = {\n view: payload.view ?? \"main\",\n layout: payload.layout,\n }\n if (payload.durationMs !== undefined) {\n rawEvent.durationMs = payload.durationMs\n }\n this.sendToNative(packageName, rawEvent, expiresAt)\n resolve?.({status: \"displayed\"})\n } else {\n // A scene payload on a device with no display block at all.\n resolve?.({status: \"blocked\", reason: \"no display connected\"})\n return\n }\n\n // If core app fired, refresh the saved snapshot with the actual processed\n // event so restore uses the wrapped text (legacy) / the retained scene.\n if (packageName === this.coreApp && this.currentDisplay) {\n this.coreAppDisplay = {\n packageName,\n processedEvent: this.currentDisplay.processedEvent,\n scene: this.currentDisplay.scene,\n expiresAt: this.currentDisplay.expiresAt,\n }\n }\n }\n\n /**\n * Send a scene through SceneRenderer. On positioning devices this emits a\n * SceneFrame; on degrade devices SceneRenderer hands back a legacy layout\n * that rides the existing (unchanged) DisplayProcessor path.\n */\n private sendScene(\n packageName: string,\n view: \"main\" | \"dashboard\",\n elements: SceneElementInput[],\n expiresAt: number | null,\n resolve?: DisplayRequestResolver,\n ): void {\n const result = sceneRenderer.emitScene(packageName, view, elements)\n\n if (result.kind === \"no-display\") {\n resolve?.({status: \"blocked\", reason: \"no display connected\"})\n return\n }\n\n if (result.kind === \"legacy\") {\n if (result.layout) {\n this.sendToNative(packageName, {view, layout: result.layout}, expiresAt)\n } else {\n // Scene degraded to nothing (e.g. all-image scene on a text-only\n // device). render() replaces the frame, so nothing means clear.\n this.sendClear()\n }\n resolve?.({status: \"displayed\", degraded: result.degraded, dropped: result.dropped})\n return\n }\n\n this.currentDisplay = {packageName, processedEvent: {}, scene: elements, expiresAt}\n this.clearExpiryTimer()\n if (expiresAt !== null) {\n const delay = Math.max(0, expiresAt - this.now())\n this.expiryTimerId = BgTimer.setTimeout(() => {\n this.expiryTimerId = null\n this.handleExpiry(packageName)\n }, delay)\n }\n resolve?.({status: \"displayed\", degraded: result.degraded, dropped: result.dropped})\n }\n\n private sendToNative(packageName: string, rawEvent: Record<string, unknown>, expiresAt: number | null): void {\n let processedEvent: Record<string, unknown>\n try {\n processedEvent = displayProcessor.processDisplayEvent(rawEvent as any)\n } catch (err) {\n console.error(`${LOG_TAG}: DisplayProcessor error, using raw event:`, err)\n processedEvent = rawEvent\n }\n\n try {\n // The display output path is a direct btsdk call now (was a host sendDisplayEvent\n // hook) so a bare OEM renders to the glasses without wiring it.\n void Promise.resolve(BluetoothSdk.displayEvent(processedEvent)).catch((err) => {\n console.error(`${LOG_TAG}: native display failed:`, err)\n })\n // The mirror store also lives in engine, so display requests update the\n // phone-side preview without any host hook.\n useDisplayStore.getState().setDisplayEvent(JSON.stringify(processedEvent))\n } catch (err) {\n console.error(`${LOG_TAG}: native display failed:`, err)\n }\n\n this.currentDisplay = {packageName, processedEvent, expiresAt}\n\n this.clearExpiryTimer()\n if (expiresAt !== null) {\n const delay = Math.max(0, expiresAt - this.now())\n this.expiryTimerId = BgTimer.setTimeout(() => {\n this.expiryTimerId = null\n this.handleExpiry(packageName)\n }, delay)\n }\n }\n\n private handleExpiry(packageName: string): void {\n // Someone else is on the glasses now — ignore.\n if (!this.currentDisplay || this.currentDisplay.packageName !== packageName) {\n return\n }\n\n // Release the lock if it belonged to this (bg) app.\n if (this.backgroundLock?.packageName === packageName) {\n this.backgroundLock = null\n }\n\n this.currentDisplay = null\n this.tryRestoreCoreDisplay()\n if (!this.currentDisplay) {\n this.sendClear()\n }\n }\n\n private tryRestoreCoreDisplay(): void {\n if (!this.coreAppDisplay) return\n // If the core snapshot has a duration and it's already elapsed, skip.\n if (this.coreAppDisplay.expiresAt !== null && this.now() >= this.coreAppDisplay.expiresAt) {\n this.coreAppDisplay = null\n return\n }\n // Re-send the core's saved display.\n this.restoreDisplay(this.coreAppDisplay)\n }\n\n private sendClear(): void {\n const clearEvent: Record<string, unknown> = {\n view: \"main\",\n layout: {layoutType: \"clear_view\"},\n }\n this.sendToNative(\"system.clear\", clearEvent, null)\n this.currentDisplay = null\n // The glasses main view is now blank: every app's retained scene is\n // replay-only until it re-renders — diffing against it would skip\n // \"unchanged\" elements onto the blank screen.\n sceneRenderer.markViewStale(\"main\")\n }\n\n // ===========================================================================\n // Internals — boot\n // ===========================================================================\n\n private cancelBoot(): void {\n if (!this.bootingApp) return\n BgTimer.clearTimeout(this.bootingApp.timerId)\n this.bootingApp = null\n }\n\n private endBoot(): void {\n if (!this.bootingApp) return\n const preBoot = this.bootingApp.preBoot\n BgTimer.clearTimeout(this.bootingApp.timerId)\n this.bootingApp = null\n\n // Drain the queue: core first, then everyone else. Per-app last wins\n // because the map holds one payload per packageName.\n const queued = Array.from(this.bootQueue.entries())\n this.bootQueue.clear()\n\n const corePayload = this.coreApp ? queued.find(([pkg]) => pkg === this.coreApp) : undefined\n if (corePayload) {\n this.arbitrateAndSend(corePayload[0], corePayload[1].payload, corePayload[1].resolve)\n }\n for (const [pkg, entry] of queued) {\n if (pkg === this.coreApp) continue\n this.arbitrateAndSend(pkg, entry.payload, entry.resolve)\n }\n\n // If the boot text is still on the glasses — nothing was queued, or every\n // drained request lost arbitration — it must not strand: it's a system\n // frame with no expiry. Restore whatever was on the glasses before the\n // boot, else clear. Two reasons NOT to restore: the frame's duration\n // elapsed during the boot window, or its owner FORFEITED it during the\n // window (its final queued intent — the queue keeps last-wins per app —\n // was a clear, which the drain above processed as a forfeit; restoring\n // would resurrect content the clear removed).\n if (this.currentDisplay?.packageName === SYSTEM_BOOT_PKG) {\n const ownerEntry = preBoot ? queued.find(([pkg]) => pkg === preBoot.packageName) : undefined\n const preBootForfeited = ownerEntry !== undefined && isClearPayload(ownerEntry[1].payload)\n if (preBoot && !preBootForfeited && (preBoot.expiresAt === null || preBoot.expiresAt > this.now())) {\n this.restoreDisplay(preBoot)\n } else {\n this.sendClear()\n }\n }\n }\n\n /**\n * Re-push a previously-captured on-glasses frame (e.g. the display a boot\n * window overwrote). Mirrors tryRestoreCoreDisplay: the saved event is already\n * processed, and any remaining duration is recomputed from its expiry.\n */\n private restoreDisplay(saved: ActiveDisplay): void {\n const remaining = saved.expiresAt !== null ? Math.max(0, saved.expiresAt - this.now()) : undefined\n\n // Scene restores must REPLAY (create-based, epoch-bumped): re-emitting the\n // scene through the normal diff would come back all-\"unchanged\" and the\n // device would skip every element (spec §4 — replay is always create-based).\n if (saved.scene) {\n const expiresAt = remaining !== undefined ? this.now() + remaining : null\n if (sceneRenderer.replayApp(saved.packageName, \"main\")) {\n this.currentDisplay = {packageName: saved.packageName, processedEvent: {}, scene: saved.scene, expiresAt}\n this.clearExpiryTimer()\n if (expiresAt !== null) {\n const delay = Math.max(0, expiresAt - this.now())\n this.expiryTimerId = BgTimer.setTimeout(() => {\n this.expiryTimerId = null\n this.handleExpiry(saved.packageName)\n }, delay)\n }\n } else {\n // Retained state gone (app stopped since) — re-render the saved\n // elements as a fresh scene instead.\n this.sendNow(saved.packageName, {view: \"main\", scene: saved.scene, durationMs: remaining})\n }\n return\n }\n\n this.sendNow(saved.packageName, {\n view: \"main\",\n layout: saved.processedEvent.layout as DisplayPayload[\"layout\"],\n durationMs: remaining,\n })\n }\n\n // ===========================================================================\n // Reconnect replay (design doc §3.4.7 — recovery = replay, host-driven)\n // ===========================================================================\n\n /**\n * Subscribe to glasses connection state so the current owner's frame is\n * replayed after a reconnect. Safe to call repeatedly; attaches once. Wired\n * from MantleManager next to displayProcessor.attachToRuntime().\n */\n public attachToRuntime(): void {\n if (this.runtimeAttached) return\n // dev wired this through the (since-removed) glassesStatus runtime hooks;\n // engine reads its own glasses store directly.\n this.glassesWasConnected = isGlassesConnected(useGlassesStore.getState().connection)\n this.unsubscribeGlassesStatus = useGlassesStore.subscribe(() => {\n const connected = isGlassesConnected(useGlassesStore.getState().connection)\n if (connected === this.glassesWasConnected) return\n this.glassesWasConnected = connected\n if (connected) {\n this.replayCurrent()\n }\n })\n this.runtimeAttached = true\n }\n\n /**\n * Re-push the current on-glasses frame after a device recovery. Scenes replay\n * from SceneRenderer's retained state (create-based, epoch-bumped); legacy\n * frames re-send their processed event. Expired frames don't come back.\n */\n public replayCurrent(): void {\n const current = this.currentDisplay\n if (!current) return\n if (current.expiresAt !== null && this.now() >= current.expiresAt) return\n\n console.log(`${LOG_TAG}: replayCurrent(${current.packageName})`)\n if (current.scene) {\n if (!sceneRenderer.replayApp(current.packageName, \"main\")) {\n // Retained state gone (app stopped between disconnect and reconnect).\n this.sendClear()\n }\n } else if (current.processedEvent.layout) {\n this.sendToNative(current.packageName, current.processedEvent, current.expiresAt)\n }\n }\n\n // ===========================================================================\n // Internals — timer helpers\n // ===========================================================================\n\n private clearExpiryTimer(): void {\n if (this.expiryTimerId !== null) {\n BgTimer.clearTimeout(this.expiryTimerId)\n this.expiryTimerId = null\n }\n }\n\n // ===========================================================================\n // Test hooks\n // ===========================================================================\n\n /** Injected clock — tests only. */\n public _setNowForTest(fn: () => number): void {\n this.now = fn\n }\n\n /** Wipe state — tests only. */\n public _resetForTest(): void {\n this.cancelBoot()\n this.clearExpiryTimer()\n this.coreApp = null\n this.coreAppDisplay = null\n this.currentDisplay = null\n this.backgroundLock = null\n this.bootQueue.clear()\n this.now = () => Date.now()\n this.unsubscribeGlassesStatus?.()\n this.unsubscribeGlassesStatus = null\n this.runtimeAttached = false\n this.glassesWasConnected = false\n sceneRenderer._resetForTest()\n }\n\n /** Read-only state snapshot — tests only. */\n public _peekForTest(): {\n coreApp: string | null\n currentDisplayPkg: string | null\n bgLockPkg: string | null\n isBooting: boolean\n } {\n return {\n coreApp: this.coreApp,\n currentDisplayPkg: this.currentDisplay?.packageName ?? null,\n bgLockPkg: this.backgroundLock?.packageName ?? null,\n isBooting: this.bootingApp !== null,\n }\n }\n}\n\nconst localDisplayManager = LocalDisplayManager.getInstance()\nexport default localDisplayManager\nexport {LocalDisplayManager}\n"]}
|