@glassly/engine 0.1.0-dev.62 → 0.1.0-dev.90

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 (85) hide show
  1. package/README.md +1 -1
  2. package/build/engine.d.ts +7 -2
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +3 -0
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/display.d.ts +10 -3
  7. package/build/facades/display.d.ts.map +1 -1
  8. package/build/facades/display.js +11 -1
  9. package/build/facades/display.js.map +1 -1
  10. package/build/facades/displayMirror.d.ts +2 -6
  11. package/build/facades/displayMirror.d.ts.map +1 -1
  12. package/build/facades/displayMirror.js +7 -14
  13. package/build/facades/displayMirror.js.map +1 -1
  14. package/build/facades/glasses.d.ts +1 -0
  15. package/build/facades/glasses.d.ts.map +1 -1
  16. package/build/facades/glasses.js +2 -0
  17. package/build/facades/glasses.js.map +1 -1
  18. package/build/facades/phoneNotifications.d.ts +12 -2
  19. package/build/facades/phoneNotifications.d.ts.map +1 -1
  20. package/build/facades/phoneNotifications.js +30 -10
  21. package/build/facades/phoneNotifications.js.map +1 -1
  22. package/build/generated/releaseMetadata.js +3 -3
  23. package/build/generated/releaseMetadata.js.map +1 -1
  24. package/build/services/CustomDashboardService.d.ts +11 -10
  25. package/build/services/CustomDashboardService.d.ts.map +1 -1
  26. package/build/services/CustomDashboardService.js +70 -69
  27. package/build/services/CustomDashboardService.js.map +1 -1
  28. package/build/services/DashboardCoordinator.d.ts +26 -0
  29. package/build/services/DashboardCoordinator.d.ts.map +1 -0
  30. package/build/services/DashboardCoordinator.js +125 -0
  31. package/build/services/DashboardCoordinator.js.map +1 -0
  32. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  33. package/build/services/DeviceEventRouter.js +5 -7
  34. package/build/services/DeviceEventRouter.js.map +1 -1
  35. package/build/services/LocalDisplayManager.d.ts +10 -0
  36. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  37. package/build/services/LocalDisplayManager.js +66 -12
  38. package/build/services/LocalDisplayManager.js.map +1 -1
  39. package/build/services/PhoneNotificationsSync.d.ts +13 -0
  40. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  41. package/build/services/PhoneNotificationsSync.js +101 -9
  42. package/build/services/PhoneNotificationsSync.js.map +1 -1
  43. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  44. package/build/services/PhoneStreamCoordinator.js +9 -6
  45. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  46. package/build/services/SceneRenderer.d.ts.map +1 -1
  47. package/build/services/SceneRenderer.js +5 -0
  48. package/build/services/SceneRenderer.js.map +1 -1
  49. package/build/services/effectiveCapabilities.d.ts +1 -1
  50. package/build/services/effectiveCapabilities.d.ts.map +1 -1
  51. package/build/services/effectiveCapabilities.js +9 -4
  52. package/build/services/effectiveCapabilities.js.map +1 -1
  53. package/build/services/iosNotificationAppCatalog.d.ts +13 -0
  54. package/build/services/iosNotificationAppCatalog.d.ts.map +1 -0
  55. package/build/services/iosNotificationAppCatalog.js +28 -0
  56. package/build/services/iosNotificationAppCatalog.js.map +1 -0
  57. package/build/stores/display.d.ts +4 -4
  58. package/build/stores/display.d.ts.map +1 -1
  59. package/build/stores/display.js +12 -36
  60. package/build/stores/display.js.map +1 -1
  61. package/build/stores/glasses.d.ts.map +1 -1
  62. package/build/stores/glasses.js +2 -0
  63. package/build/stores/glasses.js.map +1 -1
  64. package/build/stores/settings.d.ts.map +1 -1
  65. package/build/stores/settings.js +53 -2
  66. package/build/stores/settings.js.map +1 -1
  67. package/package.json +7 -7
  68. package/src/engine.ts +3 -0
  69. package/src/facades/display.ts +11 -1
  70. package/src/facades/displayMirror.ts +7 -15
  71. package/src/facades/glasses.ts +2 -0
  72. package/src/facades/phoneNotifications.ts +32 -10
  73. package/src/generated/releaseMetadata.ts +3 -3
  74. package/src/services/CustomDashboardService.ts +67 -66
  75. package/src/services/DashboardCoordinator.ts +121 -0
  76. package/src/services/DeviceEventRouter.ts +5 -7
  77. package/src/services/LocalDisplayManager.ts +66 -14
  78. package/src/services/PhoneNotificationsSync.ts +125 -9
  79. package/src/services/PhoneStreamCoordinator.ts +10 -6
  80. package/src/services/SceneRenderer.ts +4 -0
  81. package/src/services/effectiveCapabilities.ts +9 -4
  82. package/src/services/iosNotificationAppCatalog.ts +39 -0
  83. package/src/stores/display.ts +16 -48
  84. package/src/stores/glasses.ts +2 -0
  85. package/src/stores/settings.ts +53 -2
@@ -23,34 +23,22 @@ function extractDisplayText(displayEvent) {
23
23
  }
24
24
  /** Coalesce rapid caption updates (~3.3 Hz) into one store write per window. */
25
25
  const DISPLAY_COALESCE_MS = 150;
