@glassly/engine 0.3.2-dev.586 → 0.3.2-dev.596
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/engine.d.ts +14 -0
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +4 -0
- package/build/engine.js.map +1 -1
- package/build/facades/glasses.d.ts +20 -0
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +21 -0
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/pairing.js +1 -1
- package/build/facades/pairing.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 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/services/DeviceEventRouter.d.ts.map +1 -1
- package/build/services/DeviceEventRouter.js +5 -0
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/GestureDotsService.d.ts +9 -9
- package/build/services/GestureDotsService.d.ts.map +1 -1
- package/build/services/GestureDotsService.js +75 -50
- package/build/services/GestureDotsService.js.map +1 -1
- package/build/services/GlassesTouchInput.d.ts +1 -1
- package/build/services/GlassesTouchInput.d.ts.map +1 -1
- package/build/services/GlassesTouchInput.js +7 -4
- package/build/services/GlassesTouchInput.js.map +1 -1
- package/build/services/gestureDotGlyphs.d.ts +1 -1
- package/build/services/gestureDotGlyphs.d.ts.map +1 -1
- package/build/services/gestureDotGlyphs.js +5 -0
- package/build/services/gestureDotGlyphs.js.map +1 -1
- package/build/services/neuralBand/MetaAuth.d.ts +31 -0
- package/build/services/neuralBand/MetaAuth.d.ts.map +1 -0
- package/build/services/neuralBand/MetaAuth.js +161 -0
- package/build/services/neuralBand/MetaAuth.js.map +1 -0
- package/build/services/neuralBand/NeuralBandInput.d.ts +50 -0
- package/build/services/neuralBand/NeuralBandInput.d.ts.map +1 -0
- package/build/services/neuralBand/NeuralBandInput.js +114 -0
- package/build/services/neuralBand/NeuralBandInput.js.map +1 -0
- package/build/services/neuralBand/NeuralBandSettingsSync.d.ts +3 -0
- package/build/services/neuralBand/NeuralBandSettingsSync.d.ts.map +1 -0
- package/build/services/neuralBand/NeuralBandSettingsSync.js +52 -0
- package/build/services/neuralBand/NeuralBandSettingsSync.js.map +1 -0
- package/build/services/neuralBand/sha256.d.ts +7 -0
- package/build/services/neuralBand/sha256.d.ts.map +1 -0
- package/build/services/neuralBand/sha256.js +68 -0
- package/build/services/neuralBand/sha256.js.map +1 -0
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +18 -0
- package/build/stores/settings.js.map +1 -1
- package/build/types/enums.d.ts +2 -1
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -0
- package/build/types/enums.js.map +1 -1
- package/package.json +7 -7
- package/src/engine.ts +4 -0
- package/src/facades/glasses.ts +27 -0
- package/src/facades/pairing.ts +1 -1
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/index.ts +14 -0
- package/src/services/DeviceEventRouter.ts +5 -0
- package/src/services/GestureDotsService.ts +72 -49
- package/src/services/GlassesTouchInput.ts +13 -5
- package/src/services/gestureDotGlyphs.ts +6 -1
- package/src/services/neuralBand/MetaAuth.ts +192 -0
- package/src/services/neuralBand/NeuralBandInput.ts +148 -0
- package/src/services/neuralBand/NeuralBandSettingsSync.ts +63 -0
- package/src/services/neuralBand/sha256.ts +70 -0
- package/src/stores/settings.ts +18 -0
- package/src/types/enums.ts +1 -0
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `@glassly/engine` — the OEM-facing main entry.\n *\n * The `engine` namespace (configure/start/stop + typed domain facades), the\n * public contract/read-model types, and the pure helpers host UI legitimately\n * renders with (decision functions, sort/order helpers, policy constants,\n * capability tables, timers). Judgment rule: read models, commands, pure\n * functions and types belong here; anything that mutates runtime state or\n * exposes a raw store/service remains private to the Glassly host.\n * See cloud/docs/issues/020-glasses-status-boundary/integration-review.md §D.\n */\n\n// The namespaced OEM-facing engine API (the \"(A) host API\"). See ./engine.\nexport {engine} from \"./engine\"\nexport type {\n ReportAttachmentInput,\n ReportContext,\n ReportDetails,\n ReportStatus,\n ReportTrigger,\n ReportSubmitResult,\n EngineSubmitReportInput,\n} from \"./facades/reports\"\nexport type {IslandNotification, IslandNotificationKind} from \"./facades/notifications\"\nexport type {PresentationOwner} from \"./services/notifications/notificationPolicy\"\nexport {DEFAULT_HIDDEN_PACKAGES, isGatedByDevTools, isSystemMiniapp} from \"./stores/appVisibility\"\nexport type {WifiSearchResult} from \"./facades/glassesWifi\"\nexport type {IdentitySnapshot} from \"./services/PairingIdentity\"\nexport type {DisplayMirrorEvent} from \"./facades/displayMirror\"\nexport type {\n IslandConfigureOptions,\n IslandAuth,\n IslandConfigValues,\n IslandAnalytics,\n IslandUiSeams,\n ScanQrOptions,\n ScanQrResult,\n SubjectTokenType,\n} from \"./runtime/bootstrap\"\n\n// Settings contract: the typed key registry (schema descriptors — the same\n// surface engine.settings.descriptor()/keys() reads), the per-key React hook\n// (the hook layer, same precedent as the apps hooks below), and the pure\n// device-model key helpers. The raw useSettingsStore stays internal;\n// imperative access goes through engine.settings.\nexport {SETTINGS, useSetting, AI_DATA_CONSENT_VERSION} from \"./stores/settings\"\nexport {isCfwFirmwareVersion, isCustomDashboardEligible} from \"./services/pages/pageEligibility\"\n\n// AI tier registry: the curated per-tier model dropdown options and helpers\n// the Settings > AI Models screen renders. Resolution itself runs inside\n// LocalMiniappRuntime; the screen only reads/writes the ai_model_* settings.\nexport {AI_TIERS, TIER_MODEL_OPTIONS, parseTierSetting} from \"./services/AiTierResolution\"\nexport type {AiTier, AiTierProvider, TierModelOption} from \"./services/AiTierResolution\"\n\n// Shared user memory timeline (engine.memory / Settings > Memory). Entry and\n// session shapes are canonical in @glassly/miniapp (the session.memory\n// surface); the stats read model is engine-side.\nexport type {MemoryStats} from \"./services/MemoryStore\"\nexport type {SpeakerProfile} from \"./services/SpeakerProfileStore\"\nexport type {EnrollOutcome} from \"./facades/speakers\"\nexport {MENTRA_LIVE_SETTING_KEYS, getBluetoothSettingKeysForDevice} from \"./stores/bluetoothSettingKeys\"\n\n// Runtime-shared constants and contract DTOs.\nexport {ISLAND_SETTINGS_KEYS} from \"./runtime/config\"\nexport type {CloudClientStatusSnapshot, MiniappAuthToken, InteropAuditEvent, TtsSynthesisResult} from \"./runtime/config\"\n\n// Pure readiness predicates over glasses connection state.\nexport {\n isGlassesConnected,\n isGlassesReady,\n isGlassesLinkLayerBusy,\n waitForGlassesReady,\n type GlassesConnectionStatus,\n type WaitForGlassesReadyOptions,\n} from \"./services/GlassesReadiness\"\n// Pure connect/reconnect decision helpers (host UI renders the outcome).\nexport {\n decideReconnect,\n decideConnectButtonAction,\n type ReconnectDecision,\n type ReconnectDecisionInput,\n type ConnectButtonAction,\n} from \"./services/ConnectionCoordinator\"\n\n// OTA read-model types for host OTA UI (progress screen, error mapping,\n// progress section) — the check/install machinery itself is internal.\nexport type {\n VersionInfo,\n MtkPatch,\n BesFirmware,\n VersionJson,\n OtaCheckResult,\n OtaCheckCurrentGlassesResult,\n OtaCheckCurrentGlassesOptions,\n} from \"./services/OtaUpdateCheckService\"\n// OTA install policy (watchdog timings + failure copy) + display-state\n// derivation, consumed by the OTA screens and their tests.\nexport {\n MINIMUM_OTA_STATUS_BUILD,\n MAX_RETRIES,\n RETRY_INTERVAL_MS,\n PROGRESS_TIMEOUT_MS,\n DOWNLOAD_STUCK_TIMEOUT_MS,\n MTK_INSTALL_TIMEOUT_MS,\n GLOBAL_OTA_TIMEOUT_MS,\n PING_INTERVAL_MS,\n QUERY_REPLY_TIMEOUT_MS,\n BES_RESTART_TIMEOUT_MS,\n OtaProgressMessages,\n} from \"./services/otaInstallPolicy\"\nexport {deriveDisplayState} from \"./services/otaDisplayState\"\nexport type {DisplayState} from \"./services/otaDisplayState\"\nexport type {\n OtaProgress,\n OtaProgressStatus,\n OtaStatus,\n OtaUpdateInfo,\n ReleaseChangelog,\n OtaSnapshot,\n OtaInstallSnapshot,\n} from \"./facades/ota\"\nexport {\n beginOtaAutoChain,\n clearOtaAutoChainReconnectWait,\n isOtaAutoChainActive,\n otaAutoChainFingerprint,\n otaAutoChainReconnectWaitRemaining,\n stopOtaAutoChain,\n tryAdvanceOtaAutoChain,\n MAX_OTA_AUTO_CHAIN_PASSES,\n OTA_AUTO_CHAIN_RECONNECT_TIMEOUT_MS,\n} from \"./services/OtaAutoChain\"\nexport type {OtaAutoChainAdvanceResult} from \"./services/OtaAutoChain\"\nexport {\n BES_INSTALL_RESTART_MESSAGE,\n getOtaErrorMessage,\n shouldRequireGlassesRebootForBesFailure,\n shouldShowChangeWifiForOtaDownloadFailure,\n} from \"./services/OtaErrorMapping\"\n\n// Gallery read models: engine.gallery.onNotice payload types, the media\n// permission helper + display-name derivation host gallery UI uses, and the\n// glasses-camera DTO types (re-exported through the host @/types/asg shim).\nexport type {GalleryNotice, GalleryNoticeCode} from \"./services/asg/galleryNotices\"\nexport {MediaLibraryPermissions} from \"./utils/permissions/MediaLibraryPermissions\"\nexport {deriveGalleryDisplayName} from \"./utils/permissions/galleryDisplayName\"\nexport type {\n PhotoInfo,\n CaptureFile,\n CaptureGroup,\n GalleryResponse,\n ServerStatus,\n HealthResponse,\n GalleryEvent,\n} from \"./types/asg\"\n\n// Bluetooth SDK event types host UI subscribes to via engine facades (the\n// BluetoothSdk singleton passthrough itself is internal).\nexport type {PairFailureEvent, GlassesNotReadyEvent} from \"@glassly/bluetooth-sdk\"\n\n// Cloud connection status enum (read model; the connection store is internal).\nexport {WebSocketStatus} from \"./stores/connection\"\n\n// App-list hook layer + pure list helpers (the raw useAppStatusStore and\n// installAppStoreHooks wiring are internal).\nexport {\n DUMMY_APPLET,\n saveAppsOrder,\n getAppsOrder,\n sortAppsByPackageNamePriority,\n saveLastOpenTime,\n getLastOpenTime,\n sortAppsByLastOpenTime,\n useApps,\n useStart,\n useStop,\n useSetForeground,\n useClearForeground,\n useRefresh,\n useStopAll,\n useInstall,\n useUninstall,\n useActiveApps,\n useRunningAlwaysOnApps,\n useActiveBackgroundApps,\n useBackgroundApps,\n useActiveForegroundApp,\n useForegroundApp,\n useActiveBackgroundAppsCount,\n useLocalMiniApps,\n type StartOptions,\n type OrderMap,\n} from \"./stores/apps\"\n\n// Pure dev-miniapp launch-route decision (registration itself is internal).\nexport {\n decideDevLaunchRoute,\n type DecideDevLaunchOptions,\n type DevLaunchResult,\n type DevManifest,\n} from \"./utils/devMiniappLaunch\"\nexport {HardwareCompatibility, type CompatibilityResult} from \"./utils/hardware\"\nexport {BgTimer, throttle, debounce} from \"./utils/timers\"\n\n// Hardware types and capability profiles.\nexport {\n HardwareType,\n HardwareRequirementLevel,\n DeviceTypes,\n ControllerTypes,\n HARDWARE_CAPABILITIES,\n getModelCapabilities,\n simulatedGlasses,\n evenRealitiesG1,\n evenRealitiesG2,\n mentraLive,\n nimo,\n rayneoIO,\n vuzixZ100,\n mentraDisplay,\n} from \"./types\"\nexport type {\n HardwareRequirement,\n CameraCapabilities,\n DisplayCapabilities,\n MicrophoneCapabilities,\n SpeakerCapabilities,\n IMUCapabilities,\n ButtonCapabilities,\n LightCapabilities,\n PowerCapabilities,\n Capabilities,\n AppletType,\n AppPermissionType,\n AppletPermission,\n AppletInterface,\n ClientApp,\n} from \"./types\"\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `@glassly/engine` — the OEM-facing main entry.\n *\n * The `engine` namespace (configure/start/stop + typed domain facades), the\n * public contract/read-model types, and the pure helpers host UI legitimately\n * renders with (decision functions, sort/order helpers, policy constants,\n * capability tables, timers). Judgment rule: read models, commands, pure\n * functions and types belong here; anything that mutates runtime state or\n * exposes a raw store/service remains private to the Glassly host.\n * See cloud/docs/issues/020-glasses-status-boundary/integration-review.md §D.\n */\n\n// The namespaced OEM-facing engine API (the \"(A) host API\"). See ./engine.\nexport {engine} from \"./engine\"\nexport type {\n ReportAttachmentInput,\n ReportContext,\n ReportDetails,\n ReportStatus,\n ReportTrigger,\n ReportSubmitResult,\n EngineSubmitReportInput,\n} from \"./facades/reports\"\nexport type {IslandNotification, IslandNotificationKind} from \"./facades/notifications\"\nexport type {PresentationOwner} from \"./services/notifications/notificationPolicy\"\nexport {DEFAULT_HIDDEN_PACKAGES, isGatedByDevTools, isSystemMiniapp} from \"./stores/appVisibility\"\nexport type {WifiSearchResult} from \"./facades/glassesWifi\"\nexport type {IdentitySnapshot} from \"./services/PairingIdentity\"\nexport type {DisplayMirrorEvent} from \"./facades/displayMirror\"\nexport type {\n IslandConfigureOptions,\n IslandAuth,\n IslandConfigValues,\n IslandAnalytics,\n IslandUiSeams,\n ScanQrOptions,\n ScanQrResult,\n SubjectTokenType,\n} from \"./runtime/bootstrap\"\n\n// Settings contract: the typed key registry (schema descriptors — the same\n// surface engine.settings.descriptor()/keys() reads), the per-key React hook\n// (the hook layer, same precedent as the apps hooks below), and the pure\n// device-model key helpers. The raw useSettingsStore stays internal;\n// imperative access goes through engine.settings.\nexport {SETTINGS, useSetting, AI_DATA_CONSENT_VERSION} from \"./stores/settings\"\nexport {isCfwFirmwareVersion, isCustomDashboardEligible} from \"./services/pages/pageEligibility\"\n\n// AI tier registry: the curated per-tier model dropdown options and helpers\n// the Settings > AI Models screen renders. Resolution itself runs inside\n// LocalMiniappRuntime; the screen only reads/writes the ai_model_* settings.\nexport {AI_TIERS, TIER_MODEL_OPTIONS, parseTierSetting} from \"./services/AiTierResolution\"\nexport type {AiTier, AiTierProvider, TierModelOption} from \"./services/AiTierResolution\"\n\n// Shared user memory timeline (engine.memory / Settings > Memory). Entry and\n// session shapes are canonical in @glassly/miniapp (the session.memory\n// surface); the stats read model is engine-side.\nexport type {MemoryStats} from \"./services/MemoryStore\"\nexport type {SpeakerProfile} from \"./services/SpeakerProfileStore\"\nexport type {EnrollOutcome} from \"./facades/speakers\"\nexport {MENTRA_LIVE_SETTING_KEYS, getBluetoothSettingKeysForDevice} from \"./stores/bluetoothSettingKeys\"\n\n// Runtime-shared constants and contract DTOs.\nexport {ISLAND_SETTINGS_KEYS} from \"./runtime/config\"\nexport type {CloudClientStatusSnapshot, MiniappAuthToken, InteropAuditEvent, TtsSynthesisResult} from \"./runtime/config\"\n\n// Pure readiness predicates over glasses connection state.\nexport {\n isGlassesConnected,\n isGlassesReady,\n isGlassesLinkLayerBusy,\n waitForGlassesReady,\n type GlassesConnectionStatus,\n type WaitForGlassesReadyOptions,\n} from \"./services/GlassesReadiness\"\n// Pure connect/reconnect decision helpers (host UI renders the outcome).\nexport {\n decideReconnect,\n decideConnectButtonAction,\n type ReconnectDecision,\n type ReconnectDecisionInput,\n type ConnectButtonAction,\n} from \"./services/ConnectionCoordinator\"\n\n// OTA read-model types for host OTA UI (progress screen, error mapping,\n// progress section) — the check/install machinery itself is internal.\nexport type {\n VersionInfo,\n MtkPatch,\n BesFirmware,\n VersionJson,\n OtaCheckResult,\n OtaCheckCurrentGlassesResult,\n OtaCheckCurrentGlassesOptions,\n} from \"./services/OtaUpdateCheckService\"\n// OTA install policy (watchdog timings + failure copy) + display-state\n// derivation, consumed by the OTA screens and their tests.\nexport {\n MINIMUM_OTA_STATUS_BUILD,\n MAX_RETRIES,\n RETRY_INTERVAL_MS,\n PROGRESS_TIMEOUT_MS,\n DOWNLOAD_STUCK_TIMEOUT_MS,\n MTK_INSTALL_TIMEOUT_MS,\n GLOBAL_OTA_TIMEOUT_MS,\n PING_INTERVAL_MS,\n QUERY_REPLY_TIMEOUT_MS,\n BES_RESTART_TIMEOUT_MS,\n OtaProgressMessages,\n} from \"./services/otaInstallPolicy\"\nexport {deriveDisplayState} from \"./services/otaDisplayState\"\nexport type {DisplayState} from \"./services/otaDisplayState\"\nexport type {\n OtaProgress,\n OtaProgressStatus,\n OtaStatus,\n OtaUpdateInfo,\n ReleaseChangelog,\n OtaSnapshot,\n OtaInstallSnapshot,\n} from \"./facades/ota\"\nexport {\n beginOtaAutoChain,\n clearOtaAutoChainReconnectWait,\n isOtaAutoChainActive,\n otaAutoChainFingerprint,\n otaAutoChainReconnectWaitRemaining,\n stopOtaAutoChain,\n tryAdvanceOtaAutoChain,\n MAX_OTA_AUTO_CHAIN_PASSES,\n OTA_AUTO_CHAIN_RECONNECT_TIMEOUT_MS,\n} from \"./services/OtaAutoChain\"\nexport type {OtaAutoChainAdvanceResult} from \"./services/OtaAutoChain\"\nexport {\n BES_INSTALL_RESTART_MESSAGE,\n getOtaErrorMessage,\n shouldRequireGlassesRebootForBesFailure,\n shouldShowChangeWifiForOtaDownloadFailure,\n} from \"./services/OtaErrorMapping\"\n\n// Gallery read models: engine.gallery.onNotice payload types, the media\n// permission helper + display-name derivation host gallery UI uses, and the\n// glasses-camera DTO types (re-exported through the host @/types/asg shim).\nexport type {GalleryNotice, GalleryNoticeCode} from \"./services/asg/galleryNotices\"\nexport {MediaLibraryPermissions} from \"./utils/permissions/MediaLibraryPermissions\"\nexport {deriveGalleryDisplayName} from \"./utils/permissions/galleryDisplayName\"\nexport type {\n PhotoInfo,\n CaptureFile,\n CaptureGroup,\n GalleryResponse,\n ServerStatus,\n HealthResponse,\n GalleryEvent,\n} from \"./types/asg\"\n\n// Bluetooth SDK event types host UI subscribes to via engine facades (the\n// BluetoothSdk singleton passthrough itself is internal).\nexport type {PairFailureEvent, GlassesNotReadyEvent} from \"@glassly/bluetooth-sdk\"\n\n// Cloud connection status enum (read model; the connection store is internal).\nexport {WebSocketStatus} from \"./stores/connection\"\n\n// App-list hook layer + pure list helpers (the raw useAppStatusStore and\n// installAppStoreHooks wiring are internal).\nexport {\n DUMMY_APPLET,\n saveAppsOrder,\n getAppsOrder,\n sortAppsByPackageNamePriority,\n saveLastOpenTime,\n getLastOpenTime,\n sortAppsByLastOpenTime,\n useApps,\n useStart,\n useStop,\n useSetForeground,\n useClearForeground,\n useRefresh,\n useStopAll,\n useInstall,\n useUninstall,\n useActiveApps,\n useRunningAlwaysOnApps,\n useActiveBackgroundApps,\n useBackgroundApps,\n useActiveForegroundApp,\n useForegroundApp,\n useActiveBackgroundAppsCount,\n useLocalMiniApps,\n type StartOptions,\n type OrderMap,\n} from \"./stores/apps\"\n\n// Pure dev-miniapp launch-route decision (registration itself is internal).\nexport {\n decideDevLaunchRoute,\n type DecideDevLaunchOptions,\n type DevLaunchResult,\n type DevManifest,\n} from \"./utils/devMiniappLaunch\"\nexport {HardwareCompatibility, type CompatibilityResult} from \"./utils/hardware\"\nexport {BgTimer, throttle, debounce} from \"./utils/timers\"\n// Meta Neural Band: the account sign-in a claim needs (the WebView lives in the host).\nexport {\n MetaAuthError,\n authEntryUrl,\n callbackMatches,\n callbackQuery,\n decryptBlob,\n isLoginCallback,\n login,\n tokensQuery,\n type MetaSession,\n type MetaSsoTokens,\n} from \"./services/neuralBand/MetaAuth\"\nexport type {NeuralBandClaimStatus, NeuralBandHandwritingStatus} from \"./services/neuralBand/NeuralBandInput\"\n\n// Hardware types and capability profiles.\nexport {\n HardwareType,\n HardwareRequirementLevel,\n DeviceTypes,\n ControllerTypes,\n HARDWARE_CAPABILITIES,\n getModelCapabilities,\n simulatedGlasses,\n evenRealitiesG1,\n evenRealitiesG2,\n mentraLive,\n nimo,\n rayneoIO,\n vuzixZ100,\n mentraDisplay,\n} from \"./types\"\nexport type {\n HardwareRequirement,\n CameraCapabilities,\n DisplayCapabilities,\n MicrophoneCapabilities,\n SpeakerCapabilities,\n IMUCapabilities,\n ButtonCapabilities,\n LightCapabilities,\n PowerCapabilities,\n Capabilities,\n AppletType,\n AppPermissionType,\n AppletPermission,\n AppletInterface,\n ClientApp,\n} from \"./types\"\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceEventRouter.d.ts","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DeviceEventRouter.d.ts","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"AA0CA,wBAAgB,sBAAsB,IAAI,IAAI,CAuQ7C;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C"}
|
|
@@ -34,6 +34,7 @@ import { retirePendingSelectionOnPromotion } from "./PairingIdentity";
|
|
|
34
34
|
import GlobalEventEmitter from "../utils/GlobalEventEmitter";
|
|
35
35
|
import { asgCameraApi } from "./asg/asgCameraApi";
|
|
36
36
|
import { dispatchTouchEvent, resetTouchInput } from "./GlassesTouchInput";
|
|
37
|
+
import neuralBandInput from "./neuralBand/NeuralBandInput";
|
|
37
38
|
import dashboardOpenTrace from "./debug/DashboardOpenTrace";
|
|
38
39
|
let subs = [];
|
|
39
40
|
export function startDeviceEventRouter() {
|
|
@@ -177,6 +178,10 @@ export function startDeviceEventRouter() {
|
|
|
177
178
|
// Physical keyboard bridged through G2 Glassly CFW: raw HID transitions →
|
|
178
179
|
// layout-translated keyboard_event stream (with synthesized auto-repeat).
|
|
179
180
|
subs.push(BluetoothSdk.addListener("keyboard_key", (event) => glassesKeyboardInput.handle(event)));
|
|
181
|
+
// Meta Neural Band: its gestures ride touch_event above; these are its own streams.
|
|
182
|
+
subs.push(BluetoothSdk.addListener("neural_band_claim", (event) => neuralBandInput.handleClaim(event)));
|
|
183
|
+
subs.push(BluetoothSdk.addListener("neural_band_handwriting", (event) => neuralBandInput.handleHandwriting(event)));
|
|
184
|
+
subs.push(BluetoothSdk.addListener("neural_band_pointer", (event) => neuralBandInput.handlePointer(event)));
|
|
180
185
|
subs.push({
|
|
181
186
|
remove: useGlassesStore.subscribe((s) => s.keyboardConnected === true, (connected) => {
|
|
182
187
|
if (!connected)
|
|
@@ -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 glassesKeyboardInput from \"./GlassesKeyboardInput\"\nimport glassesSensorService from \"./GlassesSensorService\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport localTranslationService from \"./LocalTranslationService\"\nimport speakerLabelResolver from \"./SpeakerLabelResolver\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\nimport {dispatchTouchEvent, resetTouchInput} from \"./GlassesTouchInput\"\nimport dashboardOpenTrace from \"./debug/DashboardOpenTrace\"\n\nlet subs: Array<{remove: () => void}> = []\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n subs.push(\n BluetoothSdk.addListener(\"light_sensor\", ({type: _type, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"light_sensor\", reading)\n }),\n // Stock firmware emits these only inside its nav UI; custom firmware whenever the\n // raw compass runs. Either way they feed the one session.compass stream.\n BluetoothSdk.addListener(\"compass_heading\", ({type: _type, heading, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"glasses_compass\", {degrees: heading, ...reading})\n }),\n BluetoothSdk.addListener(\"compass_calibration\", ({type: _type, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"compass_calibration\", reading)\n }),\n {\n remove: useGlassesStore.subscribe(\n (s) => s.connection,\n (connection) => {\n if (isGlassesConnected(connection)) void glassesSensorService.resync()\n else glassesSensorService.disconnected()\n },\n ),\n },\n {\n // CFW classification lands after device-ready; compass demand recorded before\n // that only reaches the firmware once the scene canvas is published.\n remove: useGlassesStore.subscribe(\n (s) => s.sceneCanvasWidth,\n (width) => {\n if ((width ?? 0) > 0) void glassesSensorService.resync()\n },\n ),\n },\n )\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 // Battery streams follow the store, including initial native hydration and\n // onGlassesStatus patches. G2 can report its level without battery_status;\n // forwarding only that legacy event leaves a running dashboard ring-only.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => ({level: s.batteryLevel, charging: s.charging}),\n (battery) => {\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n ...battery,\n timestamp: Date.now(),\n })\n },\n {equalityFn: shallow},\n ),\n })\n\n // Legacy standalone events feed the same store subscription.\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 }),\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(BluetoothSdk.addListener(\"touch_event\", dispatchTouchEvent))\n // Physical keyboard bridged through G2 Glassly CFW: raw HID transitions →\n // layout-translated keyboard_event stream (with synthesized auto-repeat).\n subs.push(BluetoothSdk.addListener(\"keyboard_key\", (event) => glassesKeyboardInput.handle(event)))\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => s.keyboardConnected === true,\n (connected) => {\n if (!connected) glassesKeyboardInput.reset()\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 // The dashboard miniapp opens on head-up from this stream (ADR-0006).\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n if (event.up) dashboardOpenTrace.trigger(\"head-up\")\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n subs.push({remove: dashboardOpenTrace.start()})\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 localTranslationService.onLocalTranscript(event, lang)\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, launchSource: \"glasses\"})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n resetTouchInput()\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 glassesKeyboardInput from \"./GlassesKeyboardInput\"\nimport glassesSensorService from \"./GlassesSensorService\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport localTranslationService from \"./LocalTranslationService\"\nimport speakerLabelResolver from \"./SpeakerLabelResolver\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\nimport {dispatchTouchEvent, resetTouchInput} from \"./GlassesTouchInput\"\nimport neuralBandInput from \"./neuralBand/NeuralBandInput\"\nimport dashboardOpenTrace from \"./debug/DashboardOpenTrace\"\n\nlet subs: Array<{remove: () => void}> = []\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n subs.push(\n BluetoothSdk.addListener(\"light_sensor\", ({type: _type, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"light_sensor\", reading)\n }),\n // Stock firmware emits these only inside its nav UI; custom firmware whenever the\n // raw compass runs. Either way they feed the one session.compass stream.\n BluetoothSdk.addListener(\"compass_heading\", ({type: _type, heading, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"glasses_compass\", {degrees: heading, ...reading})\n }),\n BluetoothSdk.addListener(\"compass_calibration\", ({type: _type, ...reading}) => {\n localMiniappRuntime.forwardEvent(\"compass_calibration\", reading)\n }),\n {\n remove: useGlassesStore.subscribe(\n (s) => s.connection,\n (connection) => {\n if (isGlassesConnected(connection)) void glassesSensorService.resync()\n else glassesSensorService.disconnected()\n },\n ),\n },\n {\n // CFW classification lands after device-ready; compass demand recorded before\n // that only reaches the firmware once the scene canvas is published.\n remove: useGlassesStore.subscribe(\n (s) => s.sceneCanvasWidth,\n (width) => {\n if ((width ?? 0) > 0) void glassesSensorService.resync()\n },\n ),\n },\n )\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 // Battery streams follow the store, including initial native hydration and\n // onGlassesStatus patches. G2 can report its level without battery_status;\n // forwarding only that legacy event leaves a running dashboard ring-only.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => ({level: s.batteryLevel, charging: s.charging}),\n (battery) => {\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n ...battery,\n timestamp: Date.now(),\n })\n },\n {equalityFn: shallow},\n ),\n })\n\n // Legacy standalone events feed the same store subscription.\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 }),\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(BluetoothSdk.addListener(\"touch_event\", dispatchTouchEvent))\n // Physical keyboard bridged through G2 Glassly CFW: raw HID transitions →\n // layout-translated keyboard_event stream (with synthesized auto-repeat).\n subs.push(BluetoothSdk.addListener(\"keyboard_key\", (event) => glassesKeyboardInput.handle(event)))\n // Meta Neural Band: its gestures ride touch_event above; these are its own streams.\n subs.push(BluetoothSdk.addListener(\"neural_band_claim\", (event) => neuralBandInput.handleClaim(event)))\n subs.push(BluetoothSdk.addListener(\"neural_band_handwriting\", (event) => neuralBandInput.handleHandwriting(event)))\n subs.push(BluetoothSdk.addListener(\"neural_band_pointer\", (event) => neuralBandInput.handlePointer(event)))\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => s.keyboardConnected === true,\n (connected) => {\n if (!connected) glassesKeyboardInput.reset()\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 // The dashboard miniapp opens on head-up from this stream (ADR-0006).\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n if (event.up) dashboardOpenTrace.trigger(\"head-up\")\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n subs.push({remove: dashboardOpenTrace.start()})\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 localTranslationService.onLocalTranscript(event, lang)\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, launchSource: \"glasses\"})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n resetTouchInput()\n}\n"]}
|
|
@@ -2,13 +2,10 @@ declare class GestureDotsService {
|
|
|
2
2
|
private started;
|
|
3
3
|
private flash;
|
|
4
4
|
private flashTimer;
|
|
5
|
-
/**
|
|
6
|
-
private
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
* event; the tap_end that follows on release is what clears it).
|
|
10
|
-
*/
|
|
11
|
-
private holdRecognized;
|
|
5
|
+
/** Raw controller holds survive hold recognition/dial events until their own release. */
|
|
6
|
+
private presses;
|
|
7
|
+
/** Avoid sending identical images for successive phases of the same press. */
|
|
8
|
+
private renderedGlyph;
|
|
12
9
|
/** Whether the indicator scene currently has anything on it (needs a clear when the flash ends). */
|
|
13
10
|
private visible;
|
|
14
11
|
private unsubs;
|
|
@@ -21,10 +18,13 @@ declare class GestureDotsService {
|
|
|
21
18
|
/**
|
|
22
19
|
* Flash a gesture on the indicator. Other gesture names are ignored.
|
|
23
20
|
* Claimed gestures cancel any earlier press/flash without starting a new
|
|
24
|
-
* one: the claimant may also consume the release edge.
|
|
21
|
+
* one: the claimant may also consume the release edge. `fingerIndex` is the
|
|
22
|
+
* Neural Band's finger on its pinches and press edges (1 = middle).
|
|
25
23
|
*/
|
|
26
|
-
noteGesture(gestureName: string, claimed?: boolean): void;
|
|
24
|
+
noteGesture(gestureName: string, claimed?: boolean, fingerIndex?: number): void;
|
|
27
25
|
private applyGesture;
|
|
26
|
+
private clearFlash;
|
|
27
|
+
private startFlash;
|
|
28
28
|
/** Debug setting only; no payloads or retained history. Correlate seq with G2PERF. */
|
|
29
29
|
private trace;
|
|
30
30
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GestureDotsService.d.ts","sourceRoot":"","sources":["../../src/services/GestureDotsService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GestureDotsService.d.ts","sourceRoot":"","sources":["../../src/services/GestureDotsService.ts"],"names":[],"mappings":"AAyCA,cAAM,kBAAkB;IACtB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,UAAU,CAAsB;IACxC,yFAAyF;IACzF,OAAO,CAAC,OAAO,CAA4D;IAC3E,8EAA8E;IAC9E,OAAO,CAAC,aAAa,CAA+B;IACpD,oGAAoG;IACpG,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,MAAM,CAAwB;IAEtC,KAAK,IAAI,IAAI;IAqBb,IAAI,IAAI,IAAI;IAOZ,OAAO,CAAC,YAAY;IAOpB,uFAAuF;IACvF,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,OAAO;IAIf;;;;;OAKG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,UAAQ,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAM7E,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,UAAU;IAWlB,sFAAsF;IACtF,OAAO,CAAC,KAAK;IAgBb;;;;;;;;;;OAUG;IACH,OAAO,CAAC,MAAM;CAqCf;AAED,QAAA,MAAM,kBAAkB,oBAA2B,CAAA;AACnD,eAAe,kBAAkB,CAAA;AACjC,OAAO,EAAC,kBAAkB,EAAC,CAAA"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gesture dots — a debug indicator that flashes a small glyph top-right on the
|
|
3
3
|
* glasses when a touch gesture arrives, while nothing else is on the display:
|
|
4
|
-
* a filled circle for a single tap, an arrow for a swipe
|
|
5
|
-
* short pulse), a bullseye
|
|
4
|
+
* a filled circle for a single tap, two for a double tap, an arrow for a swipe
|
|
5
|
+
* (cleared after a short pulse), a bullseye for a recognized long press.
|
|
6
|
+
* The Neural Band's middle finger (index 1) draws the same shapes hollow. Extracted from
|
|
6
7
|
* the CFW dashboard service; it renders through
|
|
7
8
|
* the ordinary scene pipeline, so it works on any positioning device (stock
|
|
8
9
|
* G2 included), and is gated by the gesture_dots debug setting.
|
|
@@ -23,19 +24,26 @@ const DOTS_APP_ID = "system.gesture-dots";
|
|
|
23
24
|
const GESTURE_FLASH_MS = 700;
|
|
24
25
|
const MARGIN_X = 16;
|
|
25
26
|
const MARGIN_TOP = 10;
|
|
26
|
-
|
|
27
|
-
const
|
|
27
|
+
/** Gestures that flash a glyph. */
|
|
28
|
+
const FLASH_GLYPHS = {
|
|
29
|
+
single_tap: "dot",
|
|
30
|
+
double_tap: "double",
|
|
31
|
+
long_press: "hold",
|
|
32
|
+
swipe_up: "up",
|
|
33
|
+
swipe_down: "down",
|
|
34
|
+
swipe_left: "left",
|
|
35
|
+
swipe_right: "right",
|
|
36
|
+
};
|
|
37
|
+
/** The band's middle finger draws the filled shapes hollow. */
|
|
38
|
+
const MIDDLE_FINGER_GLYPHS = { dot: "ring", double: "doubleRing" };
|
|
28
39
|
class GestureDotsService {
|
|
29
40
|
started = false;
|
|
30
41
|
flash = null;
|
|
31
42
|
flashTimer = null;
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
* event; the tap_end that follows on release is what clears it).
|
|
37
|
-
*/
|
|
38
|
-
holdRecognized = false;
|
|
43
|
+
/** Raw controller holds survive hold recognition/dial events until their own release. */
|
|
44
|
+
presses = new Map();
|
|
45
|
+
/** Avoid sending identical images for successive phases of the same press. */
|
|
46
|
+
renderedGlyph = null;
|
|
39
47
|
/** Whether the indicator scene currently has anything on it (needs a clear when the flash ends). */
|
|
40
48
|
visible = false;
|
|
41
49
|
unsubs = [];
|
|
@@ -71,13 +79,13 @@ class GestureDotsService {
|
|
|
71
79
|
BgTimer.clearTimeout(this.flashTimer);
|
|
72
80
|
this.flashTimer = null;
|
|
73
81
|
this.flash = null;
|
|
74
|
-
this.
|
|
75
|
-
this.holdRecognized = false;
|
|
82
|
+
this.presses.clear();
|
|
76
83
|
}
|
|
77
84
|
/** Another surface owns the display; forget the indicator without painting over it. */
|
|
78
85
|
relinquish() {
|
|
79
86
|
this.resetGesture();
|
|
80
87
|
this.visible = false;
|
|
88
|
+
this.renderedGlyph = null;
|
|
81
89
|
sceneRenderer.clearApp(DOTS_APP_ID);
|
|
82
90
|
this.trace("relinquish");
|
|
83
91
|
}
|
|
@@ -87,15 +95,16 @@ class GestureDotsService {
|
|
|
87
95
|
/**
|
|
88
96
|
* Flash a gesture on the indicator. Other gesture names are ignored.
|
|
89
97
|
* Claimed gestures cancel any earlier press/flash without starting a new
|
|
90
|
-
* one: the claimant may also consume the release edge.
|
|
98
|
+
* one: the claimant may also consume the release edge. `fingerIndex` is the
|
|
99
|
+
* Neural Band's finger on its pinches and press edges (1 = middle).
|
|
91
100
|
*/
|
|
92
|
-
noteGesture(gestureName, claimed = false) {
|
|
101
|
+
noteGesture(gestureName, claimed = false, fingerIndex) {
|
|
93
102
|
if (!this.started || !this.enabled())
|
|
94
103
|
return;
|
|
95
|
-
this.applyGesture(gestureName, claimed);
|
|
104
|
+
this.applyGesture(gestureName, claimed, fingerIndex);
|
|
96
105
|
this.trace("input", { gestureName, claimed });
|
|
97
106
|
}
|
|
98
|
-
applyGesture(gestureName, claimed) {
|
|
107
|
+
applyGesture(gestureName, claimed, fingerIndex) {
|
|
99
108
|
if (localDisplayManager.hasActiveDisplay() || useDisplayStore.getState().overlayOwner !== null) {
|
|
100
109
|
this.relinquish();
|
|
101
110
|
return;
|
|
@@ -105,52 +114,63 @@ class GestureDotsService {
|
|
|
105
114
|
this.render();
|
|
106
115
|
return;
|
|
107
116
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
const finger = fingerIndex ?? 0;
|
|
118
|
+
const press = this.presses.get(finger);
|
|
119
|
+
if (gestureName === "touch_down" || gestureName === "tap_start") {
|
|
120
|
+
if (press) {
|
|
121
|
+
// Both reports describe one press. Never restart its visual feedback.
|
|
122
|
+
if (gestureName === "touch_down")
|
|
123
|
+
press.raw = true;
|
|
115
124
|
return;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
125
|
+
}
|
|
126
|
+
this.clearFlash();
|
|
127
|
+
this.presses.set(finger, { raw: gestureName === "touch_down", glyph: finger === 1 ? "ring" : "dot" });
|
|
119
128
|
this.render();
|
|
120
129
|
return;
|
|
121
130
|
}
|
|
122
|
-
if (gestureName === "
|
|
123
|
-
|
|
124
|
-
|
|
131
|
+
if (gestureName === "tap_end") {
|
|
132
|
+
if (!press)
|
|
133
|
+
return;
|
|
134
|
+
this.presses.delete(finger);
|
|
135
|
+
// The band's resolved tap arrives after release (double-tap window).
|
|
136
|
+
// Keep the glyph lit across that gap so one pinch never flashes twice.
|
|
137
|
+
if (press.raw && this.presses.size === 0)
|
|
138
|
+
this.startFlash(press.glyph);
|
|
125
139
|
this.render();
|
|
126
140
|
return;
|
|
127
141
|
}
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
: gestureName === "swipe_down"
|
|
137
|
-
? "down"
|
|
138
|
-
: null;
|
|
139
|
-
if (!flash) {
|
|
140
|
-
if (released)
|
|
141
|
-
this.render();
|
|
142
|
+
const shape = FLASH_GLYPHS[gestureName];
|
|
143
|
+
const glyph = shape && finger === 1 ? MIDDLE_FINGER_GLYPHS[shape] ?? shape : shape;
|
|
144
|
+
if (press?.raw && (!glyph || gestureName === "long_press")) {
|
|
145
|
+
// Pinch-and-turn reports do not release a finger. A recognized hold
|
|
146
|
+
// stays visible until tap_end instead of expiring halfway through it.
|
|
147
|
+
if (glyph)
|
|
148
|
+
press.glyph = glyph;
|
|
149
|
+
this.render();
|
|
142
150
|
return;
|
|
143
151
|
}
|
|
144
|
-
|
|
152
|
+
// Completed taps/swipes become one continuous pulse, even if the device
|
|
153
|
+
// omits tap_end. Older tap_start-only devices retain the same fallback.
|
|
154
|
+
this.presses.delete(finger);
|
|
155
|
+
if (glyph)
|
|
156
|
+
this.startFlash(glyph);
|
|
157
|
+
this.render();
|
|
158
|
+
}
|
|
159
|
+
clearFlash() {
|
|
145
160
|
if (this.flashTimer !== null)
|
|
146
161
|
BgTimer.clearTimeout(this.flashTimer);
|
|
162
|
+
this.flashTimer = null;
|
|
163
|
+
this.flash = null;
|
|
164
|
+
}
|
|
165
|
+
startFlash(glyph) {
|
|
166
|
+
this.clearFlash();
|
|
167
|
+
this.flash = glyph;
|
|
147
168
|
this.flashTimer = BgTimer.setTimeout(() => {
|
|
148
169
|
this.flashTimer = null;
|
|
149
170
|
this.flash = null;
|
|
150
171
|
this.render();
|
|
151
172
|
this.trace("flash-ended");
|
|
152
173
|
}, GESTURE_FLASH_MS);
|
|
153
|
-
this.render();
|
|
154
174
|
}
|
|
155
175
|
/** Debug setting only; no payloads or retained history. Correlate seq with G2PERF. */
|
|
156
176
|
trace(reason, details = {}) {
|
|
@@ -160,7 +180,7 @@ class GestureDotsService {
|
|
|
160
180
|
JSON.stringify({
|
|
161
181
|
t: Date.now(),
|
|
162
182
|
reason,
|
|
163
|
-
held: this.
|
|
183
|
+
held: this.presses.size > 0,
|
|
164
184
|
flash: this.flash,
|
|
165
185
|
overlay: useDisplayStore.getState().overlayOwner,
|
|
166
186
|
active: localDisplayManager.hasActiveDisplay(),
|
|
@@ -183,16 +203,20 @@ class GestureDotsService {
|
|
|
183
203
|
return;
|
|
184
204
|
if (localDisplayManager.hasActiveDisplay() || useDisplayStore.getState().overlayOwner !== null)
|
|
185
205
|
return;
|
|
186
|
-
const shown =
|
|
206
|
+
const shown = Array.from(this.presses.values()).at(-1)?.glyph ?? this.flash;
|
|
187
207
|
if (!this.enabled()) {
|
|
188
208
|
if (this.visible) {
|
|
189
209
|
this.visible = false;
|
|
210
|
+
this.renderedGlyph = null;
|
|
190
211
|
sceneRenderer.emitScene(DOTS_APP_ID, "main", []);
|
|
191
212
|
}
|
|
192
213
|
return;
|
|
193
214
|
}
|
|
194
215
|
if (!shown && !this.visible)
|
|
195
216
|
return;
|
|
217
|
+
const glyph = shown ?? "blank";
|
|
218
|
+
if (glyph === this.renderedGlyph)
|
|
219
|
+
return;
|
|
196
220
|
const elements = [
|
|
197
221
|
{
|
|
198
222
|
type: "image",
|
|
@@ -204,13 +228,14 @@ class GestureDotsService {
|
|
|
204
228
|
h: GESTURE_DOT_GLYPH_SIZE,
|
|
205
229
|
anchor: "top-right",
|
|
206
230
|
},
|
|
207
|
-
data: GESTURE_DOT_GLYPHS[
|
|
231
|
+
data: GESTURE_DOT_GLYPHS[glyph],
|
|
208
232
|
},
|
|
209
233
|
];
|
|
210
234
|
this.visible = true;
|
|
235
|
+
this.renderedGlyph = glyph;
|
|
211
236
|
const result = sceneRenderer.emitScene(DOTS_APP_ID, "main", elements);
|
|
212
237
|
this.trace("render", {
|
|
213
|
-
glyph
|
|
238
|
+
glyph,
|
|
214
239
|
...(result?.kind === "scene" ? { seq: result.seq, sent: result.sent } : {}),
|
|
215
240
|
});
|
|
216
241
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GestureDotsService.js","sourceRoot":"","sources":["../../src/services/GestureDotsService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Gesture dots — a debug indicator that flashes a small glyph top-right on the\n * glasses when a touch gesture arrives, while nothing else is on the display:\n * a filled circle for a single tap, an arrow for a swipe (cleared after a\n * short pulse), a bullseye while a CFW tap-then-hold is held. Extracted from\n * the CFW dashboard service; it renders through\n * the ordinary scene pipeline, so it works on any positioning device (stock\n * G2 included), and is gated by the gesture_dots debug setting.\n *\n * The glyphs are pre-baked images (not text): the stock firmware's builtin\n * font has no ●/↑/↓, and a single image element with a stable id and box maps\n * to ONE re-used firmware image container — flash and idle swap its bytes\n * (data-only update, no page rebuild) instead of creating/removing containers.\n */\nimport {useDisplayStore} from \"../stores/display\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport type {SceneElementInput} from \"../utils/display/scene\"\nimport {BgTimer} from \"../utils/timers\"\nimport {GESTURE_DOT_GLYPHS, GESTURE_DOT_GLYPH_SIZE, type GestureDotGlyph} from \"./gestureDotGlyphs\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport sceneRenderer from \"./SceneRenderer\"\n\nconst DOTS_APP_ID = \"system.gesture-dots\"\n/** How long a gesture stays on the indicator. */\nconst GESTURE_FLASH_MS = 700\nconst MARGIN_X = 16\nconst MARGIN_TOP = 10\n// Off: tap_then_hold opens the side menu now.\nconst HOLD_GLYPH_ENABLED = false\n\nclass GestureDotsService {\n private started = false\n private flash: GestureDotGlyph | null = null\n private flashTimer: number | null = null\n /** Finger currently on the touch bar (tap_start..tap_end; the CFW forwards holds as these edges). */\n private held = false\n /**\n * The CFW's tap_then_hold fired during this press (discrete recognition\n * event; the tap_end that follows on release is what clears it).\n */\n private holdRecognized = false\n /** Whether the indicator scene currently has anything on it (needs a clear when the flash ends). */\n private visible = false\n private unsubs: Array<() => void> = []\n\n start(): void {\n if (this.started) return\n this.started = true\n this.unsubs.push(\n // The indicator owns the screen only while it is empty: drop our\n // retained state when something else takes over, and clear any\n // leftover flash when the display empties again.\n localDisplayManager.onActiveDisplayChange((active) => {\n if (active) this.relinquish()\n else this.render()\n }),\n // Overlays do not change the retained main display's occupancy.\n useDisplayStore.subscribe(\n (state) => state.overlayOwner,\n (owner) => {\n if (owner !== null) this.relinquish()\n },\n ),\n )\n }\n\n stop(): void {\n for (const unsub of this.unsubs) unsub()\n this.unsubs = []\n this.relinquish()\n this.started = false\n }\n\n private resetGesture(): void {\n if (this.flashTimer !== null) BgTimer.clearTimeout(this.flashTimer)\n this.flashTimer = null\n this.flash = null\n this.held = false\n this.holdRecognized = false\n }\n\n /** Another surface owns the display; forget the indicator without painting over it. */\n private relinquish(): void {\n this.resetGesture()\n this.visible = false\n sceneRenderer.clearApp(DOTS_APP_ID)\n this.trace(\"relinquish\")\n }\n\n private enabled(): boolean {\n return useSettingsStore.getState().getSetting(SETTINGS.gesture_dots.key) === true\n }\n\n /**\n * Flash a gesture on the indicator. Other gesture names are ignored.\n * Claimed gestures cancel any earlier press/flash without starting a new\n * one: the claimant may also consume the release edge.\n */\n noteGesture(gestureName: string, claimed = false): void {\n if (!this.started || !this.enabled()) return\n this.applyGesture(gestureName, claimed)\n this.trace(\"input\", {gestureName, claimed})\n }\n\n private applyGesture(gestureName: string, claimed: boolean): void {\n if (localDisplayManager.hasActiveDisplay() || useDisplayStore.getState().overlayOwner !== null) {\n this.relinquish()\n return\n }\n if (claimed) {\n this.resetGesture()\n this.render()\n return\n }\n // Tap edges show a steady circle for as long as the finger is down (on\n // CFW these arrive for holds: ET_LONG/ET_REL forwarded as\n // tap_start/tap_end), with no timer — tap_end clears it. The ring's\n // touch_down is the same finger-down edge; the ring's tap_end releases it.\n if (gestureName === \"tap_start\" || gestureName === \"touch_down\" || gestureName === \"tap_end\") {\n const held = gestureName !== \"tap_end\"\n if (this.held === held) return\n this.held = held\n if (!held) this.holdRecognized = false\n this.render()\n return\n }\n if (gestureName === \"tap_then_hold\" && HOLD_GLYPH_ENABLED) {\n this.held = true\n this.holdRecognized = true\n this.render()\n return\n }\n // Any other gesture implies the finger is up, even if tap_end was lost.\n const released = this.held\n this.held = false\n this.holdRecognized = false\n const flash: GestureDotGlyph | null =\n gestureName === \"single_tap\"\n ? \"dot\"\n : gestureName === \"swipe_up\"\n ? \"up\"\n : gestureName === \"swipe_down\"\n ? \"down\"\n : null\n if (!flash) {\n if (released) this.render()\n return\n }\n this.flash = flash\n if (this.flashTimer !== null) BgTimer.clearTimeout(this.flashTimer)\n this.flashTimer = BgTimer.setTimeout(() => {\n this.flashTimer = null\n this.flash = null\n this.render()\n this.trace(\"flash-ended\")\n }, GESTURE_FLASH_MS)\n this.render()\n }\n\n /** Debug setting only; no payloads or retained history. Correlate seq with G2PERF. */\n private trace(reason: string, details: Record<string, unknown> = {}): void {\n if (!this.enabled()) return\n console.log(\n \"GESTUREDOTS \" +\n JSON.stringify({\n t: Date.now(),\n reason,\n held: this.held,\n flash: this.flash,\n overlay: useDisplayStore.getState().overlayOwner,\n active: localDisplayManager.hasActiveDisplay(),\n ...details,\n }),\n )\n }\n\n /**\n * Paint or clear the indicator. Nothing is drawn while anything else is on\n * the glasses (miniapp frame or system screen) or when no gesture was just\n * received. The top-right anchor resolves against the device's real canvas,\n * so no size math here.\n *\n * Between flashes the element STAYS in the scene with the blank glyph:\n * removing it would tear down the firmware image container and rebuild the\n * page on every flash (a rebuild storm on stock firmware). Only disabling\n * the setting removes the element outright.\n */\n private render(): void {\n if (!this.started) return\n if (localDisplayManager.hasActiveDisplay() || useDisplayStore.getState().overlayOwner !== null) return\n const shown = this.flash ?? (this.held ? (this.holdRecognized ? \"hold\" : \"dot\") : null)\n if (!this.enabled()) {\n if (this.visible) {\n this.visible = false\n sceneRenderer.emitScene(DOTS_APP_ID, \"main\", [])\n }\n return\n }\n if (!shown && !this.visible) return\n const elements: SceneElementInput[] = [\n {\n type: \"image\",\n id: \"gesture-dot\",\n box: {\n x: MARGIN_X,\n y: MARGIN_TOP,\n w: GESTURE_DOT_GLYPH_SIZE,\n h: GESTURE_DOT_GLYPH_SIZE,\n anchor: \"top-right\",\n },\n data: GESTURE_DOT_GLYPHS[shown ?? \"blank\"],\n },\n ]\n this.visible = true\n const result = sceneRenderer.emitScene(DOTS_APP_ID, \"main\", elements)\n this.trace(\"render\", {\n glyph: shown ?? \"blank\",\n ...(result?.kind === \"scene\" ? {seq: result.seq, sent: result.sent} : {}),\n })\n }\n}\n\nconst gestureDotsService = new GestureDotsService()\nexport default gestureDotsService\nexport {GestureDotsService}\n"]}
|
|
1
|
+
{"version":3,"file":"GestureDotsService.js","sourceRoot":"","sources":["../../src/services/GestureDotsService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Gesture dots — a debug indicator that flashes a small glyph top-right on the\n * glasses when a touch gesture arrives, while nothing else is on the display:\n * a filled circle for a single tap, two for a double tap, an arrow for a swipe\n * (cleared after a short pulse), a bullseye for a recognized long press.\n * The Neural Band's middle finger (index 1) draws the same shapes hollow. Extracted from\n * the CFW dashboard service; it renders through\n * the ordinary scene pipeline, so it works on any positioning device (stock\n * G2 included), and is gated by the gesture_dots debug setting.\n *\n * The glyphs are pre-baked images (not text): the stock firmware's builtin\n * font has no ●/↑/↓, and a single image element with a stable id and box maps\n * to ONE re-used firmware image container — flash and idle swap its bytes\n * (data-only update, no page rebuild) instead of creating/removing containers.\n */\nimport {useDisplayStore} from \"../stores/display\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport type {SceneElementInput} from \"../utils/display/scene\"\nimport {BgTimer} from \"../utils/timers\"\nimport {GESTURE_DOT_GLYPHS, GESTURE_DOT_GLYPH_SIZE, type GestureDotGlyph} from \"./gestureDotGlyphs\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport sceneRenderer from \"./SceneRenderer\"\n\nconst DOTS_APP_ID = \"system.gesture-dots\"\n/** How long a gesture stays on the indicator. */\nconst GESTURE_FLASH_MS = 700\nconst MARGIN_X = 16\nconst MARGIN_TOP = 10\n/** Gestures that flash a glyph. */\nconst FLASH_GLYPHS: Record<string, GestureDotGlyph> = {\n single_tap: \"dot\",\n double_tap: \"double\",\n long_press: \"hold\",\n swipe_up: \"up\",\n swipe_down: \"down\",\n swipe_left: \"left\",\n swipe_right: \"right\",\n}\n/** The band's middle finger draws the filled shapes hollow. */\nconst MIDDLE_FINGER_GLYPHS: Partial<Record<GestureDotGlyph, GestureDotGlyph>> = {dot: \"ring\", double: \"doubleRing\"}\n\nclass GestureDotsService {\n private started = false\n private flash: GestureDotGlyph | null = null\n private flashTimer: number | null = null\n /** Raw controller holds survive hold recognition/dial events until their own release. */\n private presses = new Map<number, {raw: boolean; glyph: GestureDotGlyph}>()\n /** Avoid sending identical images for successive phases of the same press. */\n private renderedGlyph: GestureDotGlyph | null = null\n /** Whether the indicator scene currently has anything on it (needs a clear when the flash ends). */\n private visible = false\n private unsubs: Array<() => void> = []\n\n start(): void {\n if (this.started) return\n this.started = true\n this.unsubs.push(\n // The indicator owns the screen only while it is empty: drop our\n // retained state when something else takes over, and clear any\n // leftover flash when the display empties again.\n localDisplayManager.onActiveDisplayChange((active) => {\n if (active) this.relinquish()\n else this.render()\n }),\n // Overlays do not change the retained main display's occupancy.\n useDisplayStore.subscribe(\n (state) => state.overlayOwner,\n (owner) => {\n if (owner !== null) this.relinquish()\n },\n ),\n )\n }\n\n stop(): void {\n for (const unsub of this.unsubs) unsub()\n this.unsubs = []\n this.relinquish()\n this.started = false\n }\n\n private resetGesture(): void {\n if (this.flashTimer !== null) BgTimer.clearTimeout(this.flashTimer)\n this.flashTimer = null\n this.flash = null\n this.presses.clear()\n }\n\n /** Another surface owns the display; forget the indicator without painting over it. */\n private relinquish(): void {\n this.resetGesture()\n this.visible = false\n this.renderedGlyph = null\n sceneRenderer.clearApp(DOTS_APP_ID)\n this.trace(\"relinquish\")\n }\n\n private enabled(): boolean {\n return useSettingsStore.getState().getSetting(SETTINGS.gesture_dots.key) === true\n }\n\n /**\n * Flash a gesture on the indicator. Other gesture names are ignored.\n * Claimed gestures cancel any earlier press/flash without starting a new\n * one: the claimant may also consume the release edge. `fingerIndex` is the\n * Neural Band's finger on its pinches and press edges (1 = middle).\n */\n noteGesture(gestureName: string, claimed = false, fingerIndex?: number): void {\n if (!this.started || !this.enabled()) return\n this.applyGesture(gestureName, claimed, fingerIndex)\n this.trace(\"input\", {gestureName, claimed})\n }\n\n private applyGesture(gestureName: string, claimed: boolean, fingerIndex?: number): void {\n if (localDisplayManager.hasActiveDisplay() || useDisplayStore.getState().overlayOwner !== null) {\n this.relinquish()\n return\n }\n if (claimed) {\n this.resetGesture()\n this.render()\n return\n }\n const finger = fingerIndex ?? 0\n const press = this.presses.get(finger)\n if (gestureName === \"touch_down\" || gestureName === \"tap_start\") {\n if (press) {\n // Both reports describe one press. Never restart its visual feedback.\n if (gestureName === \"touch_down\") press.raw = true\n return\n }\n this.clearFlash()\n this.presses.set(finger, {raw: gestureName === \"touch_down\", glyph: finger === 1 ? \"ring\" : \"dot\"})\n this.render()\n return\n }\n if (gestureName === \"tap_end\") {\n if (!press) return\n this.presses.delete(finger)\n // The band's resolved tap arrives after release (double-tap window).\n // Keep the glyph lit across that gap so one pinch never flashes twice.\n if (press.raw && this.presses.size === 0) this.startFlash(press.glyph)\n this.render()\n return\n }\n const shape = FLASH_GLYPHS[gestureName]\n const glyph = shape && finger === 1 ? MIDDLE_FINGER_GLYPHS[shape] ?? shape : shape\n if (press?.raw && (!glyph || gestureName === \"long_press\")) {\n // Pinch-and-turn reports do not release a finger. A recognized hold\n // stays visible until tap_end instead of expiring halfway through it.\n if (glyph) press.glyph = glyph\n this.render()\n return\n }\n // Completed taps/swipes become one continuous pulse, even if the device\n // omits tap_end. Older tap_start-only devices retain the same fallback.\n this.presses.delete(finger)\n if (glyph) this.startFlash(glyph)\n this.render()\n }\n\n private clearFlash(): void {\n if (this.flashTimer !== null) BgTimer.clearTimeout(this.flashTimer)\n this.flashTimer = null\n this.flash = null\n }\n\n private startFlash(glyph: GestureDotGlyph): void {\n this.clearFlash()\n this.flash = glyph\n this.flashTimer = BgTimer.setTimeout(() => {\n this.flashTimer = null\n this.flash = null\n this.render()\n this.trace(\"flash-ended\")\n }, GESTURE_FLASH_MS)\n }\n\n /** Debug setting only; no payloads or retained history. Correlate seq with G2PERF. */\n private trace(reason: string, details: Record<string, unknown> = {}): void {\n if (!this.enabled()) return\n console.log(\n \"GESTUREDOTS \" +\n JSON.stringify({\n t: Date.now(),\n reason,\n held: this.presses.size > 0,\n flash: this.flash,\n overlay: useDisplayStore.getState().overlayOwner,\n active: localDisplayManager.hasActiveDisplay(),\n ...details,\n }),\n )\n }\n\n /**\n * Paint or clear the indicator. Nothing is drawn while anything else is on\n * the glasses (miniapp frame or system screen) or when no gesture was just\n * received. The top-right anchor resolves against the device's real canvas,\n * so no size math here.\n *\n * Between flashes the element STAYS in the scene with the blank glyph:\n * removing it would tear down the firmware image container and rebuild the\n * page on every flash (a rebuild storm on stock firmware). Only disabling\n * the setting removes the element outright.\n */\n private render(): void {\n if (!this.started) return\n if (localDisplayManager.hasActiveDisplay() || useDisplayStore.getState().overlayOwner !== null) return\n const shown = Array.from(this.presses.values()).at(-1)?.glyph ?? this.flash\n if (!this.enabled()) {\n if (this.visible) {\n this.visible = false\n this.renderedGlyph = null\n sceneRenderer.emitScene(DOTS_APP_ID, \"main\", [])\n }\n return\n }\n if (!shown && !this.visible) return\n const glyph = shown ?? \"blank\"\n if (glyph === this.renderedGlyph) return\n const elements: SceneElementInput[] = [\n {\n type: \"image\",\n id: \"gesture-dot\",\n box: {\n x: MARGIN_X,\n y: MARGIN_TOP,\n w: GESTURE_DOT_GLYPH_SIZE,\n h: GESTURE_DOT_GLYPH_SIZE,\n anchor: \"top-right\",\n },\n data: GESTURE_DOT_GLYPHS[glyph],\n },\n ]\n this.visible = true\n this.renderedGlyph = glyph\n const result = sceneRenderer.emitScene(DOTS_APP_ID, \"main\", elements)\n this.trace(\"render\", {\n glyph,\n ...(result?.kind === \"scene\" ? {seq: result.seq, sent: result.sent} : {}),\n })\n }\n}\n\nconst gestureDotsService = new GestureDotsService()\nexport default gestureDotsService\nexport {GestureDotsService}\n"]}
|
|
@@ -10,5 +10,5 @@ import { type TouchEvent } from "@glassly/bluetooth-sdk";
|
|
|
10
10
|
export declare function dispatchTouchEvent(event: TouchEvent): void;
|
|
11
11
|
export declare function resetTouchInput(): void;
|
|
12
12
|
/** Phone mirror input uses the same dashboard/miniapp policy as real glasses. */
|
|
13
|
-
export declare function sendGesture(gestureName: "single_tap" | "tap_start" | "double_tap" | "tap_end" | "tap_then_hold" | "touch_down" | "swipe_up" | "swipe_down"): void;
|
|
13
|
+
export declare function sendGesture(gestureName: "single_tap" | "tap_start" | "double_tap" | "tap_end" | "tap_then_hold" | "touch_down" | "swipe_up" | "swipe_down" | "swipe_left" | "swipe_right" | "long_press" | "dial_up" | "dial_down"): void;
|
|
14
14
|
//# sourceMappingURL=GlassesTouchInput.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GlassesTouchInput.d.ts","sourceRoot":"","sources":["../../src/services/GlassesTouchInput.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,UAAU,EAAC,MAAM,wBAAwB,CAAA;AAwCpE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"GlassesTouchInput.d.ts","sourceRoot":"","sources":["../../src/services/GlassesTouchInput.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,UAAU,EAAC,MAAM,wBAAwB,CAAA;AAwCpE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAoD1D;AAED,wBAAgB,eAAe,IAAI,IAAI,CAGtC;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CACzB,WAAW,EACP,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,SAAS,GACT,eAAe,GACf,YAAY,GACZ,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,SAAS,GACT,WAAW,GACd,IAAI,CAQN"}
|
|
@@ -47,7 +47,10 @@ function shouldForwardScroll(event) {
|
|
|
47
47
|
export function dispatchTouchEvent(event) {
|
|
48
48
|
const gesture = event.gestureName;
|
|
49
49
|
const claimant = localMiniappRuntime.gestureClaimant(gesture);
|
|
50
|
-
|
|
50
|
+
// The Neural Band's middle finger (index 1) shares the tap names but is left
|
|
51
|
+
// to miniapps: the engine's own double-tap behaviors are the index finger's.
|
|
52
|
+
const osDoubleTap = gesture === "double_tap" && event.index !== 1;
|
|
53
|
+
if (osDoubleTap) {
|
|
51
54
|
// A visible main miniapp opts out of OS exit by registering double tap (the
|
|
52
55
|
// terminal's back-to-menu). The latest claimant still receives it. Otherwise
|
|
53
56
|
// the tap means "leave the app". The controller shows the bounded OS
|
|
@@ -66,7 +69,7 @@ export function dispatchTouchEvent(event) {
|
|
|
66
69
|
if (gesture === "double_tap" && isOverlayApp(claimant))
|
|
67
70
|
dashboardOpenTrace.trigger("double-tap", event.timestamp);
|
|
68
71
|
// The press may have started before this app claimed its release edge.
|
|
69
|
-
gestureDotsService.noteGesture(gesture, true);
|
|
72
|
+
gestureDotsService.noteGesture(gesture, true, event.index);
|
|
70
73
|
if (!shouldForwardScroll(event))
|
|
71
74
|
return;
|
|
72
75
|
localMiniappRuntime.forwardEvent("touch_event", event, undefined, claimant);
|
|
@@ -75,7 +78,7 @@ export function dispatchTouchEvent(event) {
|
|
|
75
78
|
// Gesture-dots debug indicator (any device, gated by its setting). It
|
|
76
79
|
// only paints while the screen is otherwise empty. Process even suppressed
|
|
77
80
|
// scroll repeats here so their completion still clears a held dot.
|
|
78
|
-
gestureDotsService.noteGesture(gesture);
|
|
81
|
+
gestureDotsService.noteGesture(gesture, false, event.index);
|
|
79
82
|
if (!shouldForwardScroll(event))
|
|
80
83
|
return;
|
|
81
84
|
localMiniappRuntime.forwardEvent("touch_event", event);
|
|
@@ -83,7 +86,7 @@ export function dispatchTouchEvent(event) {
|
|
|
83
86
|
// Glassly CFW glasses the dashboard miniapp claims it while it runs
|
|
84
87
|
// (ADR-0006); stock firmware keeps its native dashboard (ADR-0002). The
|
|
85
88
|
// modal nav UI owns the screen; showDashboard would fight it.
|
|
86
|
-
if (
|
|
89
|
+
if (osDoubleTap && !nativeNavigationService.isActive) {
|
|
87
90
|
useDisplayStore.getState().setDisplayEvent({ layout: { layoutType: "unavailable", reason: "firmware-dashboard" } });
|
|
88
91
|
void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {
|
|
89
92
|
console.error("DeviceEventRouter: showDashboard failed:", err);
|