@glassly/engine 0.3.2-beta.572 → 0.3.2-beta.602

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 (151) hide show
  1. package/README.md +1 -1
  2. package/build/engine.d.ts +15 -1
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +7 -3
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/dev.d.ts +2 -2
  7. package/build/facades/dev.d.ts.map +1 -1
  8. package/build/facades/dev.js +2 -2
  9. package/build/facades/dev.js.map +1 -1
  10. package/build/facades/glasses.d.ts +20 -0
  11. package/build/facades/glasses.d.ts.map +1 -1
  12. package/build/facades/glasses.js +21 -0
  13. package/build/facades/glasses.js.map +1 -1
  14. package/build/facades/pairing.js +1 -1
  15. package/build/facades/pairing.js.map +1 -1
  16. package/build/facades/reports.js +2 -2
  17. package/build/facades/reports.js.map +1 -1
  18. package/build/facades/session.d.ts +1 -1
  19. package/build/facades/session.js +2 -2
  20. package/build/facades/session.js.map +1 -1
  21. package/build/generated/releaseMetadata.js +3 -3
  22. package/build/generated/releaseMetadata.js.map +1 -1
  23. package/build/index.d.ts +3 -1
  24. package/build/index.d.ts.map +1 -1
  25. package/build/index.js +3 -1
  26. package/build/index.js.map +1 -1
  27. package/build/runtime/bootstrap.d.ts +2 -2
  28. package/build/runtime/bootstrap.d.ts.map +1 -1
  29. package/build/runtime/bootstrap.js.map +1 -1
  30. package/build/services/CloudAudioSubscriptionSync.d.ts +2 -2
  31. package/build/services/CloudAudioSubscriptionSync.js +2 -2
  32. package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
  33. package/build/services/CloudClientService.d.ts +2 -2
  34. package/build/services/CloudClientService.d.ts.map +1 -1
  35. package/build/services/CloudClientService.js +4 -4
  36. package/build/services/CloudClientService.js.map +1 -1
  37. package/build/services/CloudTranscriptE2EMetrics.d.ts +2 -2
  38. package/build/services/CloudTranscriptE2EMetrics.d.ts.map +1 -1
  39. package/build/services/CloudTranscriptE2EMetrics.js +3 -3
  40. package/build/services/CloudTranscriptE2EMetrics.js.map +1 -1
  41. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  42. package/build/services/DeviceEventRouter.js +5 -0
  43. package/build/services/DeviceEventRouter.js.map +1 -1
  44. package/build/services/GestureDotsService.d.ts +9 -9
  45. package/build/services/GestureDotsService.d.ts.map +1 -1
  46. package/build/services/GestureDotsService.js +75 -50
  47. package/build/services/GestureDotsService.js.map +1 -1
  48. package/build/services/GlassesTouchInput.d.ts +1 -1
  49. package/build/services/GlassesTouchInput.d.ts.map +1 -1
  50. package/build/services/GlassesTouchInput.js +7 -4
  51. package/build/services/GlassesTouchInput.js.map +1 -1
  52. package/build/services/LocalMiniappRuntime.d.ts +1 -1
  53. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  54. package/build/services/LocalMiniappRuntime.js +32 -8
  55. package/build/services/LocalMiniappRuntime.js.map +1 -1
  56. package/build/services/LocalSttFallbackCoordinator.d.ts +5 -2
  57. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  58. package/build/services/LocalSttFallbackCoordinator.js +7 -3
  59. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  60. package/build/services/PhonePhotoCoordinator.d.ts +1 -1
  61. package/build/services/PhonePhotoCoordinator.js +4 -4
  62. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  63. package/build/services/PhoneStreamCoordinator.d.ts +1 -1
  64. package/build/services/PhoneStreamCoordinator.js +1 -1
  65. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  66. package/build/services/SupportProfileSync.d.ts +1 -1
  67. package/build/services/SupportProfileSync.js +2 -2
  68. package/build/services/SupportProfileSync.js.map +1 -1
  69. package/build/services/TranscriptionRouting.d.ts +6 -1
  70. package/build/services/TranscriptionRouting.d.ts.map +1 -1
  71. package/build/services/TranscriptionRouting.js +7 -3
  72. package/build/services/TranscriptionRouting.js.map +1 -1
  73. package/build/services/cloudStreamApi.d.ts +2 -2
  74. package/build/services/cloudStreamApi.js +2 -2
  75. package/build/services/cloudStreamApi.js.map +1 -1
  76. package/build/services/gestureDotGlyphs.d.ts +1 -1
  77. package/build/services/gestureDotGlyphs.d.ts.map +1 -1
  78. package/build/services/gestureDotGlyphs.js +5 -0
  79. package/build/services/gestureDotGlyphs.js.map +1 -1
  80. package/build/services/neuralBand/MetaAuth.d.ts +31 -0
  81. package/build/services/neuralBand/MetaAuth.d.ts.map +1 -0
  82. package/build/services/neuralBand/MetaAuth.js +161 -0
  83. package/build/services/neuralBand/MetaAuth.js.map +1 -0
  84. package/build/services/neuralBand/NeuralBandInput.d.ts +50 -0
  85. package/build/services/neuralBand/NeuralBandInput.d.ts.map +1 -0
  86. package/build/services/neuralBand/NeuralBandInput.js +114 -0
  87. package/build/services/neuralBand/NeuralBandInput.js.map +1 -0
  88. package/build/services/neuralBand/NeuralBandSettingsSync.d.ts +3 -0
  89. package/build/services/neuralBand/NeuralBandSettingsSync.d.ts.map +1 -0
  90. package/build/services/neuralBand/NeuralBandSettingsSync.js +52 -0
  91. package/build/services/neuralBand/NeuralBandSettingsSync.js.map +1 -0
  92. package/build/services/neuralBand/sha256.d.ts +7 -0
  93. package/build/services/neuralBand/sha256.d.ts.map +1 -0
  94. package/build/services/neuralBand/sha256.js +68 -0
  95. package/build/services/neuralBand/sha256.js.map +1 -0
  96. package/build/services/settingsBridge.d.ts +12 -4
  97. package/build/services/settingsBridge.d.ts.map +1 -1
  98. package/build/services/settingsBridge.js +13 -4
  99. package/build/services/settingsBridge.js.map +1 -1
  100. package/build/services/soniox/SonioxRealtimeClient.d.ts +2 -2
  101. package/build/services/soniox/SonioxRealtimeClient.js +2 -2
  102. package/build/services/soniox/SonioxRealtimeClient.js.map +1 -1
  103. package/build/services/soniox/SonioxTokenAssembler.d.ts +1 -1
  104. package/build/services/soniox/SonioxTokenAssembler.js +1 -1
  105. package/build/services/soniox/SonioxTokenAssembler.js.map +1 -1
  106. package/build/stores/cloudClientStatus.js +1 -1
  107. package/build/stores/cloudClientStatus.js.map +1 -1
  108. package/build/stores/settings.d.ts.map +1 -1
  109. package/build/stores/settings.js +25 -7
  110. package/build/stores/settings.js.map +1 -1
  111. package/build/types/enums.d.ts +2 -1
  112. package/build/types/enums.d.ts.map +1 -1
  113. package/build/types/enums.js +1 -0
  114. package/build/types/enums.js.map +1 -1
  115. package/build/utils/cloudClient/cloudSecureStore.js +2 -2
  116. package/build/utils/cloudClient/cloudSecureStore.js.map +1 -1
  117. package/package.json +7 -7
  118. package/src/engine.ts +7 -3
  119. package/src/facades/dev.ts +2 -2
  120. package/src/facades/glasses.ts +27 -0
  121. package/src/facades/pairing.ts +1 -1
  122. package/src/facades/reports.ts +2 -2
  123. package/src/facades/session.ts +2 -2
  124. package/src/generated/releaseMetadata.ts +3 -3
  125. package/src/index.ts +15 -1
  126. package/src/runtime/bootstrap.ts +2 -2
  127. package/src/services/CloudAudioSubscriptionSync.ts +2 -2
  128. package/src/services/CloudClientService.ts +4 -4
  129. package/src/services/CloudTranscriptE2EMetrics.ts +3 -3
  130. package/src/services/DeviceEventRouter.ts +5 -0
  131. package/src/services/GestureDotsService.ts +72 -49
  132. package/src/services/GlassesTouchInput.ts +13 -5
  133. package/src/services/LocalMiniappRuntime.ts +52 -24
  134. package/src/services/LocalSttFallbackCoordinator.ts +18 -4
  135. package/src/services/PhonePhotoCoordinator.ts +4 -4
  136. package/src/services/PhoneStreamCoordinator.ts +1 -1
  137. package/src/services/SupportProfileSync.ts +2 -2
  138. package/src/services/TranscriptionRouting.ts +12 -4
  139. package/src/services/cloudStreamApi.ts +3 -3
  140. package/src/services/gestureDotGlyphs.ts +6 -1
  141. package/src/services/neuralBand/MetaAuth.ts +192 -0
  142. package/src/services/neuralBand/NeuralBandInput.ts +148 -0
  143. package/src/services/neuralBand/NeuralBandSettingsSync.ts +63 -0
  144. package/src/services/neuralBand/sha256.ts +70 -0
  145. package/src/services/settingsBridge.ts +15 -4
  146. package/src/services/soniox/SonioxRealtimeClient.ts +2 -2
  147. package/src/services/soniox/SonioxTokenAssembler.ts +1 -1
  148. package/src/stores/cloudClientStatus.ts +1 -1
  149. package/src/stores/settings.ts +25 -7
  150. package/src/types/enums.ts +1 -0
  151. package/src/utils/cloudClient/cloudSecureStore.ts +2 -2