26
- function applyDisplayEvent(set, get, event) {
27
- const currentView = get().view;
28
- const targetBucket = event.view === "dashboard" ? "dashboardEvent" : "mainEvent";
29
- const updates = {
30
- [targetBucket]: event,
31
- };
32
- // also update the current event if the view is the same:
33
- if (event.view === currentView) {
34
- updates.currentEvent = event;
35
- }
36
- const visibleEvent = (updates.currentEvent ?? event);
37
- const textLines = extractDisplayText(visibleEvent);
26
+ function applyDisplayEvent(set, event) {
27
+ const textLines = extractDisplayText(event);
38
28
  if (textLines.some((line) => line.trim() !== "")) {
39
29
  logE2EMetric("display_store_update", {
40
- view: visibleEvent.view ?? currentView,
41
- layout_type: visibleEvent.layout?.layoutType ?? "",
30
+ view: event.view ?? "main",
31
+ layout_type: event.layout?.layoutType ?? "",
42
32
  text_lines: textLines,
43
33
  });
44
34
  }
45
- set(updates);
35
+ set({ currentEvent: event });
46
36
  }
47
37
  let pendingDisplayEvent = null;
48
38
  let displayCoalesceTimer = null;
49
- export const useDisplayStore = create()(subscribeWithSelector((set, get) => ({
39
+ export const useDisplayStore = create()(subscribeWithSelector((set) => ({
50
40
  currentEvent: {},
51
- dashboardEvent: {},
52
- mainEvent: {},
53
- view: "main",
41
+ dashboardActive: false,
54
42
  setDisplayEvent: (event) => {
55
43
  const parsed = typeof event === "string" ? JSON.parse(event) : event;
56
44
  pendingDisplayEvent = parsed;
@@ -61,24 +49,12 @@ export const useDisplayStore = create()(subscribeWithSelector((set, get) => ({
61
49
  const next = pendingDisplayEvent;
62
50
  pendingDisplayEvent = null;
63
51
  if (next)
64
- applyDisplayEvent(set, get, next);
52
+ applyDisplayEvent(set, next);
65
53
  }, DISPLAY_COALESCE_MS);
66
54
  },
67
- setView: (view) => {
68
- const currentView = get().view;
69
- if (view === currentView) {
70
- return;
71
- }
72
- // update the view and the currentEvent with the corresponding event:
73
- let newEvent;
74
- if (view === "dashboard") {
75
- newEvent = get().dashboardEvent;
76
- }
77
- else {
78
- newEvent = get().mainEvent;
79
- }
80
- logE2EMetric("display_view_changed", { view });
81
- set({ view, currentEvent: newEvent });
55
+ setDashboardActive: (active) => {
56
+ logE2EMetric("display_view_changed", { view: active ? "dashboard" : "main" });
57
+ set({ dashboardActive: active });
82
58
  },
83
59
  })));
84
60
  /** Test helper — flush any pending coalesced display update immediately. */
@@ -90,7 +66,7 @@ export function flushDisplayCoalesceForTests() {
90
66
  if (pendingDisplayEvent) {
91
67
  const event = pendingDisplayEvent;
92
68
  pendingDisplayEvent = null;
93
- applyDisplayEvent(useDisplayStore.setState, useDisplayStore.getState, event);
69
+ applyDisplayEvent(useDisplayStore.setState, event);
94
70
  }
95
71
  }
96
72
  //# sourceMappingURL=display.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/stores/display.ts"],"names":[],"mappings":"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","sourcesContent":["import {create} from \"zustand\"\nimport {subscribeWithSelector} from \"zustand/middleware\"\n\nimport {BgTimer} from \"../utils/timers\"\nimport {logE2EMetric} from \"../utils/e2eMetrics\"\n\nfunction extractDisplayText(displayEvent: any): string[] {\n const layout = displayEvent?.layout\n if (!layout || typeof layout !== \"object\") {\n return []\n }\n switch (layout.layoutType) {\n case \"text_wall\":\n case \"text_line\":\n return typeof layout.text === \"string\" ? layout.text.split(\"\\n\") : []\n case \"double_text_wall\":\n return [layout.topText, layout.bottomText].filter((value): value is string => typeof value === \"string\")\n case \"text_rows\":\n return Array.isArray(layout.text)\n ? layout.text.filter((value: unknown): value is string => typeof value === \"string\")\n : []\n default:\n return []\n }\n}\n\n/** Coalesce rapid caption updates (~3.3 Hz) into one store write per window. */\nconst DISPLAY_COALESCE_MS = 150\n\ntype DisplayEvent = Record<string, unknown> & {view?: string}\n\nfunction applyDisplayEvent(\n set: (partial: Partial<DisplayStore>) => void,\n get: () => DisplayStore,\n event: DisplayEvent,\n) {\n const currentView = get().view\n const targetBucket = event.view === \"dashboard\" ? \"dashboardEvent\" : \"mainEvent\"\n\n const updates: Partial<DisplayStore> = {\n [targetBucket]: event,\n }\n\n // also update the current event if the view is the same:\n if (event.view === currentView) {\n updates.currentEvent = event\n }\n\n const visibleEvent = (updates.currentEvent ?? event) as DisplayEvent\n const textLines = extractDisplayText(visibleEvent)\n if (textLines.some((line) => line.trim() !== \"\")) {\n logE2EMetric(\"display_store_update\", {\n view: visibleEvent.view ?? currentView,\n layout_type: (visibleEvent.layout as {layoutType?: string} | undefined)?.layoutType ?? \"\",\n text_lines: textLines,\n })\n }\n\n set(updates)\n}\n\nexport interface DisplayStore {\n currentEvent: any\n dashboardEvent: any\n mainEvent: any\n setDisplayEvent: (event: DisplayEvent | string) => void\n view: string\n setView: (view: string) => void\n}\n\nlet pendingDisplayEvent: DisplayEvent | null = null\nlet displayCoalesceTimer: ReturnType<typeof BgTimer.setTimeout> | null = null\n\nexport const useDisplayStore = create<DisplayStore>()(\n subscribeWithSelector((set, get) => ({\n currentEvent: {} as any,\n dashboardEvent: {} as any,\n mainEvent: {} as any,\n view: \"main\",\n setDisplayEvent: (event: DisplayEvent | string) => {\n const parsed: DisplayEvent = typeof event === \"string\" ? JSON.parse(event) : event\n pendingDisplayEvent = parsed\n if (displayCoalesceTimer) return\n displayCoalesceTimer = BgTimer.setTimeout(() => {\n displayCoalesceTimer = null\n const next = pendingDisplayEvent\n pendingDisplayEvent = null\n if (next) applyDisplayEvent(set, get, next)\n }, DISPLAY_COALESCE_MS)\n },\n setView: (view: string) => {\n const currentView = get().view\n if (view === currentView) {\n return\n }\n\n // update the view and the currentEvent with the corresponding event:\n let newEvent\n if (view === \"dashboard\") {\n newEvent = get().dashboardEvent\n } else {\n newEvent = get().mainEvent\n }\n logE2EMetric(\"display_view_changed\", {view})\n set({view, currentEvent: newEvent})\n },\n })),\n)\n\n/** Test helper — flush any pending coalesced display update immediately. */\nexport function flushDisplayCoalesceForTests(): void {\n if (displayCoalesceTimer) {\n BgTimer.clearTimeout(displayCoalesceTimer)\n displayCoalesceTimer = null\n }\n if (pendingDisplayEvent) {\n const event = pendingDisplayEvent\n pendingDisplayEvent = null\n applyDisplayEvent(useDisplayStore.setState, useDisplayStore.getState, event)\n }\n}\n"]}
1
+ {"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/stores/display.ts"],"names":[],"mappings":"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","sourcesContent":["import {create} from \"zustand\"\nimport {subscribeWithSelector} from \"zustand/middleware\"\n\nimport {BgTimer} from \"../utils/timers\"\nimport {logE2EMetric} from \"../utils/e2eMetrics\"\n\nfunction extractDisplayText(displayEvent: any): string[] {\n const layout = displayEvent?.layout\n if (!layout || typeof layout !== \"object\") {\n return []\n }\n switch (layout.layoutType) {\n case \"text_wall\":\n case \"text_line\":\n return typeof layout.text === \"string\" ? layout.text.split(\"\\n\") : []\n case \"double_text_wall\":\n return [layout.topText, layout.bottomText].filter((value): value is string => typeof value === \"string\")\n case \"text_rows\":\n return Array.isArray(layout.text)\n ? layout.text.filter((value: unknown): value is string => typeof value === \"string\")\n : []\n default:\n return []\n }\n}\n\n/** Coalesce rapid caption updates (~3.3 Hz) into one store write per window. */\nconst DISPLAY_COALESCE_MS = 150\n\ntype DisplayEvent = Record<string, unknown> & {view?: string}\n\nfunction applyDisplayEvent(set: (partial: Partial<DisplayStore>) => void, event: DisplayEvent) {\n const textLines = extractDisplayText(event)\n if (textLines.some((line) => line.trim() !== \"\")) {\n logE2EMetric(\"display_store_update\", {\n view: event.view ?? \"main\",\n layout_type: (event.layout as {layoutType?: string} | undefined)?.layoutType ?? \"\",\n text_lines: textLines,\n })\n }\n set({currentEvent: event})\n}\n\nexport interface DisplayStore {\n /** The frame currently on the glasses — the mirror renders this. */\n currentEvent: any\n /** True while the dashboard covers the main content (see DashboardCoordinator). */\n dashboardActive: boolean\n setDisplayEvent: (event: DisplayEvent | string) => void\n setDashboardActive: (active: boolean) => void\n}\n\nlet pendingDisplayEvent: DisplayEvent | null = null\nlet displayCoalesceTimer: ReturnType<typeof BgTimer.setTimeout> | null = null\n\nexport const useDisplayStore = create<DisplayStore>()(\n subscribeWithSelector((set) => ({\n currentEvent: {} as any,\n dashboardActive: false,\n setDisplayEvent: (event: DisplayEvent | string) => {\n const parsed: DisplayEvent = typeof event === \"string\" ? JSON.parse(event) : event\n pendingDisplayEvent = parsed\n if (displayCoalesceTimer) return\n displayCoalesceTimer = BgTimer.setTimeout(() => {\n displayCoalesceTimer = null\n const next = pendingDisplayEvent\n pendingDisplayEvent = null\n if (next) applyDisplayEvent(set, next)\n }, DISPLAY_COALESCE_MS)\n },\n setDashboardActive: (active: boolean) => {\n logE2EMetric(\"display_view_changed\", {view: active ? \"dashboard\" : \"main\"})\n set({dashboardActive: active})\n },\n })),\n)\n\n/** Test helper — flush any pending coalesced display update immediately. */\nexport function flushDisplayCoalesceForTests(): void {\n if (displayCoalesceTimer) {\n BgTimer.clearTimeout(displayCoalesceTimer)\n displayCoalesceTimer = null\n }\n if (pendingDisplayEvent) {\n const event = pendingDisplayEvent\n pendingDisplayEvent = null\n applyDisplayEvent(useDisplayStore.setState, event)\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EAEvB,WAAW,EACX,SAAS,EACT,aAAa,EACb,mBAAmB,EAEpB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAE,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAMvG,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAE,cAAc,EAAC,CAAA;AAEnE,eAAO,MAAM,sBAAsB,GAAI,OAAO;IAAC,UAAU,EAAE,uBAAuB,CAAA;CAAC,YAC7C,CAAA;AAEtC,eAAO,MAAM,kBAAkB,GAAI,OAAO;IAAC,UAAU,EAAE,uBAAuB,CAAA;CAAC,YAAqC,CAAA;AAEpH,KAAK,mBAAmB,GAAG,mBAAmB,GAAG;IAC/C,kBAAkB,EAAE,aAAa,GAAG,IAAI,CAAA;IACxC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;IAC/B,aAAa,EAAE,OAAO,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,WAAW,YAAa,SAAQ,mBAAmB;IACvD,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACjD,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,KAAK,IAAI,CAAA;IAClH,WAAW,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAEtF,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IAC3B,YAAY,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAA;IAChD,qBAAqB,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,CAAA;IAC3D,cAAc,EAAE,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IACtD,gBAAgB,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAA;IAC/C,wBAAwB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACpD,aAAa,EAAE,MAAM,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,qBAAqB,EAAE,OAAO,CAAA;CAC/B;AAED,KAAK,gBAAgB,GAAG;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,KAAK,mBAAmB,GAAG;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,KAAK,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAAG,gBAAgB,GAAG,mBAAmB,CAAA;AA2B9F,eAAO,MAAM,oBAAoB,GAAI,OAAO,mBAAmB;;;;;;;;;;CAgB9D,CAAA;AAkED,eAAO,MAAM,eAAe;;;;;;;;EAsH3B,CAAA;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,MAAM,YAAY,EAC9D,KAAK,CAAC,EACN,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,EAC9C,kBAAgB,KACf,OAAO,CAAC,OAAO,CAuBjB,CAAA"}
@@ -99,6 +99,8 @@ const initialState = {
99
99
  controllerMacAddress: "",
100
100
  controllerBatteryLevel: -1,
101
101
  controllerSignalStrength: -1,
102
+ // iOS ANCS consent (G2 consumes notifications on-firmware):
103
+ ancsAuthorized: false,
102
104
  };
103
105
  export const useGlassesStore = create()(subscribeWithSelector((set) => ({
104
106
  ...initialState,
@@ -1 +1 @@
1
- {"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n GlassesConnectionStatus,\n HotspotStatus,\n OtaProgress,\n OtaStatus,\n OtaUpdateInfo,\n PublicGlassesStatus,\n WifiStatus,\n} from \"@glassly/bluetooth-sdk\"\nimport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {create} from \"zustand\"\nimport {subscribeWithSelector} from \"zustand/middleware\"\n\n// Re-export the connection predicates (single source of truth lives in\n// GlassesReadiness) so importers of this store keep getting them.\nexport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady}\n\nexport const selectGlassesConnected = (state: {connection: GlassesConnectionStatus}) =>\n isGlassesConnected(state.connection)\n\nexport const selectGlassesReady = (state: {connection: GlassesConnectionStatus}) => isGlassesReady(state.connection)\n\ntype EngineGlassesStatus = PublicGlassesStatus & {\n otaUpdateAvailable: OtaUpdateInfo | null\n otaProgress: OtaProgress | null\n otaInProgress: boolean\n otaVersionUrl: string\n}\n\nexport interface GlassesState extends EngineGlassesStatus {\n systemTimeMs: number\n wifiStatusKnown: boolean\n setGlassesInfo: (info: GlassesInfoUpdate) => void\n setBatteryInfo: (batteryLevel: number, charging: boolean, caseBatteryLevel: number, caseCharging: boolean) => void\n setWifiInfo: (connected: boolean, ssid: string) => void\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) => void\n // OTA methods\n otaStatus: OtaStatus | null\n setOtaStatus: (status: OtaStatus | null) => void\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => void\n setOtaProgress: (progress: OtaProgress | null) => void\n setOtaInProgress: (inProgress: boolean) => void\n setMtkUpdatedThisSession: (updated: boolean) => void\n clearOtaState: () => void\n reset: () => void\n mtkUpdatedThisSession: boolean\n}\n\ntype LegacyWifiFields = {\n wifiConnected?: boolean\n wifiSsid?: string\n wifiLocalIp?: string\n}\n\ntype LegacyHotspotFields = {\n hotspotEnabled?: boolean\n hotspotSsid?: string\n hotspotPassword?: string\n hotspotGatewayIp?: string\n hotspotLocalIp?: string\n}\n\ntype GlassesInfoUpdate = Partial<EngineGlassesStatus> & LegacyWifiFields & LegacyHotspotFields\n\nfunction wifiFromLegacyFields(info: LegacyWifiFields): WifiStatus | null {\n if (info.wifiConnected === true) {\n const ssid = info.wifiSsid?.trim()\n const localIp = info.wifiLocalIp?.trim()\n return ssid ? {state: \"connected\", ssid, ...(localIp ? {localIp} : {})} : null\n }\n if (info.wifiConnected === false) {\n return {state: \"disconnected\"}\n }\n return null\n}\n\nfunction hotspotFromLegacyFields(info: LegacyHotspotFields): HotspotStatus | null {\n if (info.hotspotEnabled === true) {\n const ssid = info.hotspotSsid?.trim()\n const password = info.hotspotPassword?.trim()\n const localIp = (info.hotspotGatewayIp ?? info.hotspotLocalIp)?.trim()\n return ssid && password && localIp ? {state: \"enabled\", ssid, password, localIp} : null\n }\n if (info.hotspotEnabled === false) {\n return {state: \"disabled\"}\n }\n return null\n}\n\nexport const getGlasesInfoPartial = (state: EngineGlassesStatus) => {\n const wifi = state.wifi\n const connected = isGlassesConnected(state.connection)\n return {\n batteryLevel: state.batteryLevel,\n charging: state.charging,\n caseBatteryLevel: state.caseBatteryLevel,\n caseCharging: state.caseCharging,\n connected,\n wifiConnected: wifi.state === \"connected\",\n wifiSsid: wifi.state === \"connected\" ? wifi.ssid : \"\",\n deviceModel: state.deviceModel,\n // Cloud GlassesInfo uses modelName, map from deviceModel so the cloud\n // knows which device is connected when it receives connection state updates\n modelName: state.deviceModel || null,\n }\n}\n\ninterface GlassesStore extends EngineGlassesStatus {\n systemTimeMs: number\n mtkUpdatedThisSession: boolean\n wifiStatusKnown: boolean\n otaStatus: OtaStatus | null\n}\n\nconst initialState: GlassesStore = {\n // state:\n connection: {state: \"disconnected\"},\n micEnabled: false,\n bluetoothClassicConnected: false,\n signalStrength: -1,\n signalStrengthUpdatedAt: 0,\n voiceActivityDetectionEnabled: true,\n // device info\n deviceModel: \"\",\n androidVersion: \"\",\n firmwareVersion: \"\",\n bluetoothMacAddress: \"\",\n wifiMacAddress: \"\",\n leftMacAddress: \"\",\n rightMacAddress: \"\",\n buildNumber: \"\",\n systemTimeMs: 0,\n otaVersionUrl: \"\",\n appVersion: \"\",\n bluetoothName: \"\",\n serialNumber: \"\",\n style: \"\",\n color: \"\",\n mtkFirmwareVersion: \"\",\n besFirmwareVersion: \"\",\n hotspotOtaVersion: 0,\n sceneCanvasWidth: 0,\n sceneCanvasHeight: 0,\n // wifi info\n wifi: {state: \"disconnected\"},\n wifiStatusKnown: false,\n // battery info\n batteryLevel: -1,\n charging: false,\n caseBatteryLevel: -1,\n caseCharging: false,\n caseOpen: false,\n caseRemoved: true,\n // hotspot info\n hotspot: {state: \"disabled\"},\n // OTA update info\n otaStatus: null,\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n mtkUpdatedThisSession: false,\n // ring:\n controllerConnected: false,\n controllerFullyBooted: false,\n controllerMacAddress: \"\",\n controllerBatteryLevel: -1,\n controllerSignalStrength: -1,\n}\n\nexport const useGlassesStore = create<GlassesState>()(\n subscribeWithSelector((set) => ({\n ...initialState,\n\n setGlassesInfo: (info) =>\n set((state) => {\n const {\n wifiConnected,\n wifiSsid,\n wifiLocalIp,\n hotspotEnabled,\n hotspotSsid,\n hotspotPassword,\n hotspotGatewayIp,\n hotspotLocalIp,\n wifi,\n hotspot,\n ...sdkInfo\n } = info\n const wifiUpdate = wifi ?? wifiFromLegacyFields({wifiConnected, wifiSsid, wifiLocalIp})\n const hotspotUpdate =\n hotspot ??\n hotspotFromLegacyFields({hotspotEnabled, hotspotSsid, hotspotPassword, hotspotGatewayIp, hotspotLocalIp})\n const hasWifiInfoUpdate =\n Object.prototype.hasOwnProperty.call(info, \"wifi\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiConnected\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiSsid\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiLocalIp\")\n const next = {\n ...state,\n ...sdkInfo,\n ...(wifiUpdate ? {wifi: wifiUpdate} : {}),\n ...(hotspotUpdate ? {hotspot: hotspotUpdate} : {}),\n ...(hasWifiInfoUpdate ? {wifiStatusKnown: true} : {}),\n }\n if (!isGlassesConnected(next.connection)) {\n next.wifiStatusKnown = false\n next.hotspotOtaVersion = 0\n }\n return next\n }),\n\n setBatteryInfo: (batteryLevel, charging, caseBatteryLevel, caseCharging) =>\n set({\n batteryLevel,\n charging,\n caseBatteryLevel,\n caseCharging,\n }),\n\n setWifiInfo: (connected, ssid) =>\n set(() => {\n const trimmedSsid = ssid.trim()\n if (connected && !trimmedSsid) {\n return {}\n }\n const wifi: WifiStatus = connected ? {state: \"connected\", ssid: trimmedSsid} : {state: \"disconnected\"}\n return {\n wifi,\n wifiStatusKnown: true,\n }\n }),\n\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) =>\n set(() => {\n const hotspot = hotspotFromLegacyFields({\n hotspotEnabled: enabled,\n hotspotSsid: ssid,\n hotspotPassword: password,\n hotspotGatewayIp: ip,\n })\n return hotspot ? {hotspot} : {}\n }),\n\n // OTA methods\n setOtaStatus: (status: OtaStatus | null) => set({otaStatus: status}),\n\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => set({otaUpdateAvailable: info}),\n\n setOtaProgress: (progress: OtaProgress | null) =>\n set((state) => {\n const otaInProgress = progress !== null && progress.status !== \"FINISHED\" && progress.status !== \"FAILED\"\n console.log(\"🔍 GLASSES STORE: setOtaProgress called with:\", JSON.stringify(progress))\n console.log(\"🔍 GLASSES STORE: otaInProgress =\", otaInProgress)\n\n // Never allow progress to regress within the same stage+currentUpdate — except a new\n // work wave (STARTED) or the step after FINISHED (multi-hop APK: 27→31→36 re-downloads from 0).\n const prev = state.otaProgress\n const sameWave =\n progress &&\n prev &&\n progress.stage === prev.stage &&\n progress.currentUpdate === prev.currentUpdate &&\n progress.progress < prev.progress\n if (sameWave) {\n const nextIsNewWave = progress.status === \"STARTED\" || prev.status === \"FINISHED\"\n if (!nextIsNewWave) {\n return {otaProgress: {...progress, progress: prev.progress}, otaInProgress}\n }\n }\n\n return {otaProgress: progress, otaInProgress}\n }),\n\n setOtaInProgress: (inProgress: boolean) => set({otaInProgress: inProgress}),\n\n setMtkUpdatedThisSession: (updated: boolean) => set({mtkUpdatedThisSession: updated}),\n\n clearOtaState: () =>\n set({\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n // Note: mtkUpdatedThisSession is NOT cleared here - it stays true until glasses disconnect/reboot\n }),\n\n reset: () => set(initialState),\n })),\n)\n\nexport function getGlassesSystemTimeMs(): number {\n return useGlassesStore.getState().systemTimeMs ?? 0\n}\n\nexport const waitForGlassesState = <K extends keyof GlassesState>(\n key: K,\n predicate: (value: GlassesState[K]) => boolean,\n timeoutMs = 1000,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n const state = useGlassesStore.getState()\n if (predicate(state[key])) {\n resolve(true)\n return\n }\n\n const unsubscribe = useGlassesStore.subscribe(\n (s) => s[key],\n (value) => {\n if (predicate(value)) {\n unsubscribe()\n resolve(true)\n }\n },\n )\n\n setTimeout(() => {\n unsubscribe()\n resolve(predicate(useGlassesStore.getState()[key]))\n }, timeoutMs)\n })\n}\n"]}
1
+ {"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n GlassesConnectionStatus,\n HotspotStatus,\n OtaProgress,\n OtaStatus,\n OtaUpdateInfo,\n PublicGlassesStatus,\n WifiStatus,\n} from \"@glassly/bluetooth-sdk\"\nimport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {create} from \"zustand\"\nimport {subscribeWithSelector} from \"zustand/middleware\"\n\n// Re-export the connection predicates (single source of truth lives in\n// GlassesReadiness) so importers of this store keep getting them.\nexport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady}\n\nexport const selectGlassesConnected = (state: {connection: GlassesConnectionStatus}) =>\n isGlassesConnected(state.connection)\n\nexport const selectGlassesReady = (state: {connection: GlassesConnectionStatus}) => isGlassesReady(state.connection)\n\ntype EngineGlassesStatus = PublicGlassesStatus & {\n otaUpdateAvailable: OtaUpdateInfo | null\n otaProgress: OtaProgress | null\n otaInProgress: boolean\n otaVersionUrl: string\n}\n\nexport interface GlassesState extends EngineGlassesStatus {\n systemTimeMs: number\n wifiStatusKnown: boolean\n setGlassesInfo: (info: GlassesInfoUpdate) => void\n setBatteryInfo: (batteryLevel: number, charging: boolean, caseBatteryLevel: number, caseCharging: boolean) => void\n setWifiInfo: (connected: boolean, ssid: string) => void\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) => void\n // OTA methods\n otaStatus: OtaStatus | null\n setOtaStatus: (status: OtaStatus | null) => void\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => void\n setOtaProgress: (progress: OtaProgress | null) => void\n setOtaInProgress: (inProgress: boolean) => void\n setMtkUpdatedThisSession: (updated: boolean) => void\n clearOtaState: () => void\n reset: () => void\n mtkUpdatedThisSession: boolean\n}\n\ntype LegacyWifiFields = {\n wifiConnected?: boolean\n wifiSsid?: string\n wifiLocalIp?: string\n}\n\ntype LegacyHotspotFields = {\n hotspotEnabled?: boolean\n hotspotSsid?: string\n hotspotPassword?: string\n hotspotGatewayIp?: string\n hotspotLocalIp?: string\n}\n\ntype GlassesInfoUpdate = Partial<EngineGlassesStatus> & LegacyWifiFields & LegacyHotspotFields\n\nfunction wifiFromLegacyFields(info: LegacyWifiFields): WifiStatus | null {\n if (info.wifiConnected === true) {\n const ssid = info.wifiSsid?.trim()\n const localIp = info.wifiLocalIp?.trim()\n return ssid ? {state: \"connected\", ssid, ...(localIp ? {localIp} : {})} : null\n }\n if (info.wifiConnected === false) {\n return {state: \"disconnected\"}\n }\n return null\n}\n\nfunction hotspotFromLegacyFields(info: LegacyHotspotFields): HotspotStatus | null {\n if (info.hotspotEnabled === true) {\n const ssid = info.hotspotSsid?.trim()\n const password = info.hotspotPassword?.trim()\n const localIp = (info.hotspotGatewayIp ?? info.hotspotLocalIp)?.trim()\n return ssid && password && localIp ? {state: \"enabled\", ssid, password, localIp} : null\n }\n if (info.hotspotEnabled === false) {\n return {state: \"disabled\"}\n }\n return null\n}\n\nexport const getGlasesInfoPartial = (state: EngineGlassesStatus) => {\n const wifi = state.wifi\n const connected = isGlassesConnected(state.connection)\n return {\n batteryLevel: state.batteryLevel,\n charging: state.charging,\n caseBatteryLevel: state.caseBatteryLevel,\n caseCharging: state.caseCharging,\n connected,\n wifiConnected: wifi.state === \"connected\",\n wifiSsid: wifi.state === \"connected\" ? wifi.ssid : \"\",\n deviceModel: state.deviceModel,\n // Cloud GlassesInfo uses modelName, map from deviceModel so the cloud\n // knows which device is connected when it receives connection state updates\n modelName: state.deviceModel || null,\n }\n}\n\ninterface GlassesStore extends EngineGlassesStatus {\n systemTimeMs: number\n mtkUpdatedThisSession: boolean\n wifiStatusKnown: boolean\n otaStatus: OtaStatus | null\n}\n\nconst initialState: GlassesStore = {\n // state:\n connection: {state: \"disconnected\"},\n micEnabled: false,\n bluetoothClassicConnected: false,\n signalStrength: -1,\n signalStrengthUpdatedAt: 0,\n voiceActivityDetectionEnabled: true,\n // device info\n deviceModel: \"\",\n androidVersion: \"\",\n firmwareVersion: \"\",\n bluetoothMacAddress: \"\",\n wifiMacAddress: \"\",\n leftMacAddress: \"\",\n rightMacAddress: \"\",\n buildNumber: \"\",\n systemTimeMs: 0,\n otaVersionUrl: \"\",\n appVersion: \"\",\n bluetoothName: \"\",\n serialNumber: \"\",\n style: \"\",\n color: \"\",\n mtkFirmwareVersion: \"\",\n besFirmwareVersion: \"\",\n hotspotOtaVersion: 0,\n sceneCanvasWidth: 0,\n sceneCanvasHeight: 0,\n // wifi info\n wifi: {state: \"disconnected\"},\n wifiStatusKnown: false,\n // battery info\n batteryLevel: -1,\n charging: false,\n caseBatteryLevel: -1,\n caseCharging: false,\n caseOpen: false,\n caseRemoved: true,\n // hotspot info\n hotspot: {state: \"disabled\"},\n // OTA update info\n otaStatus: null,\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n mtkUpdatedThisSession: false,\n // ring:\n controllerConnected: false,\n controllerFullyBooted: false,\n controllerMacAddress: \"\",\n controllerBatteryLevel: -1,\n controllerSignalStrength: -1,\n // iOS ANCS consent (G2 consumes notifications on-firmware):\n ancsAuthorized: false,\n}\n\nexport const useGlassesStore = create<GlassesState>()(\n subscribeWithSelector((set) => ({\n ...initialState,\n\n setGlassesInfo: (info) =>\n set((state) => {\n const {\n wifiConnected,\n wifiSsid,\n wifiLocalIp,\n hotspotEnabled,\n hotspotSsid,\n hotspotPassword,\n hotspotGatewayIp,\n hotspotLocalIp,\n wifi,\n hotspot,\n ...sdkInfo\n } = info\n const wifiUpdate = wifi ?? wifiFromLegacyFields({wifiConnected, wifiSsid, wifiLocalIp})\n const hotspotUpdate =\n hotspot ??\n hotspotFromLegacyFields({hotspotEnabled, hotspotSsid, hotspotPassword, hotspotGatewayIp, hotspotLocalIp})\n const hasWifiInfoUpdate =\n Object.prototype.hasOwnProperty.call(info, \"wifi\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiConnected\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiSsid\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiLocalIp\")\n const next = {\n ...state,\n ...sdkInfo,\n ...(wifiUpdate ? {wifi: wifiUpdate} : {}),\n ...(hotspotUpdate ? {hotspot: hotspotUpdate} : {}),\n ...(hasWifiInfoUpdate ? {wifiStatusKnown: true} : {}),\n }\n if (!isGlassesConnected(next.connection)) {\n next.wifiStatusKnown = false\n next.hotspotOtaVersion = 0\n }\n return next\n }),\n\n setBatteryInfo: (batteryLevel, charging, caseBatteryLevel, caseCharging) =>\n set({\n batteryLevel,\n charging,\n caseBatteryLevel,\n caseCharging,\n }),\n\n setWifiInfo: (connected, ssid) =>\n set(() => {\n const trimmedSsid = ssid.trim()\n if (connected && !trimmedSsid) {\n return {}\n }\n const wifi: WifiStatus = connected ? {state: \"connected\", ssid: trimmedSsid} : {state: \"disconnected\"}\n return {\n wifi,\n wifiStatusKnown: true,\n }\n }),\n\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) =>\n set(() => {\n const hotspot = hotspotFromLegacyFields({\n hotspotEnabled: enabled,\n hotspotSsid: ssid,\n hotspotPassword: password,\n hotspotGatewayIp: ip,\n })\n return hotspot ? {hotspot} : {}\n }),\n\n // OTA methods\n setOtaStatus: (status: OtaStatus | null) => set({otaStatus: status}),\n\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => set({otaUpdateAvailable: info}),\n\n setOtaProgress: (progress: OtaProgress | null) =>\n set((state) => {\n const otaInProgress = progress !== null && progress.status !== \"FINISHED\" && progress.status !== \"FAILED\"\n console.log(\"🔍 GLASSES STORE: setOtaProgress called with:\", JSON.stringify(progress))\n console.log(\"🔍 GLASSES STORE: otaInProgress =\", otaInProgress)\n\n // Never allow progress to regress within the same stage+currentUpdate — except a new\n // work wave (STARTED) or the step after FINISHED (multi-hop APK: 27→31→36 re-downloads from 0).\n const prev = state.otaProgress\n const sameWave =\n progress &&\n prev &&\n progress.stage === prev.stage &&\n progress.currentUpdate === prev.currentUpdate &&\n progress.progress < prev.progress\n if (sameWave) {\n const nextIsNewWave = progress.status === \"STARTED\" || prev.status === \"FINISHED\"\n if (!nextIsNewWave) {\n return {otaProgress: {...progress, progress: prev.progress}, otaInProgress}\n }\n }\n\n return {otaProgress: progress, otaInProgress}\n }),\n\n setOtaInProgress: (inProgress: boolean) => set({otaInProgress: inProgress}),\n\n setMtkUpdatedThisSession: (updated: boolean) => set({mtkUpdatedThisSession: updated}),\n\n clearOtaState: () =>\n set({\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n // Note: mtkUpdatedThisSession is NOT cleared here - it stays true until glasses disconnect/reboot\n }),\n\n reset: () => set(initialState),\n })),\n)\n\nexport function getGlassesSystemTimeMs(): number {\n return useGlassesStore.getState().systemTimeMs ?? 0\n}\n\nexport const waitForGlassesState = <K extends keyof GlassesState>(\n key: K,\n predicate: (value: GlassesState[K]) => boolean,\n timeoutMs = 1000,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n const state = useGlassesStore.getState()\n if (predicate(state[key])) {\n resolve(true)\n return\n }\n\n const unsubscribe = useGlassesStore.subscribe(\n (s) => s[key],\n (value) => {\n if (predicate(value)) {\n unsubscribe()\n resolve(true)\n }\n },\n )\n\n setTimeout(() => {\n unsubscribe()\n resolve(predicate(useGlassesStore.getState()[key]))\n }, timeoutMs)\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/stores/settings.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAwB,MAAM,aAAa,CAAA;AAS9D,OAAO,EAAC,wBAAwB,EAAE,gCAAgC,EAAC,MAAM,wBAAwB,CAAA;AAEjG,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,GAAG,CAAA;IACvB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IAGrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAEjC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAA;IAEpB,OAAO,EAAE,OAAO,CAAA;IAQhB,mBAAmB,CAAC,EAAE,IAAI,CAAA;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,EAAE,IAAI,CAAA;CAC7B;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CA42BnC,CAAA;AAEV,eAAO,MAAM,eAAe,EAAE,MAAM,EAAuD,CAAA;AAI3F,eAAO,MAAM,sBAAsB,EAAE,MAAM,EAkD1C,CAAA;AAeD,eAAO,MAAM,qBAAqB,EAAE,MAAM,EAEV,CAAA;AAIhC,MAAM,WAAW,aAAa;IAE5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAE7B,aAAa,EAAE,OAAO,CAAA;IAEtB,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACzF,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC3E,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAA;IAEhC,eAAe,EAAE,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAE/C,oBAAoB,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/C,uBAAuB,EAAE,MAAM,IAAI,CAAA;CACpC;AAuBD,eAAO,MAAM,gBAAgB;;;;;;;;EA+U5B,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,GAAG,EAAE,KAAK,MAAM,KAAG,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAI3F,CAAA"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/stores/settings.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAwB,MAAM,aAAa,CAAA;AAS9D,OAAO,EAAC,wBAAwB,EAAE,gCAAgC,EAAC,MAAM,wBAAwB,CAAA;AAEjG,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,GAAG,CAAA;IACvB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IAGrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAEjC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAA;IAEpB,OAAO,EAAE,OAAO,CAAA;IAQhB,mBAAmB,CAAC,EAAE,IAAI,CAAA;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,EAAE,IAAI,CAAA;CAC7B;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CA85BnC,CAAA;AAEV,eAAO,MAAM,eAAe,EAAE,MAAM,EAAuD,CAAA;AAI3F,eAAO,MAAM,sBAAsB,EAAE,MAAM,EAmD1C,CAAA;AAeD,eAAO,MAAM,qBAAqB,EAAE,MAAM,EAEV,CAAA;AAIhC,MAAM,WAAW,aAAa;IAE5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAE7B,aAAa,EAAE,OAAO,CAAA;IAEtB,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACzF,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC3E,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAA;IAEhC,eAAe,EAAE,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAE/C,oBAAoB,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/C,uBAAuB,EAAE,MAAM,IAAI,CAAA;CACpC;AAuBD,eAAO,MAAM,gBAAgB;;;;;;;;EA+U5B,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,GAAG,EAAE,KAAK,MAAM,KAAG,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAI3F,CAAA"}
@@ -98,6 +98,15 @@ export const SETTINGS = {
98
98
  saveOnServer: true,
99
99
  persist: true,
100
100
  },
101
+ // Tint the glasses display mirror text with the green chart color instead of
102
+ // the theme foreground. Toggled from Super Settings.
103
+ mirror_green_text: {
104
+ key: "mirror_green_text",
105
+ defaultValue: () => true,
106
+ writable: true,
107
+ saveOnServer: true,
108
+ persist: true,
109
+ },
101
110
  debug_navigation_history: {
102
111
  key: "debug_navigation_history",
103
112
  defaultValue: () => false,
@@ -857,6 +866,47 @@ export const SETTINGS = {
857
866
  saveOnServer: true,
858
867
  persist: true,
859
868
  },
869
+ // iOS: apps observed posting notifications, reported by the glasses' ANCS
870
+ // client ({appId, displayName, lastSeen}[]). Backs the filter UI, since iOS
871
+ // has no installed-apps enumeration. Device-local by design.
872
+ ios_notification_seen_apps: {
873
+ key: "ios_notification_seen_apps",
874
+ defaultValue: () => [],
875
+ writable: true,
876
+ saveOnServer: false,
877
+ persist: true,
878
+ },
879
+ // G2 on-glass notification display behavior (NotificationControl):
880
+ g2_notifications_enabled: {
881
+ key: "g2_notifications_enabled",
882
+ defaultValue: () => true,
883
+ writable: true,
884
+ saveOnServer: true,
885
+ persist: true,
886
+ },
887
+ g2_notification_auto_display: {
888
+ key: "g2_notification_auto_display",
889
+ defaultValue: () => true,
890
+ writable: true,
891
+ saveOnServer: true,
892
+ persist: true,
893
+ },
894
+ // Popup duration in seconds; firmware accepts 0 (default), 5, 7, 10.
895
+ g2_notification_disp_time: {
896
+ key: "g2_notification_disp_time",
897
+ defaultValue: () => 5,
898
+ writable: true,
899
+ saveOnServer: true,
900
+ persist: true,
901
+ },
902
+ // Suppress popups while another on-glass feature (navigation, teleprompter) is active.
903
+ g2_notification_avoid_disturb: {
904
+ key: "g2_notification_avoid_disturb",
905
+ defaultValue: () => true,
906
+ writable: true,
907
+ saveOnServer: true,
908
+ persist: true,
909
+ },
860
910
  // Cached required version from server - used to enforce updates even when offline
861
911
  cached_required_version: {
862
912
  key: "cached_required_version",
@@ -896,8 +946,8 @@ export const BLUETOOTH_SETTING_KEYS = [
896
946
  SETTINGS.screen_disabled.key,
897
947
  SETTINGS.auth_email.key,
898
948
  SETTINGS.core_token.key,
899
- // glasses settings:
900
- SETTINGS.contextual_dashboard.key,
949
+ // glasses settings (contextual_dashboard is phone-side policy now — native
950
+ // no longer reads it):
901
951
  SETTINGS.head_up_angle.key,
902
952
  SETTINGS.brightness.key,
903
953
  SETTINGS.auto_brightness.key,
@@ -905,6 +955,7 @@ export const BLUETOOTH_SETTING_KEYS = [
905
955
  SETTINGS.dashboard_depth.key,
906
956
  SETTINGS.menu_apps.key,
907
957
  SETTINGS.calendar_events.key,
958
+ // G2 native reads this to gate rendering while the ER firmware dashboard is open.
908
959
  SETTINGS.use_native_dashboard.key,
909
960
  SETTINGS.cfw_scene_renderer.key,
910
961
  SETTINGS.twelve_hour_time.key,