@glassly/engine 0.1.0-dev.1 → 0.1.0-dev.101
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 +57 -19
- package/build/bluetooth-sdk/debug.d.ts +2 -0
- package/build/bluetooth-sdk/debug.d.ts.map +1 -0
- package/build/bluetooth-sdk/debug.js +2 -0
- package/build/bluetooth-sdk/debug.js.map +1 -0
- package/build/bluetooth-sdk/index.d.ts +3 -0
- package/build/bluetooth-sdk/index.d.ts.map +1 -0
- package/build/bluetooth-sdk/index.js +3 -0
- package/build/bluetooth-sdk/index.js.map +1 -0
- package/build/bluetooth-sdk/ota-transport.d.ts +2 -0
- package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
- package/build/bluetooth-sdk/ota-transport.js +2 -0
- package/build/bluetooth-sdk/ota-transport.js.map +1 -0
- package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
- package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
- package/build/bluetooth-sdk/photo-receiver.js +3 -0
- package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
- package/build/bluetooth-sdk/react.d.ts +2 -0
- package/build/bluetooth-sdk/react.d.ts.map +1 -0
- package/build/bluetooth-sdk/react.js +2 -0
- package/build/bluetooth-sdk/react.js.map +1 -0
- package/build/bluetooth-sdk/types.d.ts +2 -0
- package/build/bluetooth-sdk/types.d.ts.map +1 -0
- package/build/bluetooth-sdk/types.js +2 -0
- package/build/bluetooth-sdk/types.js.map +1 -0
- package/build/engine.d.ts +30 -18
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +12 -3
- package/build/engine.js.map +1 -1
- package/build/facades/dev.d.ts +1 -1
- package/build/facades/display.d.ts +11 -4
- package/build/facades/display.d.ts.map +1 -1
- package/build/facades/display.js +12 -2
- 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 +6 -13
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +7 -3
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/ota.d.ts +21 -21
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +26 -10
- package/build/facades/ota.js.map +1 -1
- package/build/facades/pairing.d.ts +11 -5
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +93 -17
- package/build/facades/pairing.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/facades/speakers.d.ts +1 -1
- package/build/facades/speakers.d.ts.map +1 -1
- package/build/facades/speakers.js.map +1 -1
- package/build/facades/speech.d.ts +7 -6
- package/build/facades/speech.d.ts.map +1 -1
- package/build/facades/speech.js +1 -1
- package/build/facades/speech.js.map +1 -1
- package/build/generated/releaseMetadata.d.ts +12 -0
- package/build/generated/releaseMetadata.d.ts.map +1 -0
- package/build/generated/releaseMetadata.js +10 -0
- package/build/generated/releaseMetadata.js.map +1 -0
- package/build/index.d.ts +9 -6
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -5
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +44 -0
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
- package/build/react/MentraLiveOtaFlow.js +322 -0
- package/build/react/MentraLiveOtaFlow.js.map +1 -0
- package/build/react/index.d.ts +3 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +3 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useEngineSnapshot.d.ts +2 -0
- package/build/react/useEngineSnapshot.d.ts.map +1 -0
- package/build/react/useEngineSnapshot.js +14 -0
- package/build/react/useEngineSnapshot.js.map +1 -0
- package/build/react/useMentraLiveOta.d.ts +84 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -0
- package/build/react/useMentraLiveOta.js +627 -0
- package/build/react/useMentraLiveOta.js.map +1 -0
- package/build/services/AppRegistry.d.ts +8 -0
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +15 -0
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/BlobStore.d.ts +2 -0
- package/build/services/BlobStore.d.ts.map +1 -1
- package/build/services/BlobStore.js +63 -10
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/CustomDashboardModel.d.ts +48 -0
- package/build/services/CustomDashboardModel.d.ts.map +1 -0
- package/build/services/CustomDashboardModel.js +73 -0
- package/build/services/CustomDashboardModel.js.map +1 -0
- package/build/services/CustomDashboardService.d.ts +55 -0
- package/build/services/CustomDashboardService.d.ts.map +1 -0
- package/build/services/CustomDashboardService.js +520 -0
- package/build/services/CustomDashboardService.js.map +1 -0
- 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 +45 -6
- 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/GestureDotsService.d.ts +40 -0
- package/build/services/GestureDotsService.d.ts.map +1 -0
- package/build/services/GestureDotsService.js +167 -0
- package/build/services/GestureDotsService.js.map +1 -0
- package/build/services/GlassesStatusProjection.d.ts +15 -1
- package/build/services/GlassesStatusProjection.d.ts.map +1 -1
- package/build/services/GlassesStatusProjection.js +18 -11
- package/build/services/GlassesStatusProjection.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts.map +1 -1
- package/build/services/HotspotOtaTransport.js +8 -7
- package/build/services/HotspotOtaTransport.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts +55 -3
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +196 -16
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +13 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +32 -0
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MiniappExitGesture.d.ts +33 -0
- package/build/services/MiniappExitGesture.d.ts.map +1 -0
- package/build/services/MiniappExitGesture.js +54 -0
- package/build/services/MiniappExitGesture.js.map +1 -0
- package/build/services/OtaAutoChain.d.ts +41 -0
- package/build/services/OtaAutoChain.d.ts.map +1 -0
- package/build/services/OtaAutoChain.js +91 -0
- package/build/services/OtaAutoChain.js.map +1 -0
- package/build/services/OtaErrorMapping.d.ts +18 -0
- package/build/services/OtaErrorMapping.d.ts.map +1 -0
- package/build/services/OtaErrorMapping.js +63 -0
- package/build/services/OtaErrorMapping.js.map +1 -0
- package/build/services/OtaInstallCoordinator.d.ts +26 -3
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +38 -13
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/OtaService.d.ts.map +1 -1
- package/build/services/OtaService.js +1 -1
- package/build/services/OtaService.js.map +1 -1
- package/build/services/OtaUpdateCheckService.d.ts +4 -1
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +28 -16
- package/build/services/OtaUpdateCheckService.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/asg/localNetworkTransport.d.ts.map +1 -1
- package/build/services/asg/localNetworkTransport.js +13 -12
- package/build/services/asg/localNetworkTransport.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 +26 -7
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/services/gestureClaims.d.ts +18 -0
- package/build/services/gestureClaims.d.ts.map +1 -0
- package/build/services/gestureClaims.js +24 -0
- package/build/services/gestureClaims.js.map +1 -0
- package/build/services/gestureDotGlyphs.d.ts +8 -0
- package/build/services/gestureDotGlyphs.d.ts.map +1 -0
- package/build/services/gestureDotGlyphs.js +13 -0
- package/build/services/gestureDotGlyphs.js.map +1 -0
- package/build/services/glassesClockSync.d.ts.map +1 -1
- package/build/services/glassesClockSync.js +5 -1
- package/build/services/glassesClockSync.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/otaDisplayState.d.ts +1 -1
- package/build/services/otaDisplayState.d.ts.map +1 -1
- package/build/services/otaDisplayState.js.map +1 -1
- package/build/services/otaInstallPolicy.d.ts +5 -1
- 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/services/otaLegacyMapping.d.ts +1 -1
- package/build/services/otaLegacyMapping.d.ts.map +1 -1
- package/build/services/otaLegacyMapping.js.map +1 -1
- package/build/services/otaManifestPolicy.d.ts +14 -0
- package/build/services/otaManifestPolicy.d.ts.map +1 -0
- package/build/services/otaManifestPolicy.js +20 -0
- package/build/services/otaManifestPolicy.js.map +1 -0
- package/build/services/otaManifestUrl.d.ts +2 -1
- package/build/services/otaManifestUrl.d.ts.map +1 -1
- package/build/services/otaManifestUrl.js +24 -32
- package/build/services/otaManifestUrl.js.map +1 -1
- package/build/services/otaReleaseVersion.d.ts +10 -0
- package/build/services/otaReleaseVersion.d.ts.map +1 -0
- package/build/services/otaReleaseVersion.js +14 -0
- package/build/services/otaReleaseVersion.js.map +1 -0
- package/build/stores/appVisibility.d.ts +3 -0
- package/build/stores/appVisibility.d.ts.map +1 -0
- package/build/stores/appVisibility.js +3 -0
- package/build/stores/appVisibility.js.map +1 -0
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +7 -1
- package/build/stores/apps.js.map +1 -1
- package/build/stores/core.d.ts +3 -3
- package/build/stores/core.d.ts.map +1 -1
- package/build/stores/core.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 +10 -4
- 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 +140 -5
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +3 -1
- package/build/types/applet.d.ts.map +1 -1
- package/build/types/applet.js +0 -3
- 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/enums.d.ts +1 -5
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -8
- package/build/types/enums.js.map +1 -1
- package/build/types/hardware.d.ts +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 +3 -7
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js +3 -7
- 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/build/utils/display/scene/process.d.ts.map +1 -1
- package/build/utils/display/scene/process.js +4 -3
- package/build/utils/display/scene/process.js.map +1 -1
- package/build/utils/display/scene/types.d.ts +6 -2
- package/build/utils/display/scene/types.d.ts.map +1 -1
- package/build/utils/display/scene/types.js.map +1 -1
- package/package.json +69 -35
- package/src/bluetooth-sdk/debug.ts +1 -0
- package/src/bluetooth-sdk/index.ts +2 -0
- package/src/bluetooth-sdk/ota-transport.ts +1 -0
- package/src/bluetooth-sdk/photo-receiver.ts +2 -0
- package/src/bluetooth-sdk/react.ts +1 -0
- package/src/bluetooth-sdk/types.ts +1 -0
- package/src/engine.ts +12 -3
- package/src/facades/display.ts +12 -2
- package/src/facades/displayMirror.ts +7 -15
- package/src/facades/glasses.ts +8 -4
- package/src/facades/ota.ts +36 -11
- package/src/facades/pairing.ts +103 -23
- package/src/facades/phoneNotifications.ts +32 -10
- package/src/facades/speakers.ts +1 -1
- package/src/facades/speech.ts +21 -7
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +31 -5
- package/src/react/MentraLiveOtaFlow.tsx +754 -0
- package/src/react/index.ts +21 -0
- package/src/react/useEngineSnapshot.ts +16 -0
- package/src/react/useMentraLiveOta.ts +793 -0
- package/src/services/AppRegistry.ts +16 -0
- package/src/services/BlobStore.ts +57 -10
- package/src/services/CustomDashboardModel.ts +83 -0
- package/src/services/CustomDashboardService.ts +544 -0
- package/src/services/DashboardCoordinator.ts +121 -0
- package/src/services/DeviceEventRouter.ts +48 -8
- package/src/services/DisplayProcessor.ts +3 -3
- package/src/services/GestureDotsService.ts +164 -0
- package/src/services/GlassesStatusProjection.ts +20 -11
- package/src/services/HotspotOtaTransport.ts +8 -7
- package/src/services/LocalDisplayManager.ts +198 -18
- package/src/services/LocalMiniappRuntime.ts +37 -0
- package/src/services/MiniappExitGesture.ts +76 -0
- package/src/services/OtaAutoChain.ts +130 -0
- package/src/services/OtaErrorMapping.ts +80 -0
- package/src/services/OtaInstallCoordinator.ts +40 -13
- package/src/services/OtaService.ts +1 -2
- package/src/services/OtaUpdateCheckService.ts +33 -19
- 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/asg/localNetworkTransport.ts +20 -19
- package/src/services/effectiveCapabilities.ts +27 -7
- package/src/services/gestureClaims.ts +28 -0
- package/src/services/gestureDotGlyphs.ts +15 -0
- package/src/services/glassesClockSync.ts +5 -1
- package/src/services/iosNotificationAppCatalog.ts +39 -0
- package/src/services/otaDisplayState.ts +1 -1
- package/src/services/otaInstallPolicy.ts +5 -1
- package/src/services/otaLegacyMapping.ts +1 -1
- package/src/services/otaManifestPolicy.ts +43 -0
- package/src/services/otaManifestUrl.ts +27 -36
- package/src/services/otaReleaseVersion.ts +22 -0
- package/src/stores/appVisibility.ts +3 -0
- package/src/stores/apps.ts +7 -1
- package/src/stores/core.ts +4 -4
- package/src/stores/display.ts +16 -48
- package/src/stores/glasses.ts +15 -6
- package/src/stores/settings.ts +140 -5
- package/src/types/applet.ts +3 -1
- package/src/types/capabilities/even-realities-g2.ts +3 -2
- package/src/types/enums.ts +1 -9
- package/src/types/hardware.ts +2 -1
- package/src/types/index.ts +3 -7
- package/src/utils/display/composer/ColumnComposer.ts +1 -1
- package/src/utils/display/scene/process.ts +4 -3
- package/src/utils/display/scene/types.ts +0 -0
- package/build/devtools.d.ts +0 -11
- package/build/devtools.d.ts.map +0 -1
- package/build/devtools.js +0 -13
- package/build/devtools.js.map +0 -1
- package/build/internal.d.ts +0 -67
- package/build/internal.d.ts.map +0 -1
- package/build/internal.js +0 -90
- package/build/internal.js.map +0 -1
- package/src/devtools.ts +0 -13
- package/src/internal.ts +0 -155
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
declare class CustomDashboardService {
|
|
2
|
+
private started;
|
|
3
|
+
private open;
|
|
4
|
+
/** Set by DashboardCoordinator; asks it to close (and restore main content). */
|
|
5
|
+
onRequestClose: (() => void) | null;
|
|
6
|
+
private view;
|
|
7
|
+
private mainIndex;
|
|
8
|
+
private appsIndex;
|
|
9
|
+
/** First visible row of the app list's scrolling window. */
|
|
10
|
+
private appsScroll;
|
|
11
|
+
/** "loading" until the first fetch resolves; "no-access" only for a real permission denial. */
|
|
12
|
+
private calendarStatus;
|
|
13
|
+
private calendarEvents;
|
|
14
|
+
private unsubs;
|
|
15
|
+
private clockTimer;
|
|
16
|
+
private calendarTimer;
|
|
17
|
+
/** Last observed eligibility, for transition detection. */
|
|
18
|
+
private eligible;
|
|
19
|
+
start(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Track eligible⇄ineligible transitions from any trigger (setting toggle,
|
|
22
|
+
* connect/disconnect, firmware change). Leaving closes the dashboard via the
|
|
23
|
+
* coordinator (which restores main content) and drops the retained scene so
|
|
24
|
+
* a stale frame can't be replayed after the feature was turned off.
|
|
25
|
+
*/
|
|
26
|
+
private syncEligibility;
|
|
27
|
+
stop(): void;
|
|
28
|
+
isEligible(): boolean;
|
|
29
|
+
get isOpen(): boolean;
|
|
30
|
+
/** Coordinator opened the dashboard: render fresh from the top. */
|
|
31
|
+
show(): void;
|
|
32
|
+
/** Coordinator closed the dashboard. */
|
|
33
|
+
hide(): void;
|
|
34
|
+
/** Returns true when an open custom dashboard consumed the gesture. */
|
|
35
|
+
handleGesture(gestureName: string): boolean;
|
|
36
|
+
private resetNavigation;
|
|
37
|
+
private handleMainGesture;
|
|
38
|
+
private handleAppMenuGesture;
|
|
39
|
+
private menuApps;
|
|
40
|
+
private widgets;
|
|
41
|
+
private openNotificationsPanel;
|
|
42
|
+
private toggleApp;
|
|
43
|
+
private calendarWidgetEnabled;
|
|
44
|
+
private refreshCalendarAndRender;
|
|
45
|
+
/** Underline hugging a builtin-font label. x/rowY are the label box's origin. */
|
|
46
|
+
private labelUnderline;
|
|
47
|
+
private canvas;
|
|
48
|
+
private render;
|
|
49
|
+
private mainElements;
|
|
50
|
+
private appMenuElements;
|
|
51
|
+
}
|
|
52
|
+
declare const customDashboardService: CustomDashboardService;
|
|
53
|
+
export default customDashboardService;
|
|
54
|
+
export { CustomDashboardService };
|
|
55
|
+
//# sourceMappingURL=CustomDashboardService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomDashboardService.d.ts","sourceRoot":"","sources":["../../src/services/CustomDashboardService.ts"],"names":[],"mappings":"AA6FA,cAAM,sBAAsB;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,IAAI,CAAQ;IACpB,gFAAgF;IACzE,cAAc,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAO;IACjD,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,SAAS,CAAI;IACrB,4DAA4D;IAC5D,OAAO,CAAC,UAAU,CAAI;IACtB,+FAA+F;IAC/F,OAAO,CAAC,cAAc,CAA4D;IAClF,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,aAAa,CAAsB;IAC3C,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAQ;IAExB,KAAK,IAAI,IAAI;IA2Cb;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAavB,IAAI,IAAI,IAAI;IAeZ,UAAU,IAAI,OAAO;IAYrB,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,mEAAmE;IACnE,IAAI,IAAI,IAAI;IAMZ,wCAAwC;IACxC,IAAI,IAAI,IAAI;IAKZ,uEAAuE;IACvE,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAiB3C,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,OAAO;YAID,sBAAsB;YAOtB,SAAS;IAcvB,OAAO,CAAC,qBAAqB;YAIf,wBAAwB;IAsCtC,iFAAiF;IACjF,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,YAAY;IAiGpB,OAAO,CAAC,eAAe;CAqDxB;AAED,QAAA,MAAM,sBAAsB,wBAA+B,CAAA;AAC3D,eAAe,sBAAsB,CAAA;AACrC,OAAO,EAAC,sBAAsB,EAAC,CAAA"}
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scene API dashboard for G2 custom firmware.
|
|
3
|
+
*
|
|
4
|
+
* G2.swift marks the published firmware version after it detects the CFW
|
|
5
|
+
* capability advertisement. DashboardCoordinator owns open/close and display
|
|
6
|
+
* switching; this service renders the widgets and handles gestures while open.
|
|
7
|
+
*
|
|
8
|
+
* The dashboard is composed of user-orderable widgets (clock, calendar, apps
|
|
9
|
+
* button, notifications button) stacked vertically on the CFW scene canvas.
|
|
10
|
+
* The apps button switches to a second, dashboard-rendered app menu view.
|
|
11
|
+
*/
|
|
12
|
+
import { BluetoothSdk } from "@glassly/bluetooth-sdk";
|
|
13
|
+
import { MiniappErrorCode } from "@glassly/miniapp";
|
|
14
|
+
import { useAppStatusStore } from "../stores/apps";
|
|
15
|
+
import { useGlassesStore } from "../stores/glasses";
|
|
16
|
+
import { SETTINGS, useSettingsStore } from "../stores/settings";
|
|
17
|
+
import { TextMeasurer } from "../utils/display/measurer";
|
|
18
|
+
import { BgTimer } from "../utils/timers";
|
|
19
|
+
import { appMenuActions, dashboardActions, formatDashboardClock, isCustomDashboardEligible, normalizeDashboardWidgets, orderedDashboardApps, } from "./CustomDashboardModel";
|
|
20
|
+
import displayProcessor from "./DisplayProcessor";
|
|
21
|
+
import { isGlassesConnected } from "./GlassesReadiness";
|
|
22
|
+
import localDisplayManager from "./LocalDisplayManager";
|
|
23
|
+
import { listPhoneCalendarEvents, PhoneCalendarError } from "./PhoneCalendarService";
|
|
24
|
+
import sceneRenderer from "./SceneRenderer";
|
|
25
|
+
const DASHBOARD_APP_ID = "system.custom-dashboard";
|
|
26
|
+
const CLOCK_REFRESH_MS = 30_000;
|
|
27
|
+
const CALENDAR_REFRESH_MS = 5 * 60_000;
|
|
28
|
+
/** Matches the boot message's window; stops have no unmount-driven end signal. */
|
|
29
|
+
const STOP_NOTICE_MS = 1500;
|
|
30
|
+
// CFW blits the stock compositor's 576×288 canvas; used when the firmware has
|
|
31
|
+
// not reported scene capabilities yet.
|
|
32
|
+
const FALLBACK_CANVAS = { width: 576, height: 288 };
|
|
33
|
+
const MARGIN_X = 16;
|
|
34
|
+
const MARGIN_TOP = 10;
|
|
35
|
+
const WIDGET_GAP = 10;
|
|
36
|
+
/** Row height for the glasses' builtin 20px font engine. */
|
|
37
|
+
const LINE_H = 24;
|
|
38
|
+
const BUTTON_H = 34;
|
|
39
|
+
/**
|
|
40
|
+
* Selection marker: a text-hugging bar under the focused label instead of a
|
|
41
|
+
* boxed border. Drawn as a rect (h 3, border 2 — the strokes overlap into a
|
|
42
|
+
* solid bar) because mode 15 renders only glyphs; the bar must be in the
|
|
43
|
+
* raster or the firmware never shows it.
|
|
44
|
+
*/
|
|
45
|
+
const UNDERLINE_H = 3;
|
|
46
|
+
/** Row-top → underline-top: just under the builtin font's 27px line. */
|
|
47
|
+
const UNDERLINE_OFFSET_Y = BUTTON_H - 4;
|
|
48
|
+
function sameLocalDay(a, b) {
|
|
49
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
50
|
+
}
|
|
51
|
+
/** Local midnight after tomorrow: the widget never shows a "day after tomorrow". */
|
|
52
|
+
function endOfTomorrow(now) {
|
|
53
|
+
const end = new Date(now);
|
|
54
|
+
end.setHours(0, 0, 0, 0);
|
|
55
|
+
end.setDate(end.getDate() + 2);
|
|
56
|
+
return end;
|
|
57
|
+
}
|
|
58
|
+
/** Up to three lines per entry: title, location (when set), then the time — "tmr @ …" for tomorrow. */
|
|
59
|
+
function eventLines(event, now) {
|
|
60
|
+
const lines = [event.title];
|
|
61
|
+
const location = event.location?.trim();
|
|
62
|
+
if (location)
|
|
63
|
+
lines.push(location);
|
|
64
|
+
const start = new Date(event.startsAt);
|
|
65
|
+
const tomorrow = new Date(now);
|
|
66
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
67
|
+
const isTomorrow = sameLocalDay(start, tomorrow);
|
|
68
|
+
if (event.allDay) {
|
|
69
|
+
lines.push(isTomorrow ? "tmr, all day" : "All day");
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const time = new Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "2-digit" }).format(start);
|
|
73
|
+
lines.push(isTomorrow ? `tmr @ ${time}` : time);
|
|
74
|
+
}
|
|
75
|
+
return lines;
|
|
76
|
+
}
|
|
77
|
+
class CustomDashboardService {
|
|
78
|
+
started = false;
|
|
79
|
+
open = false;
|
|
80
|
+
/** Set by DashboardCoordinator; asks it to close (and restore main content). */
|
|
81
|
+
onRequestClose = null;
|
|
82
|
+
view = "main";
|
|
83
|
+
mainIndex = 0;
|
|
84
|
+
appsIndex = 0;
|
|
85
|
+
/** First visible row of the app list's scrolling window. */
|
|
86
|
+
appsScroll = 0;
|
|
87
|
+
/** "loading" until the first fetch resolves; "no-access" only for a real permission denial. */
|
|
88
|
+
calendarStatus = "loading";
|
|
89
|
+
calendarEvents = [];
|
|
90
|
+
unsubs = [];
|
|
91
|
+
clockTimer = null;
|
|
92
|
+
calendarTimer = null;
|
|
93
|
+
/** Last observed eligibility, for transition detection. */
|
|
94
|
+
eligible = false;
|
|
95
|
+
start() {
|
|
96
|
+
if (this.started)
|
|
97
|
+
return;
|
|
98
|
+
this.started = true;
|
|
99
|
+
this.unsubs.push(useGlassesStore.subscribe((state) => `${state.connection.state}:${state.deviceModel}:${state.firmwareVersion}`, () => {
|
|
100
|
+
if (!this.syncEligibility())
|
|
101
|
+
return;
|
|
102
|
+
void this.refreshCalendarAndRender();
|
|
103
|
+
}),
|
|
104
|
+
// Selector: only dashboard-relevant settings retrigger a refresh. The
|
|
105
|
+
// whole-store form fired a calendar query on every settings write.
|
|
106
|
+
useSettingsStore.subscribe((state) => JSON.stringify([
|
|
107
|
+
state.getSetting(SETTINGS.custom_dashboard_enabled.key),
|
|
108
|
+
state.getSetting(SETTINGS.custom_dashboard_widgets.key),
|
|
109
|
+
state.getSetting(SETTINGS.custom_dashboard_clock_style.key),
|
|
110
|
+
state.getSetting(SETTINGS.custom_dashboard_clock_show_date.key),
|
|
111
|
+
state.getSetting(SETTINGS.twelve_hour_time.key),
|
|
112
|
+
state.getSetting(SETTINGS.custom_dashboard_calendar_event_count.key),
|
|
113
|
+
state.getSetting(SETTINGS.custom_dashboard_calendar_days_ahead.key),
|
|
114
|
+
state.getSetting(SETTINGS.menu_apps.key),
|
|
115
|
+
]), () => {
|
|
116
|
+
if (!this.syncEligibility())
|
|
117
|
+
return;
|
|
118
|
+
void this.refreshCalendarAndRender();
|
|
119
|
+
}), useAppStatusStore.subscribe(() => {
|
|
120
|
+
if (this.isEligible())
|
|
121
|
+
this.render();
|
|
122
|
+
}));
|
|
123
|
+
this.clockTimer = BgTimer.setInterval(() => {
|
|
124
|
+
if (this.isEligible())
|
|
125
|
+
this.render();
|
|
126
|
+
}, CLOCK_REFRESH_MS);
|
|
127
|
+
this.calendarTimer = BgTimer.setInterval(() => {
|
|
128
|
+
if (this.isEligible())
|
|
129
|
+
void this.refreshCalendarAndRender();
|
|
130
|
+
}, CALENDAR_REFRESH_MS);
|
|
131
|
+
this.eligible = this.isEligible();
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Track eligible⇄ineligible transitions from any trigger (setting toggle,
|
|
135
|
+
* connect/disconnect, firmware change). Leaving closes the dashboard via the
|
|
136
|
+
* coordinator (which restores main content) and drops the retained scene so
|
|
137
|
+
* a stale frame can't be replayed after the feature was turned off.
|
|
138
|
+
*/
|
|
139
|
+
syncEligibility() {
|
|
140
|
+
const eligible = this.isEligible();
|
|
141
|
+
if (eligible === this.eligible)
|
|
142
|
+
return eligible;
|
|
143
|
+
this.eligible = eligible;
|
|
144
|
+
if (!eligible) {
|
|
145
|
+
if (this.open)
|
|
146
|
+
this.onRequestClose?.();
|
|
147
|
+
this.open = false;
|
|
148
|
+
this.resetNavigation();
|
|
149
|
+
sceneRenderer.clearApp(DASHBOARD_APP_ID);
|
|
150
|
+
}
|
|
151
|
+
return eligible;
|
|
152
|
+
}
|
|
153
|
+
stop() {
|
|
154
|
+
for (const unsub of this.unsubs)
|
|
155
|
+
unsub();
|
|
156
|
+
this.unsubs = [];
|
|
157
|
+
if (this.clockTimer !== null)
|
|
158
|
+
BgTimer.clearInterval(this.clockTimer);
|
|
159
|
+
if (this.calendarTimer !== null)
|
|
160
|
+
BgTimer.clearInterval(this.calendarTimer);
|
|
161
|
+
this.clockTimer = null;
|
|
162
|
+
this.calendarTimer = null;
|
|
163
|
+
if (this.open)
|
|
164
|
+
this.onRequestClose?.();
|
|
165
|
+
this.open = false;
|
|
166
|
+
this.eligible = false;
|
|
167
|
+
this.resetNavigation();
|
|
168
|
+
this.started = false;
|
|
169
|
+
sceneRenderer.clearApp(DASHBOARD_APP_ID);
|
|
170
|
+
}
|
|
171
|
+
isEligible() {
|
|
172
|
+
if (useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_enabled.key) === false)
|
|
173
|
+
return false;
|
|
174
|
+
const state = useGlassesStore.getState();
|
|
175
|
+
return (isGlassesConnected(state.connection) &&
|
|
176
|
+
isCustomDashboardEligible({
|
|
177
|
+
model: state.deviceModel,
|
|
178
|
+
firmwareVersion: state.firmwareVersion,
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
get isOpen() {
|
|
182
|
+
return this.open;
|
|
183
|
+
}
|
|
184
|
+
/** Coordinator opened the dashboard: render fresh from the top. */
|
|
185
|
+
show() {
|
|
186
|
+
this.open = true;
|
|
187
|
+
this.resetNavigation();
|
|
188
|
+
void this.refreshCalendarAndRender();
|
|
189
|
+
}
|
|
190
|
+
/** Coordinator closed the dashboard. */
|
|
191
|
+
hide() {
|
|
192
|
+
this.open = false;
|
|
193
|
+
this.resetNavigation();
|
|
194
|
+
}
|
|
195
|
+
/** Returns true when an open custom dashboard consumed the gesture. */
|
|
196
|
+
handleGesture(gestureName) {
|
|
197
|
+
if (!this.open || !this.isEligible())
|
|
198
|
+
return false;
|
|
199
|
+
if (gestureName === "double_tap") {
|
|
200
|
+
// In the app menu a double tap backs out to the top of the dashboard
|
|
201
|
+
// (there is no Back row); on the main view it closes the dashboard.
|
|
202
|
+
if (this.view === "apps") {
|
|
203
|
+
this.resetNavigation();
|
|
204
|
+
this.render();
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
this.onRequestClose?.();
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
if (this.view === "apps")
|
|
211
|
+
return this.handleAppMenuGesture(gestureName);
|
|
212
|
+
return this.handleMainGesture(gestureName);
|
|
213
|
+
}
|
|
214
|
+
resetNavigation() {
|
|
215
|
+
this.view = "main";
|
|
216
|
+
this.mainIndex = 0;
|
|
217
|
+
this.appsIndex = 0;
|
|
218
|
+
this.appsScroll = 0;
|
|
219
|
+
}
|
|
220
|
+
handleMainGesture(gestureName) {
|
|
221
|
+
const actions = dashboardActions(this.widgets());
|
|
222
|
+
if (this.mainIndex >= actions.length)
|
|
223
|
+
this.mainIndex = Math.max(0, actions.length - 1);
|
|
224
|
+
if (gestureName === "swipe_down" || gestureName === "swipe_up") {
|
|
225
|
+
if (actions.length === 0)
|
|
226
|
+
return true;
|
|
227
|
+
const delta = gestureName === "swipe_down" ? 1 : -1;
|
|
228
|
+
this.mainIndex = (this.mainIndex + delta + actions.length) % actions.length;
|
|
229
|
+
this.render();
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
if (gestureName === "single_tap") {
|
|
233
|
+
const action = actions[this.mainIndex];
|
|
234
|
+
if (action?.kind === "apps-menu") {
|
|
235
|
+
this.view = "apps";
|
|
236
|
+
this.appsIndex = 0;
|
|
237
|
+
this.appsScroll = 0;
|
|
238
|
+
this.render();
|
|
239
|
+
}
|
|
240
|
+
else if (action?.kind === "notifications") {
|
|
241
|
+
void this.openNotificationsPanel();
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
handleAppMenuGesture(gestureName) {
|
|
248
|
+
const rows = appMenuActions(this.menuApps());
|
|
249
|
+
if (this.appsIndex >= rows.length)
|
|
250
|
+
this.appsIndex = Math.max(0, rows.length - 1);
|
|
251
|
+
if (gestureName === "swipe_down" || gestureName === "swipe_up") {
|
|
252
|
+
const delta = gestureName === "swipe_down" ? 1 : -1;
|
|
253
|
+
this.appsIndex = (this.appsIndex + delta + rows.length) % rows.length;
|
|
254
|
+
this.render();
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
if (gestureName === "single_tap") {
|
|
258
|
+
const row = rows[this.appsIndex];
|
|
259
|
+
if (row?.kind === "app") {
|
|
260
|
+
void this.toggleApp(row.packageName);
|
|
261
|
+
}
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
menuApps() {
|
|
267
|
+
const menu = useSettingsStore.getState().getSetting(SETTINGS.menu_apps.key);
|
|
268
|
+
return orderedDashboardApps(menu, useAppStatusStore.getState().apps);
|
|
269
|
+
}
|
|
270
|
+
widgets() {
|
|
271
|
+
return normalizeDashboardWidgets(useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_widgets.key));
|
|
272
|
+
}
|
|
273
|
+
async openNotificationsPanel() {
|
|
274
|
+
this.onRequestClose?.();
|
|
275
|
+
await BluetoothSdk.showNativeNotificationPanel().catch((error) => {
|
|
276
|
+
console.error("CustomDashboard: notification panel failed", error);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
async toggleApp(packageName) {
|
|
280
|
+
this.onRequestClose?.();
|
|
281
|
+
const app = useAppStatusStore.getState().apps.find((candidate) => candidate.packageName === packageName);
|
|
282
|
+
if (!app)
|
|
283
|
+
return;
|
|
284
|
+
if (app.running) {
|
|
285
|
+
// Mirror the "Starting X…" boot message: without this the dashboard just
|
|
286
|
+
// closes and the stop is invisible on the glasses.
|
|
287
|
+
localDisplayManager.showSystemNotice(`Stopping ${app.name}…`, STOP_NOTICE_MS);
|
|
288
|
+
await useAppStatusStore.getState().stop(app.packageName);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
await useAppStatusStore.getState().start(app, { skipNavigation: true });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
calendarWidgetEnabled() {
|
|
295
|
+
return this.widgets().some((widget) => widget.id === "calendar" && widget.enabled);
|
|
296
|
+
}
|
|
297
|
+
async refreshCalendarAndRender() {
|
|
298
|
+
if (!this.open)
|
|
299
|
+
return;
|
|
300
|
+
if (!this.calendarWidgetEnabled()) {
|
|
301
|
+
this.calendarStatus = "loading";
|
|
302
|
+
this.calendarEvents = [];
|
|
303
|
+
this.render();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const settings = useSettingsStore.getState();
|
|
307
|
+
const count = Number(settings.getSetting(SETTINGS.custom_dashboard_calendar_event_count.key) ?? 2);
|
|
308
|
+
const days = Number(settings.getSetting(SETTINGS.custom_dashboard_calendar_days_ahead.key) ?? 3);
|
|
309
|
+
const startsAt = new Date();
|
|
310
|
+
// Clamped to local end-of-tomorrow: the widget only ever shows today and
|
|
311
|
+
// tomorrow ("tmr"), whatever the days-ahead setting says.
|
|
312
|
+
const daysEnd = new Date(startsAt.getTime() + Math.max(1, days) * 24 * 60 * 60 * 1000);
|
|
313
|
+
const windowEnd = endOfTomorrow(startsAt);
|
|
314
|
+
const endsAt = daysEnd < windowEnd ? daysEnd : windowEnd;
|
|
315
|
+
try {
|
|
316
|
+
const result = await listPhoneCalendarEvents({
|
|
317
|
+
startsAt: startsAt.toISOString(),
|
|
318
|
+
endsAt: endsAt.toISOString(),
|
|
319
|
+
limit: Math.max(1, Math.min(3, count)),
|
|
320
|
+
});
|
|
321
|
+
this.calendarStatus = "ok";
|
|
322
|
+
this.calendarEvents = result.events;
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
// Permission is requested from the Custom Dashboard settings screen;
|
|
326
|
+
// only a real denial should read as one — any other failure is
|
|
327
|
+
// "Unavailable" (and logged) so it can't masquerade as a permission
|
|
328
|
+
// problem.
|
|
329
|
+
const denied = error instanceof PhoneCalendarError && error.code === MiniappErrorCode.PERMISSION_DENIED;
|
|
330
|
+
this.calendarStatus = denied ? "no-access" : "unavailable";
|
|
331
|
+
this.calendarEvents = [];
|
|
332
|
+
if (!denied)
|
|
333
|
+
console.warn("CustomDashboard: calendar fetch failed", error);
|
|
334
|
+
}
|
|
335
|
+
this.render();
|
|
336
|
+
}
|
|
337
|
+
/** Underline hugging a builtin-font label. x/rowY are the label box's origin. */
|
|
338
|
+
labelUnderline(id, x, rowY, label, maxW) {
|
|
339
|
+
// Same metrics processScene uses to ellipsize builtin text; the raster
|
|
340
|
+
// insets text by 2px, so the bar starts under the first glyph.
|
|
341
|
+
const textW = new TextMeasurer(displayProcessor.getProfile()).measureText(label);
|
|
342
|
+
const w = maxW === undefined ? textW + 4 : Math.min(textW + 4, maxW);
|
|
343
|
+
return {
|
|
344
|
+
type: "rect",
|
|
345
|
+
id,
|
|
346
|
+
box: { x: x + 2, y: rowY + UNDERLINE_OFFSET_Y, w, h: UNDERLINE_H },
|
|
347
|
+
style: { border: 2 },
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
canvas() {
|
|
351
|
+
const { sceneCanvasWidth, sceneCanvasHeight } = useGlassesStore.getState();
|
|
352
|
+
if (sceneCanvasWidth && sceneCanvasWidth > 0 && sceneCanvasHeight && sceneCanvasHeight > 0) {
|
|
353
|
+
return { width: sceneCanvasWidth, height: sceneCanvasHeight };
|
|
354
|
+
}
|
|
355
|
+
return FALLBACK_CANVAS;
|
|
356
|
+
}
|
|
357
|
+
render() {
|
|
358
|
+
// Only while open: emitting into the single native view while closed
|
|
359
|
+
// would clobber the main content.
|
|
360
|
+
if (!this.open || !this.isEligible())
|
|
361
|
+
return;
|
|
362
|
+
const elements = this.view === "apps" ? this.appMenuElements() : this.mainElements();
|
|
363
|
+
sceneRenderer.emitScene(DASHBOARD_APP_ID, "dashboard", elements);
|
|
364
|
+
}
|
|
365
|
+
mainElements() {
|
|
366
|
+
const canvas = this.canvas();
|
|
367
|
+
const settings = useSettingsStore.getState();
|
|
368
|
+
const widgets = this.widgets().filter((widget) => widget.enabled);
|
|
369
|
+
const actions = dashboardActions(widgets);
|
|
370
|
+
if (this.mainIndex >= actions.length)
|
|
371
|
+
this.mainIndex = Math.max(0, actions.length - 1);
|
|
372
|
+
const selected = actions[this.mainIndex];
|
|
373
|
+
const clockStyle = settings.getSetting(SETTINGS.custom_dashboard_clock_style.key) === "large"
|
|
374
|
+
? "large"
|
|
375
|
+
: "compact";
|
|
376
|
+
const calendarEnabled = widgets.some((widget) => widget.id === "calendar");
|
|
377
|
+
// Calendar lives in the right half; everything else stacks in the left
|
|
378
|
+
// half (full width when the calendar widget is off).
|
|
379
|
+
const halfW = Math.floor(canvas.width / 2) - MARGIN_X - Math.floor(WIDGET_GAP / 2);
|
|
380
|
+
const width = calendarEnabled ? halfW : canvas.width - MARGIN_X * 2;
|
|
381
|
+
const rightX = Math.floor(canvas.width / 2) + Math.floor(WIDGET_GAP / 2);
|
|
382
|
+
const elements = [];
|
|
383
|
+
let y = MARGIN_TOP;
|
|
384
|
+
for (const widget of widgets) {
|
|
385
|
+
if (widget.id === "clock") {
|
|
386
|
+
const fontSize = clockStyle === "large" ? 48 : 32;
|
|
387
|
+
const clock = formatDashboardClock(new Date(), {
|
|
388
|
+
twelveHour: settings.getSetting(SETTINGS.twelve_hour_time.key) === true,
|
|
389
|
+
showDate: settings.getSetting(SETTINGS.custom_dashboard_clock_show_date.key) !== false,
|
|
390
|
+
});
|
|
391
|
+
elements.push({
|
|
392
|
+
type: "text",
|
|
393
|
+
id: "clock",
|
|
394
|
+
box: { x: MARGIN_X, y, w: width, h: fontSize + 6 },
|
|
395
|
+
text: clock.time,
|
|
396
|
+
style: { font: "mono", fontSize, breakMode: "strict-word" },
|
|
397
|
+
});
|
|
398
|
+
y += fontSize + 6;
|
|
399
|
+
if (clock.date) {
|
|
400
|
+
elements.push({
|
|
401
|
+
type: "text",
|
|
402
|
+
id: "clock-date",
|
|
403
|
+
box: { x: MARGIN_X, y, w: width, h: LINE_H },
|
|
404
|
+
text: clock.date,
|
|
405
|
+
style: { font: "builtin", breakMode: "strict-word" },
|
|
406
|
+
});
|
|
407
|
+
y += LINE_H;
|
|
408
|
+
}
|
|
409
|
+
y += WIDGET_GAP;
|
|
410
|
+
}
|
|
411
|
+
else if (widget.id === "calendar") {
|
|
412
|
+
// Right half, top-aligned, independent of the left stack. A missing
|
|
413
|
+
// calendar shows a hint instead of silently vanishing — "No access"
|
|
414
|
+
// strictly for permission denial, "Unavailable" for any other failure.
|
|
415
|
+
const now = new Date();
|
|
416
|
+
const lines = this.calendarStatus === "ok"
|
|
417
|
+
? [
|
|
418
|
+
"Calendar",
|
|
419
|
+
...(this.calendarEvents.length
|
|
420
|
+
? this.calendarEvents.slice(0, 3).flatMap((event) => eventLines(event, now))
|
|
421
|
+
: ["No upcoming events"]),
|
|
422
|
+
]
|
|
423
|
+
: this.calendarStatus === "loading"
|
|
424
|
+
? ["Calendar"]
|
|
425
|
+
: ["Calendar", this.calendarStatus === "no-access" ? "No access" : "Unavailable"];
|
|
426
|
+
elements.push({
|
|
427
|
+
type: "text",
|
|
428
|
+
id: "calendar",
|
|
429
|
+
box: { x: rightX, y: MARGIN_TOP, w: halfW, h: lines.length * LINE_H },
|
|
430
|
+
text: lines.join("\n"),
|
|
431
|
+
style: { font: "builtin", breakMode: "strict-word", overflow: "ellipsis" },
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
else if (widget.id === "apps") {
|
|
435
|
+
elements.push({
|
|
436
|
+
type: "text",
|
|
437
|
+
id: "apps-button",
|
|
438
|
+
box: { x: MARGIN_X, y, w: width, h: BUTTON_H },
|
|
439
|
+
text: "Apps",
|
|
440
|
+
style: { font: "builtin" },
|
|
441
|
+
});
|
|
442
|
+
if (selected?.kind === "apps-menu")
|
|
443
|
+
elements.push(this.labelUnderline("apps-underline", MARGIN_X, y, "Apps"));
|
|
444
|
+
y += BUTTON_H + WIDGET_GAP;
|
|
445
|
+
}
|
|
446
|
+
else if (widget.id === "notifications") {
|
|
447
|
+
elements.push({
|
|
448
|
+
type: "text",
|
|
449
|
+
id: "notifications",
|
|
450
|
+
box: { x: MARGIN_X, y, w: width, h: BUTTON_H },
|
|
451
|
+
text: "Notifications",
|
|
452
|
+
style: { font: "builtin" },
|
|
453
|
+
});
|
|
454
|
+
if (selected?.kind === "notifications") {
|
|
455
|
+
elements.push(this.labelUnderline("notifications-underline", MARGIN_X, y, "Notifications"));
|
|
456
|
+
}
|
|
457
|
+
y += BUTTON_H + WIDGET_GAP;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return elements;
|
|
461
|
+
}
|
|
462
|
+
appMenuElements() {
|
|
463
|
+
const canvas = this.canvas();
|
|
464
|
+
const width = canvas.width - MARGIN_X * 2;
|
|
465
|
+
const apps = this.menuApps();
|
|
466
|
+
const rows = appMenuActions(apps);
|
|
467
|
+
if (this.appsIndex >= rows.length)
|
|
468
|
+
this.appsIndex = Math.max(0, rows.length - 1);
|
|
469
|
+
const elements = [
|
|
470
|
+
{
|
|
471
|
+
type: "text",
|
|
472
|
+
id: "apps-title",
|
|
473
|
+
box: { x: MARGIN_X, y: MARGIN_TOP, w: width, h: LINE_H },
|
|
474
|
+
text: "Apps",
|
|
475
|
+
style: { font: "builtin" },
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
const listTop = MARGIN_TOP + LINE_H + 16;
|
|
479
|
+
if (apps.length === 0) {
|
|
480
|
+
elements.push({
|
|
481
|
+
type: "text",
|
|
482
|
+
id: "apps-empty",
|
|
483
|
+
box: { x: MARGIN_X, y: listTop, w: width, h: LINE_H },
|
|
484
|
+
text: "No apps in Glasses Menu",
|
|
485
|
+
style: { font: "builtin" },
|
|
486
|
+
});
|
|
487
|
+
return elements;
|
|
488
|
+
}
|
|
489
|
+
// Vertical side menu down the left half, stock-firmware style. The list
|
|
490
|
+
// scrolls as a window: the selection stays visible and rows shift under it.
|
|
491
|
+
const listW = Math.floor(canvas.width / 2) - MARGIN_X;
|
|
492
|
+
const visibleRows = Math.max(1, Math.floor((canvas.height - listTop) / BUTTON_H));
|
|
493
|
+
const maxScroll = Math.max(0, apps.length - visibleRows);
|
|
494
|
+
if (this.appsScroll > maxScroll)
|
|
495
|
+
this.appsScroll = maxScroll;
|
|
496
|
+
if (this.appsIndex < this.appsScroll)
|
|
497
|
+
this.appsScroll = this.appsIndex;
|
|
498
|
+
else if (this.appsIndex >= this.appsScroll + visibleRows)
|
|
499
|
+
this.appsScroll = this.appsIndex - visibleRows + 1;
|
|
500
|
+
apps.slice(this.appsScroll, this.appsScroll + visibleRows).forEach((app, row) => {
|
|
501
|
+
const y = listTop + row * BUTTON_H;
|
|
502
|
+
const label = app.running ? `${app.name} (running)` : app.name;
|
|
503
|
+
elements.push({
|
|
504
|
+
type: "text",
|
|
505
|
+
id: `app-row:${app.packageName}`,
|
|
506
|
+
box: { x: MARGIN_X, y, w: listW, h: BUTTON_H },
|
|
507
|
+
text: label,
|
|
508
|
+
style: { font: "builtin", breakMode: "strict-word", overflow: "ellipsis" },
|
|
509
|
+
});
|
|
510
|
+
if (this.appsScroll + row === this.appsIndex) {
|
|
511
|
+
elements.push(this.labelUnderline("apps-selection", MARGIN_X, y, label, listW - 2));
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
return elements;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const customDashboardService = new CustomDashboardService();
|
|
518
|
+
export default customDashboardService;
|
|
519
|
+
export { CustomDashboardService };
|
|
520
|
+
//# sourceMappingURL=CustomDashboardService.js.map
|