@glassly/engine 0.1.0-dev.61 → 0.1.0-dev.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/build/engine.d.ts +7 -2
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +3 -0
- package/build/engine.js.map +1 -1
- package/build/facades/display.d.ts +10 -3
- package/build/facades/display.d.ts.map +1 -1
- package/build/facades/display.js +11 -1
- package/build/facades/display.js.map +1 -1
- package/build/facades/displayMirror.d.ts +2 -6
- package/build/facades/displayMirror.d.ts.map +1 -1
- package/build/facades/displayMirror.js +7 -14
- package/build/facades/displayMirror.js.map +1 -1
- package/build/facades/glasses.d.ts +1 -0
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +2 -0
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/ota.d.ts +3 -1
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +4 -2
- package/build/facades/ota.js.map +1 -1
- package/build/facades/phoneNotifications.d.ts +12 -2
- package/build/facades/phoneNotifications.d.ts.map +1 -1
- package/build/facades/phoneNotifications.js +30 -10
- package/build/facades/phoneNotifications.js.map +1 -1
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -2
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +6 -1
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
- package/build/react/MentraLiveOtaFlow.js +136 -14
- package/build/react/MentraLiveOtaFlow.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts +5 -1
- package/build/react/useMentraLiveOta.d.ts.map +1 -1
- package/build/react/useMentraLiveOta.js +135 -58
- package/build/react/useMentraLiveOta.js.map +1 -1
- package/build/services/CustomDashboardService.d.ts +11 -10
- package/build/services/CustomDashboardService.d.ts.map +1 -1
- package/build/services/CustomDashboardService.js +70 -69
- package/build/services/CustomDashboardService.js.map +1 -1
- package/build/services/DashboardCoordinator.d.ts +26 -0
- package/build/services/DashboardCoordinator.d.ts.map +1 -0
- package/build/services/DashboardCoordinator.js +125 -0
- package/build/services/DashboardCoordinator.js.map +1 -0
- package/build/services/DeviceEventRouter.d.ts.map +1 -1
- package/build/services/DeviceEventRouter.js +5 -7
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/DisplayProcessor.d.ts +3 -3
- package/build/services/DisplayProcessor.js +3 -3
- package/build/services/DisplayProcessor.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts +12 -2
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +68 -14
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/OtaInstallCoordinator.d.ts +2 -0
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +21 -1
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/PhoneNotificationsSync.d.ts +13 -0
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +101 -9
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- package/build/services/PhonePhotoCoordinator.js +1 -1
- package/build/services/PhonePhotoCoordinator.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js +9 -6
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/SceneRenderer.d.ts.map +1 -1
- package/build/services/SceneRenderer.js +5 -0
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/StreamLifecycleController.d.ts +4 -0
- package/build/services/StreamLifecycleController.d.ts.map +1 -1
- package/build/services/StreamLifecycleController.js +4 -0
- package/build/services/StreamLifecycleController.js.map +1 -1
- package/build/services/effectiveCapabilities.d.ts +1 -1
- package/build/services/effectiveCapabilities.d.ts.map +1 -1
- package/build/services/effectiveCapabilities.js +9 -4
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/services/iosNotificationAppCatalog.d.ts +13 -0
- package/build/services/iosNotificationAppCatalog.d.ts.map +1 -0
- package/build/services/iosNotificationAppCatalog.js +28 -0
- package/build/services/iosNotificationAppCatalog.js.map +1 -0
- package/build/services/otaInstallPolicy.d.ts +4 -0
- package/build/services/otaInstallPolicy.d.ts.map +1 -1
- package/build/services/otaInstallPolicy.js +4 -0
- package/build/services/otaInstallPolicy.js.map +1 -1
- package/build/stores/display.d.ts +4 -4
- package/build/stores/display.d.ts.map +1 -1
- package/build/stores/display.js +12 -36
- package/build/stores/display.js.map +1 -1
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js +2 -0
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +53 -2
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +1 -1
- package/build/types/applet.js.map +1 -1
- package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g2.js +2 -2
- package/build/types/capabilities/even-realities-g2.js.map +1 -1
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js +1 -0
- package/build/types/hardware.js.map +1 -1
- package/build/types/index.d.ts +1 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js +1 -1
- package/build/types/index.js.map +1 -1
- package/build/utils/display/composer/ColumnComposer.d.ts +1 -1
- package/build/utils/display/composer/ColumnComposer.js +1 -1
- package/build/utils/display/composer/ColumnComposer.js.map +1 -1
- package/package.json +8 -7
- package/src/engine.ts +3 -0
- package/src/facades/display.ts +11 -1
- package/src/facades/displayMirror.ts +7 -15
- package/src/facades/glasses.ts +2 -0
- package/src/facades/ota.ts +4 -2
- package/src/facades/phoneNotifications.ts +32 -10
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/index.ts +2 -1
- package/src/react/MentraLiveOtaFlow.tsx +226 -41
- package/src/react/useMentraLiveOta.ts +146 -60
- package/src/services/CustomDashboardService.ts +67 -66
- package/src/services/DashboardCoordinator.ts +121 -0
- package/src/services/DeviceEventRouter.ts +5 -7
- package/src/services/DisplayProcessor.ts +3 -3
- package/src/services/LocalDisplayManager.ts +68 -16
- package/src/services/OtaInstallCoordinator.ts +21 -0
- package/src/services/PhoneNotificationsSync.ts +125 -9
- package/src/services/PhonePhotoCoordinator.ts +1 -1
- package/src/services/PhoneStreamCoordinator.ts +10 -6
- package/src/services/SceneRenderer.ts +4 -0
- package/src/services/StreamLifecycleController.ts +4 -0
- package/src/services/effectiveCapabilities.ts +9 -4
- package/src/services/iosNotificationAppCatalog.ts +39 -0
- package/src/services/otaInstallPolicy.ts +4 -0
- package/src/stores/display.ts +16 -48
- package/src/stores/glasses.ts +2 -0
- package/src/stores/settings.ts +53 -2
- package/src/types/applet.ts +1 -1
- package/src/types/capabilities/even-realities-g2.ts +3 -2
- package/src/types/hardware.ts +1 -0
- package/src/types/index.ts +1 -1
- package/src/utils/display/composer/ColumnComposer.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomDashboardService.js","sourceRoot":"","sources":["../../src/services/CustomDashboardService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Scene API dashboard for G2 custom firmware.\n *\n * G2.swift marks the published firmware version after it detects the CFW\n * capability advertisement. The service retains a dashboard frame, owns\n * dashboard gestures while head-up is active, and leaves every other device\n * on its native behavior.\n *\n * The dashboard is composed of user-orderable widgets (clock, calendar, apps\n * button, notifications button) stacked vertically on the CFW scene canvas.\n * The apps button switches to a second, dashboard-rendered app menu view.\n */\nimport {File} from \"expo-file-system\"\n\nimport {BluetoothSdk} from \"@glassly/bluetooth-sdk\"\nimport {MiniappErrorCode, type CalendarEvent} from \"@glassly/miniapp\"\n\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport type {ClientApp} from \"../types/applet\"\nimport type {SceneElementInput} from \"../utils/display/scene\"\nimport {BgTimer} from \"../utils/timers\"\nimport {\n appMenuActions,\n dashboardActions,\n formatDashboardClock,\n isCustomDashboardEligible,\n normalizeDashboardWidgets,\n orderedDashboardApps,\n type CustomDashboardClockStyle,\n type DashboardWidgetSetting,\n type MenuAppSetting,\n} from \"./CustomDashboardModel\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {listPhoneCalendarEvents, PhoneCalendarError} from \"./PhoneCalendarService\"\nimport sceneRenderer from \"./SceneRenderer\"\n\nconst DASHBOARD_APP_ID = \"system.custom-dashboard\"\nconst CLOCK_REFRESH_MS = 30_000\nconst CALENDAR_REFRESH_MS = 5 * 60_000\n\n// CFW blits the stock compositor's 576×288 canvas; used when the firmware has\n// not reported scene capabilities yet.\nconst FALLBACK_CANVAS = {width: 576, height: 288}\nconst MARGIN_X = 16\nconst MARGIN_TOP = 10\nconst WIDGET_GAP = 10\n/** Row height for the glasses' builtin 20px font engine. */\nconst LINE_H = 24\nconst BUTTON_H = 34\n/**\n * App-menu icon grid: 2 rows × 4 columns per page. CFW rasterizes scenes\n * phone-side, so the stock firmware's 4-image container pool does not bound\n * the page (getEffectiveCapabilities lifts the element caps while the CFW\n * canvas is active).\n */\nconst APP_GRID_COLS = 4\nconst APP_GRID_ROWS = 2\nconst APPS_PER_PAGE = APP_GRID_COLS * APP_GRID_ROWS\nconst APP_CELL = 36\nconst APP_CELL_GAP = 12\nconst APP_ICON_INSET = 2\nconst APP_ICON = APP_CELL - APP_ICON_INSET * 2\n\nfunction eventLine(event: CalendarEvent): string {\n const when = event.allDay\n ? \"All day\"\n : new Intl.DateTimeFormat(undefined, {hour: \"numeric\", minute: \"2-digit\"}).format(new Date(event.startsAt))\n return `${when} ${event.title}`\n}\n\nclass CustomDashboardService {\n private started = false\n private open = false\n private view: \"main\" | \"apps\" = \"main\"\n private mainIndex = 0\n private appsIndex = 0\n /** \"loading\" until the first fetch resolves; \"no-access\" only for a real permission denial. */\n private calendarStatus: \"loading\" | \"ok\" | \"no-access\" | \"unavailable\" = \"loading\"\n /** packageName → base64 icon bytes, or null when the icon can't be loaded (monogram fallback). */\n private iconCache = new Map<string, string | null>()\n private iconLoads = new Set<string>()\n private calendarEvents: CalendarEvent[] = []\n private unsubs: Array<() => void> = []\n private clockTimer: number | null = null\n private calendarTimer: number | null = null\n /** Last observed eligibility, for transition detection. */\n private eligible = false\n\n start(): void {\n if (this.started) return\n this.started = true\n this.unsubs.push(\n useGlassesStore.subscribe(\n (state) => `${state.connection.state}:${state.deviceModel}:${state.firmwareVersion}`,\n () => {\n if (!this.syncEligibility()) return\n void this.refreshCalendarAndRender()\n },\n ),\n // Selector: only dashboard-relevant settings retrigger a refresh. The\n // whole-store form fired a calendar query on every settings write.\n useSettingsStore.subscribe(\n (state) =>\n JSON.stringify([\n state.getSetting(SETTINGS.custom_dashboard_enabled.key),\n state.getSetting(SETTINGS.custom_dashboard_widgets.key),\n state.getSetting(SETTINGS.custom_dashboard_clock_style.key),\n state.getSetting(SETTINGS.custom_dashboard_clock_show_date.key),\n state.getSetting(SETTINGS.twelve_hour_time.key),\n state.getSetting(SETTINGS.custom_dashboard_calendar_event_count.key),\n state.getSetting(SETTINGS.custom_dashboard_calendar_days_ahead.key),\n state.getSetting(SETTINGS.menu_apps.key),\n ]),\n () => {\n if (!this.syncEligibility()) return\n void this.refreshCalendarAndRender()\n },\n ),\n useAppStatusStore.subscribe(() => {\n if (this.isEligible()) this.render()\n }),\n )\n this.clockTimer = BgTimer.setInterval(() => {\n if (this.isEligible()) this.render()\n }, CLOCK_REFRESH_MS)\n this.calendarTimer = BgTimer.setInterval(() => {\n if (this.isEligible()) void this.refreshCalendarAndRender()\n }, CALENDAR_REFRESH_MS)\n this.eligible = this.isEligible()\n if (this.eligible) {\n this.ensureContextualDashboard()\n void this.refreshCalendarAndRender()\n }\n }\n\n /**\n * Track eligible⇄ineligible transitions from any trigger (setting toggle,\n * connect/disconnect, firmware change). Entering enables the contextual\n * dashboard once — not on every settings write, so a user's manual\n * contextual-dashboard choice is no longer ratcheted back. Leaving closes\n * the dashboard, blanks the glasses view, and drops the retained scene so a\n * stale frame can't be replayed after the feature was turned off.\n */\n private syncEligibility(): boolean {\n const eligible = this.isEligible()\n if (eligible === this.eligible) return eligible\n this.eligible = eligible\n if (eligible) {\n this.ensureContextualDashboard()\n } else {\n this.open = false\n this.resetNavigation()\n sceneRenderer.emitScene(DASHBOARD_APP_ID, \"dashboard\", [])\n sceneRenderer.clearApp(DASHBOARD_APP_ID)\n }\n return eligible\n }\n\n stop(): void {\n for (const unsub of this.unsubs) unsub()\n this.unsubs = []\n if (this.clockTimer !== null) BgTimer.clearInterval(this.clockTimer)\n if (this.calendarTimer !== null) BgTimer.clearInterval(this.calendarTimer)\n this.clockTimer = null\n this.calendarTimer = null\n this.open = false\n this.eligible = false\n this.resetNavigation()\n this.started = false\n sceneRenderer.clearApp(DASHBOARD_APP_ID)\n }\n\n isEligible(): boolean {\n if (useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_enabled.key) === false) return false\n const state = useGlassesStore.getState()\n return (\n isGlassesConnected(state.connection) &&\n isCustomDashboardEligible({\n model: state.deviceModel,\n firmwareVersion: state.firmwareVersion,\n })\n )\n }\n\n get isOpen(): boolean {\n return this.open\n }\n\n setHeadUp(up: boolean): void {\n if (!this.isEligible()) {\n this.open = false\n return\n }\n if (up && !this.open) this.resetNavigation()\n this.open = up\n if (up) void this.refreshCalendarAndRender()\n }\n\n /** Toggle the custom dashboard for an eligible device. */\n toggle(): boolean {\n if (!this.isEligible()) return false\n this.open = !this.open\n if (this.open) {\n this.resetNavigation()\n void this.refreshCalendarAndRender()\n }\n void Promise.resolve(BluetoothSdk.toggleHeadUp()).catch((error) => {\n this.open = !this.open\n console.error(\"CustomDashboard: toggleHeadUp failed\", error)\n })\n return true\n }\n\n /** Returns true when an open custom dashboard consumed the gesture. */\n handleGesture(gestureName: string): boolean {\n if (!this.open || !this.isEligible()) return false\n if (gestureName === \"double_tap\") {\n // In the app menu a double tap backs out to the top of the dashboard\n // (there is no Back row); on the main view it closes the dashboard.\n if (this.view === \"apps\") {\n this.resetNavigation()\n this.render()\n return true\n }\n return this.toggle()\n }\n if (this.view === \"apps\") return this.handleAppMenuGesture(gestureName)\n return this.handleMainGesture(gestureName)\n }\n\n private resetNavigation(): void {\n this.view = \"main\"\n this.mainIndex = 0\n this.appsIndex = 0\n }\n\n private handleMainGesture(gestureName: string): boolean {\n const actions = dashboardActions(this.widgets())\n if (this.mainIndex >= actions.length) this.mainIndex = Math.max(0, actions.length - 1)\n if (gestureName === \"swipe_down\" || gestureName === \"swipe_up\") {\n if (actions.length === 0) return true\n const delta = gestureName === \"swipe_down\" ? 1 : -1\n this.mainIndex = (this.mainIndex + delta + actions.length) % actions.length\n this.render()\n return true\n }\n if (gestureName === \"single_tap\") {\n const action = actions[this.mainIndex]\n if (action?.kind === \"apps-menu\") {\n this.view = \"apps\"\n this.appsIndex = 0\n this.render()\n } else if (action?.kind === \"notifications\") {\n void this.openNotificationsPanel()\n }\n return true\n }\n return false\n }\n\n private handleAppMenuGesture(gestureName: string): boolean {\n const rows = appMenuActions(this.menuApps())\n if (this.appsIndex >= rows.length) this.appsIndex = Math.max(0, rows.length - 1)\n if (gestureName === \"swipe_down\" || gestureName === \"swipe_up\") {\n const delta = gestureName === \"swipe_down\" ? 1 : -1\n this.appsIndex = (this.appsIndex + delta + rows.length) % rows.length\n this.render()\n return true\n }\n if (gestureName === \"single_tap\") {\n const row = rows[this.appsIndex]\n if (row?.kind === \"app\") {\n void this.toggleApp(row.packageName)\n }\n return true\n }\n return false\n }\n\n private menuApps(): ClientApp[] {\n const menu = useSettingsStore.getState().getSetting(SETTINGS.menu_apps.key) as MenuAppSetting[] | null\n return orderedDashboardApps(menu, useAppStatusStore.getState().apps)\n }\n\n private widgets(): DashboardWidgetSetting[] {\n return normalizeDashboardWidgets(useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_widgets.key))\n }\n\n private async close(): Promise<void> {\n if (!this.open) return\n this.open = false\n await Promise.resolve(BluetoothSdk.toggleHeadUp()).catch((error) => {\n console.error(\"CustomDashboard: close failed\", error)\n })\n }\n\n private async openNotificationsPanel(): Promise<void> {\n await this.close()\n await BluetoothSdk.showNativeNotificationPanel().catch((error) => {\n console.error(\"CustomDashboard: notification panel failed\", error)\n })\n }\n\n private async toggleApp(packageName: string): Promise<void> {\n await this.close()\n const app = useAppStatusStore.getState().apps.find((candidate) => candidate.packageName === packageName)\n if (!app) return\n if (app.running) {\n await useAppStatusStore.getState().stop(app.packageName)\n } else {\n await useAppStatusStore.getState().start(app, {skipNavigation: true})\n }\n }\n\n private calendarWidgetEnabled(): boolean {\n return this.widgets().some((widget) => widget.id === \"calendar\" && widget.enabled)\n }\n\n private ensureContextualDashboard(): void {\n const settings = useSettingsStore.getState()\n if (settings.getSetting(SETTINGS.contextual_dashboard.key) !== true) {\n // DeviceManager selects its retained dashboard Scene only while this is\n // enabled. Custom firmware eligibility makes that dashboard automatic.\n void settings.setSetting(SETTINGS.contextual_dashboard.key, true, false)\n }\n }\n\n private async refreshCalendarAndRender(): Promise<void> {\n if (!this.calendarWidgetEnabled()) {\n this.calendarStatus = \"loading\"\n this.calendarEvents = []\n this.render()\n return\n }\n const settings = useSettingsStore.getState()\n const count = Number(settings.getSetting(SETTINGS.custom_dashboard_calendar_event_count.key) ?? 2)\n const days = Number(settings.getSetting(SETTINGS.custom_dashboard_calendar_days_ahead.key) ?? 3)\n const startsAt = new Date()\n const endsAt = new Date(startsAt.getTime() + Math.max(1, days) * 24 * 60 * 60 * 1000)\n try {\n const result = await listPhoneCalendarEvents({\n startsAt: startsAt.toISOString(),\n endsAt: endsAt.toISOString(),\n limit: Math.max(1, Math.min(3, count)),\n })\n this.calendarStatus = \"ok\"\n this.calendarEvents = result.events\n } catch (error) {\n // Permission is requested from the Custom Dashboard settings screen;\n // only a real denial should read as one — any other failure is\n // \"Unavailable\" (and logged) so it can't masquerade as a permission\n // problem.\n const denied = error instanceof PhoneCalendarError && error.code === MiniappErrorCode.PERMISSION_DENIED\n this.calendarStatus = denied ? \"no-access\" : \"unavailable\"\n this.calendarEvents = []\n if (!denied) console.warn(\"CustomDashboard: calendar fetch failed\", error)\n }\n this.render()\n }\n\n private canvas(): {width: number; height: number} {\n const {sceneCanvasWidth, sceneCanvasHeight} = useGlassesStore.getState()\n if (sceneCanvasWidth && sceneCanvasWidth > 0 && sceneCanvasHeight && sceneCanvasHeight > 0) {\n return {width: sceneCanvasWidth, height: sceneCanvasHeight}\n }\n return FALLBACK_CANVAS\n }\n\n private render(): void {\n if (!this.isEligible()) return\n const elements = this.view === \"apps\" ? this.appMenuElements() : this.mainElements()\n sceneRenderer.emitScene(DASHBOARD_APP_ID, \"dashboard\", elements)\n }\n\n private mainElements(): SceneElementInput[] {\n const canvas = this.canvas()\n const settings = useSettingsStore.getState()\n const widgets = this.widgets().filter((widget) => widget.enabled)\n const actions = dashboardActions(widgets)\n if (this.mainIndex >= actions.length) this.mainIndex = Math.max(0, actions.length - 1)\n const selected = actions[this.mainIndex]\n\n const clockStyle =\n (settings.getSetting(SETTINGS.custom_dashboard_clock_style.key) as CustomDashboardClockStyle) === \"large\"\n ? \"large\"\n : \"compact\"\n const calendarEnabled = widgets.some((widget) => widget.id === \"calendar\")\n // Calendar lives in the right half; everything else stacks in the left\n // half (full width when the calendar widget is off).\n const halfW = Math.floor(canvas.width / 2) - MARGIN_X - Math.floor(WIDGET_GAP / 2)\n const width = calendarEnabled ? halfW : canvas.width - MARGIN_X * 2\n const rightX = Math.floor(canvas.width / 2) + Math.floor(WIDGET_GAP / 2)\n\n const elements: SceneElementInput[] = []\n let y = MARGIN_TOP\n for (const widget of widgets) {\n if (widget.id === \"clock\") {\n const fontSize = clockStyle === \"large\" ? 48 : 32\n const clock = formatDashboardClock(new Date(), {\n twelveHour: settings.getSetting(SETTINGS.twelve_hour_time.key) === true,\n showDate: settings.getSetting(SETTINGS.custom_dashboard_clock_show_date.key) !== false,\n })\n elements.push({\n type: \"text\",\n id: \"clock\",\n box: {x: MARGIN_X, y, w: width, h: fontSize + 6},\n text: clock.time,\n style: {font: \"mono\", fontSize, breakMode: \"strict-word\"},\n })\n y += fontSize + 6\n if (clock.date) {\n elements.push({\n type: \"text\",\n id: \"clock-date\",\n box: {x: MARGIN_X, y, w: width, h: LINE_H},\n text: clock.date,\n style: {font: \"builtin\", breakMode: \"strict-word\"},\n })\n y += LINE_H\n }\n y += WIDGET_GAP\n } else if (widget.id === \"calendar\") {\n // Right half, top-aligned, independent of the left stack. A missing\n // calendar shows a hint instead of silently vanishing — \"No access\"\n // strictly for permission denial, \"Unavailable\" for any other failure.\n const lines =\n this.calendarStatus === \"ok\"\n ? [\n \"Calendar\",\n ...(this.calendarEvents.length\n ? this.calendarEvents.slice(0, 3).map(eventLine)\n : [\"No upcoming events\"]),\n ]\n : this.calendarStatus === \"loading\"\n ? [\"Calendar\"]\n : [\"Calendar\", this.calendarStatus === \"no-access\" ? \"No access\" : \"Unavailable\"]\n elements.push({\n type: \"text\",\n id: \"calendar\",\n box: {x: rightX, y: MARGIN_TOP, w: halfW, h: lines.length * LINE_H},\n text: lines.join(\"\\n\"),\n style: {font: \"builtin\", breakMode: \"strict-word\", overflow: \"ellipsis\"},\n })\n } else if (widget.id === \"apps\") {\n const isSelected = selected?.kind === \"apps-menu\"\n elements.push({\n type: \"text\",\n id: \"apps-button\",\n box: {x: MARGIN_X, y, w: width, h: BUTTON_H},\n text: \"Apps\",\n style: {font: \"builtin\", border: isSelected ? 2 : 0, radius: 8},\n })\n y += BUTTON_H + WIDGET_GAP\n } else if (widget.id === \"notifications\") {\n const isSelected = selected?.kind === \"notifications\"\n elements.push({\n type: \"text\",\n id: \"notifications\",\n box: {x: MARGIN_X, y, w: width, h: BUTTON_H},\n text: \"Notifications\",\n style: {font: \"builtin\", border: isSelected ? 2 : 0, radius: 8},\n })\n y += BUTTON_H + WIDGET_GAP\n }\n }\n return elements\n }\n\n /**\n * Resolve a miniapp icon to base64 for the glasses. Local/bundled miniapps\n * carry a file:// icon.png; store apps an https URL; the offline system\n * tiles a bundler asset id (unsupported here — monogram fallback). Loads\n * are async and cached; a finished load repaints the open app menu.\n */\n private loadIcon(app: ClientApp): void {\n if (this.iconCache.has(app.packageName) || this.iconLoads.has(app.packageName)) return\n const url = typeof app.logoUrl === \"string\" ? app.logoUrl : \"\"\n if (!/^(file|https?):/.test(url)) {\n this.iconCache.set(app.packageName, null)\n return\n }\n this.iconLoads.add(app.packageName)\n void (async () => {\n try {\n let base64: string\n if (url.startsWith(\"file:\")) {\n base64 = await new File(url).base64()\n } else {\n const response = await fetch(url)\n const blob = await response.blob()\n base64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(String(reader.result).split(\",\")[1] ?? \"\")\n reader.onerror = () => reject(reader.error)\n reader.readAsDataURL(blob)\n })\n }\n this.iconCache.set(app.packageName, base64 || null)\n } catch {\n this.iconCache.set(app.packageName, null)\n } finally {\n this.iconLoads.delete(app.packageName)\n if (this.open && this.view === \"apps\") this.render()\n }\n })()\n }\n\n private appMenuElements(): SceneElementInput[] {\n const canvas = this.canvas()\n const width = canvas.width - MARGIN_X * 2\n const apps = this.menuApps()\n const rows = appMenuActions(apps)\n if (this.appsIndex >= rows.length) this.appsIndex = Math.max(0, rows.length - 1)\n\n const elements: SceneElementInput[] = [\n {\n type: \"text\",\n id: \"apps-title\",\n box: {x: MARGIN_X, y: MARGIN_TOP, w: width, h: LINE_H},\n text: \"Apps\",\n style: {font: \"builtin\"},\n },\n ]\n const listTop = MARGIN_TOP + LINE_H + 16\n if (apps.length === 0) {\n elements.push({\n type: \"text\",\n id: \"apps-empty\",\n box: {x: MARGIN_X, y: listTop, w: width, h: LINE_H},\n text: \"No apps in Glasses Menu\",\n style: {font: \"builtin\"},\n })\n return elements\n }\n\n // Icon grid, paged, row-major. Selection is the bordered cell.\n const perPage = APPS_PER_PAGE\n const pageStart = Math.floor(this.appsIndex / perPage) * perPage\n const page = apps.slice(pageStart, pageStart + perPage)\n const cols = Math.min(page.length, APP_GRID_COLS)\n const totalW = cols * APP_CELL + (cols - 1) * APP_CELL_GAP\n const startX = Math.max(MARGIN_X, Math.round((canvas.width - totalW) / 2))\n page.forEach((app, cell) => {\n const x = startX + (cell % APP_GRID_COLS) * (APP_CELL + APP_CELL_GAP)\n const y = listTop + Math.floor(cell / APP_GRID_COLS) * (APP_CELL + APP_CELL_GAP)\n const selected = pageStart + cell === this.appsIndex\n const icon = this.iconCache.get(app.packageName)\n if (icon === undefined) this.loadIcon(app)\n if (icon) {\n elements.push({\n type: \"image\",\n id: `app-icon:${app.packageName}`,\n box: {x: x + APP_ICON_INSET, y: y + APP_ICON_INSET, w: APP_ICON, h: APP_ICON},\n data: icon,\n })\n } else {\n // Monogram fallback for icons that can't load (bundler-asset tiles).\n elements.push({\n type: \"text\",\n id: `app-icon:${app.packageName}`,\n box: {x: x + APP_ICON_INSET, y: y + APP_ICON_INSET, w: APP_ICON, h: APP_ICON},\n text: (app.name.trim()[0] ?? \"?\").toUpperCase(),\n style: {font: \"mono\", fontSize: 20},\n })\n }\n if (selected) {\n elements.push({\n type: \"rect\",\n id: \"apps-selection\",\n box: {x, y, w: APP_CELL, h: APP_CELL},\n style: {border: 2, radius: 6},\n })\n }\n })\n\n // Grayscale mini icons are ambiguous on their own: caption the selection.\n const gridRows = Math.ceil(page.length / APP_GRID_COLS)\n const gridH = gridRows * APP_CELL + (gridRows - 1) * APP_CELL_GAP\n const selectedApp = apps[this.appsIndex]\n if (selectedApp) {\n elements.push({\n type: \"text\",\n id: \"apps-selected-name\",\n box: {x: MARGIN_X, y: listTop + gridH + 12, w: width, h: LINE_H},\n text: `${selectedApp.name}${selectedApp.running ? \" ●\" : \"\"}`,\n style: {font: \"builtin\", breakMode: \"strict-word\", overflow: \"ellipsis\"},\n })\n }\n return elements\n }\n}\n\nconst customDashboardService = new CustomDashboardService()\nexport default customDashboardService\nexport {CustomDashboardService}\n"]}
|
|
1
|
+
{"version":3,"file":"CustomDashboardService.js","sourceRoot":"","sources":["../../src/services/CustomDashboardService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Scene API dashboard for G2 custom firmware.\n *\n * G2.swift marks the published firmware version after it detects the CFW\n * capability advertisement. DashboardCoordinator owns open/close and display\n * switching; this service renders the widgets and handles gestures while open.\n *\n * The dashboard is composed of user-orderable widgets (clock, calendar, apps\n * button, notifications button) stacked vertically on the CFW scene canvas.\n * The apps button switches to a second, dashboard-rendered app menu view.\n */\nimport {File} from \"expo-file-system\"\n\nimport {BluetoothSdk} from \"@glassly/bluetooth-sdk\"\nimport {MiniappErrorCode, type CalendarEvent} from \"@glassly/miniapp\"\n\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport type {ClientApp} from \"../types/applet\"\nimport {TextMeasurer} from \"../utils/display/measurer\"\nimport type {SceneElementInput} from \"../utils/display/scene\"\nimport {BgTimer} from \"../utils/timers\"\nimport {\n appMenuActions,\n dashboardActions,\n formatDashboardClock,\n isCustomDashboardEligible,\n normalizeDashboardWidgets,\n orderedDashboardApps,\n type CustomDashboardClockStyle,\n type DashboardWidgetSetting,\n type MenuAppSetting,\n} from \"./CustomDashboardModel\"\nimport displayProcessor from \"./DisplayProcessor\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport {listPhoneCalendarEvents, PhoneCalendarError} from \"./PhoneCalendarService\"\nimport sceneRenderer from \"./SceneRenderer\"\n\nconst DASHBOARD_APP_ID = \"system.custom-dashboard\"\nconst CLOCK_REFRESH_MS = 30_000\nconst CALENDAR_REFRESH_MS = 5 * 60_000\n/** Matches the boot message's window; stops have no unmount-driven end signal. */\nconst STOP_NOTICE_MS = 1500\n\n// CFW blits the stock compositor's 576×288 canvas; used when the firmware has\n// not reported scene capabilities yet.\nconst FALLBACK_CANVAS = {width: 576, height: 288}\nconst MARGIN_X = 16\nconst MARGIN_TOP = 10\nconst WIDGET_GAP = 10\n/** Row height for the glasses' builtin 20px font engine. */\nconst LINE_H = 24\nconst BUTTON_H = 34\n/**\n * Selection marker: a text-hugging bar under the focused label instead of a\n * boxed border. Drawn as a rect (h 3, border 2 — the strokes overlap into a\n * solid bar) because mode 15 renders only glyphs; the bar must be in the\n * raster or the firmware never shows it.\n */\nconst UNDERLINE_H = 3\n/** Row-top → underline-top: just under the builtin font's 27px line. */\nconst UNDERLINE_OFFSET_Y = BUTTON_H - 4\n/**\n * App-menu icon grid: 2 rows × 4 columns per page. CFW rasterizes scenes\n * phone-side, so the stock firmware's 4-image container pool does not bound\n * the page (getEffectiveCapabilities lifts the element caps while the CFW\n * canvas is active).\n */\nconst APP_GRID_COLS = 4\nconst APP_GRID_ROWS = 2\nconst APPS_PER_PAGE = APP_GRID_COLS * APP_GRID_ROWS\nconst APP_CELL = 36\nconst APP_CELL_GAP = 12\nconst APP_ICON_INSET = 2\nconst APP_ICON = APP_CELL - APP_ICON_INSET * 2\n\nfunction eventLine(event: CalendarEvent): string {\n const when = event.allDay\n ? \"All day\"\n : new Intl.DateTimeFormat(undefined, {hour: \"numeric\", minute: \"2-digit\"}).format(new Date(event.startsAt))\n return `${when} ${event.title}`\n}\n\nclass CustomDashboardService {\n private started = false\n private open = false\n /** Set by DashboardCoordinator; asks it to close (and restore main content). */\n public onRequestClose: (() => void) | null = null\n private view: \"main\" | \"apps\" = \"main\"\n private mainIndex = 0\n private appsIndex = 0\n /** \"loading\" until the first fetch resolves; \"no-access\" only for a real permission denial. */\n private calendarStatus: \"loading\" | \"ok\" | \"no-access\" | \"unavailable\" = \"loading\"\n /** packageName → base64 icon bytes, or null when the icon can't be loaded (monogram fallback). */\n private iconCache = new Map<string, string | null>()\n private iconLoads = new Set<string>()\n private calendarEvents: CalendarEvent[] = []\n private unsubs: Array<() => void> = []\n private clockTimer: number | null = null\n private calendarTimer: number | null = null\n /** Last observed eligibility, for transition detection. */\n private eligible = false\n\n start(): void {\n if (this.started) return\n this.started = true\n this.unsubs.push(\n useGlassesStore.subscribe(\n (state) => `${state.connection.state}:${state.deviceModel}:${state.firmwareVersion}`,\n () => {\n if (!this.syncEligibility()) return\n void this.refreshCalendarAndRender()\n },\n ),\n // Selector: only dashboard-relevant settings retrigger a refresh. The\n // whole-store form fired a calendar query on every settings write.\n useSettingsStore.subscribe(\n (state) =>\n JSON.stringify([\n state.getSetting(SETTINGS.custom_dashboard_enabled.key),\n state.getSetting(SETTINGS.custom_dashboard_widgets.key),\n state.getSetting(SETTINGS.custom_dashboard_clock_style.key),\n state.getSetting(SETTINGS.custom_dashboard_clock_show_date.key),\n state.getSetting(SETTINGS.twelve_hour_time.key),\n state.getSetting(SETTINGS.custom_dashboard_calendar_event_count.key),\n state.getSetting(SETTINGS.custom_dashboard_calendar_days_ahead.key),\n state.getSetting(SETTINGS.menu_apps.key),\n ]),\n () => {\n if (!this.syncEligibility()) return\n void this.refreshCalendarAndRender()\n },\n ),\n useAppStatusStore.subscribe(() => {\n if (this.isEligible()) this.render()\n }),\n )\n this.clockTimer = BgTimer.setInterval(() => {\n if (this.isEligible()) this.render()\n }, CLOCK_REFRESH_MS)\n this.calendarTimer = BgTimer.setInterval(() => {\n if (this.isEligible()) void this.refreshCalendarAndRender()\n }, CALENDAR_REFRESH_MS)\n this.eligible = this.isEligible()\n }\n\n /**\n * Track eligible⇄ineligible transitions from any trigger (setting toggle,\n * connect/disconnect, firmware change). Leaving closes the dashboard via the\n * coordinator (which restores main content) and drops the retained scene so\n * a stale frame can't be replayed after the feature was turned off.\n */\n private syncEligibility(): boolean {\n const eligible = this.isEligible()\n if (eligible === this.eligible) return eligible\n this.eligible = eligible\n if (!eligible) {\n if (this.open) this.onRequestClose?.()\n this.open = false\n this.resetNavigation()\n sceneRenderer.clearApp(DASHBOARD_APP_ID)\n }\n return eligible\n }\n\n stop(): void {\n for (const unsub of this.unsubs) unsub()\n this.unsubs = []\n if (this.clockTimer !== null) BgTimer.clearInterval(this.clockTimer)\n if (this.calendarTimer !== null) BgTimer.clearInterval(this.calendarTimer)\n this.clockTimer = null\n this.calendarTimer = null\n if (this.open) this.onRequestClose?.()\n this.open = false\n this.eligible = false\n this.resetNavigation()\n this.started = false\n sceneRenderer.clearApp(DASHBOARD_APP_ID)\n }\n\n isEligible(): boolean {\n if (useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_enabled.key) === false) return false\n const state = useGlassesStore.getState()\n return (\n isGlassesConnected(state.connection) &&\n isCustomDashboardEligible({\n model: state.deviceModel,\n firmwareVersion: state.firmwareVersion,\n })\n )\n }\n\n get isOpen(): boolean {\n return this.open\n }\n\n /** Coordinator opened the dashboard: render fresh from the top. */\n show(): void {\n this.open = true\n this.resetNavigation()\n void this.refreshCalendarAndRender()\n }\n\n /** Coordinator closed the dashboard. */\n hide(): void {\n this.open = false\n this.resetNavigation()\n }\n\n /** Returns true when an open custom dashboard consumed the gesture. */\n handleGesture(gestureName: string): boolean {\n if (!this.open || !this.isEligible()) return false\n if (gestureName === \"double_tap\") {\n // In the app menu a double tap backs out to the top of the dashboard\n // (there is no Back row); on the main view it closes the dashboard.\n if (this.view === \"apps\") {\n this.resetNavigation()\n this.render()\n return true\n }\n this.onRequestClose?.()\n return true\n }\n if (this.view === \"apps\") return this.handleAppMenuGesture(gestureName)\n return this.handleMainGesture(gestureName)\n }\n\n private resetNavigation(): void {\n this.view = \"main\"\n this.mainIndex = 0\n this.appsIndex = 0\n }\n\n private handleMainGesture(gestureName: string): boolean {\n const actions = dashboardActions(this.widgets())\n if (this.mainIndex >= actions.length) this.mainIndex = Math.max(0, actions.length - 1)\n if (gestureName === \"swipe_down\" || gestureName === \"swipe_up\") {\n if (actions.length === 0) return true\n const delta = gestureName === \"swipe_down\" ? 1 : -1\n this.mainIndex = (this.mainIndex + delta + actions.length) % actions.length\n this.render()\n return true\n }\n if (gestureName === \"single_tap\") {\n const action = actions[this.mainIndex]\n if (action?.kind === \"apps-menu\") {\n this.view = \"apps\"\n this.appsIndex = 0\n this.render()\n } else if (action?.kind === \"notifications\") {\n void this.openNotificationsPanel()\n }\n return true\n }\n return false\n }\n\n private handleAppMenuGesture(gestureName: string): boolean {\n const rows = appMenuActions(this.menuApps())\n if (this.appsIndex >= rows.length) this.appsIndex = Math.max(0, rows.length - 1)\n if (gestureName === \"swipe_down\" || gestureName === \"swipe_up\") {\n const delta = gestureName === \"swipe_down\" ? 1 : -1\n this.appsIndex = (this.appsIndex + delta + rows.length) % rows.length\n this.render()\n return true\n }\n if (gestureName === \"single_tap\") {\n const row = rows[this.appsIndex]\n if (row?.kind === \"app\") {\n void this.toggleApp(row.packageName)\n }\n return true\n }\n return false\n }\n\n private menuApps(): ClientApp[] {\n const menu = useSettingsStore.getState().getSetting(SETTINGS.menu_apps.key) as MenuAppSetting[] | null\n return orderedDashboardApps(menu, useAppStatusStore.getState().apps)\n }\n\n private widgets(): DashboardWidgetSetting[] {\n return normalizeDashboardWidgets(useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_widgets.key))\n }\n\n private async openNotificationsPanel(): Promise<void> {\n this.onRequestClose?.()\n await BluetoothSdk.showNativeNotificationPanel().catch((error) => {\n console.error(\"CustomDashboard: notification panel failed\", error)\n })\n }\n\n private async toggleApp(packageName: string): Promise<void> {\n this.onRequestClose?.()\n const app = useAppStatusStore.getState().apps.find((candidate) => candidate.packageName === packageName)\n if (!app) return\n if (app.running) {\n // Mirror the \"Starting X…\" boot message: without this the dashboard just\n // closes and the stop is invisible on the glasses.\n localDisplayManager.showSystemNotice(`Stopping ${app.name}…`, STOP_NOTICE_MS)\n await useAppStatusStore.getState().stop(app.packageName)\n } else {\n await useAppStatusStore.getState().start(app, {skipNavigation: true})\n }\n }\n\n private calendarWidgetEnabled(): boolean {\n return this.widgets().some((widget) => widget.id === \"calendar\" && widget.enabled)\n }\n\n private async refreshCalendarAndRender(): Promise<void> {\n if (!this.open) return\n if (!this.calendarWidgetEnabled()) {\n this.calendarStatus = \"loading\"\n this.calendarEvents = []\n this.render()\n return\n }\n const settings = useSettingsStore.getState()\n const count = Number(settings.getSetting(SETTINGS.custom_dashboard_calendar_event_count.key) ?? 2)\n const days = Number(settings.getSetting(SETTINGS.custom_dashboard_calendar_days_ahead.key) ?? 3)\n const startsAt = new Date()\n const endsAt = new Date(startsAt.getTime() + Math.max(1, days) * 24 * 60 * 60 * 1000)\n try {\n const result = await listPhoneCalendarEvents({\n startsAt: startsAt.toISOString(),\n endsAt: endsAt.toISOString(),\n limit: Math.max(1, Math.min(3, count)),\n })\n this.calendarStatus = \"ok\"\n this.calendarEvents = result.events\n } catch (error) {\n // Permission is requested from the Custom Dashboard settings screen;\n // only a real denial should read as one — any other failure is\n // \"Unavailable\" (and logged) so it can't masquerade as a permission\n // problem.\n const denied = error instanceof PhoneCalendarError && error.code === MiniappErrorCode.PERMISSION_DENIED\n this.calendarStatus = denied ? \"no-access\" : \"unavailable\"\n this.calendarEvents = []\n if (!denied) console.warn(\"CustomDashboard: calendar fetch failed\", error)\n }\n this.render()\n }\n\n /** Underline hugging a builtin-font label. x/rowY are the label box's origin. */\n private labelUnderline(id: string, x: number, rowY: number, label: string): SceneElementInput {\n // Same metrics processScene uses to ellipsize builtin text; the raster\n // insets text by 2px, so the bar starts under the first glyph.\n const textW = new TextMeasurer(displayProcessor.getProfile()).measureText(label)\n return {\n type: \"rect\",\n id,\n box: {x: x + 2, y: rowY + UNDERLINE_OFFSET_Y, w: textW + 4, h: UNDERLINE_H},\n style: {border: 2},\n }\n }\n\n private canvas(): {width: number; height: number} {\n const {sceneCanvasWidth, sceneCanvasHeight} = useGlassesStore.getState()\n if (sceneCanvasWidth && sceneCanvasWidth > 0 && sceneCanvasHeight && sceneCanvasHeight > 0) {\n return {width: sceneCanvasWidth, height: sceneCanvasHeight}\n }\n return FALLBACK_CANVAS\n }\n\n private render(): void {\n // Only while open: emitting into the single native view while closed\n // would clobber the main content.\n if (!this.open || !this.isEligible()) return\n const elements = this.view === \"apps\" ? this.appMenuElements() : this.mainElements()\n sceneRenderer.emitScene(DASHBOARD_APP_ID, \"dashboard\", elements)\n }\n\n private mainElements(): SceneElementInput[] {\n const canvas = this.canvas()\n const settings = useSettingsStore.getState()\n const widgets = this.widgets().filter((widget) => widget.enabled)\n const actions = dashboardActions(widgets)\n if (this.mainIndex >= actions.length) this.mainIndex = Math.max(0, actions.length - 1)\n const selected = actions[this.mainIndex]\n\n const clockStyle =\n (settings.getSetting(SETTINGS.custom_dashboard_clock_style.key) as CustomDashboardClockStyle) === \"large\"\n ? \"large\"\n : \"compact\"\n const calendarEnabled = widgets.some((widget) => widget.id === \"calendar\")\n // Calendar lives in the right half; everything else stacks in the left\n // half (full width when the calendar widget is off).\n const halfW = Math.floor(canvas.width / 2) - MARGIN_X - Math.floor(WIDGET_GAP / 2)\n const width = calendarEnabled ? halfW : canvas.width - MARGIN_X * 2\n const rightX = Math.floor(canvas.width / 2) + Math.floor(WIDGET_GAP / 2)\n\n const elements: SceneElementInput[] = []\n let y = MARGIN_TOP\n for (const widget of widgets) {\n if (widget.id === \"clock\") {\n const fontSize = clockStyle === \"large\" ? 48 : 32\n const clock = formatDashboardClock(new Date(), {\n twelveHour: settings.getSetting(SETTINGS.twelve_hour_time.key) === true,\n showDate: settings.getSetting(SETTINGS.custom_dashboard_clock_show_date.key) !== false,\n })\n elements.push({\n type: \"text\",\n id: \"clock\",\n box: {x: MARGIN_X, y, w: width, h: fontSize + 6},\n text: clock.time,\n style: {font: \"mono\", fontSize, breakMode: \"strict-word\"},\n })\n y += fontSize + 6\n if (clock.date) {\n elements.push({\n type: \"text\",\n id: \"clock-date\",\n box: {x: MARGIN_X, y, w: width, h: LINE_H},\n text: clock.date,\n style: {font: \"builtin\", breakMode: \"strict-word\"},\n })\n y += LINE_H\n }\n y += WIDGET_GAP\n } else if (widget.id === \"calendar\") {\n // Right half, top-aligned, independent of the left stack. A missing\n // calendar shows a hint instead of silently vanishing — \"No access\"\n // strictly for permission denial, \"Unavailable\" for any other failure.\n const lines =\n this.calendarStatus === \"ok\"\n ? [\n \"Calendar\",\n ...(this.calendarEvents.length\n ? this.calendarEvents.slice(0, 3).map(eventLine)\n : [\"No upcoming events\"]),\n ]\n : this.calendarStatus === \"loading\"\n ? [\"Calendar\"]\n : [\"Calendar\", this.calendarStatus === \"no-access\" ? \"No access\" : \"Unavailable\"]\n elements.push({\n type: \"text\",\n id: \"calendar\",\n box: {x: rightX, y: MARGIN_TOP, w: halfW, h: lines.length * LINE_H},\n text: lines.join(\"\\n\"),\n style: {font: \"builtin\", breakMode: \"strict-word\", overflow: \"ellipsis\"},\n })\n } else if (widget.id === \"apps\") {\n elements.push({\n type: \"text\",\n id: \"apps-button\",\n box: {x: MARGIN_X, y, w: width, h: BUTTON_H},\n text: \"Apps\",\n style: {font: \"builtin\"},\n })\n if (selected?.kind === \"apps-menu\") elements.push(this.labelUnderline(\"apps-underline\", MARGIN_X, y, \"Apps\"))\n y += BUTTON_H + WIDGET_GAP\n } else if (widget.id === \"notifications\") {\n elements.push({\n type: \"text\",\n id: \"notifications\",\n box: {x: MARGIN_X, y, w: width, h: BUTTON_H},\n text: \"Notifications\",\n style: {font: \"builtin\"},\n })\n if (selected?.kind === \"notifications\") {\n elements.push(this.labelUnderline(\"notifications-underline\", MARGIN_X, y, \"Notifications\"))\n }\n y += BUTTON_H + WIDGET_GAP\n }\n }\n return elements\n }\n\n /**\n * Resolve a miniapp icon to base64 for the glasses. Local/bundled miniapps\n * carry a file:// icon.png; store apps an https URL; the offline system\n * tiles a bundler asset id (unsupported here — monogram fallback). Loads\n * are async and cached; a finished load repaints the open app menu.\n */\n private loadIcon(app: ClientApp): void {\n if (this.iconCache.has(app.packageName) || this.iconLoads.has(app.packageName)) return\n const url = typeof app.logoUrl === \"string\" ? app.logoUrl : \"\"\n if (!/^(file|https?):/.test(url)) {\n this.iconCache.set(app.packageName, null)\n return\n }\n this.iconLoads.add(app.packageName)\n void (async () => {\n try {\n let base64: string\n if (url.startsWith(\"file:\")) {\n base64 = await new File(url).base64()\n } else {\n const response = await fetch(url)\n const blob = await response.blob()\n base64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(String(reader.result).split(\",\")[1] ?? \"\")\n reader.onerror = () => reject(reader.error)\n reader.readAsDataURL(blob)\n })\n }\n this.iconCache.set(app.packageName, base64 || null)\n } catch {\n this.iconCache.set(app.packageName, null)\n } finally {\n this.iconLoads.delete(app.packageName)\n if (this.open && this.view === \"apps\") this.render()\n }\n })()\n }\n\n private appMenuElements(): SceneElementInput[] {\n const canvas = this.canvas()\n const width = canvas.width - MARGIN_X * 2\n const apps = this.menuApps()\n const rows = appMenuActions(apps)\n if (this.appsIndex >= rows.length) this.appsIndex = Math.max(0, rows.length - 1)\n\n const elements: SceneElementInput[] = [\n {\n type: \"text\",\n id: \"apps-title\",\n box: {x: MARGIN_X, y: MARGIN_TOP, w: width, h: LINE_H},\n text: \"Apps\",\n style: {font: \"builtin\"},\n },\n ]\n const listTop = MARGIN_TOP + LINE_H + 16\n if (apps.length === 0) {\n elements.push({\n type: \"text\",\n id: \"apps-empty\",\n box: {x: MARGIN_X, y: listTop, w: width, h: LINE_H},\n text: \"No apps in Glasses Menu\",\n style: {font: \"builtin\"},\n })\n return elements\n }\n\n // Icon grid, paged, row-major. Selection is the underlined cell.\n const perPage = APPS_PER_PAGE\n const pageStart = Math.floor(this.appsIndex / perPage) * perPage\n const page = apps.slice(pageStart, pageStart + perPage)\n const cols = Math.min(page.length, APP_GRID_COLS)\n const totalW = cols * APP_CELL + (cols - 1) * APP_CELL_GAP\n const startX = Math.max(MARGIN_X, Math.round((canvas.width - totalW) / 2))\n page.forEach((app, cell) => {\n const x = startX + (cell % APP_GRID_COLS) * (APP_CELL + APP_CELL_GAP)\n const y = listTop + Math.floor(cell / APP_GRID_COLS) * (APP_CELL + APP_CELL_GAP)\n const selected = pageStart + cell === this.appsIndex\n const icon = this.iconCache.get(app.packageName)\n if (icon === undefined) this.loadIcon(app)\n if (icon) {\n elements.push({\n type: \"image\",\n id: `app-icon:${app.packageName}`,\n box: {x: x + APP_ICON_INSET, y: y + APP_ICON_INSET, w: APP_ICON, h: APP_ICON},\n data: icon,\n })\n } else {\n // Monogram fallback for icons that can't load (bundler-asset tiles).\n elements.push({\n type: \"text\",\n id: `app-icon:${app.packageName}`,\n box: {x: x + APP_ICON_INSET, y: y + APP_ICON_INSET, w: APP_ICON, h: APP_ICON},\n text: (app.name.trim()[0] ?? \"?\").toUpperCase(),\n style: {font: \"mono\", fontSize: 20},\n })\n }\n if (selected) {\n elements.push({\n type: \"rect\",\n id: \"apps-selection\",\n box: {x, y: y + APP_CELL + 2, w: APP_CELL, h: UNDERLINE_H},\n style: {border: 2},\n })\n }\n })\n\n // Grayscale mini icons are ambiguous on their own: caption the selection.\n const gridRows = Math.ceil(page.length / APP_GRID_COLS)\n const gridH = gridRows * APP_CELL + (gridRows - 1) * APP_CELL_GAP\n const selectedApp = apps[this.appsIndex]\n if (selectedApp) {\n elements.push({\n type: \"text\",\n id: \"apps-selected-name\",\n box: {x: MARGIN_X, y: listTop + gridH + 12, w: width, h: LINE_H},\n text: `${selectedApp.name}${selectedApp.running ? \" (running)\" : \"\"}`,\n style: {font: \"builtin\", breakMode: \"strict-word\", overflow: \"ellipsis\"},\n })\n }\n return elements\n }\n}\n\nconst customDashboardService = new CustomDashboardService()\nexport default customDashboardService\nexport {CustomDashboardService}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare class DashboardCoordinator {
|
|
2
|
+
private opened;
|
|
3
|
+
private renderer;
|
|
4
|
+
private legacyTimer;
|
|
5
|
+
start(): void;
|
|
6
|
+
stop(): void;
|
|
7
|
+
get isOpen(): boolean;
|
|
8
|
+
/** Head position (real or simulated). Opening respects contextual_dashboard. */
|
|
9
|
+
setHeadUp(up: boolean): void;
|
|
10
|
+
/** Double-tap on a CFW-eligible device. False when not eligible. */
|
|
11
|
+
toggleCustom(): boolean;
|
|
12
|
+
/** Double-tap fallback for devices without a firmware dashboard. */
|
|
13
|
+
toggleLegacy(): void;
|
|
14
|
+
/** True when an open dashboard consumed the gesture. */
|
|
15
|
+
handleGesture(gestureName: string): boolean;
|
|
16
|
+
/** Show the dashboard over the main content (ignores contextual_dashboard). */
|
|
17
|
+
open(): void;
|
|
18
|
+
/** Close and restore the retained main content. */
|
|
19
|
+
close(): void;
|
|
20
|
+
/** Parity with the retired native head-up wall: time, date, battery. */
|
|
21
|
+
private renderLegacy;
|
|
22
|
+
}
|
|
23
|
+
declare const dashboardCoordinator: DashboardCoordinator;
|
|
24
|
+
export default dashboardCoordinator;
|
|
25
|
+
export { DashboardCoordinator };
|
|
26
|
+
//# sourceMappingURL=DashboardCoordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DashboardCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/DashboardCoordinator.ts"],"names":[],"mappings":"AAgBA,cAAM,oBAAoB;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,WAAW,CAAsB;IAEzC,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI;IAKZ,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,gFAAgF;IAChF,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI;IAU5B,oEAAoE;IACpE,YAAY,IAAI,OAAO;IAOvB,oEAAoE;IACpE,YAAY,IAAI,IAAI;IAKpB,wDAAwD;IACxD,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAU3C,+EAA+E;IAC/E,IAAI,IAAI,IAAI;IAeZ,mDAAmD;IACnD,KAAK,IAAI,IAAI;IAYb,wEAAwE;IACxE,OAAO,CAAC,YAAY;CAgBrB;AAED,QAAA,MAAM,oBAAoB,sBAA6B,CAAA;AACvD,eAAe,oBAAoB,CAAA;AACnC,OAAO,EAAC,oBAAoB,EAAC,CAAA"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phone-side dashboard switching. Native retains a single display state; this
|
|
3
|
+
* service decides when the dashboard covers the main content (head-up,
|
|
4
|
+
* double-tap) and which renderer draws it: the CFW custom dashboard, or the
|
|
5
|
+
* legacy time/battery wall for stock devices.
|
|
6
|
+
*/
|
|
7
|
+
import { useGlassesStore } from "../stores/glasses";
|
|
8
|
+
import { SETTINGS, useSettingsStore } from "../stores/settings";
|
|
9
|
+
import { BgTimer } from "../utils/timers";
|
|
10
|
+
import customDashboardService from "./CustomDashboardService";
|
|
11
|
+
import { isGlassesConnected } from "./GlassesReadiness";
|
|
12
|
+
import localDisplayManager from "./LocalDisplayManager";
|
|
13
|
+
const LEGACY_CLOCK_REFRESH_MS = 30_000;
|
|
14
|
+
const LEGACY_DASHBOARD_PKG = "system.dashboard";
|
|
15
|
+
class DashboardCoordinator {
|
|
16
|
+
opened = false;
|
|
17
|
+
renderer = null;
|
|
18
|
+
legacyTimer = null;
|
|
19
|
+
start() {
|
|
20
|
+
customDashboardService.onRequestClose = () => this.close();
|
|
21
|
+
}
|
|
22
|
+
stop() {
|
|
23
|
+
this.close();
|
|
24
|
+
customDashboardService.onRequestClose = null;
|
|
25
|
+
}
|
|
26
|
+
get isOpen() {
|
|
27
|
+
return this.opened;
|
|
28
|
+
}
|
|
29
|
+
/** Head position (real or simulated). Opening respects contextual_dashboard. */
|
|
30
|
+
setHeadUp(up) {
|
|
31
|
+
if (up === this.opened)
|
|
32
|
+
return;
|
|
33
|
+
if (up) {
|
|
34
|
+
if (useSettingsStore.getState().getSetting(SETTINGS.contextual_dashboard.key) === false)
|
|
35
|
+
return;
|
|
36
|
+
this.open();
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.close();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Double-tap on a CFW-eligible device. False when not eligible. */
|
|
43
|
+
toggleCustom() {
|
|
44
|
+
if (!customDashboardService.isEligible())
|
|
45
|
+
return false;
|
|
46
|
+
if (this.opened)
|
|
47
|
+
this.close();
|
|
48
|
+
else
|
|
49
|
+
this.open();
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
/** Double-tap fallback for devices without a firmware dashboard. */
|
|
53
|
+
toggleLegacy() {
|
|
54
|
+
if (this.opened)
|
|
55
|
+
this.close();
|
|
56
|
+
else
|
|
57
|
+
this.open();
|
|
58
|
+
}
|
|
59
|
+
/** True when an open dashboard consumed the gesture. */
|
|
60
|
+
handleGesture(gestureName) {
|
|
61
|
+
if (!this.opened)
|
|
62
|
+
return false;
|
|
63
|
+
if (this.renderer === "custom")
|
|
64
|
+
return customDashboardService.handleGesture(gestureName);
|
|
65
|
+
if (gestureName === "double_tap") {
|
|
66
|
+
this.close();
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/** Show the dashboard over the main content (ignores contextual_dashboard). */
|
|
72
|
+
open() {
|
|
73
|
+
if (this.opened)
|
|
74
|
+
return;
|
|
75
|
+
if (!isGlassesConnected(useGlassesStore.getState().connection))
|
|
76
|
+
return;
|
|
77
|
+
this.opened = true;
|
|
78
|
+
localDisplayManager.setDashboardActive(true);
|
|
79
|
+
if (customDashboardService.isEligible()) {
|
|
80
|
+
this.renderer = "custom";
|
|
81
|
+
customDashboardService.show();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.renderer = "legacy";
|
|
85
|
+
this.renderLegacy();
|
|
86
|
+
this.legacyTimer = BgTimer.setInterval(() => this.renderLegacy(), LEGACY_CLOCK_REFRESH_MS);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Close and restore the retained main content. */
|
|
90
|
+
close() {
|
|
91
|
+
if (!this.opened)
|
|
92
|
+
return;
|
|
93
|
+
this.opened = false;
|
|
94
|
+
if (this.legacyTimer !== null) {
|
|
95
|
+
BgTimer.clearInterval(this.legacyTimer);
|
|
96
|
+
this.legacyTimer = null;
|
|
97
|
+
}
|
|
98
|
+
if (this.renderer === "custom")
|
|
99
|
+
customDashboardService.hide();
|
|
100
|
+
this.renderer = null;
|
|
101
|
+
localDisplayManager.setDashboardActive(false);
|
|
102
|
+
}
|
|
103
|
+
/** Parity with the retired native head-up wall: time, date, battery. */
|
|
104
|
+
renderLegacy() {
|
|
105
|
+
if (!this.opened)
|
|
106
|
+
return;
|
|
107
|
+
const now = new Date();
|
|
108
|
+
const time = new Intl.DateTimeFormat(undefined, {
|
|
109
|
+
hour: "2-digit",
|
|
110
|
+
minute: "2-digit",
|
|
111
|
+
hour12: useSettingsStore.getState().getSetting(SETTINGS.twelve_hour_time.key) === true,
|
|
112
|
+
}).format(now);
|
|
113
|
+
const date = `${String(now.getMonth() + 1).padStart(2, "0")}/${String(now.getDate()).padStart(2, "0")}`;
|
|
114
|
+
const level = useGlassesStore.getState().batteryLevel;
|
|
115
|
+
const batt = typeof level === "number" && level >= 0 ? ` ${level}%` : "";
|
|
116
|
+
localDisplayManager.renderDashboard(LEGACY_DASHBOARD_PKG, {
|
|
117
|
+
view: "dashboard",
|
|
118
|
+
layout: { layoutType: "text_wall", text: `${time} ${date}${batt} Connected` },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const dashboardCoordinator = new DashboardCoordinator();
|
|
123
|
+
export default dashboardCoordinator;
|
|
124
|
+
export { DashboardCoordinator };
|
|
125
|
+
//# sourceMappingURL=DashboardCoordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DashboardCoordinator.js","sourceRoot":"","sources":["../../src/services/DashboardCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Phone-side dashboard switching. Native retains a single display state; this\n * service decides when the dashboard covers the main content (head-up,\n * double-tap) and which renderer draws it: the CFW custom dashboard, or the\n * legacy time/battery wall for stock devices.\n */\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {BgTimer} from \"../utils/timers\"\nimport customDashboardService from \"./CustomDashboardService\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport localDisplayManager from \"./LocalDisplayManager\"\n\nconst LEGACY_CLOCK_REFRESH_MS = 30_000\nconst LEGACY_DASHBOARD_PKG = \"system.dashboard\"\n\nclass DashboardCoordinator {\n private opened = false\n private renderer: \"custom\" | \"legacy\" | null = null\n private legacyTimer: number | null = null\n\n start(): void {\n customDashboardService.onRequestClose = () => this.close()\n }\n\n stop(): void {\n this.close()\n customDashboardService.onRequestClose = null\n }\n\n get isOpen(): boolean {\n return this.opened\n }\n\n /** Head position (real or simulated). Opening respects contextual_dashboard. */\n setHeadUp(up: boolean): void {\n if (up === this.opened) return\n if (up) {\n if (useSettingsStore.getState().getSetting(SETTINGS.contextual_dashboard.key) === false) return\n this.open()\n } else {\n this.close()\n }\n }\n\n /** Double-tap on a CFW-eligible device. False when not eligible. */\n toggleCustom(): boolean {\n if (!customDashboardService.isEligible()) return false\n if (this.opened) this.close()\n else this.open()\n return true\n }\n\n /** Double-tap fallback for devices without a firmware dashboard. */\n toggleLegacy(): void {\n if (this.opened) this.close()\n else this.open()\n }\n\n /** True when an open dashboard consumed the gesture. */\n handleGesture(gestureName: string): boolean {\n if (!this.opened) return false\n if (this.renderer === \"custom\") return customDashboardService.handleGesture(gestureName)\n if (gestureName === \"double_tap\") {\n this.close()\n return true\n }\n return false\n }\n\n /** Show the dashboard over the main content (ignores contextual_dashboard). */\n open(): void {\n if (this.opened) return\n if (!isGlassesConnected(useGlassesStore.getState().connection)) return\n this.opened = true\n localDisplayManager.setDashboardActive(true)\n if (customDashboardService.isEligible()) {\n this.renderer = \"custom\"\n customDashboardService.show()\n } else {\n this.renderer = \"legacy\"\n this.renderLegacy()\n this.legacyTimer = BgTimer.setInterval(() => this.renderLegacy(), LEGACY_CLOCK_REFRESH_MS)\n }\n }\n\n /** Close and restore the retained main content. */\n close(): void {\n if (!this.opened) return\n this.opened = false\n if (this.legacyTimer !== null) {\n BgTimer.clearInterval(this.legacyTimer)\n this.legacyTimer = null\n }\n if (this.renderer === \"custom\") customDashboardService.hide()\n this.renderer = null\n localDisplayManager.setDashboardActive(false)\n }\n\n /** Parity with the retired native head-up wall: time, date, battery. */\n private renderLegacy(): void {\n if (!this.opened) return\n const now = new Date()\n const time = new Intl.DateTimeFormat(undefined, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: useSettingsStore.getState().getSetting(SETTINGS.twelve_hour_time.key) === true,\n }).format(now)\n const date = `${String(now.getMonth() + 1).padStart(2, \"0\")}/${String(now.getDate()).padStart(2, \"0\")}`\n const level = useGlassesStore.getState().batteryLevel\n const batt = typeof level === \"number\" && level >= 0 ? ` ${level}%` : \"\"\n localDisplayManager.renderDashboard(LEGACY_DASHBOARD_PKG, {\n view: \"dashboard\",\n layout: {layoutType: \"text_wall\", text: `${time} ${date}${batt} Connected`},\n })\n }\n}\n\nconst dashboardCoordinator = new DashboardCoordinator()\nexport default dashboardCoordinator\nexport {DashboardCoordinator}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceEventRouter.d.ts","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"AAoDA,wBAAgB,sBAAsB,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"DeviceEventRouter.d.ts","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"AAoDA,wBAAgB,sBAAsB,IAAI,IAAI,CAuP7C;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C"}
|
|
@@ -34,7 +34,7 @@ import { retirePendingSelectionOnPromotion } from "./PairingIdentity";
|
|
|
34
34
|
import GlobalEventEmitter from "../utils/GlobalEventEmitter";
|
|
35
35
|
import { BgTimer } from "../utils/timers";
|
|
36
36
|
import { asgCameraApi } from "./asg/asgCameraApi";
|
|
37
|
-
import
|
|
37
|
+
import dashboardCoordinator from "./DashboardCoordinator";
|
|
38
38
|
import gestureDotsService from "./GestureDotsService";
|
|
39
39
|
let subs = [];
|
|
40
40
|
const miniappExitGesture = new MiniappExitGesture({
|
|
@@ -156,7 +156,7 @@ export function startDeviceEventRouter() {
|
|
|
156
156
|
subs.push(BluetoothSdk.addListener("touch_event", (event) => {
|
|
157
157
|
// While the custom dashboard is visible it owns its focus/navigation
|
|
158
158
|
// gestures; do not leak those into a miniapp running underneath it.
|
|
159
|
-
if (
|
|
159
|
+
if (dashboardCoordinator.handleGesture(event.gestureName))
|
|
160
160
|
return;
|
|
161
161
|
// Gesture-dots debug indicator (any device, gated by its setting). It
|
|
162
162
|
// only paints while the screen is otherwise empty; the gesture still
|
|
@@ -182,7 +182,7 @@ export function startDeviceEventRouter() {
|
|
|
182
182
|
if (miniappExitGesture.tryExitVisibleMiniapp())
|
|
183
183
|
return;
|
|
184
184
|
// Eligible G2 custom firmware replaces the native dashboard.
|
|
185
|
-
if (
|
|
185
|
+
if (dashboardCoordinator.toggleCustom())
|
|
186
186
|
return;
|
|
187
187
|
const useNativeDashboard = useSettingsStore.getState().getSetting(SETTINGS.use_native_dashboard.key) !== false;
|
|
188
188
|
if (useNativeDashboard) {
|
|
@@ -194,9 +194,7 @@ export function startDeviceEventRouter() {
|
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
else {
|
|
197
|
-
|
|
198
|
-
console.error("DeviceEventRouter: toggleHeadUp failed:", err);
|
|
199
|
-
});
|
|
197
|
+
dashboardCoordinator.toggleLegacy();
|
|
200
198
|
}
|
|
201
199
|
}
|
|
202
200
|
}));
|
|
@@ -211,7 +209,7 @@ export function startDeviceEventRouter() {
|
|
|
211
209
|
}));
|
|
212
210
|
// Head position — translate native {up:boolean} → SDK {position:"up"|"down"}.
|
|
213
211
|
subs.push(BluetoothSdk.addListener("head_up", (event) => {
|
|
214
|
-
|
|
212
|
+
dashboardCoordinator.setHeadUp(event.up);
|
|
215
213
|
localMiniappRuntime.forwardEvent("head_up", { position: event.up ? "up" : "down", timestamp: Date.now() });
|
|
216
214
|
}));
|
|
217
215
|
// On-device STT transcripts → local miniapps only while the local engine is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceEventRouter.js","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Device-event router — engine-owned. Subscribes to the inbound native BLE/device\n * events and routes them into the engine runtime: device stores, the process event\n * bus, the photo/stream coordinators, and local miniapps (via forwardEvent).\n *\n * Why this exists: engine owns the stores, coordinators, miniapp runtime, and facades,\n * but it never *subscribed to the device* for most events — the host MantleManager was\n * still the event router the whole runtime secretly depended on. So a bare OEM that\n * imported engine + called engine.start() got a connected runtime with almost no device\n * data flowing in (no miniapp input, dead gallery sync, starved coordinators). This\n * service moves those inbound bridges into engine so ANY host gets them.\n *\n * Scope: the engine legs only. The Cloud V1 relays that used to live beside them in\n * MantleManager (touch→cloud, the cloud-SDK stream/photo legs, etc.) were deleted with\n * Cloud V1 app end-of-life.\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport {shallow} from \"zustand/shallow\"\n\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport {MiniappExitGesture} from \"./MiniappExitGesture\"\nimport nativeNavigationService from \"./NativeNavigationService\"\nimport speakerLabelResolver from \"./SpeakerLabelResolver\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {BgTimer} from \"../utils/timers\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\nimport customDashboardService from \"./CustomDashboardService\"\nimport gestureDotsService from \"./GestureDotsService\"\n\nlet subs: Array<{remove: () => void}> = []\n\nconst miniappExitGesture = new MiniappExitGesture({\n getState: () => useAppStatusStore.getState(),\n getVisiblePackageName: () => localDisplayManager.getVisibleMiniappPackageName(),\n beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),\n finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),\n cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),\n setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),\n clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),\n})\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n // --- device state → engine stores ---\n\n // Standalone WiFi status → glasses store.\n subs.push(\n BluetoothSdk.addListener(\"wifi_status_change\", (event) => {\n const {type: _type, ...wifi} = event\n useGlassesStore.getState().setGlassesInfo({wifi})\n }),\n )\n\n // Forward glasses Wi-Fi to miniapps (session.glasses.onWifi) from the STORE — the\n // single source of truth — so every path converges here: wifi_status_change,\n // onGlassesStatus, and BLE disconnect. Effective connectivity requires the glasses\n // to be connected AND on Wi-Fi, so a disconnect correctly flips `connected` false.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => {\n const connected = isGlassesConnected(s.connection) && s.wifi.state === \"connected\"\n return {\n connected,\n ssid: s.wifi.state === \"connected\" ? s.wifi.ssid : undefined,\n localIp: s.wifi.state === \"connected\" ? s.wifi.localIp : undefined,\n }\n },\n (wifi) => localMiniappRuntime.forwardEvent(\"glasses_wifi\", wifi),\n {equalityFn: shallow},\n ),\n })\n\n // Incremental battery status → glasses store + local miniapps.\n subs.push(\n BluetoothSdk.addListener(\"battery_status\", (event) => {\n const state = useGlassesStore.getState()\n state.setBatteryInfo(event.level, event.charging, state.caseBatteryLevel, state.caseCharging)\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n level: event.level,\n charging: event.charging,\n timestamp: event.timestamp ?? Date.now(),\n })\n }),\n )\n\n // Hotspot status → glasses store + event bus. engine's own gallerySyncService listens\n // on the bus for these, so without this bridge engine's gallery sync is dead.\n subs.push(\n BluetoothSdk.addListener(\"hotspot_status_change\", (event) => {\n const enabled = event.state === \"enabled\"\n const ssid = enabled ? event.ssid : \"\"\n const password = enabled ? event.password : \"\"\n const localIp = enabled ? event.localIp : \"\"\n useGlassesStore.getState().setHotspotInfo(enabled, ssid, password, localIp)\n // Always retarget the ASG camera client: on disable (or an event with no\n // IP) fall back to the localhost default so later calls fail fast instead\n // of hitting the previous network's stale glasses IP.\n asgCameraApi.setServer(localIp || \"localhost\", 8089)\n GlobalEventEmitter.emit(\"hotspot_status_change\", {enabled, ssid, password, local_ip: localIp})\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"hotspot_error\", (event) => {\n GlobalEventEmitter.emit(\"hotspot_error\", {error_message: event.errorMessage, timestamp: event.timestamp})\n }),\n )\n\n // Glasses gallery content counts → event bus (consumed by gallerySyncService).\n subs.push(\n BluetoothSdk.addListener(\"gallery_status\", (event) => {\n GlobalEventEmitter.emit(\"gallery_status\", {\n photos: event.photos,\n videos: event.videos,\n total: event.total,\n has_content: event.hasContent,\n camera_busy: event.cameraBusy,\n })\n }),\n )\n\n // Hardware-originated setting changes (user changes a setting ON the glasses) → store.\n // The inbound complement to GlassesSettingsSync (which only pushes store→device).\n subs.push(\n BluetoothSdk.addListener(\"save_setting\", async (event) => {\n const settings = useSettingsStore.getState()\n // Damp the relay: native re-echoes some settings unconditionally (e.g.\n // the identity block at every handleDeviceReady) — a same-value echo\n // must not become a store write (persistence churn + change-push noise).\n if (settings.getSetting(event.key) !== event.value) {\n await settings.setSetting(event.key, event.value)\n }\n // Two-phase identity: a promoted default retires the pending selection\n // marker. The rule lives with PairingIdentity (a no-op for other keys).\n await retirePendingSelectionOnPromotion(event.key, event.value)\n }),\n )\n\n // --- coordinators (owns()-gated) ---\n\n // Phone-owned photo errors settle the in-flight long-poll fast (vs. timeout).\n // Non-owned photo responses used to forward to the Cloud V1 photo pipeline via\n // restComms; that relay was removed with Cloud V1 app end-of-life, so they drop.\n // Wire v2 sends a short 4-hex BLE requestId; the coordinator maps it back to\n // the cloud requestId (resolveCloudRequestId) before ownership checks.\n subs.push(\n BluetoothSdk.addListener(\"photo_response\", (event) => {\n const cloudRequestId = event.requestId ? phonePhotoCoordinator.resolveCloudRequestId(event.requestId) : \"\"\n if (cloudRequestId && phonePhotoCoordinator.owns(cloudRequestId)) {\n if (event.state === \"error\") {\n phonePhotoCoordinator.handlePhotoError(\n event.requestId ?? cloudRequestId,\n event.errorCode ?? \"GLASSES_ERROR\",\n event.errorMessage ?? \"Glasses reported an error\",\n )\n }\n }\n }),\n )\n\n // Phone-owned stream status / keep-alive → the stream coordinator. The Cloud V1 relay\n // for non-owned streams was removed with Cloud V1 app end-of-life.\n subs.push(\n BluetoothSdk.addListener(\"stream_status\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleGlassesStatus(event)\n }\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"keep_alive_ack\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleKeepAliveAck(event)\n }\n }),\n )\n\n // --- device input → local miniapps (forwardEvent is subscriber-gated; a no-op when\n // no miniapp listens). The v1 SocketComms cloud legs for these were removed with\n // the Cloud V1 ripout. ---\n\n subs.push(\n BluetoothSdk.addListener(\"button_press\", (event) => {\n localMiniappRuntime.forwardEvent(\"button_press\", event)\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"touch_event\", (event) => {\n // While the custom dashboard is visible it owns its focus/navigation\n // gestures; do not leak those into a miniapp running underneath it.\n if (customDashboardService.handleGesture(event.gestureName)) return\n\n // Gesture-dots debug indicator (any device, gated by its setting). It\n // only paints while the screen is otherwise empty; the gesture still\n // flows to miniapps below.\n gestureDotsService.noteGesture(event.gestureName)\n\n localMiniappRuntime.forwardEvent(\"touch_event\", event)\n // Double-tap policy, moved out of the native G2 handlers: exit a visible\n // unclaimed miniapp; otherwise open the dashboard or toggle head-up.\n // Native only reports the gesture now.\n if (event.gestureName === \"double_tap\") {\n // A foreground miniapp that claimed double_tap (CLAIM_GESTURES) is\n // using it as an in-app gesture (e.g. the terminal's back-to-menu) —\n // the event was already forwarded above; skip the system default.\n if (localMiniappRuntime.foregroundClaimsGesture(\"double_tap\")) return\n\n // An unclaimed double tap exits only the miniapp whose frame is visibly\n // on the glasses. The controller shows the bounded OS notice first,\n // then uses the normal store stop path (and closes its phone overlay).\n // Checked before the dashboard toggle: while an app owns the screen an\n // unclaimed double-tap always means \"leave the app\" — the (custom or\n // native) dashboard is for the empty screen, and head-up still opens\n // the custom dashboard mid-app.\n if (miniappExitGesture.tryExitVisibleMiniapp()) return\n\n // Eligible G2 custom firmware replaces the native dashboard.\n if (customDashboardService.toggle()) return\n\n const useNativeDashboard = useSettingsStore.getState().getSetting(SETTINGS.use_native_dashboard.key) !== false\n if (useNativeDashboard) {\n // The modal nav UI owns the screen; showDashboard would fight it.\n if (!nativeNavigationService.isActive) {\n void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {\n console.error(\"DeviceEventRouter: showDashboard failed:\", err)\n })\n }\n } else {\n void Promise.resolve(BluetoothSdk.toggleHeadUp()).catch((err) => {\n console.error(\"DeviceEventRouter: toggleHeadUp failed:\", err)\n })\n }\n }\n }),\n )\n // G2 IMU accelerometer — payload already matches the miniapp AccelData shape.\n subs.push(\n BluetoothSdk.addListener(\"accel_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"accel_event\", {\n x: event.x,\n y: event.y,\n z: event.z,\n timestamp: typeof event.timestamp === \"number\" ? event.timestamp : Date.now(),\n })\n }),\n )\n // Head position — translate native {up:boolean} → SDK {position:\"up\"|\"down\"}.\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n customDashboardService.setHeadUp(event.up)\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n // On-device STT transcripts → local miniapps only while the local engine is\n // active (cloud fallback or an explicit forceLocal subscription). The runtime\n // filters mixed local/cloud delivery per miniapp to avoid double-delivery.\n // forwardEvent is subscriber-gated — a no-op when no miniapp listens.\n // (Was MantleManager.handle_local_transcription; its offline-captions display branch\n // went away with the pseudo captions renderer.)\n subs.push(\n BluetoothSdk.addListener(\"local_transcription\", (event) => {\n if (!localSttFallbackCoordinator.isActive()) return\n const lang = event.transcribeLanguage ?? localSttFallbackCoordinator.getActiveLanguage() ?? \"en-US\"\n // Native stamps resolvedSpeakerId when the voice matched the enrolled\n // profile; surface that as `isUser` (the native join has no\n // confident-other signal, so unmatched stays unannotated).\n localMiniappRuntime.forwardEvent(\n `transcription:${lang}`,\n speakerLabelResolver.annotateLocalTranscript(event),\n \"local\",\n )\n }),\n )\n\n // --- glasses swipe-menu app launcher (G2) ---\n subs.push(\n BluetoothSdk.addListener(\"miniapp_selected\", (event) => {\n const packageName = event.packageName as string\n if (!packageName) return\n const app = useAppStatusStore.getState().apps.find((a) => a.packageName === packageName)\n if (!app) return\n // Toggle: stop if running, else start.\n if (app.running) {\n useAppStatusStore.getState().stop(packageName)\n } else {\n useAppStatusStore.getState().start(app, {skipNavigation: true})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n miniappExitGesture.reset()\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DeviceEventRouter.js","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Device-event router — engine-owned. Subscribes to the inbound native BLE/device\n * events and routes them into the engine runtime: device stores, the process event\n * bus, the photo/stream coordinators, and local miniapps (via forwardEvent).\n *\n * Why this exists: engine owns the stores, coordinators, miniapp runtime, and facades,\n * but it never *subscribed to the device* for most events — the host MantleManager was\n * still the event router the whole runtime secretly depended on. So a bare OEM that\n * imported engine + called engine.start() got a connected runtime with almost no device\n * data flowing in (no miniapp input, dead gallery sync, starved coordinators). This\n * service moves those inbound bridges into engine so ANY host gets them.\n *\n * Scope: the engine legs only. The Cloud V1 relays that used to live beside them in\n * MantleManager (touch→cloud, the cloud-SDK stream/photo legs, etc.) were deleted with\n * Cloud V1 app end-of-life.\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport {shallow} from \"zustand/shallow\"\n\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport {MiniappExitGesture} from \"./MiniappExitGesture\"\nimport nativeNavigationService from \"./NativeNavigationService\"\nimport speakerLabelResolver from \"./SpeakerLabelResolver\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {BgTimer} from \"../utils/timers\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\nimport dashboardCoordinator from \"./DashboardCoordinator\"\nimport gestureDotsService from \"./GestureDotsService\"\n\nlet subs: Array<{remove: () => void}> = []\n\nconst miniappExitGesture = new MiniappExitGesture({\n getState: () => useAppStatusStore.getState(),\n getVisiblePackageName: () => localDisplayManager.getVisibleMiniappPackageName(),\n beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),\n finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),\n cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),\n setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),\n clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),\n})\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n // --- device state → engine stores ---\n\n // Standalone WiFi status → glasses store.\n subs.push(\n BluetoothSdk.addListener(\"wifi_status_change\", (event) => {\n const {type: _type, ...wifi} = event\n useGlassesStore.getState().setGlassesInfo({wifi})\n }),\n )\n\n // Forward glasses Wi-Fi to miniapps (session.glasses.onWifi) from the STORE — the\n // single source of truth — so every path converges here: wifi_status_change,\n // onGlassesStatus, and BLE disconnect. Effective connectivity requires the glasses\n // to be connected AND on Wi-Fi, so a disconnect correctly flips `connected` false.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => {\n const connected = isGlassesConnected(s.connection) && s.wifi.state === \"connected\"\n return {\n connected,\n ssid: s.wifi.state === \"connected\" ? s.wifi.ssid : undefined,\n localIp: s.wifi.state === \"connected\" ? s.wifi.localIp : undefined,\n }\n },\n (wifi) => localMiniappRuntime.forwardEvent(\"glasses_wifi\", wifi),\n {equalityFn: shallow},\n ),\n })\n\n // Incremental battery status → glasses store + local miniapps.\n subs.push(\n BluetoothSdk.addListener(\"battery_status\", (event) => {\n const state = useGlassesStore.getState()\n state.setBatteryInfo(event.level, event.charging, state.caseBatteryLevel, state.caseCharging)\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n level: event.level,\n charging: event.charging,\n timestamp: event.timestamp ?? Date.now(),\n })\n }),\n )\n\n // Hotspot status → glasses store + event bus. engine's own gallerySyncService listens\n // on the bus for these, so without this bridge engine's gallery sync is dead.\n subs.push(\n BluetoothSdk.addListener(\"hotspot_status_change\", (event) => {\n const enabled = event.state === \"enabled\"\n const ssid = enabled ? event.ssid : \"\"\n const password = enabled ? event.password : \"\"\n const localIp = enabled ? event.localIp : \"\"\n useGlassesStore.getState().setHotspotInfo(enabled, ssid, password, localIp)\n // Always retarget the ASG camera client: on disable (or an event with no\n // IP) fall back to the localhost default so later calls fail fast instead\n // of hitting the previous network's stale glasses IP.\n asgCameraApi.setServer(localIp || \"localhost\", 8089)\n GlobalEventEmitter.emit(\"hotspot_status_change\", {enabled, ssid, password, local_ip: localIp})\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"hotspot_error\", (event) => {\n GlobalEventEmitter.emit(\"hotspot_error\", {error_message: event.errorMessage, timestamp: event.timestamp})\n }),\n )\n\n // Glasses gallery content counts → event bus (consumed by gallerySyncService).\n subs.push(\n BluetoothSdk.addListener(\"gallery_status\", (event) => {\n GlobalEventEmitter.emit(\"gallery_status\", {\n photos: event.photos,\n videos: event.videos,\n total: event.total,\n has_content: event.hasContent,\n camera_busy: event.cameraBusy,\n })\n }),\n )\n\n // Hardware-originated setting changes (user changes a setting ON the glasses) → store.\n // The inbound complement to GlassesSettingsSync (which only pushes store→device).\n subs.push(\n BluetoothSdk.addListener(\"save_setting\", async (event) => {\n const settings = useSettingsStore.getState()\n // Damp the relay: native re-echoes some settings unconditionally (e.g.\n // the identity block at every handleDeviceReady) — a same-value echo\n // must not become a store write (persistence churn + change-push noise).\n if (settings.getSetting(event.key) !== event.value) {\n await settings.setSetting(event.key, event.value)\n }\n // Two-phase identity: a promoted default retires the pending selection\n // marker. The rule lives with PairingIdentity (a no-op for other keys).\n await retirePendingSelectionOnPromotion(event.key, event.value)\n }),\n )\n\n // --- coordinators (owns()-gated) ---\n\n // Phone-owned photo errors settle the in-flight long-poll fast (vs. timeout).\n // Non-owned photo responses used to forward to the Cloud V1 photo pipeline via\n // restComms; that relay was removed with Cloud V1 app end-of-life, so they drop.\n // Wire v2 sends a short 4-hex BLE requestId; the coordinator maps it back to\n // the cloud requestId (resolveCloudRequestId) before ownership checks.\n subs.push(\n BluetoothSdk.addListener(\"photo_response\", (event) => {\n const cloudRequestId = event.requestId ? phonePhotoCoordinator.resolveCloudRequestId(event.requestId) : \"\"\n if (cloudRequestId && phonePhotoCoordinator.owns(cloudRequestId)) {\n if (event.state === \"error\") {\n phonePhotoCoordinator.handlePhotoError(\n event.requestId ?? cloudRequestId,\n event.errorCode ?? \"GLASSES_ERROR\",\n event.errorMessage ?? \"Glasses reported an error\",\n )\n }\n }\n }),\n )\n\n // Phone-owned stream status / keep-alive → the stream coordinator. The Cloud V1 relay\n // for non-owned streams was removed with Cloud V1 app end-of-life.\n subs.push(\n BluetoothSdk.addListener(\"stream_status\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleGlassesStatus(event)\n }\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"keep_alive_ack\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleKeepAliveAck(event)\n }\n }),\n )\n\n // --- device input → local miniapps (forwardEvent is subscriber-gated; a no-op when\n // no miniapp listens). The v1 SocketComms cloud legs for these were removed with\n // the Cloud V1 ripout. ---\n\n subs.push(\n BluetoothSdk.addListener(\"button_press\", (event) => {\n localMiniappRuntime.forwardEvent(\"button_press\", event)\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"touch_event\", (event) => {\n // While the custom dashboard is visible it owns its focus/navigation\n // gestures; do not leak those into a miniapp running underneath it.\n if (dashboardCoordinator.handleGesture(event.gestureName)) return\n\n // Gesture-dots debug indicator (any device, gated by its setting). It\n // only paints while the screen is otherwise empty; the gesture still\n // flows to miniapps below.\n gestureDotsService.noteGesture(event.gestureName)\n\n localMiniappRuntime.forwardEvent(\"touch_event\", event)\n // Double-tap policy, moved out of the native G2 handlers: exit a visible\n // unclaimed miniapp; otherwise open the dashboard or toggle head-up.\n // Native only reports the gesture now.\n if (event.gestureName === \"double_tap\") {\n // A foreground miniapp that claimed double_tap (CLAIM_GESTURES) is\n // using it as an in-app gesture (e.g. the terminal's back-to-menu) —\n // the event was already forwarded above; skip the system default.\n if (localMiniappRuntime.foregroundClaimsGesture(\"double_tap\")) return\n\n // An unclaimed double tap exits only the miniapp whose frame is visibly\n // on the glasses. The controller shows the bounded OS notice first,\n // then uses the normal store stop path (and closes its phone overlay).\n // Checked before the dashboard toggle: while an app owns the screen an\n // unclaimed double-tap always means \"leave the app\" — the (custom or\n // native) dashboard is for the empty screen, and head-up still opens\n // the custom dashboard mid-app.\n if (miniappExitGesture.tryExitVisibleMiniapp()) return\n\n // Eligible G2 custom firmware replaces the native dashboard.\n if (dashboardCoordinator.toggleCustom()) return\n\n const useNativeDashboard = useSettingsStore.getState().getSetting(SETTINGS.use_native_dashboard.key) !== false\n if (useNativeDashboard) {\n // The modal nav UI owns the screen; showDashboard would fight it.\n if (!nativeNavigationService.isActive) {\n void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {\n console.error(\"DeviceEventRouter: showDashboard failed:\", err)\n })\n }\n } else {\n dashboardCoordinator.toggleLegacy()\n }\n }\n }),\n )\n // G2 IMU accelerometer — payload already matches the miniapp AccelData shape.\n subs.push(\n BluetoothSdk.addListener(\"accel_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"accel_event\", {\n x: event.x,\n y: event.y,\n z: event.z,\n timestamp: typeof event.timestamp === \"number\" ? event.timestamp : Date.now(),\n })\n }),\n )\n // Head position — translate native {up:boolean} → SDK {position:\"up\"|\"down\"}.\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n dashboardCoordinator.setHeadUp(event.up)\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n // On-device STT transcripts → local miniapps only while the local engine is\n // active (cloud fallback or an explicit forceLocal subscription). The runtime\n // filters mixed local/cloud delivery per miniapp to avoid double-delivery.\n // forwardEvent is subscriber-gated — a no-op when no miniapp listens.\n // (Was MantleManager.handle_local_transcription; its offline-captions display branch\n // went away with the pseudo captions renderer.)\n subs.push(\n BluetoothSdk.addListener(\"local_transcription\", (event) => {\n if (!localSttFallbackCoordinator.isActive()) return\n const lang = event.transcribeLanguage ?? localSttFallbackCoordinator.getActiveLanguage() ?? \"en-US\"\n // Native stamps resolvedSpeakerId when the voice matched the enrolled\n // profile; surface that as `isUser` (the native join has no\n // confident-other signal, so unmatched stays unannotated).\n localMiniappRuntime.forwardEvent(\n `transcription:${lang}`,\n speakerLabelResolver.annotateLocalTranscript(event),\n \"local\",\n )\n }),\n )\n\n // --- glasses swipe-menu app launcher (G2) ---\n subs.push(\n BluetoothSdk.addListener(\"miniapp_selected\", (event) => {\n const packageName = event.packageName as string\n if (!packageName) return\n const app = useAppStatusStore.getState().apps.find((a) => a.packageName === packageName)\n if (!app) return\n // Toggle: stop if running, else start.\n if (app.running) {\n useAppStatusStore.getState().stop(packageName)\n } else {\n useAppStatusStore.getState().start(app, {skipNavigation: true})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n miniappExitGesture.reset()\n}\n"]}
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* DisplayProcessor
|
|
3
3
|
*
|
|
4
4
|
* Processes display events with pixel-accurate text wrapping using the same
|
|
5
|
-
* display
|
|
6
|
-
*
|
|
5
|
+
* Engine display utilities. This ensures the mobile preview matches exactly
|
|
6
|
+
* what is shown on the glasses.
|
|
7
7
|
*
|
|
8
8
|
* Key responsibilities:
|
|
9
9
|
* 1. Intercept display events before sending to native SGC
|
|
10
10
|
* 2. Wrap text using the correct device profile (G1, etc.)
|
|
11
11
|
* 3. Ensure GlassesDisplayMirror shows exactly what glasses will show
|
|
12
12
|
*
|
|
13
|
-
* @see
|
|
13
|
+
* @see notes/miniapp-display-api-redesign.md for the current display design
|
|
14
14
|
*/
|
|
15
15
|
import { type DisplayProfile, type WrapOptions, type BreakMode } from "../utils/display";
|
|
16
16
|
/**
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* DisplayProcessor
|
|
3
3
|
*
|
|
4
4
|
* Processes display events with pixel-accurate text wrapping using the same
|
|
5
|
-
* display
|
|
6
|
-
*
|
|
5
|
+
* Engine display utilities. This ensures the mobile preview matches exactly
|
|
6
|
+
* what is shown on the glasses.
|
|
7
7
|
*
|
|
8
8
|
* Key responsibilities:
|
|
9
9
|
* 1. Intercept display events before sending to native SGC
|
|
10
10
|
* 2. Wrap text using the correct device profile (G1, etc.)
|
|
11
11
|
* 3. Ensure GlassesDisplayMirror shows exactly what glasses will show
|
|
12
12
|
*
|
|
13
|
-
* @see
|
|
13
|
+
* @see notes/miniapp-display-api-redesign.md for the current display design
|
|
14
14
|
*/
|
|
15
15
|
import { createDisplayToolkit, G1_PROFILE, G2_PROFILE, Z100_PROFILE, NEX_PROFILE, ColumnComposer, } from "../utils/display";
|
|
16
16
|
import { ISLAND_SETTINGS_KEYS } from "../runtime/config";
|