@@ -0,0 +1,31 @@
1
+ export declare class MetaAuthError extends Error {
2
+ }
3
+ export interface MetaSsoTokens {
4
+ nativeSsoToken: string;
5
+ etoken: string;
6
+ }
7
+ export interface MetaSession {
8
+ accessToken: string;
9
+ userId: string;
10
+ }
11
+ export declare function authEntryUrl(tokens: MetaSsoTokens): string;
12
+ /** The redirect that ends the sign-in: a custom scheme, or the frl_login marker on https. */
13
+ export declare function isLoginCallback(url: string): boolean;
14
+ export declare function callbackQuery(url: string): {
15
+ token?: string;
16
+ blob?: string;
17
+ };
18
+ /** The first 16 hex digits of the request token's SHA-256, which the callback must present. */
19
+ export declare function expectedCallbackToken(nativeSsoToken: string): string;
20
+ export declare function callbackMatches(token: string | undefined, nativeSsoToken: string): boolean;
21
+ export declare function makeLsd(): string;
22
+ export declare function jazoest(lsd: string): string;
23
+ export declare function multipartBody(fields: [string, string][], boundary: string): string;
24
+ export declare function urlForm(fields: [string, string][]): string;
25
+ /** Step 1: the anonymous WebView tokens query. */
26
+ export declare function tokensQuery(): Promise<MetaSsoTokens>;
27
+ /** Step 3: decrypt the login blob into the FRL access token. */
28
+ export declare function decryptBlob(blob: string, requestToken: string): Promise<string>;
29
+ /** Step 4: exchange the FRL token for the ar user session. */
30
+ export declare function login(frlAccessToken: string): Promise<MetaSession>;
31
+ //# sourceMappingURL=MetaAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetaAuth.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/MetaAuth.ts"],"names":[],"mappings":"AAsBA,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAE3C,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAE1D;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,CAS1E;AAED,+FAA+F;AAC/F,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAM1F;AAED,wBAAgB,OAAO,IAAI,MAAM,CAIhC;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMlF;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,MAAM,CAE1D;AAqCD,kDAAkD;AAClD,wBAAsB,WAAW,IAAI,OAAO,CAAC,aAAa,CAAC,CAa1D;AAED,gEAAgE;AAChE,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYrF;AAUD,8DAA8D;AAC9D,wBAAsB,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAyBxE"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * The Meta account sign-in a Neural Band claim needs, ported from callbacked/kinesis
3
+ * (MIT, Copyright (c) 2026 callbacked): Sources/Kinesis/MetaAuth.swift.
4
+ *
5
+ * The chain: an anonymous tokens query, a WebView sign-in on auth.meta.com whose
6
+ * fb-viewapp://…frl_login redirect carries a token and a blob, a blob decrypt, and
7
+ * the ar-genai exchange that yields the user session. The session then goes to the
8
+ * native Bluetooth SDK (keychain / keystore), which runs the ownership exchanges;
9
+ * nothing here persists it.
10
+ */
11
+ import { sha256Hex } from "./sha256";
12
+ // Meta's own client identifiers, the same in every copy of Meta's apps. They name the
13
+ // app that asks, not a person. They are not secrets, and no account or session is in them.
14
+ const FRL_CLIENT = "FRL|388177446008673|083800dd7efbbd42eab18c9886d79c18";
15
+ const AR_CLIENT = "AR|306760944872162|a919421a55a8ea18080ab2f10f57f1be";
16
+ const TOKENS_QUERY_URL = "https://meta.graph.meta.com/webview_tokens_query";
17
+ const BLOBS_DECRYPT_URL = "https://meta.graph.meta.com/webview_blobs_decrypt";
18
+ const LOGIN_URL = "https://ar-genai.graph.meta.com/login";
19
+ const AUTH_ENTRY_BASE = "https://auth.meta.com/?native_app_id=388177446008673&source_app_id=388177446008673&native_sso_etoken=";
20
+ export class MetaAuthError extends Error {
21
+ }
22
+ export function authEntryUrl(tokens) {
23
+ return AUTH_ENTRY_BASE + encodeURIComponent(tokens.etoken);
24
+ }
25
+ /** The redirect that ends the sign-in: a custom scheme, or the frl_login marker on https. */
26
+ export function isLoginCallback(url) {
27
+ return url.toLowerCase().startsWith("fb-viewapp:") || url.includes("frl_login");
28
+ }
29
+ export function callbackQuery(url) {
30
+ const query = url.includes("?") ? url.slice(url.indexOf("?") + 1) : "";
31
+ const out = {};
32
+ for (const pair of query.split("&")) {
33
+ const [key, value = ""] = pair.split("=");
34
+ if (key === "token")
35
+ out.token = decodeURIComponent(value);
36
+ if (key === "blob")
37
+ out.blob = decodeURIComponent(value);
38
+ }
39
+ return out;
40
+ }
41
+ /** The first 16 hex digits of the request token's SHA-256, which the callback must present. */
42
+ export function expectedCallbackToken(nativeSsoToken) {
43
+ return sha256Hex(nativeSsoToken).slice(0, 16);
44
+ }
45
+ export function callbackMatches(token, nativeSsoToken) {
46
+ if (!token || token.length !== 16)
47
+ return false;
48
+ const expected = expectedCallbackToken(nativeSsoToken);
49
+ let difference = 0;
50
+ for (let i = 0; i < 16; i++)
51
+ difference |= token.charCodeAt(i) ^ expected.charCodeAt(i);
52
+ return difference === 0;
53
+ }
54
+ export function makeLsd() {
55
+ let digits = "";
56
+ for (let i = 0; i < 6; i++)
57
+ digits += Math.floor(Math.random() * 10);
58
+ return "S0." + digits;
59
+ }
60
+ export function jazoest(lsd) {
61
+ let sum = 0;
62
+ for (const char of lsd)
63
+ sum += char.codePointAt(0) ?? 0;
64
+ return "2" + sum;
65
+ }
66
+ export function multipartBody(fields, boundary) {
67
+ let body = "";
68
+ for (const [name, value] of fields) {
69
+ body += `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`;
70
+ }
71
+ return body + `--${boundary}--\r\n`;
72
+ }
73
+ export function urlForm(fields) {
74
+ return fields.map(([name, value]) => `${name}=${encodeURIComponent(value)}`).join("&");
75
+ }
76
+ async function post(url, headers, body) {
77
+ const response = await fetch(url, { method: "POST", headers, body });
78
+ let json = null;
79
+ try {
80
+ json = (await response.json());
81
+ }
82
+ catch {
83
+ json = null;
84
+ }
85
+ if (response.status !== 200 || json === null) {
86
+ throw new MetaAuthError(`Meta sign-in failed (HTTP ${response.status}). Try again.`);
87
+ }
88
+ if (json.error !== undefined) {
89
+ const error = json.error;
90
+ throw new MetaAuthError(`Meta sign-in failed (code ${error?.code ?? "-"}, subcode ${error?.error_subcode ?? "-"}). Try again.`);
91
+ }
92
+ return json;
93
+ }
94
+ async function postMultipart(url, fields) {
95
+ const boundary = `glassly.form.${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
96
+ return post(url, {
97
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
98
+ "Accept": "application/json",
99
+ "Origin": "https://auth.meta.com",
100
+ }, multipartBody(fields, boundary));
101
+ }
102
+ /** Step 1: the anonymous WebView tokens query. */
103
+ export async function tokensQuery() {
104
+ const lsd = makeLsd();
105
+ const json = await postMultipart(TOKENS_QUERY_URL, [
106
+ ["access_token", FRL_CLIENT],
107
+ ["lsd", lsd],
108
+ ["jazoest", jazoest(lsd)],
109
+ ]);
110
+ const nativeSsoToken = json.native_sso_token;
111
+ const etoken = json.native_sso_etoken;
112
+ if (typeof nativeSsoToken !== "string" || !nativeSsoToken || typeof etoken !== "string" || !etoken) {
113
+ throw new MetaAuthError("Meta didn't return sign-in tokens. Try again.");
114
+ }
115
+ return { nativeSsoToken, etoken };
116
+ }
117
+ /** Step 3: decrypt the login blob into the FRL access token. */
118
+ export async function decryptBlob(blob, requestToken) {
119
+ const lsd = makeLsd();
120
+ const json = await postMultipart(BLOBS_DECRYPT_URL, [
121
+ ["blob", blob],
122
+ ["request_token", requestToken],
123
+ ["access_token", FRL_CLIENT],
124
+ ["lsd", lsd],
125
+ ["jazoest", jazoest(lsd)],
126
+ ]);
127
+ const token = json.access_token;
128
+ if (typeof token !== "string" || !token)
129
+ throw new MetaAuthError("Meta didn't confirm the sign-in. Try again.");
130
+ return token;
131
+ }
132
+ function randomId() {
133
+ const hex = () => Math.floor(Math.random() * 0x10000)
134
+ .toString(16)
135
+ .padStart(4, "0");
136
+ return `${hex()}${hex()}-${hex()}-${hex()}-${hex()}-${hex()}${hex()}${hex()}`.toUpperCase();
137
+ }
138
+ /** Step 4: exchange the FRL token for the ar user session. */
139
+ export async function login(frlAccessToken) {
140
+ const json = await post(LOGIN_URL, {
141
+ "Content-Type": "application/x-www-form-urlencoded",
142
+ "Accept": "application/json",
143
+ "Authorization": "OAuth " + AR_CLIENT,
144
+ }, urlForm([
145
+ ["frl_access_token", frlAccessToken],
146
+ ["logging_session_id", randomId()],
147
+ ["format", "json"],
148
+ ["device_id", randomId()],
149
+ ["generate_session_cookies", "1"],
150
+ ["generate_analytics_claim", "1"],
151
+ ["method", "POST"],
152
+ ]));
153
+ const accessToken = json.access_token;
154
+ const rawUserId = json.user_id;
155
+ const userId = typeof rawUserId === "string" ? rawUserId : typeof rawUserId === "number" ? String(rawUserId) : "";
156
+ if (typeof accessToken !== "string" || !accessToken || !userId) {
157
+ throw new MetaAuthError("Meta didn't return an account session. Try again.");
158
+ }
159
+ return { accessToken, userId };
160
+ }
161
+ //# sourceMappingURL=MetaAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetaAuth.js","sourceRoot":"","sources":["../../../src/services/neuralBand/MetaAuth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Meta account sign-in a Neural Band claim needs, ported from callbacked/kinesis\n * (MIT, Copyright (c) 2026 callbacked): Sources/Kinesis/MetaAuth.swift.\n *\n * The chain: an anonymous tokens query, a WebView sign-in on auth.meta.com whose\n * fb-viewapp://…frl_login redirect carries a token and a blob, a blob decrypt, and\n * the ar-genai exchange that yields the user session. The session then goes to the\n * native Bluetooth SDK (keychain / keystore), which runs the ownership exchanges;\n * nothing here persists it.\n */\nimport {sha256Hex} from \"./sha256\"\n\n// Meta's own client identifiers, the same in every copy of Meta's apps. They name the\n// app that asks, not a person. They are not secrets, and no account or session is in them.\nconst FRL_CLIENT = \"FRL|388177446008673|083800dd7efbbd42eab18c9886d79c18\"\nconst AR_CLIENT = \"AR|306760944872162|a919421a55a8ea18080ab2f10f57f1be\"\nconst TOKENS_QUERY_URL = \"https://meta.graph.meta.com/webview_tokens_query\"\nconst BLOBS_DECRYPT_URL = \"https://meta.graph.meta.com/webview_blobs_decrypt\"\nconst LOGIN_URL = \"https://ar-genai.graph.meta.com/login\"\nconst AUTH_ENTRY_BASE =\n \"https://auth.meta.com/?native_app_id=388177446008673&source_app_id=388177446008673&native_sso_etoken=\"\n\nexport class MetaAuthError extends Error {}\n\nexport interface MetaSsoTokens {\n nativeSsoToken: string\n etoken: string\n}\n\nexport interface MetaSession {\n accessToken: string\n userId: string\n}\n\nexport function authEntryUrl(tokens: MetaSsoTokens): string {\n return AUTH_ENTRY_BASE + encodeURIComponent(tokens.etoken)\n}\n\n/** The redirect that ends the sign-in: a custom scheme, or the frl_login marker on https. */\nexport function isLoginCallback(url: string): boolean {\n return url.toLowerCase().startsWith(\"fb-viewapp:\") || url.includes(\"frl_login\")\n}\n\nexport function callbackQuery(url: string): {token?: string; blob?: string} {\n const query = url.includes(\"?\") ? url.slice(url.indexOf(\"?\") + 1) : \"\"\n const out: {token?: string; blob?: string} = {}\n for (const pair of query.split(\"&\")) {\n const [key, value = \"\"] = pair.split(\"=\")\n if (key === \"token\") out.token = decodeURIComponent(value)\n if (key === \"blob\") out.blob = decodeURIComponent(value)\n }\n return out\n}\n\n/** The first 16 hex digits of the request token's SHA-256, which the callback must present. */\nexport function expectedCallbackToken(nativeSsoToken: string): string {\n return sha256Hex(nativeSsoToken).slice(0, 16)\n}\n\nexport function callbackMatches(token: string | undefined, nativeSsoToken: string): boolean {\n if (!token || token.length !== 16) return false\n const expected = expectedCallbackToken(nativeSsoToken)\n let difference = 0\n for (let i = 0; i < 16; i++) difference |= token.charCodeAt(i) ^ expected.charCodeAt(i)\n return difference === 0\n}\n\nexport function makeLsd(): string {\n let digits = \"\"\n for (let i = 0; i < 6; i++) digits += Math.floor(Math.random() * 10)\n return \"S0.\" + digits\n}\n\nexport function jazoest(lsd: string): string {\n let sum = 0\n for (const char of lsd) sum += char.codePointAt(0) ?? 0\n return \"2\" + sum\n}\n\nexport function multipartBody(fields: [string, string][], boundary: string): string {\n let body = \"\"\n for (const [name, value] of fields) {\n body += `--${boundary}\\r\\nContent-Disposition: form-data; name=\"${name}\"\\r\\n\\r\\n${value}\\r\\n`\n }\n return body + `--${boundary}--\\r\\n`\n}\n\nexport function urlForm(fields: [string, string][]): string {\n return fields.map(([name, value]) => `${name}=${encodeURIComponent(value)}`).join(\"&\")\n}\n\ntype Json = Record<string, unknown>\n\nasync function post(url: string, headers: Record<string, string>, body: string): Promise<Json> {\n const response = await fetch(url, {method: \"POST\", headers, body})\n let json: Json | null = null\n try {\n json = (await response.json()) as Json\n } catch {\n json = null\n }\n if (response.status !== 200 || json === null) {\n throw new MetaAuthError(`Meta sign-in failed (HTTP ${response.status}). Try again.`)\n }\n if (json.error !== undefined) {\n const error = json.error as {code?: number; error_subcode?: number} | null\n throw new MetaAuthError(\n `Meta sign-in failed (code ${error?.code ?? \"-\"}, subcode ${error?.error_subcode ?? \"-\"}). Try again.`,\n )\n }\n return json\n}\n\nasync function postMultipart(url: string, fields: [string, string][]): Promise<Json> {\n const boundary = `glassly.form.${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`\n return post(\n url,\n {\n \"Content-Type\": `multipart/form-data; boundary=${boundary}`,\n \"Accept\": \"application/json\",\n \"Origin\": \"https://auth.meta.com\",\n },\n multipartBody(fields, boundary),\n )\n}\n\n/** Step 1: the anonymous WebView tokens query. */\nexport async function tokensQuery(): Promise<MetaSsoTokens> {\n const lsd = makeLsd()\n const json = await postMultipart(TOKENS_QUERY_URL, [\n [\"access_token\", FRL_CLIENT],\n [\"lsd\", lsd],\n [\"jazoest\", jazoest(lsd)],\n ])\n const nativeSsoToken = json.native_sso_token\n const etoken = json.native_sso_etoken\n if (typeof nativeSsoToken !== \"string\" || !nativeSsoToken || typeof etoken !== \"string\" || !etoken) {\n throw new MetaAuthError(\"Meta didn't return sign-in tokens. Try again.\")\n }\n return {nativeSsoToken, etoken}\n}\n\n/** Step 3: decrypt the login blob into the FRL access token. */\nexport async function decryptBlob(blob: string, requestToken: string): Promise<string> {\n const lsd = makeLsd()\n const json = await postMultipart(BLOBS_DECRYPT_URL, [\n [\"blob\", blob],\n [\"request_token\", requestToken],\n [\"access_token\", FRL_CLIENT],\n [\"lsd\", lsd],\n [\"jazoest\", jazoest(lsd)],\n ])\n const token = json.access_token\n if (typeof token !== \"string\" || !token) throw new MetaAuthError(\"Meta didn't confirm the sign-in. Try again.\")\n return token\n}\n\nfunction randomId(): string {\n const hex = () =>\n Math.floor(Math.random() * 0x10000)\n .toString(16)\n .padStart(4, \"0\")\n return `${hex()}${hex()}-${hex()}-${hex()}-${hex()}-${hex()}${hex()}${hex()}`.toUpperCase()\n}\n\n/** Step 4: exchange the FRL token for the ar user session. */\nexport async function login(frlAccessToken: string): Promise<MetaSession> {\n const json = await post(\n LOGIN_URL,\n {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"Accept\": \"application/json\",\n \"Authorization\": \"OAuth \" + AR_CLIENT,\n },\n urlForm([\n [\"frl_access_token\", frlAccessToken],\n [\"logging_session_id\", randomId()],\n [\"format\", \"json\"],\n [\"device_id\", randomId()],\n [\"generate_session_cookies\", \"1\"],\n [\"generate_analytics_claim\", \"1\"],\n [\"method\", \"POST\"],\n ]),\n )\n const accessToken = json.access_token\n const rawUserId = json.user_id\n const userId = typeof rawUserId === \"string\" ? rawUserId : typeof rawUserId === \"number\" ? String(rawUserId) : \"\"\n if (typeof accessToken !== \"string\" || !accessToken || !userId) {\n throw new MetaAuthError(\"Meta didn't return an account session. Try again.\")\n }\n return {accessToken, userId}\n}\n"]}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The Meta Neural Band's own event streams, past the gestures that ride `touch_event`:
3
+ * claim progress (for the pairing screens), the handwriting model (typed into the
4
+ * miniapp keyboard stream as synthesized key presses) and the air mouse (forwarded
5
+ * to miniapps on the pointer stream).
6
+ */
7
+ import type { NeuralBandClaimEvent, NeuralBandHandwritingEvent, NeuralBandPointerEvent } from "@glassly/bluetooth-sdk";
8
+ import type { KeyboardEventPayload } from "../GlassesKeyboardInput";
9
+ export interface NeuralBandClaimStatus {
10
+ stage: "idle" | "progress" | "completed" | "failed";
11
+ message?: string;
12
+ sessionExpired: boolean;
13
+ bandName?: string;
14
+ }
15
+ export interface NeuralBandHandwritingStatus {
16
+ phase: "idle" | "preparing" | "ready" | "restoring" | "finished";
17
+ message: string;
18
+ problem?: string;
19
+ /** The last text the band produced since handwriting was switched on. */
20
+ text: string;
21
+ }
22
+ export interface NeuralBandInputOptions {
23
+ forward?: (stream: string, payload: unknown) => void;
24
+ now?: () => number;
25
+ }
26
+ /** One handwriting change as the key presses that would have typed it. */
27
+ export declare function keyEventsForTextChange(previous: string, next: string, timestamp: number): KeyboardEventPayload[];
28
+ export declare class NeuralBandInput {
29
+ private claim;
30
+ private handwriting;
31
+ private claimListeners;
32
+ private handwritingListeners;
33
+ private readonly forward;
34
+ private readonly now;
35
+ constructor(options?: NeuralBandInputOptions);
36
+ claimStatus(): NeuralBandClaimStatus;
37
+ onClaimStatus(cb: (status: NeuralBandClaimStatus) => void): () => void;
38
+ handwritingStatus(): NeuralBandHandwritingStatus;
39
+ onHandwritingStatus(cb: (status: NeuralBandHandwritingStatus) => void): () => void;
40
+ /** A new pairing attempt starts from a clean slate. */
41
+ resetClaim(): void;
42
+ handleClaim(event: NeuralBandClaimEvent): void;
43
+ handleHandwriting(event: NeuralBandHandwritingEvent): void;
44
+ handlePointer(event: NeuralBandPointerEvent): void;
45
+ private setClaim;
46
+ private setHandwriting;
47
+ }
48
+ declare const neuralBandInput: NeuralBandInput;
49
+ export default neuralBandInput;
50
+ //# sourceMappingURL=NeuralBandInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NeuralBandInput.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandInput.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAC,oBAAoB,EAAE,0BAA0B,EAAE,sBAAsB,EAAC,MAAM,wBAAwB,CAAA;AAGpH,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAA;AAGjE,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAA;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU,CAAA;IAChE,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACpD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAID,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAehH;AAQD,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAgE;IAC7E,OAAO,CAAC,WAAW,CAAsE;IACzF,OAAO,CAAC,cAAc,CAAqD;IAC3E,OAAO,CAAC,oBAAoB,CAA2D;IACvF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4C;IACpE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;gBAEtB,OAAO,GAAE,sBAA2B;IAKhD,WAAW,IAAI,qBAAqB;IAIpC,aAAa,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,GAAG,MAAM,IAAI;IAKtE,iBAAiB,IAAI,2BAA2B;IAIhD,mBAAmB,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,2BAA2B,KAAK,IAAI,GAAG,MAAM,IAAI;IAKlF,uDAAuD;IACvD,UAAU,IAAI,IAAI;IAIlB,WAAW,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI;IAS9C,iBAAiB,CAAC,KAAK,EAAE,0BAA0B,GAAG,IAAI;IAe1D,aAAa,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI;IAiBlD,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,cAAc;CAIvB;AAED,QAAA,MAAM,eAAe,iBAAwB,CAAA;AAC7C,eAAe,eAAe,CAAA"}
@@ -0,0 +1,114 @@
1
+ import { MiniappStreamType } from "@glassly/miniapp";
2
+ import localMiniappRuntime from "../LocalMiniappRuntime";
3
+ const MODIFIERS = { shift: false, ctrl: false, alt: false, meta: false };
4
+ /** One handwriting change as the key presses that would have typed it. */
5
+ export function keyEventsForTextChange(previous, next, timestamp) {
6
+ let common = 0;
7
+ while (common < previous.length && common < next.length && previous[common] === next[common])
8
+ common++;
9
+ const events = [];
10
+ const press = (key, code, text) => {
11
+ events.push({ phase: "down", key, code, modifiers: MODIFIERS, text, repeat: false, timestamp });
12
+ events.push({ phase: "up", key, code, modifiers: MODIFIERS, repeat: false, timestamp });
13
+ };
14
+ for (let i = common; i < previous.length; i++)
15
+ press("Backspace", "Backspace");
16
+ for (const char of Array.from(next.slice(common))) {
17
+ if (char === "\n")
18
+ press("Enter", "Enter");
19
+ else if (char === " ")
20
+ press(" ", "Space", " ");
21
+ else
22
+ press(char, codeFor(char), char);
23
+ }
24
+ return events;
25
+ }
26
+ function codeFor(char) {
27
+ if (/^[a-z]$/i.test(char))
28
+ return `Key${char.toUpperCase()}`;
29
+ if (/^[0-9]$/.test(char))
30
+ return `Digit${char}`;
31
+ return "Unidentified";
32
+ }
33
+ export class NeuralBandInput {
34
+ claim = { stage: "idle", sessionExpired: false };
35
+ handwriting = { phase: "idle", message: "", text: "" };
36
+ claimListeners = new Set();
37
+ handwritingListeners = new Set();
38
+ forward;
39
+ now;
40
+ constructor(options = {}) {
41
+ this.forward = options.forward ?? ((stream, payload) => localMiniappRuntime.forwardEvent(stream, payload));
42
+ this.now = options.now ?? (() => Date.now());
43
+ }
44
+ claimStatus() {
45
+ return this.claim;
46
+ }
47
+ onClaimStatus(cb) {
48
+ this.claimListeners.add(cb);
49
+ return () => this.claimListeners.delete(cb);
50
+ }
51
+ handwritingStatus() {
52
+ return this.handwriting;
53
+ }
54
+ onHandwritingStatus(cb) {
55
+ this.handwritingListeners.add(cb);
56
+ return () => this.handwritingListeners.delete(cb);
57
+ }
58
+ /** A new pairing attempt starts from a clean slate. */
59
+ resetClaim() {
60
+ this.setClaim({ stage: "idle", sessionExpired: false });
61
+ }
62
+ handleClaim(event) {
63
+ this.setClaim({
64
+ stage: event.stage,
65
+ message: event.message,
66
+ sessionExpired: event.sessionExpired === true,
67
+ bandName: event.bandName,
68
+ });
69
+ }
70
+ handleHandwriting(event) {
71
+ if (event.kind === "state") {
72
+ const text = event.phase === "preparing" ? "" : this.handwriting.text;
73
+ this.setHandwriting({ phase: event.phase, message: event.message, problem: event.problem, text });
74
+ return;
75
+ }
76
+ const previous = this.handwriting.text;
77
+ if (event.text !== previous) {
78
+ for (const key of keyEventsForTextChange(previous, event.text, this.now())) {
79
+ this.forward(MiniappStreamType.KEYBOARD_EVENT, key);
80
+ }
81
+ }
82
+ this.setHandwriting({ ...this.handwriting, text: event.text });
83
+ }
84
+ handlePointer(event) {
85
+ if (event.kind === "move") {
86
+ this.forward(MiniappStreamType.POINTER_EVENT, {
87
+ kind: "move",
88
+ dx: event.dx,
89
+ dy: event.dy,
90
+ timestamp: event.timestamp,
91
+ });
92
+ }
93
+ else {
94
+ this.forward(MiniappStreamType.POINTER_EVENT, {
95
+ kind: "buttons",
96
+ buttons: event.buttons,
97
+ timestamp: event.timestamp,
98
+ });
99
+ }
100
+ }
101
+ setClaim(status) {
102
+ this.claim = status;
103
+ for (const cb of this.claimListeners)
104
+ cb(status);
105
+ }
106
+ setHandwriting(status) {
107
+ this.handwriting = status;
108
+ for (const cb of this.handwritingListeners)
109
+ cb(status);
110
+ }
111
+ }
112
+ const neuralBandInput = new NeuralBandInput();
113
+ export default neuralBandInput;
114
+ //# sourceMappingURL=NeuralBandInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NeuralBandInput.js","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandInput.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Meta Neural Band's own event streams, past the gestures that ride `touch_event`:\n * claim progress (for the pairing screens), the handwriting model (typed into the\n * miniapp keyboard stream as synthesized key presses) and the air mouse (forwarded\n * to miniapps on the pointer stream).\n */\nimport type {NeuralBandClaimEvent, NeuralBandHandwritingEvent, NeuralBandPointerEvent} from \"@glassly/bluetooth-sdk\"\nimport {MiniappStreamType} from \"@glassly/miniapp\"\n\nimport type {KeyboardEventPayload} from \"../GlassesKeyboardInput\"\nimport localMiniappRuntime from \"../LocalMiniappRuntime\"\n\nexport interface NeuralBandClaimStatus {\n stage: \"idle\" | \"progress\" | \"completed\" | \"failed\"\n message?: string\n sessionExpired: boolean\n bandName?: string\n}\n\nexport interface NeuralBandHandwritingStatus {\n phase: \"idle\" | \"preparing\" | \"ready\" | \"restoring\" | \"finished\"\n message: string\n problem?: string\n /** The last text the band produced since handwriting was switched on. */\n text: string\n}\n\nexport interface NeuralBandInputOptions {\n forward?: (stream: string, payload: unknown) => void\n now?: () => number\n}\n\nconst MODIFIERS = {shift: false, ctrl: false, alt: false, meta: false}\n\n/** One handwriting change as the key presses that would have typed it. */\nexport function keyEventsForTextChange(previous: string, next: string, timestamp: number): KeyboardEventPayload[] {\n let common = 0\n while (common < previous.length && common < next.length && previous[common] === next[common]) common++\n const events: KeyboardEventPayload[] = []\n const press = (key: string, code: string, text?: string) => {\n events.push({phase: \"down\", key, code, modifiers: MODIFIERS, text, repeat: false, timestamp})\n events.push({phase: \"up\", key, code, modifiers: MODIFIERS, repeat: false, timestamp})\n }\n for (let i = common; i < previous.length; i++) press(\"Backspace\", \"Backspace\")\n for (const char of Array.from(next.slice(common))) {\n if (char === \"\\n\") press(\"Enter\", \"Enter\")\n else if (char === \" \") press(\" \", \"Space\", \" \")\n else press(char, codeFor(char), char)\n }\n return events\n}\n\nfunction codeFor(char: string): string {\n if (/^[a-z]$/i.test(char)) return `Key${char.toUpperCase()}`\n if (/^[0-9]$/.test(char)) return `Digit${char}`\n return \"Unidentified\"\n}\n\nexport class NeuralBandInput {\n private claim: NeuralBandClaimStatus = {stage: \"idle\", sessionExpired: false}\n private handwriting: NeuralBandHandwritingStatus = {phase: \"idle\", message: \"\", text: \"\"}\n private claimListeners = new Set<(status: NeuralBandClaimStatus) => void>()\n private handwritingListeners = new Set<(status: NeuralBandHandwritingStatus) => void>()\n private readonly forward: (stream: string, payload: unknown) => void\n private readonly now: () => number\n\n constructor(options: NeuralBandInputOptions = {}) {\n this.forward = options.forward ?? ((stream, payload) => localMiniappRuntime.forwardEvent(stream, payload))\n this.now = options.now ?? (() => Date.now())\n }\n\n claimStatus(): NeuralBandClaimStatus {\n return this.claim\n }\n\n onClaimStatus(cb: (status: NeuralBandClaimStatus) => void): () => void {\n this.claimListeners.add(cb)\n return () => this.claimListeners.delete(cb)\n }\n\n handwritingStatus(): NeuralBandHandwritingStatus {\n return this.handwriting\n }\n\n onHandwritingStatus(cb: (status: NeuralBandHandwritingStatus) => void): () => void {\n this.handwritingListeners.add(cb)\n return () => this.handwritingListeners.delete(cb)\n }\n\n /** A new pairing attempt starts from a clean slate. */\n resetClaim(): void {\n this.setClaim({stage: \"idle\", sessionExpired: false})\n }\n\n handleClaim(event: NeuralBandClaimEvent): void {\n this.setClaim({\n stage: event.stage,\n message: event.message,\n sessionExpired: event.sessionExpired === true,\n bandName: event.bandName,\n })\n }\n\n handleHandwriting(event: NeuralBandHandwritingEvent): void {\n if (event.kind === \"state\") {\n const text = event.phase === \"preparing\" ? \"\" : this.handwriting.text\n this.setHandwriting({phase: event.phase, message: event.message, problem: event.problem, text})\n return\n }\n const previous = this.handwriting.text\n if (event.text !== previous) {\n for (const key of keyEventsForTextChange(previous, event.text, this.now())) {\n this.forward(MiniappStreamType.KEYBOARD_EVENT, key)\n }\n }\n this.setHandwriting({...this.handwriting, text: event.text})\n }\n\n handlePointer(event: NeuralBandPointerEvent): void {\n if (event.kind === \"move\") {\n this.forward(MiniappStreamType.POINTER_EVENT, {\n kind: \"move\",\n dx: event.dx,\n dy: event.dy,\n timestamp: event.timestamp,\n })\n } else {\n this.forward(MiniappStreamType.POINTER_EVENT, {\n kind: \"buttons\",\n buttons: event.buttons,\n timestamp: event.timestamp,\n })\n }\n }\n\n private setClaim(status: NeuralBandClaimStatus): void {\n this.claim = status\n for (const cb of this.claimListeners) cb(status)\n }\n\n private setHandwriting(status: NeuralBandHandwritingStatus): void {\n this.handwriting = status\n for (const cb of this.handwritingListeners) cb(status)\n }\n}\n\nconst neuralBandInput = new NeuralBandInput()\nexport default neuralBandInput\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function startNeuralBandSettingsSync(): void;
2
+ export declare function stopNeuralBandSettingsSync(): void;
3
+ //# sourceMappingURL=NeuralBandSettingsSync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NeuralBandSettingsSync.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandSettingsSync.ts"],"names":[],"mappings":"AAuCA,wBAAgB,2BAA2B,IAAI,IAAI,CAkBlD;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Pushes the Neural Band's user settings (air mouse, wearing hand) to the native
3
+ * controller: when the band connects, and whenever either setting changes while
4
+ * it is connected. The native side keeps nothing across connections.
5
+ */
6
+ import BluetoothSdk from "@glassly/bluetooth-sdk/internal";
7
+ import { useGlassesStore } from "../../stores/glasses";
8
+ import { SETTINGS, useSettingsStore } from "../../stores/settings";
9
+ import { ControllerTypes } from "../../types/enums";
10
+ let unsubscribers = [];
11
+ function bandConnected() {
12
+ const glasses = useGlassesStore.getState();
13
+ const settings = useSettingsStore.getState();
14
+ return (glasses.controllerConnected === true &&
15
+ glasses.controllerFullyBooted === true &&
16
+ settings.getSetting(SETTINGS.default_controller.key) === ControllerTypes.NEURAL_BAND);
17
+ }
18
+ function pushAirMouse() {
19
+ if (!bandConnected())
20
+ return;
21
+ const enabled = useSettingsStore.getState().getSetting(SETTINGS.neural_band_air_mouse.key) === true;
22
+ void BluetoothSdk.neuralBandSetPointer(enabled).catch((err) => console.warn("NeuralBandSettingsSync: air mouse push failed:", err));
23
+ }
24
+ function pushHand() {
25
+ if (!bandConnected())
26
+ return;
27
+ const hand = useSettingsStore.getState().getSetting(SETTINGS.neural_band_hand.key) === "left" ? "left" : "right";
28
+ void BluetoothSdk.neuralBandSetHand(hand).catch((err) => console.warn("NeuralBandSettingsSync: hand push failed:", err));
29
+ }
30
+ export function startNeuralBandSettingsSync() {
31
+ if (unsubscribers.length > 0)
32
+ return;
33
+ let wasConnected = bandConnected();
34
+ unsubscribers = [
35
+ useGlassesStore.subscribe((s) => s.controllerConnected === true && s.controllerFullyBooted === true, () => {
36
+ const connected = bandConnected();
37
+ if (connected && !wasConnected) {
38
+ pushAirMouse();
39
+ pushHand();
40
+ }
41
+ wasConnected = connected;
42
+ }),
43
+ useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.neural_band_air_mouse.key), pushAirMouse),
44
+ useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.neural_band_hand.key), pushHand),
45
+ ];
46
+ }
47
+ export function stopNeuralBandSettingsSync() {
48
+ for (const unsubscribe of unsubscribers)
49
+ unsubscribe();
50
+ unsubscribers = [];
51
+ }
52
+ //# sourceMappingURL=NeuralBandSettingsSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NeuralBandSettingsSync.js","sourceRoot":"","sources":["../../../src/services/neuralBand/NeuralBandSettingsSync.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,YAAY,MAAM,iCAAiC,CAAA;AAE1D,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAC,QAAQ,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AAEjD,IAAI,aAAa,GAAmB,EAAE,CAAA;AAEtC,SAAS,aAAa;IACpB,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IAC1C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAA;IAC5C,OAAO,CACL,OAAO,CAAC,mBAAmB,KAAK,IAAI;QACpC,OAAO,CAAC,qBAAqB,KAAK,IAAI;QACtC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,eAAe,CAAC,WAAW,CACrF,CAAA;AACH,CAAC;AAED,SAAS,YAAY;IACnB,IAAI,CAAC,aAAa,EAAE;QAAE,OAAM;IAC5B,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAA;IACnG,KAAK,YAAY,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAC5D,OAAO,CAAC,IAAI,CAAC,gDAAgD,EAAE,GAAG,CAAC,CACpE,CAAA;AACH,CAAC;AAED,SAAS,QAAQ;IACf,IAAI,CAAC,aAAa,EAAE;QAAE,OAAM;IAC5B,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;IAChH,KAAK,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACtD,OAAO,CAAC,IAAI,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAC/D,CAAA;AACH,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QAAE,OAAM;IACpC,IAAI,YAAY,GAAG,aAAa,EAAE,CAAA;IAClC,aAAa,GAAG;QACd,eAAe,CAAC,SAAS,CACvB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB,KAAK,IAAI,IAAI,CAAC,CAAC,qBAAqB,KAAK,IAAI,EACzE,GAAG,EAAE;YACH,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;YACjC,IAAI,SAAS,IAAI,CAAC,YAAY,EAAE,CAAC;gBAC/B,YAAY,EAAE,CAAA;gBACd,QAAQ,EAAE,CAAA;YACZ,CAAC;YACD,YAAY,GAAG,SAAS,CAAA;QAC1B,CAAC,CACF;QACD,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC;QACjG,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC;KACzF,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 * Pushes the Neural Band's user settings (air mouse, wearing hand) to the native\n * controller: when the band connects, and whenever either setting changes while\n * it is connected. The native side keeps nothing across connections.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\n\nimport {useGlassesStore} from \"../../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../../stores/settings\"\nimport {ControllerTypes} from \"../../types/enums\"\n\nlet unsubscribers: (() => void)[] = []\n\nfunction bandConnected(): boolean {\n const glasses = useGlassesStore.getState()\n const settings = useSettingsStore.getState()\n return (\n glasses.controllerConnected === true &&\n glasses.controllerFullyBooted === true &&\n settings.getSetting(SETTINGS.default_controller.key) === ControllerTypes.NEURAL_BAND\n )\n}\n\nfunction pushAirMouse(): void {\n if (!bandConnected()) return\n const enabled = useSettingsStore.getState().getSetting(SETTINGS.neural_band_air_mouse.key) === true\n void BluetoothSdk.neuralBandSetPointer(enabled).catch((err) =>\n console.warn(\"NeuralBandSettingsSync: air mouse push failed:\", err),\n )\n}\n\nfunction pushHand(): void {\n if (!bandConnected()) return\n const hand = useSettingsStore.getState().getSetting(SETTINGS.neural_band_hand.key) === \"left\" ? \"left\" : \"right\"\n void BluetoothSdk.neuralBandSetHand(hand).catch((err) =>\n console.warn(\"NeuralBandSettingsSync: hand push failed:\", err),\n )\n}\n\nexport function startNeuralBandSettingsSync(): void {\n if (unsubscribers.length > 0) return\n let wasConnected = bandConnected()\n unsubscribers = [\n useGlassesStore.subscribe(\n (s) => s.controllerConnected === true && s.controllerFullyBooted === true,\n () => {\n const connected = bandConnected()\n if (connected && !wasConnected) {\n pushAirMouse()\n pushHand()\n }\n wasConnected = connected\n },\n ),\n useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.neural_band_air_mouse.key), pushAirMouse),\n useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.neural_band_hand.key), pushHand),\n ]\n}\n\nexport function stopNeuralBandSettingsSync(): void {\n for (const unsubscribe of unsubscribers) unsubscribe()\n unsubscribers = []\n}\n"]}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * SHA-256 over a UTF-8 string, hex encoded. The Meta sign-in callback carries the
3
+ * first 16 hex digits of the request token's digest; nothing else here needs a
4
+ * hash, and the app ships no crypto dependency, so this stays small and local.
5
+ */
6
+ export declare function sha256Hex(text: string): string;
7
+ //# sourceMappingURL=sha256.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.d.ts","sourceRoot":"","sources":["../../../src/services/neuralBand/sha256.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiBH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgD9C"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * SHA-256 over a UTF-8 string, hex encoded. The Meta sign-in callback carries the
3
+ * first 16 hex digits of the request token's digest; nothing else here needs a
4
+ * hash, and the app ships no crypto dependency, so this stays small and local.
5
+ */
6
+ const K = new Uint32Array([
7
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98,
8
+ 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
9
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8,
10
+ 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
11
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
12
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
13
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
14
+ 0xc67178f2,
15
+ ]);
16
+ function rotr(x, n) {
17
+ return (x >>> n) | (x << (32 - n));
18
+ }
19
+ export function sha256Hex(text) {
20
+ const bytes = new TextEncoder().encode(text);
21
+ const bitLength = bytes.length * 8;
22
+ const padded = new Uint8Array(Math.ceil((bytes.length + 9) / 64) * 64);
23
+ padded.set(bytes);
24
+ padded[bytes.length] = 0x80;
25
+ const view = new DataView(padded.buffer);
26
+ view.setUint32(padded.length - 8, Math.floor(bitLength / 0x100000000));
27
+ view.setUint32(padded.length - 4, bitLength >>> 0);
28
+ const h = new Uint32Array([
29
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
30
+ ]);
31
+ const w = new Uint32Array(64);
32
+ for (let offset = 0; offset < padded.length; offset += 64) {
33
+ for (let i = 0; i < 16; i++)
34
+ w[i] = view.getUint32(offset + i * 4);
35
+ for (let i = 16; i < 64; i++) {
36
+ const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
37
+ const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
38
+ w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
39
+ }
40
+ let [a, b, c, d, e, f, g, hh] = h;
41
+ for (let i = 0; i < 64; i++) {
42
+ const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
43
+ const ch = (e & f) ^ (~e & g);
44
+ const t1 = (hh + s1 + ch + K[i] + w[i]) >>> 0;
45
+ const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
46
+ const maj = (a & b) ^ (a & c) ^ (b & c);
47
+ const t2 = (s0 + maj) >>> 0;
48
+ hh = g;
49
+ g = f;
50
+ f = e;
51
+ e = (d + t1) >>> 0;
52
+ d = c;
53
+ c = b;
54
+ b = a;
55
+ a = (t1 + t2) >>> 0;
56
+ }
57
+ h[0] = (h[0] + a) >>> 0;
58
+ h[1] = (h[1] + b) >>> 0;
59
+ h[2] = (h[2] + c) >>> 0;
60
+ h[3] = (h[3] + d) >>> 0;
61
+ h[4] = (h[4] + e) >>> 0;
62
+ h[5] = (h[5] + f) >>> 0;
63
+ h[6] = (h[6] + g) >>> 0;
64
+ h[7] = (h[7] + hh) >>> 0;
65
+ }
66
+ return Array.from(h, (word) => word.toString(16).padStart(8, "0")).join("");
67
+ }
68
+ //# sourceMappingURL=sha256.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.js","sourceRoot":"","sources":["../../../src/services/neuralBand/sha256.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SHA-256 over a UTF-8 string, hex encoded. The Meta sign-in callback carries the\n * first 16 hex digits of the request token's digest; nothing else here needs a\n * hash, and the app ships no crypto dependency, so this stays small and local.\n */\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98,\n 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8,\n 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,\n 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,\n 0xc67178f2,\n])\n\nfunction rotr(x: number, n: number): number {\n return (x >>> n) | (x << (32 - n))\n}\n\nexport function sha256Hex(text: string): string {\n const bytes = new TextEncoder().encode(text)\n const bitLength = bytes.length * 8\n const padded = new Uint8Array(Math.ceil((bytes.length + 9) / 64) * 64)\n padded.set(bytes)\n padded[bytes.length] = 0x80\n const view = new DataView(padded.buffer)\n view.setUint32(padded.length - 8, Math.floor(bitLength / 0x100000000))\n view.setUint32(padded.length - 4, bitLength >>> 0)\n\n const h = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ])\n const w = new Uint32Array(64)\n for (let offset = 0; offset < padded.length; offset += 64) {\n for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4)\n for (let i = 16; i < 64; i++) {\n const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3)\n const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10)\n w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0\n }\n let [a, b, c, d, e, f, g, hh] = h\n for (let i = 0; i < 64; i++) {\n const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)\n const ch = (e & f) ^ (~e & g)\n const t1 = (hh + s1 + ch + K[i] + w[i]) >>> 0\n const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)\n const maj = (a & b) ^ (a & c) ^ (b & c)\n const t2 = (s0 + maj) >>> 0\n hh = g\n g = f\n f = e\n e = (d + t1) >>> 0\n d = c\n c = b\n b = a\n a = (t1 + t2) >>> 0\n }\n h[0] = (h[0] + a) >>> 0\n h[1] = (h[1] + b) >>> 0\n h[2] = (h[2] + c) >>> 0\n h[3] = (h[3] + d) >>> 0\n h[4] = (h[4] + e) >>> 0\n h[5] = (h[5] + f) >>> 0\n h[6] = (h[6] + g) >>> 0\n h[7] = (h[7] + hh) >>> 0\n }\n return Array.from(h, (word) => word.toString(16).padStart(8, \"0\")).join(\"\")\n}\n"]}
@@ -16,11 +16,11 @@ export type SettingsBridgeAccess = "read" | "write";
16
16
  export declare const SETTINGS_BRIDGE_KEYS: Readonly<Record<string, SettingsBridgeAccess>>;
