@glassly/engine 0.3.7-beta.934 → 0.3.7-beta.966

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.
Files changed (57) hide show
  1. package/build/generated/releaseMetadata.js +3 -3
  2. package/build/generated/releaseMetadata.js.map +1 -1
  3. package/build/services/BlobStore.d.ts.map +1 -1
  4. package/build/services/BlobStore.js +5 -12
  5. package/build/services/BlobStore.js.map +1 -1
  6. package/build/services/DisplayProcessor.d.ts.map +1 -1
  7. package/build/services/DisplayProcessor.js +3 -1
  8. package/build/services/DisplayProcessor.js.map +1 -1
  9. package/build/services/GlassesTouchInput.d.ts.map +1 -1
  10. package/build/services/GlassesTouchInput.js +4 -2
  11. package/build/services/GlassesTouchInput.js.map +1 -1
  12. package/build/services/PhoneCalendarService.d.ts.map +1 -1
  13. package/build/services/PhoneCalendarService.js +8 -0
  14. package/build/services/PhoneCalendarService.js.map +1 -1
  15. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  16. package/build/services/PhoneNotificationsSync.js +7 -3
  17. package/build/services/PhoneNotificationsSync.js.map +1 -1
  18. package/build/services/controllers.d.ts.map +1 -1
  19. package/build/services/controllers.js +1 -0
  20. package/build/services/controllers.js.map +1 -1
  21. package/build/services/pages/pageEligibility.d.ts.map +1 -1
  22. package/build/services/pages/pageEligibility.js +8 -1
  23. package/build/services/pages/pageEligibility.js.map +1 -1
  24. package/build/types/capabilities/memomind-one.d.ts +3 -0
  25. package/build/types/capabilities/memomind-one.d.ts.map +1 -0
  26. package/build/types/capabilities/memomind-one.js +41 -0
  27. package/build/types/capabilities/memomind-one.js.map +1 -0
  28. package/build/types/enums.d.ts +4 -2
  29. package/build/types/enums.d.ts.map +1 -1
  30. package/build/types/enums.js +2 -0
  31. package/build/types/enums.js.map +1 -1
  32. package/build/types/hardware.d.ts +3 -1
  33. package/build/types/hardware.d.ts.map +1 -1
  34. package/build/types/hardware.js +3 -1
  35. package/build/types/hardware.js.map +1 -1
  36. package/build/utils/display/index.d.ts +1 -0
  37. package/build/utils/display/index.d.ts.map +1 -1
  38. package/build/utils/display/index.js +1 -0
  39. package/build/utils/display/index.js.map +1 -1
  40. package/build/utils/display/profiles/index.d.ts +1 -0
  41. package/build/utils/display/profiles/index.d.ts.map +1 -1
  42. package/build/utils/display/profiles/index.js +1 -0
  43. package/build/utils/display/profiles/index.js.map +1 -1
  44. package/package.json +7 -7
  45. package/src/generated/releaseMetadata.ts +3 -3
  46. package/src/services/BlobStore.ts +5 -11
  47. package/src/services/DisplayProcessor.ts +5 -8
  48. package/src/services/GlassesTouchInput.ts +6 -2
  49. package/src/services/PhoneCalendarService.ts +7 -0
  50. package/src/services/PhoneNotificationsSync.ts +8 -3
  51. package/src/services/controllers.ts +1 -0
  52. package/src/services/pages/pageEligibility.ts +9 -1
  53. package/src/types/capabilities/memomind-one.ts +42 -0
  54. package/src/types/enums.ts +2 -0
  55. package/src/types/hardware.ts +14 -1
  56. package/src/utils/display/index.ts +2 -0
  57. package/src/utils/display/profiles/index.ts +2 -0