17
17
  /**
18
18
  * Read-only keys the engine computes: whether the wearer can see the phone,
19
- * whether the foreground miniapp is drawing on the glasses, and the ring's
20
- * battery. They change on their own clocks; the runtime pushes them like
21
- * stored keys.
19
+ * whether the foreground miniapp is drawing on the glasses, the ring's
20
+ * battery, and whether a 2D controller is connected. They change on their
21
+ * own clocks; the runtime pushes them like stored keys.
22
22
  */
23
- export declare const DERIVED_SETTINGS_KEYS: readonly ["phone_active", "foreground_app_on_screen", "ring_battery"];
23
+ export declare const DERIVED_SETTINGS_KEYS: readonly ["phone_active", "foreground_app_on_screen", "ring_battery", "input_2d"];
24
24
  export type DerivedSettingsKey = (typeof DERIVED_SETTINGS_KEYS)[number];
25
25
  export declare function isDerivedSettingsKey(key: string): key is DerivedSettingsKey;
26
26
  /** What a permitted app may do with `key`, or null when the bridge does not serve it. */
@@ -56,4 +56,12 @@ export declare function ringBatteryFrom(glasses: {
56
56
  controllerConnected: boolean;
57
57
  controllerBatteryLevel: number;
58
58
  }): RingBattery;
59
+ /**
60
+ * Whether the connected controller swipes in two dimensions (sideways as well
61
+ * as up and down): the Meta Neural Band's thumb swipes. The R1 ring and the
62
+ * glasses' temples are one-dimensional.
63
+ */
64
+ export declare function input2dFrom(glasses: {
65
+ controllerConnected: boolean;
66
+ }, controllerType: unknown): boolean;
59
67
  //# sourceMappingURL=settingsBridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"settingsBridge.d.ts","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAkB/E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,uEAAwE,CAAA;AAC1G,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAG7E;AAED,wDAAwD;AACxD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,EAAE,CAIrE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAC,GACvC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAA;AAE5E,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,qBAAqB,CAY3F;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,MAAM,CAAA;CAC/B,GAAG,WAAW,CAMd"}
1
+ {"version":3,"file":"settingsBridge.d.ts","sourceRoot":"","sources":["../../src/services/settingsBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAkB/E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,mFAAoF,CAAA;AACtH,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAG7E;AAED,wDAAwD;AACxD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,EAAE,CAIrE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAC,GACvC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAA;AAE5E,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,qBAAqB,CAY3F;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,MAAM,CAAA;CAC/B,GAAG,WAAW,CAMd;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAC,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CAErG"}