@@ -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;AAkFpE;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAQ1D;AA8DD,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,GACX,SAAS,GACT,WAAW,GACd,IAAI,CAQN"}
1
+ {"version":3,"file":"GlassesTouchInput.d.ts","sourceRoot":"","sources":["../../src/services/GlassesTouchInput.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,UAAU,EAAC,MAAM,wBAAwB,CAAA;AAkFpE;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAQ1D;AAkED,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,GACX,SAAS,GACT,WAAW,GACd,IAAI,CAQN"}
@@ -101,7 +101,7 @@ function routeTouchEvent(event) {
101
101
  const doubleTap = gesture === "double_tap";
102
102
  // The Neural Band's middle finger (index 1) shares the tap names. Either
103
103
  // finger leaves the app on screen; the firmware dashboard is the index finger's.
104
- const osDoubleTap = doubleTap && event.index !== 1;
104
+ const osDoubleTap = doubleTap && !(event.source === 3 && event.index === 1);
105
105
  const osHeadUp = gesture === "head_up";
106
106
  if (doubleTap) {
107
107
  // The app being left opts out of OS exit by registering double tap (the
@@ -140,7 +140,9 @@ function routeTouchEvent(event) {
140
140
  // pagesEligible: connected Glassly CFW glasses, whose firmware leaves the
141
141
  // dashboard to the phone; other firmware opens its own on a lift.
142
142
  const opensNativeDashboard = osDoubleTap || (osHeadUp && pagesEligible());
143
- if (opensNativeDashboard && !nativeNavigationService.isActive && isGlassesConnected(useGlassesStore.getState().connection)) {
143
+ if (opensNativeDashboard &&
144
+ !nativeNavigationService.isActive &&
145
+ isGlassesConnected(useGlassesStore.getState().connection)) {
144
146
  useDisplayStore.getState().setDisplayEvent({ layout: { layoutType: "unavailable", reason: "firmware-dashboard" } });
145
147
  void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {
146
148
  console.error("DeviceEventRouter: showDashboard failed:", err);
@@ -1 +1 @@
1
- {"version":3,"file":"GlassesTouchInput.js","sourceRoot":"","sources":["../../src/services/GlassesTouchInput.ts"],"names":[],"mappings":"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","sourcesContent":["import {useDisplayStore} from \"../stores/display\"\nimport {DeviceModels, type TouchEvent} from \"@glassly/bluetooth-sdk\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\n\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {BgTimer} from \"../utils/timers\"\nimport {hasInput2d} from \"./controllers\"\nimport dashboardOpenTrace from \"./debug/DashboardOpenTrace\"\nimport gestureDotsService from \"./GestureDotsService\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport {MiniappExitGesture} from \"./MiniappExitGesture\"\nimport nativeNavigationService from \"./NativeNavigationService\"\nimport {isOverlayApp} from \"./pages/overlayApps\"\nimport {pagesEligible} from \"./pages/pageEligibility\"\n\nconst miniappExitGesture = new MiniappExitGesture({\n getState: () => useAppStatusStore.getState(),\n getTargetPackageName: () => localDisplayManager.getMiniappToLeave(),\n beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),\n finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),\n cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),\n setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),\n clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),\n})\n\n// The ring reports a slide once per step of travel, whether the CFW forwards\n// it (deviceModel G2) or the ring sends it to the phone itself (deviceModel\n// R1); both tag source 2. One press is one swipe: the stroke's first step goes\n// out as swipe_up / swipe_down, and every step, the first included, as\n// scroll_up / scroll_down with its distance and speed for smooth scrolling. A\n// stroke ends at the ring's next touch_down or tap_end, or after\n// RING_STROKE_IDLE_MS without a step when a report was lost. Reversing within\n// a stroke fires no swipe (a finger rolling back at lift-off). Native receipt\n// timestamps keep strokes apart when JS drains a backlog.\nconst RING_STROKE_IDLE_MS = 1000\n/** Per ring, when the open stroke's last step arrived. */\nconst ringStrokes = new Map<string, number>()\n\nfunction ringKey(event: TouchEvent): string {\n return event.controllerId ?? `source:${event.source}`\n}\n\nfunction isRingSlideStep(event: TouchEvent): boolean {\n return event.source === 2 && (event.gestureName === \"swipe_up\" || event.gestureName === \"swipe_down\")\n}\n\nfunction opensRingStroke(event: TouchEvent): boolean {\n const last = ringStrokes.get(ringKey(event))\n ringStrokes.set(ringKey(event), event.timestamp)\n const elapsed = last === undefined ? null : event.timestamp - last\n return elapsed === null || elapsed < 0 || elapsed >= RING_STROKE_IDLE_MS\n}\n\nfunction endRingStroke(event: TouchEvent): void {\n if (event.source === 2 && (event.gestureName === \"touch_down\" || event.gestureName === \"tap_end\")) {\n ringStrokes.delete(ringKey(event))\n }\n}\n\n/** The discrete swipe a stroke opens with: the step's travel belongs to scroll_*. */\nfunction strokeSwipe(step: TouchEvent): TouchEvent {\n const swipe = {...step}\n delete swipe.distance\n delete swipe.speed\n return swipe\n}\n\n/**\n * Miniapps' copy of the touch: `input2d: true` when it came from a controller\n * that swipes in two dimensions (the Neural Band), the field absent otherwise,\n * so a 1D swipe from the temples or the ring stays tellable while a band is\n * connected under the same gesture names.\n */\nfunction toMiniapps(event: TouchEvent): TouchEvent & {input2d?: true} {\n const id = event.controllerId\n if (id === undefined) return event\n const model = useGlassesStore.getState().controllers?.[id]?.model\n return model !== undefined && hasInput2d(model) ? {...event, input2d: true} : event\n}\n\n/**\n * Route both real glasses input and phone mirror controls through the same\n * policy. A claimed gesture goes to the app holding the claim, alone; an\n * unclaimed one goes to every subscriber and then the engine's default. The\n * one rule ahead of the claims: an unclaimed double tap leaves the miniapp\n * the wearer is in — the one visibly on the glasses, else the running\n * standard app on an empty screen — whoever else claimed the gesture; the\n * dashboard's claim is for the screen with no app in it. Head position\n * (head_up / head_down) is a gesture like any other: the dashboard claims it\n * while it runs, and an unclaimed head_up on glasses that defer the dashboard\n * to the phone opens the native one, as it would with no app at all. A ring\n * slide step becomes one swipe per stroke plus a scroll step (see above).\n */\nexport function dispatchTouchEvent(event: TouchEvent): void {\n if (isRingSlideStep(event)) {\n if (opensRingStroke(event)) routeTouchEvent(strokeSwipe(event))\n routeTouchEvent({...event, gestureName: event.gestureName === \"swipe_up\" ? \"scroll_up\" : \"scroll_down\"})\n return\n }\n endRingStroke(event)\n routeTouchEvent(event)\n}\n\nfunction routeTouchEvent(event: TouchEvent): void {\n const gesture = event.gestureName\n const claimant = localMiniappRuntime.gestureClaimant(gesture)\n const doubleTap = gesture === \"double_tap\"\n // The Neural Band's middle finger (index 1) shares the tap names. Either\n // finger leaves the app on screen; the firmware dashboard is the index finger's.\n const osDoubleTap = doubleTap && event.index !== 1\n const osHeadUp = gesture === \"head_up\"\n\n if (doubleTap) {\n // The app being left opts out of OS exit by registering double tap (the\n // terminal's back-to-menu). The latest claimant still receives it. Otherwise\n // the tap means \"leave the app\". The controller shows the bounded OS\n // notice first, then uses the normal store stop path (and closes its\n // phone overlay).\n const leaving = localDisplayManager.getMiniappToLeave()\n if (\n leaving !== null &&\n !localMiniappRuntime.hasGestureClaim(leaving, gesture) &&\n miniappExitGesture.tryExitMiniapp()\n ) {\n gestureDotsService.noteGesture(gesture, true, event.index, event.controllerId)\n return\n }\n }\n\n if (claimant !== null) {\n // An overlay app claims the double tap and head-up on an empty screen to open the dashboard.\n if ((gesture === \"double_tap\" || osHeadUp) && isOverlayApp(claimant)) {\n dashboardOpenTrace.trigger(osHeadUp ? \"head-up\" : \"double-tap\", event.timestamp)\n }\n // The press may have started before this app claimed its release edge.\n gestureDotsService.noteGesture(gesture, true, event.index, event.controllerId)\n localMiniappRuntime.forwardEvent(\"touch_event\", toMiniapps(event), undefined, claimant)\n return\n }\n\n // Gesture-dots debug indicator (any device, gated by its setting). It\n // only paints while the screen is otherwise empty.\n gestureDotsService.noteGesture(gesture, false, event.index, event.controllerId)\n\n localMiniappRuntime.forwardEvent(\"touch_event\", toMiniapps(event))\n\n // Nobody claimed a double tap or a head-up: the glasses' native dashboard.\n // On eligible Glassly CFW glasses the dashboard miniapp claims both while it\n // runs (ADR-0006); stock firmware keeps its native dashboard (ADR-0002) and\n // opens it on a head-up by itself, so only the double tap is relayed there.\n // The modal nav UI owns the screen; showDashboard would fight it. Without\n // glasses there is no dashboard to open, and the mirror keeps its frame.\n // pagesEligible: connected Glassly CFW glasses, whose firmware leaves the\n // dashboard to the phone; other firmware opens its own on a lift.\n const opensNativeDashboard = osDoubleTap || (osHeadUp && pagesEligible())\n if (opensNativeDashboard && !nativeNavigationService.isActive && isGlassesConnected(useGlassesStore.getState().connection)) {\n useDisplayStore.getState().setDisplayEvent({layout: {layoutType: \"unavailable\", reason: \"firmware-dashboard\"}})\n void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {\n console.error(\"DeviceEventRouter: showDashboard failed:\", err)\n })\n }\n}\n\nexport function resetTouchInput(): void {\n miniappExitGesture.reset()\n ringStrokes.clear()\n}\n\n/** Phone mirror input uses the same dashboard/miniapp policy as real glasses. */\nexport function sendGesture(\n gestureName:\n | \"single_tap\"\n | \"tap_start\"\n | \"double_tap\"\n | \"tap_end\"\n | \"tap_then_hold\"\n | \"touch_down\"\n | \"swipe_up\"\n | \"swipe_down\"\n | \"swipe_left\"\n | \"swipe_right\"\n | \"long_press\"\n | \"dial_up\"\n | \"dial_down\"\n | \"head_up\"\n | \"head_down\",\n): void {\n const model = useGlassesStore.getState().deviceModel\n dispatchTouchEvent({\n type: \"touch_event\",\n gestureName,\n deviceModel: Object.values(DeviceModels).find((candidate) => candidate === model) ?? DeviceModels.Simulated,\n timestamp: Date.now(),\n })\n}\n"]}
1
+ {"version":3,"file":"GlassesTouchInput.js","sourceRoot":"","sources":["../../src/services/GlassesTouchInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,YAAY,EAAkB,MAAM,wBAAwB,CAAA;AACpE,OAAO,YAAY,MAAM,iCAAiC,CAAA;AAE1D,OAAO,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AACvC,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAA;AACxC,OAAO,kBAAkB,MAAM,4BAA4B,CAAA;AAC3D,OAAO,kBAAkB,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAA;AACrD,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;AACvD,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAA;AACvD,OAAO,uBAAuB,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAC,YAAY,EAAC,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAA;AAErD,MAAM,kBAAkB,GAAG,IAAI,kBAAkB,CAAC;IAChD,QAAQ,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE;IAC5C,oBAAoB,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,iBAAiB,EAAE;IACnE,eAAe,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC;IAC/F,gBAAgB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,WAAW,CAAC;IACrF,gBAAgB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,WAAW,CAAC;IACrF,UAAU,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC;IACpE,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC;CACzD,CAAC,CAAA;AAEF,6EAA6E;AAC7E,4EAA4E;AAC5E,+EAA+E;AAC/E,uEAAuE;AACvE,8EAA8E;AAC9E,iEAAiE;AACjE,8EAA8E;AAC9E,8EAA8E;AAC9E,0DAA0D;AAC1D,MAAM,mBAAmB,GAAG,IAAI,CAAA;AAChC,0DAA0D;AAC1D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAA;AAE7C,SAAS,OAAO,CAAC,KAAiB;IAChC,OAAO,KAAK,CAAC,YAAY,IAAI,UAAU,KAAK,CAAC,MAAM,EAAE,CAAA;AACvD,CAAC;AAED,SAAS,eAAe,CAAC,KAAiB;IACxC,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,KAAK,UAAU,IAAI,KAAK,CAAC,WAAW,KAAK,YAAY,CAAC,CAAA;AACvG,CAAC;AAED,SAAS,eAAe,CAAC,KAAiB;IACxC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IAC5C,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;IAChD,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAA;IAClE,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,mBAAmB,CAAA;AAC1E,CAAC;AAED,SAAS,aAAa,CAAC,KAAiB;IACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,KAAK,YAAY,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,EAAE,CAAC;QAClG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IACpC,CAAC;AACH,CAAC;AAED,qFAAqF;AACrF,SAAS,WAAW,CAAC,IAAgB;IACnC,MAAM,KAAK,GAAG,EAAC,GAAG,IAAI,EAAC,CAAA;IACvB,OAAO,KAAK,CAAC,QAAQ,CAAA;IACrB,OAAO,KAAK,CAAC,KAAK,CAAA;IAClB,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,KAAiB;IACnC,MAAM,EAAE,GAAG,KAAK,CAAC,YAAY,CAAA;IAC7B,IAAI,EAAE,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAClC,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,CAAA;IACjE,OAAO,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAC,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AACrF,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAiB;IAClD,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,IAAI,eAAe,CAAC,KAAK,CAAC;YAAE,eAAe,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAA;QAC/D,eAAe,CAAC,EAAC,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAC,CAAC,CAAA;QACxG,OAAM;IACR,CAAC;IACD,aAAa,CAAC,KAAK,CAAC,CAAA;IACpB,eAAe,CAAC,KAAK,CAAC,CAAA;AACxB,CAAC;AAED,SAAS,eAAe,CAAC,KAAiB;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAA;IACjC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IAC7D,MAAM,SAAS,GAAG,OAAO,KAAK,YAAY,CAAA;IAC1C,yEAAyE;IACzE,iFAAiF;IACjF,MAAM,WAAW,GAAG,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,CAAA;IAC3E,MAAM,QAAQ,GAAG,OAAO,KAAK,SAAS,CAAA;IAEtC,IAAI,SAAS,EAAE,CAAC;QACd,wEAAwE;QACxE,6EAA6E;QAC7E,qEAAqE;QACrE,qEAAqE;QACrE,kBAAkB;QAClB,MAAM,OAAO,GAAG,mBAAmB,CAAC,iBAAiB,EAAE,CAAA;QACvD,IACE,OAAO,KAAK,IAAI;YAChB,CAAC,mBAAmB,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC;YACtD,kBAAkB,CAAC,cAAc,EAAE,EACnC,CAAC;YACD,kBAAkB,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA;YAC9E,OAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,6FAA6F;QAC7F,IAAI,CAAC,OAAO,KAAK,YAAY,IAAI,QAAQ,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrE,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;QAClF,CAAC;QACD,uEAAuE;QACvE,kBAAkB,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA;QAC9E,mBAAmB,CAAC,YAAY,CAAC,aAAa,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;QACvF,OAAM;IACR,CAAC;IAED,sEAAsE;IACtE,mDAAmD;IACnD,kBAAkB,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA;IAE/E,mBAAmB,CAAC,YAAY,CAAC,aAAa,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;IAElE,2EAA2E;IAC3E,6EAA6E;IAC7E,4EAA4E;IAC5E,4EAA4E;IAC5E,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,kEAAkE;IAClE,MAAM,oBAAoB,GAAG,WAAW,IAAI,CAAC,QAAQ,IAAI,aAAa,EAAE,CAAC,CAAA;IACzE,IACE,oBAAoB;QACpB,CAAC,uBAAuB,CAAC,QAAQ;QACjC,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,EACzD,CAAC;QACD,eAAe,CAAC,QAAQ,EAAE,CAAC,eAAe,CAAC,EAAC,MAAM,EAAE,EAAC,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,oBAAoB,EAAC,EAAC,CAAC,CAAA;QAC/G,KAAK,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YAC/D,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,GAAG,CAAC,CAAA;QAChE,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,kBAAkB,CAAC,KAAK,EAAE,CAAA;IAC1B,WAAW,CAAC,KAAK,EAAE,CAAA;AACrB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,WAAW,CACzB,WAee;IAEf,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAA;IACpD,kBAAkB,CAAC;QACjB,IAAI,EAAE,aAAa;QACnB,WAAW;QACX,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,KAAK,CAAC,IAAI,YAAY,CAAC,SAAS;QAC3G,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import {useDisplayStore} from \"../stores/display\"\nimport {DeviceModels, type TouchEvent} from \"@glassly/bluetooth-sdk\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\n\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {BgTimer} from \"../utils/timers\"\nimport {hasInput2d} from \"./controllers\"\nimport dashboardOpenTrace from \"./debug/DashboardOpenTrace\"\nimport gestureDotsService from \"./GestureDotsService\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport localDisplayManager from \"./LocalDisplayManager\"\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport {MiniappExitGesture} from \"./MiniappExitGesture\"\nimport nativeNavigationService from \"./NativeNavigationService\"\nimport {isOverlayApp} from \"./pages/overlayApps\"\nimport {pagesEligible} from \"./pages/pageEligibility\"\n\nconst miniappExitGesture = new MiniappExitGesture({\n getState: () => useAppStatusStore.getState(),\n getTargetPackageName: () => localDisplayManager.getMiniappToLeave(),\n beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),\n finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),\n cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),\n setTimeout: (callback, delay) => BgTimer.setTimeout(callback, delay),\n clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),\n})\n\n// The ring reports a slide once per step of travel, whether the CFW forwards\n// it (deviceModel G2) or the ring sends it to the phone itself (deviceModel\n// R1); both tag source 2. One press is one swipe: the stroke's first step goes\n// out as swipe_up / swipe_down, and every step, the first included, as\n// scroll_up / scroll_down with its distance and speed for smooth scrolling. A\n// stroke ends at the ring's next touch_down or tap_end, or after\n// RING_STROKE_IDLE_MS without a step when a report was lost. Reversing within\n// a stroke fires no swipe (a finger rolling back at lift-off). Native receipt\n// timestamps keep strokes apart when JS drains a backlog.\nconst RING_STROKE_IDLE_MS = 1000\n/** Per ring, when the open stroke's last step arrived. */\nconst ringStrokes = new Map<string, number>()\n\nfunction ringKey(event: TouchEvent): string {\n return event.controllerId ?? `source:${event.source}`\n}\n\nfunction isRingSlideStep(event: TouchEvent): boolean {\n return event.source === 2 && (event.gestureName === \"swipe_up\" || event.gestureName === \"swipe_down\")\n}\n\nfunction opensRingStroke(event: TouchEvent): boolean {\n const last = ringStrokes.get(ringKey(event))\n ringStrokes.set(ringKey(event), event.timestamp)\n const elapsed = last === undefined ? null : event.timestamp - last\n return elapsed === null || elapsed < 0 || elapsed >= RING_STROKE_IDLE_MS\n}\n\nfunction endRingStroke(event: TouchEvent): void {\n if (event.source === 2 && (event.gestureName === \"touch_down\" || event.gestureName === \"tap_end\")) {\n ringStrokes.delete(ringKey(event))\n }\n}\n\n/** The discrete swipe a stroke opens with: the step's travel belongs to scroll_*. */\nfunction strokeSwipe(step: TouchEvent): TouchEvent {\n const swipe = {...step}\n delete swipe.distance\n delete swipe.speed\n return swipe\n}\n\n/**\n * Miniapps' copy of the touch: `input2d: true` when it came from a controller\n * that swipes in two dimensions (the Neural Band), the field absent otherwise,\n * so a 1D swipe from the temples or the ring stays tellable while a band is\n * connected under the same gesture names.\n */\nfunction toMiniapps(event: TouchEvent): TouchEvent & {input2d?: true} {\n const id = event.controllerId\n if (id === undefined) return event\n const model = useGlassesStore.getState().controllers?.[id]?.model\n return model !== undefined && hasInput2d(model) ? {...event, input2d: true} : event\n}\n\n/**\n * Route both real glasses input and phone mirror controls through the same\n * policy. A claimed gesture goes to the app holding the claim, alone; an\n * unclaimed one goes to every subscriber and then the engine's default. The\n * one rule ahead of the claims: an unclaimed double tap leaves the miniapp\n * the wearer is in — the one visibly on the glasses, else the running\n * standard app on an empty screen — whoever else claimed the gesture; the\n * dashboard's claim is for the screen with no app in it. Head position\n * (head_up / head_down) is a gesture like any other: the dashboard claims it\n * while it runs, and an unclaimed head_up on glasses that defer the dashboard\n * to the phone opens the native one, as it would with no app at all. A ring\n * slide step becomes one swipe per stroke plus a scroll step (see above).\n */\nexport function dispatchTouchEvent(event: TouchEvent): void {\n if (isRingSlideStep(event)) {\n if (opensRingStroke(event)) routeTouchEvent(strokeSwipe(event))\n routeTouchEvent({...event, gestureName: event.gestureName === \"swipe_up\" ? \"scroll_up\" : \"scroll_down\"})\n return\n }\n endRingStroke(event)\n routeTouchEvent(event)\n}\n\nfunction routeTouchEvent(event: TouchEvent): void {\n const gesture = event.gestureName\n const claimant = localMiniappRuntime.gestureClaimant(gesture)\n const doubleTap = gesture === \"double_tap\"\n // The Neural Band's middle finger (index 1) shares the tap names. Either\n // finger leaves the app on screen; the firmware dashboard is the index finger's.\n const osDoubleTap = doubleTap && !(event.source === 3 && event.index === 1)\n const osHeadUp = gesture === \"head_up\"\n\n if (doubleTap) {\n // The app being left opts out of OS exit by registering double tap (the\n // terminal's back-to-menu). The latest claimant still receives it. Otherwise\n // the tap means \"leave the app\". The controller shows the bounded OS\n // notice first, then uses the normal store stop path (and closes its\n // phone overlay).\n const leaving = localDisplayManager.getMiniappToLeave()\n if (\n leaving !== null &&\n !localMiniappRuntime.hasGestureClaim(leaving, gesture) &&\n miniappExitGesture.tryExitMiniapp()\n ) {\n gestureDotsService.noteGesture(gesture, true, event.index, event.controllerId)\n return\n }\n }\n\n if (claimant !== null) {\n // An overlay app claims the double tap and head-up on an empty screen to open the dashboard.\n if ((gesture === \"double_tap\" || osHeadUp) && isOverlayApp(claimant)) {\n dashboardOpenTrace.trigger(osHeadUp ? \"head-up\" : \"double-tap\", event.timestamp)\n }\n // The press may have started before this app claimed its release edge.\n gestureDotsService.noteGesture(gesture, true, event.index, event.controllerId)\n localMiniappRuntime.forwardEvent(\"touch_event\", toMiniapps(event), undefined, claimant)\n return\n }\n\n // Gesture-dots debug indicator (any device, gated by its setting). It\n // only paints while the screen is otherwise empty.\n gestureDotsService.noteGesture(gesture, false, event.index, event.controllerId)\n\n localMiniappRuntime.forwardEvent(\"touch_event\", toMiniapps(event))\n\n // Nobody claimed a double tap or a head-up: the glasses' native dashboard.\n // On eligible Glassly CFW glasses the dashboard miniapp claims both while it\n // runs (ADR-0006); stock firmware keeps its native dashboard (ADR-0002) and\n // opens it on a head-up by itself, so only the double tap is relayed there.\n // The modal nav UI owns the screen; showDashboard would fight it. Without\n // glasses there is no dashboard to open, and the mirror keeps its frame.\n // pagesEligible: connected Glassly CFW glasses, whose firmware leaves the\n // dashboard to the phone; other firmware opens its own on a lift.\n const opensNativeDashboard = osDoubleTap || (osHeadUp && pagesEligible())\n if (\n opensNativeDashboard &&\n !nativeNavigationService.isActive &&\n isGlassesConnected(useGlassesStore.getState().connection)\n ) {\n useDisplayStore.getState().setDisplayEvent({layout: {layoutType: \"unavailable\", reason: \"firmware-dashboard\"}})\n void Promise.resolve(BluetoothSdk.showDashboard()).catch((err) => {\n console.error(\"DeviceEventRouter: showDashboard failed:\", err)\n })\n }\n}\n\nexport function resetTouchInput(): void {\n miniappExitGesture.reset()\n ringStrokes.clear()\n}\n\n/** Phone mirror input uses the same dashboard/miniapp policy as real glasses. */\nexport function sendGesture(\n gestureName:\n | \"single_tap\"\n | \"tap_start\"\n | \"double_tap\"\n | \"tap_end\"\n | \"tap_then_hold\"\n | \"touch_down\"\n | \"swipe_up\"\n | \"swipe_down\"\n | \"swipe_left\"\n | \"swipe_right\"\n | \"long_press\"\n | \"dial_up\"\n | \"dial_down\"\n | \"head_up\"\n | \"head_down\",\n): void {\n const model = useGlassesStore.getState().deviceModel\n dispatchTouchEvent({\n type: \"touch_event\",\n gestureName,\n deviceModel: Object.values(DeviceModels).find((candidate) => candidate === model) ?? DeviceModels.Simulated,\n timestamp: Date.now(),\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneCalendarService.d.ts","sourceRoot":"","sources":["../../src/services/PhoneCalendarService.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,kBAAkB,EAAC,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EAEL,KAAK,mBAAmB,EAIzB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAC,kBAAkB,EAAC,MAAM,wBAAwB,CAAA;AA0BzD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoBvG"}
1
+ {"version":3,"file":"PhoneCalendarService.d.ts","sourceRoot":"","sources":["../../src/services/PhoneCalendarService.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,kBAAkB,EAAC,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EAEL,KAAK,mBAAmB,EAIzB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAC,kBAAkB,EAAC,MAAM,wBAAwB,CAAA;AA0BzD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2BvG"}
@@ -33,9 +33,17 @@ export async function listPhoneCalendarEvents(request) {
33
33
  if (calendars.length === 0)
34
34
  return { events: [], truncated: false };
35
35
  const rawEvents = await Calendar.listEvents(calendars, startsAt, endsAt);
36
+ const seen = new Set();
36
37
  const events = rawEvents
37
38
  .map(normalizeCalendarEvent)
38
39
  .filter((event) => calendarEventOverlapsWindow(event, startsAt, endsAt))
40
+ .filter((event) => {
41
+ const key = `${event.title}\n${event.startsAt}\n${event.endsAt}\n${event.allDay}`;
42
+ if (seen.has(key))
43
+ return false;
44
+ seen.add(key);
45
+ return true;
46
+ })
39
47
  .sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || Date.parse(a.endsAt) - Date.parse(b.endsAt));
40
48
  return {
41
49
  events: events.slice(0, limit),
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneCalendarService.js","sourceRoot":"","sources":["../../src/services/PhoneCalendarService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AACrC,OAAO,EAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAC,MAAM,0BAA0B,CAAA;AAEpE,OAAO,EAAC,gBAAgB,EAA0B,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EACL,2BAA2B,EAE3B,sBAAsB,EACtB,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAC,kBAAkB,EAAC,MAAM,wBAAwB,CAAA;AAMzD;;;;;GAKG;AACH,KAAK,UAAU,yBAAyB,CAAC,MAAgC;IACvE,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;QAC9B,OAAO,6FAA6F,CAAA;IACtG,CAAC;IACD,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QACrF,IAAI,SAAS,KAAK,OAAO,CAAC,OAAO,IAAI,SAAS,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;YACnE,OAAO,0HAA0H,CAAA;QACnI,CAAC;QACD,OAAO,gGAAgG,CAAA;IACzG,CAAC;IACD,OAAO,qGAAqG,CAAA;AAC9G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,OAA4B;IACxE,MAAM,EAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAC,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAA;IACtE,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,sBAAsB,EAAE,CAAA;IAC1D,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,yBAAyB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;IACtH,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;IACzE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAC,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAC,CAAA;IAEjE,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IACxE,MAAM,MAAM,GAAG,SAAS;SACrB,GAAG,CAAC,sBAAsB,CAAC;SAC3B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,2BAA2B,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;SACvE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAEjH,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;QAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,GAAG,KAAK;KACjC,CAAA;AACH,CAAC","sourcesContent":["import * as Calendar from \"expo-calendar\"\nimport {Platform} from \"react-native\"\nimport {check, PERMISSIONS, RESULTS} from \"react-native-permissions\"\n\nimport {MiniappErrorCode, type CalendarListResult} from \"@glassly/miniapp\"\n\nimport {\n calendarEventOverlapsWindow,\n type CalendarListRequest,\n normalizeCalendarEvent,\n PhoneCalendarError,\n validateCalendarListRequest,\n} from \"./PhoneCalendarHelpers\"\n\nexport {PhoneCalendarError} from \"./PhoneCalendarHelpers\"\n\ntype CalendarPermissionStatus = Awaited<\n ReturnType<typeof Calendar.getCalendarPermissionsAsync>\n>[\"status\"]\n\n/**\n * expo-calendar reports anything below EventKit full access as not granted,\n * so distinguish the states the user can actually fix. \"Reopen the miniapp\"\n * is only true while the permission is undetermined — once iOS has resolved\n * it (denied or write-only/limited), only Settings can change it.\n */\nasync function calendarPermissionMessage(status: CalendarPermissionStatus): Promise<string> {\n if (status === \"undetermined\") {\n return \"Calendar permission has not been granted yet. Reopen the miniapp and allow calendar access.\"\n }\n if (Platform.OS === \"ios\") {\n const writeOnly = await check(PERMISSIONS.IOS.CALENDARS_WRITE_ONLY).catch(() => null)\n if (writeOnly === RESULTS.GRANTED || writeOnly === RESULTS.LIMITED) {\n return 'Calendar access is limited to write-only. Enable \"Full Access\" for Glassly in Settings › Privacy & Security › Calendars.'\n }\n return \"Calendar access is denied. Enable it for Glassly in Settings › Privacy & Security › Calendars.\"\n }\n return \"Calendar access is denied. Enable the Calendar permission for Glassly in your phone's app settings.\"\n}\n\nexport async function listPhoneCalendarEvents(request: CalendarListRequest): Promise<CalendarListResult> {\n const {startsAt, endsAt, limit} = validateCalendarListRequest(request)\n const permission = await Calendar.getCalendarPermissions()\n if (permission.status !== \"granted\") {\n throw new PhoneCalendarError(MiniappErrorCode.PERMISSION_DENIED, await calendarPermissionMessage(permission.status))\n }\n\n const calendars = await Calendar.getCalendars(Calendar.EntityTypes.EVENT)\n if (calendars.length === 0) return {events: [], truncated: false}\n\n const rawEvents = await Calendar.listEvents(calendars, startsAt, endsAt)\n const events = rawEvents\n .map(normalizeCalendarEvent)\n .filter((event) => calendarEventOverlapsWindow(event, startsAt, endsAt))\n .sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || Date.parse(a.endsAt) - Date.parse(b.endsAt))\n\n return {\n events: events.slice(0, limit),\n truncated: events.length > limit,\n }\n}\n"]}
1
+ {"version":3,"file":"PhoneCalendarService.js","sourceRoot":"","sources":["../../src/services/PhoneCalendarService.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Calendar from \"expo-calendar\"\nimport {Platform} from \"react-native\"\nimport {check, PERMISSIONS, RESULTS} from \"react-native-permissions\"\n\nimport {MiniappErrorCode, type CalendarListResult} from \"@glassly/miniapp\"\n\nimport {\n calendarEventOverlapsWindow,\n type CalendarListRequest,\n normalizeCalendarEvent,\n PhoneCalendarError,\n validateCalendarListRequest,\n} from \"./PhoneCalendarHelpers\"\n\nexport {PhoneCalendarError} from \"./PhoneCalendarHelpers\"\n\ntype CalendarPermissionStatus = Awaited<\n ReturnType<typeof Calendar.getCalendarPermissionsAsync>\n>[\"status\"]\n\n/**\n * expo-calendar reports anything below EventKit full access as not granted,\n * so distinguish the states the user can actually fix. \"Reopen the miniapp\"\n * is only true while the permission is undetermined — once iOS has resolved\n * it (denied or write-only/limited), only Settings can change it.\n */\nasync function calendarPermissionMessage(status: CalendarPermissionStatus): Promise<string> {\n if (status === \"undetermined\") {\n return \"Calendar permission has not been granted yet. Reopen the miniapp and allow calendar access.\"\n }\n if (Platform.OS === \"ios\") {\n const writeOnly = await check(PERMISSIONS.IOS.CALENDARS_WRITE_ONLY).catch(() => null)\n if (writeOnly === RESULTS.GRANTED || writeOnly === RESULTS.LIMITED) {\n return 'Calendar access is limited to write-only. Enable \"Full Access\" for Glassly in Settings › Privacy & Security › Calendars.'\n }\n return \"Calendar access is denied. Enable it for Glassly in Settings › Privacy & Security › Calendars.\"\n }\n return \"Calendar access is denied. Enable the Calendar permission for Glassly in your phone's app settings.\"\n}\n\nexport async function listPhoneCalendarEvents(request: CalendarListRequest): Promise<CalendarListResult> {\n const {startsAt, endsAt, limit} = validateCalendarListRequest(request)\n const permission = await Calendar.getCalendarPermissions()\n if (permission.status !== \"granted\") {\n throw new PhoneCalendarError(MiniappErrorCode.PERMISSION_DENIED, await calendarPermissionMessage(permission.status))\n }\n\n const calendars = await Calendar.getCalendars(Calendar.EntityTypes.EVENT)\n if (calendars.length === 0) return {events: [], truncated: false}\n\n const rawEvents = await Calendar.listEvents(calendars, startsAt, endsAt)\n const seen = new Set<string>()\n const events = rawEvents\n .map(normalizeCalendarEvent)\n .filter((event) => calendarEventOverlapsWindow(event, startsAt, endsAt))\n .filter((event) => {\n const key = `${event.title}\\n${event.startsAt}\\n${event.endsAt}\\n${event.allDay}`\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n .sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || Date.parse(a.endsAt) - Date.parse(b.endsAt))\n\n return {\n events: events.slice(0, limit),\n truncated: events.length > limit,\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneNotificationsSync.d.ts","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gBAAgB,CAAA;AAC1D,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,wBAAwB,CAAA;AAUrE,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAA;AAK3C,YAAY,EAAC,iBAAiB,EAAC,CAAA;AAE/B,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAID,wBAAgB,uBAAuB,IAAI,mBAAmB,EAAE,CAG/D;AAED,uEAAuE;AACvE,wBAAgB,wBAAwB,IAAI,eAAe,EAAE,CAS5D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAClD,KAAK,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,CAAC,CACnE,CAMA;AAED,8EAA8E;AAC9E,wBAAgB,4BAA4B,IAAI,OAAO,CAGtD;AAED,gFAAgF;AAChF,wBAAgB,8BAA8B,IAAI,MAAM,EAAE,CAGzD;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,gCAAgC,IAAI,yBAAyB,CAa5E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,OAAO,GAAG,iBAAiB,CAU/E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAMtF;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAapF;AAuCD,wBAAgB,2BAA2B,IAAI,IAAI,CAgClD;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
1
+ {"version":3,"file":"PhoneNotificationsSync.d.ts","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gBAAgB,CAAA;AAC1D,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,wBAAwB,CAAA;AAUrE,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAA;AAK3C,YAAY,EAAC,iBAAiB,EAAC,CAAA;AAE/B,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAID,wBAAgB,uBAAuB,IAAI,mBAAmB,EAAE,CAG/D;AAED,uEAAuE;AACvE,wBAAgB,wBAAwB,IAAI,eAAe,EAAE,CAS5D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAClD,KAAK,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,CAAC,CACnE,CAMA;AAED,8EAA8E;AAC9E,wBAAgB,4BAA4B,IAAI,OAAO,CAGtD;AAOD,gFAAgF;AAChF,wBAAgB,8BAA8B,IAAI,MAAM,EAAE,CAGzD;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,gCAAgC,IAAI,yBAAyB,CAa5E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,OAAO,GAAG,iBAAiB,CAU/E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAMtF;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAapF;AAuCD,wBAAgB,2BAA2B,IAAI,IAAI,CAgClD;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
@@ -67,6 +67,10 @@ export function nativeNotificationsSupported() {
67
67
  const model = useGlassesStore.getState().deviceModel;
68
68
  return getModelCapabilities(model).hasNativeNotifications === true;
69
69
  }
70
+ function notificationRelaySupported() {
71
+ const model = useGlassesStore.getState().deviceModel;
72
+ return Platform.OS === "ios" && getModelCapabilities(model).hasNotificationRelay === true;
73
+ }
70
74
  /** Popup durations the firmware accepts, or an empty list when it takes any. */
71
75
  export function nativeNotificationPopupSeconds() {
72
76
  const model = useGlassesStore.getState().deviceModel;
@@ -114,12 +118,12 @@ export function currentPresentationOwner(hostActive) {
114
118
  export function captureAuthorization() {
115
119
  if (Platform.OS !== "ios")
116
120
  return "not_needed";
117
- if (!nativeNotificationsSupported())
121
+ if (!nativeNotificationsSupported() && !notificationRelaySupported())
118
122
  return "not_needed";
119
123
  const glasses = useGlassesStore.getState();
120
124
  if (!isGlassesConnected(glasses.connection))
121
125
  return "unknown";
122
- return glasses.ancsAuthorized ? "granted" : "denied";
126
+ return glasses.ancsAuthorized ? "granted" : notificationRelaySupported() ? "unknown" : "denied";
123
127
  }
124
128
  /**
125
129
  * Hand one captured notification to glasses whose firmware presents it from
@@ -148,7 +152,7 @@ function pushListenerConfig() {
148
152
  CrustModule.setNotificationConfig(listenerEnabled, Array.isArray(blocklist) ? blocklist : []).catch((err) => console.warn(`PhoneNotificationsSync: setNotificationConfig failed: ${err?.message ?? err}`));
149
153
  }
150
154
  function pushGlassesConfig() {
151
- if (!nativeNotificationsSupported())
155
+ if (!nativeNotificationsSupported() && !notificationRelaySupported())
152
156
  return;
153
157
  if (!isGlassesConnected(useGlassesStore.getState().connection))
154
158
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneNotificationsSync.js","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Phone-notifications sync — engine-owned, started by `engine.start()`. Keeps\n * three things in step with settings and the connected glasses, for any host:\n *\n * - Android: the notification listener's kill switch + per-app blocklist\n * (`CrustModule.setNotificationConfig`). The listener runs in its own\n * guarded process and is desired by default; the debug setting stays a\n * hidden emergency switch. Notify's running state gates presentation, not\n * capture.\n * - Glasses whose firmware presents notifications (`hasNativeNotifications`):\n * one `GlassesNotificationConfig` pushed through the Bluetooth SDK whenever\n * a setting, the phone's activity, the seen-apps list or the connected\n * glasses change. Drivers re-apply their last config on reconnect; the push\n * repeats on connection so a fresh driver gets it too.\n * - The seen-apps list (`notification_seen_apps`) from `notification_app_seen`:\n * glasses that read the phone's stream report each source app, which is\n * the only app enumeration iOS offers.\n *\n * Presentation is one question with one answer (`presentationOwner`); hosts\n * ask it per event instead of reasoning about models or platforms.\n */\nimport {Platform} from \"react-native\"\nimport {shallow} from \"zustand/shallow\"\nimport CrustModule from \"@glassly/crust\"\nimport type {PhoneNotificationEvent} from \"@glassly/crust\"\nimport type {GlassesNotificationConfig} from \"@glassly/bluetooth-sdk\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {NotificationAppSeenEvent} from \"@glassly/bluetooth-sdk/internal\"\n\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\nimport {DeviceTypes} from \"../types/enums\"\nimport {getModelCapabilities} from \"../types/hardware\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {notificationAppCatalog} from \"./notificationAppCatalog\"\nimport {\n glassesNotificationConfig,\n presentationOwner,\n type NotificationApp,\n type PresentationOwner,\n} from \"./notifications/notificationPolicy\"\nimport {overlayViewerRunning} from \"./pages/overlayApps\"\nimport {pagesEligible} from \"./pages/pageEligibility\"\nimport {isPhoneActive, onPhoneActivityChange} from \"./PhoneActivity\"\n\nexport type {PresentationOwner}\n\nexport interface SeenNotificationApp {\n appId: string\n displayName: string\n lastSeen: number\n}\n\nlet unsubscribers: Array<() => void> = []\n\nexport function getSeenNotificationApps(): SeenNotificationApp[] {\n const seen = useSettingsStore.getState().getSetting(SETTINGS.notification_seen_apps.key)\n return Array.isArray(seen) ? (seen as SeenNotificationApp[]) : []\n}\n\n/** Catalog ∪ seen apps, seen display names winning. Sorted by name. */\nexport function getKnownNotificationApps(): NotificationApp[] {\n const merged = new Map<string, string>()\n for (const app of notificationAppCatalog()) merged.set(app.appId, app.displayName)\n for (const app of getSeenNotificationApps()) {\n if (app.appId) merged.set(app.appId, app.displayName || app.appId)\n }\n return [...merged.entries()]\n .map(([appId, displayName]) => ({appId, displayName}))\n .sort((a, b) => a.displayName.localeCompare(b.displayName))\n}\n\n/**\n * Apps to show in a notification filter UI (package/bundle id, name, icon).\n * Android: installed apps. iOS: seeded catalog ∪ glasses-reported apps\n * (icons unavailable — iOS offers no cross-app icon lookup).\n */\nexport function installedNotificationApps(): Promise<\n Array<{packageName: string; appName: string; icon: string | null}>\n> {\n if (Platform.OS === \"android\") return CrustModule.getInstalledApps()\n if (Platform.OS !== \"ios\") return Promise.resolve([])\n return Promise.resolve(\n getKnownNotificationApps().map((app) => ({packageName: app.appId, appName: app.displayName, icon: null})),\n )\n}\n\n/** The connected (or remembered) model presents notifications in firmware. */\nexport function nativeNotificationsSupported(): boolean {\n const model = useGlassesStore.getState().deviceModel as DeviceTypes\n return getModelCapabilities(model).hasNativeNotifications === true\n}\n\n/** Popup durations the firmware accepts, or an empty list when it takes any. */\nexport function nativeNotificationPopupSeconds(): number[] {\n const model = useGlassesStore.getState().deviceModel as DeviceTypes\n return getModelCapabilities(model).nativeNotificationPopupSeconds ?? []\n}\n\nexport function notificationsEnabled(): boolean {\n return useSettingsStore.getState().getSetting(SETTINGS.notifications_enabled.key) !== false\n}\n\nexport function desiredGlassesNotificationConfig(): GlassesNotificationConfig {\n const s = useSettingsStore.getState()\n const blocklist = s.getSetting(SETTINGS.notifications_blocklist.key)\n return glassesNotificationConfig({\n enabled: notificationsEnabled(),\n popups: s.getSetting(SETTINGS.notifications_popups.key) !== false,\n popupSeconds: Number(s.getSetting(SETTINGS.notifications_popup_seconds.key)),\n holdDuringApps: s.getSetting(SETTINGS.notifications_hold_during_apps.key) !== false,\n ignoreWhileActive: s.getSetting(SETTINGS.notifications_ignore_while_active.key) === true,\n phoneActive: isPhoneActive(),\n blockedApps: Array.isArray(blocklist) ? (blocklist as string[]) : [],\n knownApps: getKnownNotificationApps(),\n })\n}\n\n/**\n * Who presents the next phone notification. `hostActive` is the host's own\n * presentation surface (Notify in the Glassly App); the engine has no view of\n * it.\n */\nexport function currentPresentationOwner(hostActive: boolean): PresentationOwner {\n return presentationOwner({\n enabled: notificationsEnabled(),\n pagesEligible: pagesEligible(),\n // The viewer is an overlay miniapp listening to phone notifications\n // (ADR-0006): with none running, nothing draws one on eligible glasses.\n viewerRunning: overlayViewerRunning(),\n nativeNotifications: nativeNotificationsSupported() && isGlassesConnected(useGlassesStore.getState().connection),\n hostActive,\n })\n}\n\n/**\n * Whether the glasses may read the phone's notifications. iOS glasses read\n * the stream themselves under the Bluetooth notification-sharing consent the\n * driver reports as `ancsAuthorized`; Android glasses get content from the\n * phone's listener, whose grant the permission flow owns.\n */\nexport function captureAuthorization(): \"granted\" | \"denied\" | \"unknown\" | \"not_needed\" {\n if (Platform.OS !== \"ios\") return \"not_needed\"\n if (!nativeNotificationsSupported()) return \"not_needed\"\n const glasses = useGlassesStore.getState()\n if (!isGlassesConnected(glasses.connection)) return \"unknown\"\n return glasses.ancsAuthorized ? \"granted\" : \"denied\"\n}\n\n/**\n * Hand one captured notification to glasses whose firmware presents it from\n * uploaded content (Android). Glasses that read the phone's stream reject it;\n * callers only reach here when the owner is `firmware`.\n */\nexport function uploadPhoneNotification(event: PhoneNotificationEvent): Promise<void> {\n if (Platform.OS !== \"android\") return Promise.resolve()\n return Promise.resolve(\n BluetoothSdk.uploadPhoneNotification({\n notificationId: String(event.notificationId ?? \"\"),\n packageName: String(event.packageName ?? \"\"),\n appName: String(event.app ?? \"\"),\n title: String(event.title ?? \"\"),\n subtitle: \"\",\n body: String(event.content ?? \"\"),\n timestampMs: Number(event.timestamp) || Date.now(),\n }),\n )\n}\n\nfunction pushListenerConfig(): void {\n if (Platform.OS !== \"android\") return\n const s = useSettingsStore.getState()\n const listenerEnabled = Boolean(s.getSetting(SETTINGS.android_notification_listener_enabled.key))\n const blocklist = s.getSetting(SETTINGS.notifications_blocklist.key)\n CrustModule.setNotificationConfig(listenerEnabled, Array.isArray(blocklist) ? blocklist : []).catch((err: unknown) =>\n console.warn(`PhoneNotificationsSync: setNotificationConfig failed: ${(err as Error)?.message ?? err}`),\n )\n}\n\nfunction pushGlassesConfig(): void {\n if (!nativeNotificationsSupported()) return\n if (!isGlassesConnected(useGlassesStore.getState().connection)) return\n void Promise.resolve(BluetoothSdk.setGlassesNotificationConfig(desiredGlassesNotificationConfig())).catch(\n (err: unknown) =>\n console.warn(`PhoneNotificationsSync: glasses notification config failed: ${(err as Error)?.message ?? err}`),\n )\n}\n\nfunction pushAll(): void {\n pushListenerConfig()\n pushGlassesConfig()\n}\n\n/** Record a glasses-reported source app. Writes only on new app / renamed app. */\nfunction recordSeenApp(event: NotificationAppSeenEvent): void {\n if (!event.appId) return\n const seen = getSeenNotificationApps()\n const existing = seen.find((app) => app.appId === event.appId)\n const displayName = event.displayName || existing?.displayName || event.appId\n if (existing && existing.displayName === displayName) return\n const next = seen\n .filter((app) => app.appId !== event.appId)\n .concat([{appId: event.appId, displayName, lastSeen: event.timestamp || Date.now()}])\n useSettingsStore.getState().setSetting(SETTINGS.notification_seen_apps.key, next)\n}\n\nexport function startPhoneNotificationsSync(): void {\n if (unsubscribers.length) return\n pushAll()\n const seenApps = BluetoothSdk.addListener(\"notification_app_seen\", recordSeenApp)\n unsubscribers = [\n useSettingsStore.subscribe(\n (state) =>\n [\n SETTINGS.android_notification_listener_enabled,\n SETTINGS.notifications_blocklist,\n SETTINGS.notification_seen_apps,\n SETTINGS.notifications_enabled,\n SETTINGS.notifications_popups,\n SETTINGS.notifications_popup_seconds,\n SETTINGS.notifications_hold_during_apps,\n SETTINGS.notifications_ignore_while_active,\n ].map((setting) => state.getSetting(setting.key)),\n pushAll,\n {equalityFn: shallow},\n ),\n // A (re)connected driver gets the current config; the SDK dedupes nothing\n // here, so only connection state and model are watched, not every field.\n useGlassesStore.subscribe(\n (state) => ({model: state.deviceModel, connected: isGlassesConnected(state.connection)}),\n pushGlassesConfig,\n {equalityFn: shallow},\n ),\n // Lock / foreground changes flip the effective popups flag for firmware\n // that pops notifications up by itself.\n onPhoneActivityChange(pushGlassesConfig),\n () => seenApps.remove(),\n ]\n}\n\nexport function stopPhoneNotificationsSync(): void {\n for (const unsubscribe of unsubscribers) unsubscribe()\n unsubscribers = []\n}\n"]}
1
+ {"version":3,"file":"PhoneNotificationsSync.js","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AACrC,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AACvC,OAAO,WAAW,MAAM,gBAAgB,CAAA;AAGxC,OAAO,YAAY,MAAM,iCAAiC,CAAA;AAG1D,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,gBAAgB,EAAE,QAAQ,EAAC,MAAM,oBAAoB,CAAA;AAE7D,OAAO,EAAC,oBAAoB,EAAC,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAC,sBAAsB,EAAC,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EACL,yBAAyB,EACzB,iBAAiB,GAGlB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAC,aAAa,EAAE,qBAAqB,EAAC,MAAM,iBAAiB,CAAA;AAUpE,IAAI,aAAa,GAAsB,EAAE,CAAA;AAEzC,MAAM,UAAU,uBAAuB;IACrC,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAA;IACxF,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAA8B,CAAC,CAAC,CAAC,EAAE,CAAA;AACnE,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,wBAAwB;IACtC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IACxC,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,WAAW,CAAC,CAAA;IAClF,KAAK,MAAM,GAAG,IAAI,uBAAuB,EAAE,EAAE,CAAC;QAC5C,IAAI,GAAG,CAAC,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACpE,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SACzB,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC,EAAC,KAAK,EAAE,WAAW,EAAC,CAAC,CAAC;SACrD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAA;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB;IAGvC,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC,gBAAgB,EAAE,CAAA;IACpE,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACrD,OAAO,OAAO,CAAC,OAAO,CACpB,wBAAwB,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAC,WAAW,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAC,CAAC,CAAC,CAC1G,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,4BAA4B;IAC1C,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,WAA0B,CAAA;IACnE,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,sBAAsB,KAAK,IAAI,CAAA;AACpE,CAAC;AAED,SAAS,0BAA0B;IACjC,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,WAA0B,CAAA;IACnE,OAAO,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,oBAAoB,CAAC,KAAK,CAAC,CAAC,oBAAoB,KAAK,IAAI,CAAA;AAC3F,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,8BAA8B;IAC5C,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,WAA0B,CAAA;IACnE,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,8BAA8B,IAAI,EAAE,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,KAAK,CAAA;AAC7F,CAAC;AAED,MAAM,UAAU,gCAAgC;IAC9C,MAAM,CAAC,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAA;IACrC,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAA;IACpE,OAAO,yBAAyB,CAAC;QAC/B,OAAO,EAAE,oBAAoB,EAAE;QAC/B,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,KAAK;QACjE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,2BAA2B,CAAC,GAAG,CAAC,CAAC;QAC5E,cAAc,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,8BAA8B,CAAC,GAAG,CAAC,KAAK,KAAK;QACnF,iBAAiB,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,iCAAiC,CAAC,GAAG,CAAC,KAAK,IAAI;QACxF,WAAW,EAAE,aAAa,EAAE;QAC5B,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAE,SAAsB,CAAC,CAAC,CAAC,EAAE;QACpE,SAAS,EAAE,wBAAwB,EAAE;KACtC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAmB;IAC1D,OAAO,iBAAiB,CAAC;QACvB,OAAO,EAAE,oBAAoB,EAAE;QAC/B,aAAa,EAAE,aAAa,EAAE;QAC9B,oEAAoE;QACpE,wEAAwE;QACxE,aAAa,EAAE,oBAAoB,EAAE;QACrC,mBAAmB,EAAE,4BAA4B,EAAE,IAAI,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC;QAChH,UAAU;KACX,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK;QAAE,OAAO,YAAY,CAAA;IAC9C,IAAI,CAAC,4BAA4B,EAAE,IAAI,CAAC,0BAA0B,EAAE;QAAE,OAAO,YAAY,CAAA;IACzF,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IAC1C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAA;IAC7D,OAAO,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,0BAA0B,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;AACjG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAA6B;IACnE,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IACvD,OAAO,OAAO,CAAC,OAAO,CACpB,YAAY,CAAC,uBAAuB,CAAC;QACnC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC;QAClD,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;QAC5C,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;QAChC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;QAChC,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;QACjC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE;KACnD,CAAC,CACH,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB;IACzB,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;QAAE,OAAM;IACrC,MAAM,CAAC,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAA;IACrC,MAAM,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,qCAAqC,CAAC,GAAG,CAAC,CAAC,CAAA;IACjG,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAA;IACpE,WAAW,CAAC,qBAAqB,CAAC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CACnH,OAAO,CAAC,IAAI,CAAC,yDAA0D,GAAa,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CACxG,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB;IACxB,IAAI,CAAC,4BAA4B,EAAE,IAAI,CAAC,0BAA0B,EAAE;QAAE,OAAM;IAC5E,IAAI,CAAC,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC;QAAE,OAAM;IACtE,KAAK,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,4BAA4B,CAAC,gCAAgC,EAAE,CAAC,CAAC,CAAC,KAAK,CACvG,CAAC,GAAY,EAAE,EAAE,CACf,OAAO,CAAC,IAAI,CAAC,+DAAgE,GAAa,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CAChH,CAAA;AACH,CAAC;AAED,SAAS,OAAO;IACd,kBAAkB,EAAE,CAAA;IACpB,iBAAiB,EAAE,CAAA;AACrB,CAAC;AAED,kFAAkF;AAClF,SAAS,aAAa,CAAC,KAA+B;IACpD,IAAI,CAAC,KAAK,CAAC,KAAK;QAAE,OAAM;IACxB,MAAM,IAAI,GAAG,uBAAuB,EAAE,CAAA;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,CAAA;IAC9D,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,QAAQ,EAAE,WAAW,IAAI,KAAK,CAAC,KAAK,CAAA;IAC7E,IAAI,QAAQ,IAAI,QAAQ,CAAC,WAAW,KAAK,WAAW;QAAE,OAAM;IAC5D,MAAM,IAAI,GAAG,IAAI;SACd,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC;SAC1C,MAAM,CAAC,CAAC,EAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAC,CAAC,CAAC,CAAA;IACvF,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;AACnF,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,IAAI,aAAa,CAAC,MAAM;QAAE,OAAM;IAChC,OAAO,EAAE,CAAA;IACT,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,CAAC,uBAAuB,EAAE,aAAa,CAAC,CAAA;IACjF,aAAa,GAAG;QACd,gBAAgB,CAAC,SAAS,CACxB,CAAC,KAAK,EAAE,EAAE,CACR;YACE,QAAQ,CAAC,qCAAqC;YAC9C,QAAQ,CAAC,uBAAuB;YAChC,QAAQ,CAAC,sBAAsB;YAC/B,QAAQ,CAAC,qBAAqB;YAC9B,QAAQ,CAAC,oBAAoB;YAC7B,QAAQ,CAAC,2BAA2B;YACpC,QAAQ,CAAC,8BAA8B;YACvC,QAAQ,CAAC,iCAAiC;SAC3C,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EACnD,OAAO,EACP,EAAC,UAAU,EAAE,OAAO,EAAC,CACtB;QACD,0EAA0E;QAC1E,yEAAyE;QACzE,eAAe,CAAC,SAAS,CACvB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,SAAS,EAAE,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAC,CAAC,EACxF,iBAAiB,EACjB,EAAC,UAAU,EAAE,OAAO,EAAC,CACtB;QACD,wEAAwE;QACxE,wCAAwC;QACxC,qBAAqB,CAAC,iBAAiB,CAAC;QACxC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE;KACxB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,KAAK,MAAM,WAAW,IAAI,aAAa;QAAE,WAAW,EAAE,CAAA;IACtD,aAAa,GAAG,EAAE,CAAA;AACpB,CAAC","sourcesContent":["/**\n * Phone-notifications sync — engine-owned, started by `engine.start()`. Keeps\n * three things in step with settings and the connected glasses, for any host:\n *\n * - Android: the notification listener's kill switch + per-app blocklist\n * (`CrustModule.setNotificationConfig`). The listener runs in its own\n * guarded process and is desired by default; the debug setting stays a\n * hidden emergency switch. Notify's running state gates presentation, not\n * capture.\n * - Glasses whose firmware presents notifications (`hasNativeNotifications`):\n * one `GlassesNotificationConfig` pushed through the Bluetooth SDK whenever\n * a setting, the phone's activity, the seen-apps list or the connected\n * glasses change. Drivers re-apply their last config on reconnect; the push\n * repeats on connection so a fresh driver gets it too.\n * - The seen-apps list (`notification_seen_apps`) from `notification_app_seen`:\n * glasses that read the phone's stream report each source app, which is\n * the only app enumeration iOS offers.\n *\n * Presentation is one question with one answer (`presentationOwner`); hosts\n * ask it per event instead of reasoning about models or platforms.\n */\nimport {Platform} from \"react-native\"\nimport {shallow} from \"zustand/shallow\"\nimport CrustModule from \"@glassly/crust\"\nimport type {PhoneNotificationEvent} from \"@glassly/crust\"\nimport type {GlassesNotificationConfig} from \"@glassly/bluetooth-sdk\"\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {NotificationAppSeenEvent} from \"@glassly/bluetooth-sdk/internal\"\n\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\nimport {DeviceTypes} from \"../types/enums\"\nimport {getModelCapabilities} from \"../types/hardware\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {notificationAppCatalog} from \"./notificationAppCatalog\"\nimport {\n glassesNotificationConfig,\n presentationOwner,\n type NotificationApp,\n type PresentationOwner,\n} from \"./notifications/notificationPolicy\"\nimport {overlayViewerRunning} from \"./pages/overlayApps\"\nimport {pagesEligible} from \"./pages/pageEligibility\"\nimport {isPhoneActive, onPhoneActivityChange} from \"./PhoneActivity\"\n\nexport type {PresentationOwner}\n\nexport interface SeenNotificationApp {\n appId: string\n displayName: string\n lastSeen: number\n}\n\nlet unsubscribers: Array<() => void> = []\n\nexport function getSeenNotificationApps(): SeenNotificationApp[] {\n const seen = useSettingsStore.getState().getSetting(SETTINGS.notification_seen_apps.key)\n return Array.isArray(seen) ? (seen as SeenNotificationApp[]) : []\n}\n\n/** Catalog ∪ seen apps, seen display names winning. Sorted by name. */\nexport function getKnownNotificationApps(): NotificationApp[] {\n const merged = new Map<string, string>()\n for (const app of notificationAppCatalog()) merged.set(app.appId, app.displayName)\n for (const app of getSeenNotificationApps()) {\n if (app.appId) merged.set(app.appId, app.displayName || app.appId)\n }\n return [...merged.entries()]\n .map(([appId, displayName]) => ({appId, displayName}))\n .sort((a, b) => a.displayName.localeCompare(b.displayName))\n}\n\n/**\n * Apps to show in a notification filter UI (package/bundle id, name, icon).\n * Android: installed apps. iOS: seeded catalog ∪ glasses-reported apps\n * (icons unavailable — iOS offers no cross-app icon lookup).\n */\nexport function installedNotificationApps(): Promise<\n Array<{packageName: string; appName: string; icon: string | null}>\n> {\n if (Platform.OS === \"android\") return CrustModule.getInstalledApps()\n if (Platform.OS !== \"ios\") return Promise.resolve([])\n return Promise.resolve(\n getKnownNotificationApps().map((app) => ({packageName: app.appId, appName: app.displayName, icon: null})),\n )\n}\n\n/** The connected (or remembered) model presents notifications in firmware. */\nexport function nativeNotificationsSupported(): boolean {\n const model = useGlassesStore.getState().deviceModel as DeviceTypes\n return getModelCapabilities(model).hasNativeNotifications === true\n}\n\nfunction notificationRelaySupported(): boolean {\n const model = useGlassesStore.getState().deviceModel as DeviceTypes\n return Platform.OS === \"ios\" && getModelCapabilities(model).hasNotificationRelay === true\n}\n\n/** Popup durations the firmware accepts, or an empty list when it takes any. */\nexport function nativeNotificationPopupSeconds(): number[] {\n const model = useGlassesStore.getState().deviceModel as DeviceTypes\n return getModelCapabilities(model).nativeNotificationPopupSeconds ?? []\n}\n\nexport function notificationsEnabled(): boolean {\n return useSettingsStore.getState().getSetting(SETTINGS.notifications_enabled.key) !== false\n}\n\nexport function desiredGlassesNotificationConfig(): GlassesNotificationConfig {\n const s = useSettingsStore.getState()\n const blocklist = s.getSetting(SETTINGS.notifications_blocklist.key)\n return glassesNotificationConfig({\n enabled: notificationsEnabled(),\n popups: s.getSetting(SETTINGS.notifications_popups.key) !== false,\n popupSeconds: Number(s.getSetting(SETTINGS.notifications_popup_seconds.key)),\n holdDuringApps: s.getSetting(SETTINGS.notifications_hold_during_apps.key) !== false,\n ignoreWhileActive: s.getSetting(SETTINGS.notifications_ignore_while_active.key) === true,\n phoneActive: isPhoneActive(),\n blockedApps: Array.isArray(blocklist) ? (blocklist as string[]) : [],\n knownApps: getKnownNotificationApps(),\n })\n}\n\n/**\n * Who presents the next phone notification. `hostActive` is the host's own\n * presentation surface (Notify in the Glassly App); the engine has no view of\n * it.\n */\nexport function currentPresentationOwner(hostActive: boolean): PresentationOwner {\n return presentationOwner({\n enabled: notificationsEnabled(),\n pagesEligible: pagesEligible(),\n // The viewer is an overlay miniapp listening to phone notifications\n // (ADR-0006): with none running, nothing draws one on eligible glasses.\n viewerRunning: overlayViewerRunning(),\n nativeNotifications: nativeNotificationsSupported() && isGlassesConnected(useGlassesStore.getState().connection),\n hostActive,\n })\n}\n\n/**\n * Whether the glasses may read the phone's notifications. iOS glasses read\n * the stream themselves under the Bluetooth notification-sharing consent the\n * driver reports as `ancsAuthorized`; Android glasses get content from the\n * phone's listener, whose grant the permission flow owns.\n */\nexport function captureAuthorization(): \"granted\" | \"denied\" | \"unknown\" | \"not_needed\" {\n if (Platform.OS !== \"ios\") return \"not_needed\"\n if (!nativeNotificationsSupported() && !notificationRelaySupported()) return \"not_needed\"\n const glasses = useGlassesStore.getState()\n if (!isGlassesConnected(glasses.connection)) return \"unknown\"\n return glasses.ancsAuthorized ? \"granted\" : notificationRelaySupported() ? \"unknown\" : \"denied\"\n}\n\n/**\n * Hand one captured notification to glasses whose firmware presents it from\n * uploaded content (Android). Glasses that read the phone's stream reject it;\n * callers only reach here when the owner is `firmware`.\n */\nexport function uploadPhoneNotification(event: PhoneNotificationEvent): Promise<void> {\n if (Platform.OS !== \"android\") return Promise.resolve()\n return Promise.resolve(\n BluetoothSdk.uploadPhoneNotification({\n notificationId: String(event.notificationId ?? \"\"),\n packageName: String(event.packageName ?? \"\"),\n appName: String(event.app ?? \"\"),\n title: String(event.title ?? \"\"),\n subtitle: \"\",\n body: String(event.content ?? \"\"),\n timestampMs: Number(event.timestamp) || Date.now(),\n }),\n )\n}\n\nfunction pushListenerConfig(): void {\n if (Platform.OS !== \"android\") return\n const s = useSettingsStore.getState()\n const listenerEnabled = Boolean(s.getSetting(SETTINGS.android_notification_listener_enabled.key))\n const blocklist = s.getSetting(SETTINGS.notifications_blocklist.key)\n CrustModule.setNotificationConfig(listenerEnabled, Array.isArray(blocklist) ? blocklist : []).catch((err: unknown) =>\n console.warn(`PhoneNotificationsSync: setNotificationConfig failed: ${(err as Error)?.message ?? err}`),\n )\n}\n\nfunction pushGlassesConfig(): void {\n if (!nativeNotificationsSupported() && !notificationRelaySupported()) return\n if (!isGlassesConnected(useGlassesStore.getState().connection)) return\n void Promise.resolve(BluetoothSdk.setGlassesNotificationConfig(desiredGlassesNotificationConfig())).catch(\n (err: unknown) =>\n console.warn(`PhoneNotificationsSync: glasses notification config failed: ${(err as Error)?.message ?? err}`),\n )\n}\n\nfunction pushAll(): void {\n pushListenerConfig()\n pushGlassesConfig()\n}\n\n/** Record a glasses-reported source app. Writes only on new app / renamed app. */\nfunction recordSeenApp(event: NotificationAppSeenEvent): void {\n if (!event.appId) return\n const seen = getSeenNotificationApps()\n const existing = seen.find((app) => app.appId === event.appId)\n const displayName = event.displayName || existing?.displayName || event.appId\n if (existing && existing.displayName === displayName) return\n const next = seen\n .filter((app) => app.appId !== event.appId)\n .concat([{appId: event.appId, displayName, lastSeen: event.timestamp || Date.now()}])\n useSettingsStore.getState().setSetting(SETTINGS.notification_seen_apps.key, next)\n}\n\nexport function startPhoneNotificationsSync(): void {\n if (unsubscribers.length) return\n pushAll()\n const seenApps = BluetoothSdk.addListener(\"notification_app_seen\", recordSeenApp)\n unsubscribers = [\n useSettingsStore.subscribe(\n (state) =>\n [\n SETTINGS.android_notification_listener_enabled,\n SETTINGS.notifications_blocklist,\n SETTINGS.notification_seen_apps,\n SETTINGS.notifications_enabled,\n SETTINGS.notifications_popups,\n SETTINGS.notifications_popup_seconds,\n SETTINGS.notifications_hold_during_apps,\n SETTINGS.notifications_ignore_while_active,\n ].map((setting) => state.getSetting(setting.key)),\n pushAll,\n {equalityFn: shallow},\n ),\n // A (re)connected driver gets the current config; the SDK dedupes nothing\n // here, so only connection state and model are watched, not every field.\n useGlassesStore.subscribe(\n (state) => ({model: state.deviceModel, connected: isGlassesConnected(state.connection)}),\n pushGlassesConfig,\n {equalityFn: shallow},\n ),\n // Lock / foreground changes flip the effective popups flag for firmware\n // that pops notifications up by itself.\n onPhoneActivityChange(pushGlassesConfig),\n () => seenApps.remove(),\n ]\n}\n\nexport function stopPhoneNotificationsSync(): void {\n for (const unsubscribe of unsubscribers) unsubscribe()\n unsubscribers = []\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"controllers.d.ts","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAE9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,UAAU,CAAA;AAGhD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAiD7E,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAErE;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEpE;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,EAAE,CAUxE;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAAG,gBAAgB,EAAE,CAE3G;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAAG,MAAM,CAKlG"}
1
+ {"version":3,"file":"controllers.d.ts","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAE9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,UAAU,CAAA;AAGhD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAkD7E,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAErE;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEpE;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,EAAE,CAUxE;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAAG,gBAAgB,EAAE,CAE3G;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAAG,MAAM,CAKlG"}
@@ -40,6 +40,7 @@ const INPUT_SURFACES = {
40
40
  [ControllerTypes.APPLE_WATCH]: [WATCH_SURFACE],
41
41
  [ControllerTypes.WEAR_OS]: [WATCH_SURFACE],
42
42
  [ControllerTypes.GAMEBRICK]: [GAMEBRICK_SURFACE],
43
+ [ControllerTypes.IINE_ANANKE]: [GAMEBRICK_SURFACE],
43
44
  [ControllerTypes.R1]: [],
44
45
  };
45
46
  /** The input surfaces a connected controller of `model` adds to the glasses'. */
@@ -1 +1 @@
1
- {"version":3,"file":"controllers.js","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Controller registry read-model: the paired records (the native-authoritative\n * `controllers` setting) and the live driver statuses (glasses status\n * `controllers`), plus what each model adds to the input surfaces. Pure.\n */\nimport type {ControllerStatus, PairedController} from \"@glassly/bluetooth-sdk\"\n\nimport type {ButtonCapabilities} from \"../types\"\nimport {ControllerTypes} from \"../types/enums\"\n\nexport type InputSurface = NonNullable<ButtonCapabilities[\"buttons\"]>[number]\n\n/** The Neural Band's thumb swipes, pinches and dial. */\nconst NEURAL_BAND_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\n \"TAP\",\n \"DOUBLE_TAP\",\n \"PRESS_HOLD\",\n \"SWIPE_UP\",\n \"SWIPE_DOWN\",\n \"SWIPE_LEFT\",\n \"SWIPE_RIGHT\",\n \"DIAL_UP\",\n \"DIAL_DOWN\",\n ],\n}\n\n/** A watch trackpad's taps and four-way swipes, and the crown (Apple) or rotary bezel (Wear OS) as the dial. */\nconst WATCH_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\n \"TAP\",\n \"DOUBLE_TAP\",\n \"PRESS_HOLD\",\n \"SWIPE_UP\",\n \"SWIPE_DOWN\",\n \"SWIPE_LEFT\",\n \"SWIPE_RIGHT\",\n \"DIAL_UP\",\n \"DIAL_DOWN\",\n ],\n}\n\n/** The Gamebrick's D-pad is four absolute swipes; A is the press lifecycle (tap and hold) and B a double tap. */\nconst GAMEBRICK_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\"TAP\", \"DOUBLE_TAP\", \"PRESS_HOLD\", \"SWIPE_UP\", \"SWIPE_DOWN\", \"SWIPE_LEFT\", \"SWIPE_RIGHT\"],\n}\n\n// The R1 swipes up/down like the temples it stands in for, so it adds nothing.\nconst INPUT_SURFACES: Readonly<Record<string, readonly InputSurface[]>> = {\n [ControllerTypes.NEURAL_BAND]: [NEURAL_BAND_SURFACE],\n [ControllerTypes.APPLE_WATCH]: [WATCH_SURFACE],\n [ControllerTypes.WEAR_OS]: [WATCH_SURFACE],\n [ControllerTypes.GAMEBRICK]: [GAMEBRICK_SURFACE],\n [ControllerTypes.R1]: [],\n}\n\n/** The input surfaces a connected controller of `model` adds to the glasses'. */\nexport function controllerInputSurfaces(model: string): InputSurface[] {\n return (INPUT_SURFACES[model] ?? []).map((surface) => ({...surface, events: [...surface.events]}))\n}\n\n/** The model swipes in two dimensions. */\nexport function hasInput2d(model: string): boolean {\n return (INPUT_SURFACES[model] ?? []).some((surface) => surface.type === \"swipe2d\")\n}\n\nexport function isControllerModel(model: string | undefined): boolean {\n return model !== undefined && (Object.values(ControllerTypes) as string[]).includes(model)\n}\n\n/** The `controllers` setting as records; malformed entries are dropped. */\nexport function pairedControllersFrom(value: unknown): PairedController[] {\n if (!Array.isArray(value)) return []\n const records: PairedController[] = []\n for (const entry of value) {\n if (!entry || typeof entry !== \"object\") continue\n const {id, model, name} = entry as Record<string, unknown>\n if (typeof id !== \"string\" || !id || typeof model !== \"string\") continue\n records.push({id, model, name: typeof name === \"string\" ? name : id})\n }\n return records\n}\n\n/** Delivering input: link up and the route or claim done. */\nexport function isControllerReady(status: ControllerStatus | undefined): boolean {\n return status?.connected === true && status.fullyBooted === true\n}\n\nexport function readyControllers(statuses: Record<string, ControllerStatus> | undefined): ControllerStatus[] {\n return Object.values(statuses ?? {}).filter(isControllerReady)\n}\n\n/** Change key over the controllers delivering input. */\nexport function readyControllersKey(statuses: Record<string, ControllerStatus> | undefined): string {\n return readyControllers(statuses)\n .map((status) => `${status.id}=${status.model}`)\n .sort()\n .join(\",\")\n}\n"]}
1
+ {"version":3,"file":"controllers.js","sourceRoot":"","sources":["../../src/services/controllers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Controller registry read-model: the paired records (the native-authoritative\n * `controllers` setting) and the live driver statuses (glasses status\n * `controllers`), plus what each model adds to the input surfaces. Pure.\n */\nimport type {ControllerStatus, PairedController} from \"@glassly/bluetooth-sdk\"\n\nimport type {ButtonCapabilities} from \"../types\"\nimport {ControllerTypes} from \"../types/enums\"\n\nexport type InputSurface = NonNullable<ButtonCapabilities[\"buttons\"]>[number]\n\n/** The Neural Band's thumb swipes, pinches and dial. */\nconst NEURAL_BAND_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\n \"TAP\",\n \"DOUBLE_TAP\",\n \"PRESS_HOLD\",\n \"SWIPE_UP\",\n \"SWIPE_DOWN\",\n \"SWIPE_LEFT\",\n \"SWIPE_RIGHT\",\n \"DIAL_UP\",\n \"DIAL_DOWN\",\n ],\n}\n\n/** A watch trackpad's taps and four-way swipes, and the crown (Apple) or rotary bezel (Wear OS) as the dial. */\nconst WATCH_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\n \"TAP\",\n \"DOUBLE_TAP\",\n \"PRESS_HOLD\",\n \"SWIPE_UP\",\n \"SWIPE_DOWN\",\n \"SWIPE_LEFT\",\n \"SWIPE_RIGHT\",\n \"DIAL_UP\",\n \"DIAL_DOWN\",\n ],\n}\n\n/** The Gamebrick's D-pad is four absolute swipes; A is the press lifecycle (tap and hold) and B a double tap. */\nconst GAMEBRICK_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\"TAP\", \"DOUBLE_TAP\", \"PRESS_HOLD\", \"SWIPE_UP\", \"SWIPE_DOWN\", \"SWIPE_LEFT\", \"SWIPE_RIGHT\"],\n}\n\n// The R1 swipes up/down like the temples it stands in for, so it adds nothing.\nconst INPUT_SURFACES: Readonly<Record<string, readonly InputSurface[]>> = {\n [ControllerTypes.NEURAL_BAND]: [NEURAL_BAND_SURFACE],\n [ControllerTypes.APPLE_WATCH]: [WATCH_SURFACE],\n [ControllerTypes.WEAR_OS]: [WATCH_SURFACE],\n [ControllerTypes.GAMEBRICK]: [GAMEBRICK_SURFACE],\n [ControllerTypes.IINE_ANANKE]: [GAMEBRICK_SURFACE],\n [ControllerTypes.R1]: [],\n}\n\n/** The input surfaces a connected controller of `model` adds to the glasses'. */\nexport function controllerInputSurfaces(model: string): InputSurface[] {\n return (INPUT_SURFACES[model] ?? []).map((surface) => ({...surface, events: [...surface.events]}))\n}\n\n/** The model swipes in two dimensions. */\nexport function hasInput2d(model: string): boolean {\n return (INPUT_SURFACES[model] ?? []).some((surface) => surface.type === \"swipe2d\")\n}\n\nexport function isControllerModel(model: string | undefined): boolean {\n return model !== undefined && (Object.values(ControllerTypes) as string[]).includes(model)\n}\n\n/** The `controllers` setting as records; malformed entries are dropped. */\nexport function pairedControllersFrom(value: unknown): PairedController[] {\n if (!Array.isArray(value)) return []\n const records: PairedController[] = []\n for (const entry of value) {\n if (!entry || typeof entry !== \"object\") continue\n const {id, model, name} = entry as Record<string, unknown>\n if (typeof id !== \"string\" || !id || typeof model !== \"string\") continue\n records.push({id, model, name: typeof name === \"string\" ? name : id})\n }\n return records\n}\n\n/** Delivering input: link up and the route or claim done. */\nexport function isControllerReady(status: ControllerStatus | undefined): boolean {\n return status?.connected === true && status.fullyBooted === true\n}\n\nexport function readyControllers(statuses: Record<string, ControllerStatus> | undefined): ControllerStatus[] {\n return Object.values(statuses ?? {}).filter(isControllerReady)\n}\n\n/** Change key over the controllers delivering input. */\nexport function readyControllersKey(statuses: Record<string, ControllerStatus> | undefined): string {\n return readyControllers(statuses)\n .map((status) => `${status.id}=${status.model}`)\n .sort()\n .join(\",\")\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pageEligibility.d.ts","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAaA,0GAA0G;AAC1G,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAExF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,GAAG,OAAO,CAMjH;AAED,wBAAgB,aAAa,IAAI,OAAO,CAOvC"}
1
+ {"version":3,"file":"pageEligibility.d.ts","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAaA,0GAA0G;AAC1G,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAKxF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,GAAG,OAAO,CAWjH;AAED,wBAAgB,aAAa,IAAI,OAAO,CAOvC"}
@@ -7,11 +7,13 @@
7
7
  */
8
8
  import { useGlassesStore } from "../../stores/glasses";
9
9
  import { SETTINGS, useSettingsStore } from "../../stores/settings";
10
+ import { DeviceTypes } from "../../types/enums";
10
11
  import { getModelCapabilities } from "../../types/hardware";
11
12
  import { isGlassesConnected } from "../GlassesReadiness";
12
13
  /** The SGC appends this marker after the firmware advertises Glassly CFW capabilities (G2, RayNeo iO). */
13
14
  export function isCfwFirmwareVersion(firmwareVersion) {
14
- return firmwareVersion?.toLowerCase().includes("glassly cfw") === true;
15
+ return (firmwareVersion?.toLowerCase().includes("glassly cfw") === true ||
16
+ /^GLASSLYCFW\/[0-9]+$/.test(firmwareVersion ?? ""));
15
17
  }
16
18
  /**
17
19
  * Only models that ship a Glassly custom firmware, and only while they run it.
@@ -21,6 +23,11 @@ export function isCfwFirmwareVersion(firmwareVersion) {
21
23
  export function isCustomDashboardEligible(info) {
22
24
  if (!info.model)
23
25
  return false;
26
+ if (info.model === DeviceTypes.MEMOMIND_ONE) {
27
+ const match = /^GLASSLYCFW\/([0-9]+)$/.exec(info.firmwareVersion ?? "");
28
+ const revision = match ? Number(match[1]) : 0;
29
+ return Number.isSafeInteger(revision) && revision >= 5;
30
+ }
24
31
  return (getModelCapabilities(info.model).hasCustomFirmware === true &&
25
32
  isCfwFirmwareVersion(info.firmwareVersion));
26
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pageEligibility.js","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AAEhE,OAAO,EAAC,oBAAoB,EAAC,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAC,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AAEtD,0GAA0G;AAC1G,MAAM,UAAU,oBAAoB,CAAC,eAA0C;IAC7E,OAAO,eAAe,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,IAAI,CAAA;AACxE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAA8D;IACtG,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAC7B,OAAO,CACL,oBAAoB,CAAC,IAAI,CAAC,KAAoB,CAAC,CAAC,iBAAiB,KAAK,IAAI;QAC1E,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,CAC3C,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,wBAAwB,CAAC,GAAG,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAA;IACzG,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IACxC,OAAO,CACL,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC;QACpC,yBAAyB,CAAC,EAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAC,CAAC,CAC9F,CAAA;AACH,CAAC","sourcesContent":["/**\n * Whether the dashboard view may show right now: connected Glassly CFW\n * glasses with the dashboard enabled. The page host reads it before showing\n * a page and hides the showing page when it turns false; the miniapp runtime\n * folds it into the designated app's `display.views` capability. Read at\n * decision time, never cached.\n */\nimport {useGlassesStore} from \"../../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../../stores/settings\"\nimport {DeviceTypes} from \"../../types/enums\"\nimport {getModelCapabilities} from \"../../types/hardware\"\nimport {isGlassesConnected} from \"../GlassesReadiness\"\n\n/** The SGC appends this marker after the firmware advertises Glassly CFW capabilities (G2, RayNeo iO). */\nexport function isCfwFirmwareVersion(firmwareVersion: string | null | undefined): boolean {\n return firmwareVersion?.toLowerCase().includes(\"glassly cfw\") === true\n}\n\n/**\n * Only models that ship a Glassly custom firmware, and only while they run it.\n * Stock firmware on those models and every other model (NIMO, G1, ...) are\n * intentionally excluded.\n */\nexport function isCustomDashboardEligible(info: {model?: string | null; firmwareVersion?: string | null}): boolean {\n if (!info.model) return false\n return (\n getModelCapabilities(info.model as DeviceTypes).hasCustomFirmware === true &&\n isCfwFirmwareVersion(info.firmwareVersion)\n )\n}\n\nexport function pagesEligible(): boolean {\n if (useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_enabled.key) === false) return false\n const state = useGlassesStore.getState()\n return (\n isGlassesConnected(state.connection) &&\n isCustomDashboardEligible({model: state.deviceModel, firmwareVersion: state.firmwareVersion})\n )\n}\n"]}
1
+ {"version":3,"file":"pageEligibility.js","sourceRoot":"","sources":["../../../src/services/pages/pageEligibility.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAC,oBAAoB,EAAC,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAC,kBAAkB,EAAC,MAAM,qBAAqB,CAAA;AAEtD,0GAA0G;AAC1G,MAAM,UAAU,oBAAoB,CAAC,eAA0C;IAC7E,OAAO,CACL,eAAe,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,IAAI;QAC/D,sBAAsB,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CACnD,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAA8D;IACtG,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAC7B,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,YAAY,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAA;QACvE,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7C,OAAO,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,CACL,oBAAoB,CAAC,IAAI,CAAC,KAAoB,CAAC,CAAC,iBAAiB,KAAK,IAAI;QAC1E,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,CAC3C,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,wBAAwB,CAAC,GAAG,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAA;IACzG,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IACxC,OAAO,CACL,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC;QACpC,yBAAyB,CAAC,EAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAC,CAAC,CAC9F,CAAA;AACH,CAAC","sourcesContent":["/**\n * Whether the dashboard view may show right now: connected Glassly CFW\n * glasses with the dashboard enabled. The page host reads it before showing\n * a page and hides the showing page when it turns false; the miniapp runtime\n * folds it into the designated app's `display.views` capability. Read at\n * decision time, never cached.\n */\nimport {useGlassesStore} from \"../../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../../stores/settings\"\nimport {DeviceTypes} from \"../../types/enums\"\nimport {getModelCapabilities} from \"../../types/hardware\"\nimport {isGlassesConnected} from \"../GlassesReadiness\"\n\n/** The SGC appends this marker after the firmware advertises Glassly CFW capabilities (G2, RayNeo iO). */\nexport function isCfwFirmwareVersion(firmwareVersion: string | null | undefined): boolean {\n return (\n firmwareVersion?.toLowerCase().includes(\"glassly cfw\") === true ||\n /^GLASSLYCFW\\/[0-9]+$/.test(firmwareVersion ?? \"\")\n )\n}\n\n/**\n * Only models that ship a Glassly custom firmware, and only while they run it.\n * Stock firmware on those models and every other model (NIMO, G1, ...) are\n * intentionally excluded.\n */\nexport function isCustomDashboardEligible(info: {model?: string | null; firmwareVersion?: string | null}): boolean {\n if (!info.model) return false\n if (info.model === DeviceTypes.MEMOMIND_ONE) {\n const match = /^GLASSLYCFW\\/([0-9]+)$/.exec(info.firmwareVersion ?? \"\")\n const revision = match ? Number(match[1]) : 0\n return Number.isSafeInteger(revision) && revision >= 5\n }\n return (\n getModelCapabilities(info.model as DeviceTypes).hasCustomFirmware === true &&\n isCfwFirmwareVersion(info.firmwareVersion)\n )\n}\n\nexport function pagesEligible(): boolean {\n if (useSettingsStore.getState().getSetting(SETTINGS.custom_dashboard_enabled.key) === false) return false\n const state = useGlassesStore.getState()\n return (\n isGlassesConnected(state.connection) &&\n isCustomDashboardEligible({model: state.deviceModel, firmwareVersion: state.firmwareVersion})\n )\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { Capabilities } from "../hardware";
2
+ export declare const memoMindOne: Capabilities;
3
+ //# sourceMappingURL=memomind-one.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memomind-one.d.ts","sourceRoot":"","sources":["../../../src/types/capabilities/memomind-one.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AAE7C,eAAO,MAAM,WAAW,EAAE,YAuCzB,CAAA"}
@@ -0,0 +1,41 @@
1
+ export const memoMindOne = {
2
+ modelName: "MemoMind One",
3
+ hasCamera: false,
4
+ camera: null,
5
+ hasDisplay: true,
6
+ display: {
7
+ count: 1,
8
+ isColor: false,
9
+ canDisplayBitmap: true,
10
+ resolution: { width: 600, height: 350 },
11
+ width: 600,
12
+ height: 350,
13
+ canPosition: true,
14
+ intensityLevels: 16,
15
+ partialUpdate: true,
16
+ adjustBrightness: true,
17
+ positionRange: { height: { min: 0, max: 8 }, depth: { min: 0, max: 8 } },
18
+ },
19
+ hasMicrophone: true,
20
+ microphone: { count: 1, hasVAD: false },
21
+ hasSpeaker: false,
22
+ speaker: null,
23
+ hasIMU: false,
24
+ imu: null,
25
+ hasButton: true,
26
+ button: {
27
+ count: 1,
28
+ buttons: [{ type: "press", events: ["TAP", "DOUBLE_TAP", "PRESS_HOLD", "RELEASE"], isCapacitive: false }],
29
+ },
30
+ hasLight: false,
31
+ light: null,
32
+ power: { hasExternalBattery: false },
33
+ hasWifi: false,
34
+ hasOta: false,
35
+ hasCustomFirmware: true,
36
+ hasNativeDashboard: false,
37
+ hasNativeNavigation: false,
38
+ hasNativeNotifications: false,
39
+ hasNotificationRelay: true,
40
+ };
41
+ //# sourceMappingURL=memomind-one.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memomind-one.js","sourceRoot":"","sources":["../../../src/types/capabilities/memomind-one.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,WAAW,GAAiB;IACvC,SAAS,EAAE,cAAc;IACzB,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,IAAI;IACZ,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE;QACP,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,gBAAgB,EAAE,IAAI;QACtB,UAAU,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC;QACrC,KAAK,EAAE,GAAG;QACV,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,IAAI;QACnB,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,EAAC,MAAM,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAC,EAAE,KAAK,EAAE,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAC,EAAC;KACnE;IACD,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,EAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAC;IACrC,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,IAAI;IACb,MAAM,EAAE,KAAK;IACb,GAAG,EAAE,IAAI;IACT,SAAS,EAAE,IAAI;IACf,MAAM,EAAE;QACN,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,YAAY,EAAE,KAAK,EAAC,CAAC;KACxG;IACD,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,EAAC,kBAAkB,EAAE,KAAK,EAAC;IAClC,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,KAAK;IACb,iBAAiB,EAAE,IAAI;IACvB,kBAAkB,EAAE,KAAK;IACzB,mBAAmB,EAAE,KAAK;IAC1B,sBAAsB,EAAE,KAAK;IAC7B,oBAAoB,EAAE,IAAI;CAC3B,CAAA","sourcesContent":["import type {Capabilities} from \"../hardware\"\n\nexport const memoMindOne: Capabilities = {\n modelName: \"MemoMind One\",\n hasCamera: false,\n camera: null,\n hasDisplay: true,\n display: {\n count: 1,\n isColor: false,\n canDisplayBitmap: true,\n resolution: {width: 600, height: 350},\n width: 600,\n height: 350,\n canPosition: true,\n intensityLevels: 16,\n partialUpdate: true,\n adjustBrightness: true,\n positionRange: {height: {min: 0, max: 8}, depth: {min: 0, max: 8}},\n },\n hasMicrophone: true,\n microphone: {count: 1, hasVAD: false},\n hasSpeaker: false,\n speaker: null,\n hasIMU: false,\n imu: null,\n hasButton: true,\n button: {\n count: 1,\n buttons: [{type: \"press\", events: [\"TAP\", \"DOUBLE_TAP\", \"PRESS_HOLD\", \"RELEASE\"], isCapacitive: false}],\n },\n hasLight: false,\n light: null,\n power: {hasExternalBattery: false},\n hasWifi: false,\n hasOta: false,\n hasCustomFirmware: true,\n hasNativeDashboard: false,\n hasNativeNavigation: false,\n hasNativeNotifications: false,\n hasNotificationRelay: true,\n}\n"]}
@@ -11,13 +11,15 @@ export declare enum DeviceTypes {
11
11
  NEX = "Mentra Display",
12
12
  NIMO = "NIMO",
13
13
  FRAME = "Brilliant Frame",
14
- RAYNEO_IO = "RayNeo iO"
14
+ RAYNEO_IO = "RayNeo iO",
15
+ MEMOMIND_ONE = "MemoMind One"
15
16
  }
16
17
  export declare enum ControllerTypes {
17
18
  R1 = "Even Realities R1",
18
19
  NEURAL_BAND = "Meta Neural Band",
19
20
  APPLE_WATCH = "Apple Watch",
20
21
  WEAR_OS = "Wear OS Watch",
21
- GAMEBRICK = "IINE Gamebrick"
22
+ GAMEBRICK = "IINE Gamebrick",
23
+ IINE_ANANKE = "IINE Ananke Gen 2"
22
24
  }
23
25
  //# sourceMappingURL=enums.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,SAAS,sBAAsB;IAC/B,IAAI,SAAS;IACb,EAAE,sBAAsB;IACxB,EAAE,sBAAsB;IACxB,IAAI,gBAAgB;IACpB,KAAK,iBAAiB;IACtB,IAAI,eAAe;IACnB,GAAG,mBAAmB;IACtB,IAAI,SAAS;IACb,KAAK,oBAAoB;IACzB,SAAS,cAAc;CACxB;AAED,oBAAY,eAAe;IACzB,EAAE,sBAAsB;IACxB,WAAW,qBAAqB;IAChC,WAAW,gBAAgB;IAC3B,OAAO,kBAAkB;IACzB,SAAS,mBAAmB;CAC7B"}
1
+ {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,SAAS,sBAAsB;IAC/B,IAAI,SAAS;IACb,EAAE,sBAAsB;IACxB,EAAE,sBAAsB;IACxB,IAAI,gBAAgB;IACpB,KAAK,iBAAiB;IACtB,IAAI,eAAe;IACnB,GAAG,mBAAmB;IACtB,IAAI,SAAS;IACb,KAAK,oBAAoB;IACzB,SAAS,cAAc;IACvB,YAAY,iBAAiB;CAC9B;AAED,oBAAY,eAAe;IACzB,EAAE,sBAAsB;IACxB,WAAW,qBAAqB;IAChC,WAAW,gBAAgB;IAC3B,OAAO,kBAAkB;IACzB,SAAS,mBAAmB;IAC5B,WAAW,sBAAsB;CAClC"}
@@ -13,6 +13,7 @@ export var DeviceTypes;
13
13
  DeviceTypes["NIMO"] = "NIMO";
14
14
  DeviceTypes["FRAME"] = "Brilliant Frame";
15
15
  DeviceTypes["RAYNEO_IO"] = "RayNeo iO";
16
+ DeviceTypes["MEMOMIND_ONE"] = "MemoMind One";
16
17
  })(DeviceTypes || (DeviceTypes = {}));
17
18
  export var ControllerTypes;
18
19
  (function (ControllerTypes) {
@@ -21,5 +22,6 @@ export var ControllerTypes;
21
22
  ControllerTypes["APPLE_WATCH"] = "Apple Watch";
22
23
  ControllerTypes["WEAR_OS"] = "Wear OS Watch";
23
24
  ControllerTypes["GAMEBRICK"] = "IINE Gamebrick";
25
+ ControllerTypes["IINE_ANANKE"] = "IINE Ananke Gen 2";
24
26
  })(ControllerTypes || (ControllerTypes = {}));
25
27
  //# sourceMappingURL=enums.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,MAAM,CAAN,IAAY,WAaX;AAbD,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,8CAA+B,CAAA;IAC/B,4BAAa,CAAA;IACb,uCAAwB,CAAA;IACxB,uCAAwB,CAAA;IACxB,mCAAoB,CAAA;IACpB,qCAAsB,CAAA;IACtB,kCAAmB,CAAA;IACnB,qCAAsB,CAAA;IACtB,4BAAa,CAAA;IACb,wCAAyB,CAAA;IACzB,sCAAuB,CAAA;AACzB,CAAC,EAbW,WAAW,KAAX,WAAW,QAatB;AAED,MAAM,CAAN,IAAY,eAMX;AAND,WAAY,eAAe;IACzB,2CAAwB,CAAA;IACxB,mDAAgC,CAAA;IAChC,8CAA2B,CAAA;IAC3B,4CAAyB,CAAA;IACzB,+CAA4B,CAAA;AAC9B,CAAC,EANW,eAAe,KAAf,eAAe,QAM1B","sourcesContent":["export {HardwareType, HardwareRequirementLevel} from \"@glassly/miniapp/hardware\"\n\nexport enum DeviceTypes {\n NONE = \"None\",\n SIMULATED = \"Simulated Glasses\",\n AR99 = \"AR99\",\n G1 = \"Even Realities G1\",\n G2 = \"Even Realities G2\",\n LIVE = \"Mentra Live\",\n MACH1 = \"Mentra Mach1\",\n Z100 = \"Vuzix Z100\",\n NEX = \"Mentra Display\",\n NIMO = \"NIMO\",\n FRAME = \"Brilliant Frame\",\n RAYNEO_IO = \"RayNeo iO\",\n}\n\nexport enum ControllerTypes {\n R1 = \"Even Realities R1\",\n NEURAL_BAND = \"Meta Neural Band\",\n APPLE_WATCH = \"Apple Watch\",\n WEAR_OS = \"Wear OS Watch\",\n GAMEBRICK = \"IINE Gamebrick\",\n}\n"]}
1
+ {"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAA;AAEhF,MAAM,CAAN,IAAY,WAcX;AAdD,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,8CAA+B,CAAA;IAC/B,4BAAa,CAAA;IACb,uCAAwB,CAAA;IACxB,uCAAwB,CAAA;IACxB,mCAAoB,CAAA;IACpB,qCAAsB,CAAA;IACtB,kCAAmB,CAAA;IACnB,qCAAsB,CAAA;IACtB,4BAAa,CAAA;IACb,wCAAyB,CAAA;IACzB,sCAAuB,CAAA;IACvB,4CAA6B,CAAA;AAC/B,CAAC,EAdW,WAAW,KAAX,WAAW,QActB;AAED,MAAM,CAAN,IAAY,eAOX;AAPD,WAAY,eAAe;IACzB,2CAAwB,CAAA;IACxB,mDAAgC,CAAA;IAChC,8CAA2B,CAAA;IAC3B,4CAAyB,CAAA;IACzB,+CAA4B,CAAA;IAC5B,oDAAiC,CAAA;AACnC,CAAC,EAPW,eAAe,KAAf,eAAe,QAO1B","sourcesContent":["export {HardwareType, HardwareRequirementLevel} from \"@glassly/miniapp/hardware\"\n\nexport enum DeviceTypes {\n NONE = \"None\",\n SIMULATED = \"Simulated Glasses\",\n AR99 = \"AR99\",\n G1 = \"Even Realities G1\",\n G2 = \"Even Realities G2\",\n LIVE = \"Mentra Live\",\n MACH1 = \"Mentra Mach1\",\n Z100 = \"Vuzix Z100\",\n NEX = \"Mentra Display\",\n NIMO = \"NIMO\",\n FRAME = \"Brilliant Frame\",\n RAYNEO_IO = \"RayNeo iO\",\n MEMOMIND_ONE = \"MemoMind One\",\n}\n\nexport enum ControllerTypes {\n R1 = \"Even Realities R1\",\n NEURAL_BAND = \"Meta Neural Band\",\n APPLE_WATCH = \"Apple Watch\",\n WEAR_OS = \"Wear OS Watch\",\n GAMEBRICK = \"IINE Gamebrick\",\n IINE_ANANKE = \"IINE Ananke Gen 2\",\n}\n"]}
@@ -8,6 +8,7 @@ import { mentraLive } from "./capabilities/mentra-live";
8
8
  import { simulatedGlasses } from "./capabilities/simulated-glasses";
9
9
  import { nimo } from "./capabilities/nimo";
10
10
  import { rayneoIO } from "./capabilities/rayneo-io";
11
+ import { memoMindOne } from "./capabilities/memomind-one";
11
12
  import { vuzixZ100 } from "./capabilities/vuzix-z100";
12
13
  import { DeviceTypes } from "./enums";
13
14
  export type { HardwareRequirement } from "@glassly/miniapp/hardware";
@@ -172,6 +173,7 @@ export interface Capabilities {
172
173
  hasNativeDashboard?: boolean;
173
174
  hasNativeNavigation?: boolean;
174
175
  hasNativeNotifications?: boolean;
176
+ hasNotificationRelay?: boolean;
175
177
  nativeNotificationPopupSeconds?: number[];
176
178
  }
177
179
  /**
@@ -181,5 +183,5 @@ export interface Capabilities {
181
183
  */
182
184
  export declare const HARDWARE_CAPABILITIES: Record<string, Capabilities>;
183
185
  export declare const getModelCapabilities: (deviceType: DeviceTypes) => Capabilities;
184
- export { simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, nimo, rayneoIO };
186
+ export { simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, nimo, rayneoIO, memoMindOne, };
185
187
  //# sourceMappingURL=hardware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hardware.d.ts","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hardware.d.ts","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { mentraLive } from "./capabilities/mentra-live";
8
8
  import { simulatedGlasses } from "./capabilities/simulated-glasses";
9
9
  import { nimo } from "./capabilities/nimo";
10
10
  import { rayneoIO } from "./capabilities/rayneo-io";
11
+ import { memoMindOne } from "./capabilities/memomind-one";
11
12
  import { vuzixZ100 } from "./capabilities/vuzix-z100";
12
13
  import { none } from "./capabilities/none";
13
14
  import { DeviceTypes } from "./enums";
@@ -27,6 +28,7 @@ export const HARDWARE_CAPABILITIES = {
27
28
  [vuzixZ100.modelName]: vuzixZ100,
28
29
  [nimo.modelName]: nimo,
29
30
  [rayneoIO.modelName]: rayneoIO,
31
+ [memoMindOne.modelName]: memoMindOne,
30
32
  [DeviceTypes.MACH1]: vuzixZ100, // Mach1 uses same Vuzix Ultralite hardware as Z100
31
33
  [DeviceTypes.AR99]: {
32
34
  ...evenRealitiesG1,
@@ -54,5 +56,5 @@ export const getModelCapabilities = (deviceType) => {
54
56
  return HARDWARE_CAPABILITIES[modelName];
55
57
  };
56
58
  // export * from "./capabilities"
57
- export { simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, nimo, rayneoIO };
59
+ export { simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, nimo, rayneoIO, memoMindOne, };
58
60
  //# sourceMappingURL=hardware.js.map