@glassly/engine 0.3.2-dev.574 → 0.3.2-dev.580
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +23 -4
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.d.ts +5 -2
- package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.js +7 -3
- package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
- package/build/services/TranscriptionRouting.d.ts +6 -1
- package/build/services/TranscriptionRouting.d.ts.map +1 -1
- package/build/services/TranscriptionRouting.js +7 -3
- package/build/services/TranscriptionRouting.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +0 -14
- package/build/stores/settings.js.map +1 -1
- package/package.json +7 -7
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/services/LocalMiniappRuntime.ts +43 -21
- package/src/services/LocalSttFallbackCoordinator.ts +18 -4
- package/src/services/TranscriptionRouting.ts +12 -4
- package/src/stores/settings.ts +0 -14
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
import {normalizeAiTier, resolveAiTier} from "./AiTierResolution"
|
|
55
55
|
import type {AiTier, AiTierProvider, SavedLlmKeys} from "./AiTierResolution"
|
|
56
56
|
import {HttpError} from "@glassly/cloud-client"
|
|
57
|
+
import type {QuotaStatusPayload} from "@glassly/cloud-protocol"
|
|
57
58
|
import localDisplayManager from "./LocalDisplayManager"
|
|
58
59
|
import sttModelManager from "./STTModelManager"
|
|
59
60
|
import localTranslationService, {type LocalTranslationSubscription} from "./LocalTranslationService"
|
|
@@ -108,7 +109,12 @@ import {phonePhotoCoordinator} from "./PhonePhotoCoordinator"
|
|
|
108
109
|
import {phoneStreamCoordinator} from "./PhoneStreamCoordinator"
|
|
109
110
|
import {phoneVideoCoordinator} from "./PhoneVideoCoordinator"
|
|
110
111
|
import {runSentenceTtsPipeline} from "./SentenceTtsPipeline"
|
|
111
|
-
import {
|
|
112
|
+
import {
|
|
113
|
+
summarizeTranscriptionRoutes,
|
|
114
|
+
transcriptionDeliveryRoute,
|
|
115
|
+
type TranscriptionDeliveryRoute,
|
|
116
|
+
type TranscriptionSubscriberRoutes,
|
|
117
|
+
} from "./TranscriptionRouting"
|
|
112
118
|
import {prepareTtsSentences} from "./TtsTextSanitizer"
|
|
113
119
|
import {handleWifiAdbRequest} from "./WifiAdbRequest"
|
|
114
120
|
import {cloudClientService, setBlobNamespaceMigrator} from "./CloudClientService"
|
|
@@ -171,6 +177,8 @@ interface ConnectedMiniapp {
|
|
|
171
177
|
forceLocalTranscriptionStreams: Set<string>
|
|
172
178
|
/** Transcription/translation streams with at least one listener using the default/cloud route. */
|
|
173
179
|
cloudTranscriptionStreams: Set<string>
|
|
180
|
+
/** Transcription streams whose default-route listeners all refuse on-device fallback text. */
|
|
181
|
+
noFallbackTranscriptionStreams: Set<string>
|
|
174
182
|
sendMessage: (raw: string) => void
|
|
175
183
|
lastPongAt: number
|
|
176
184
|
unansweredPingRounds: number
|
|
@@ -213,6 +221,9 @@ interface SpeechRun {
|
|
|
213
221
|
|
|
214
222
|
type TranscriptionEventSource = "cloud" | "local"
|
|
215
223
|
|
|
224
|
+
/** The cloud snapshot miniapps see: the phone's connection state plus plan-quota blocks. */
|
|
225
|
+
type MiniappCloudStatus = CloudClientStatusSnapshot & {quotaBlocked: QuotaStatusPayload["feature"][]}
|
|
226
|
+
|
|
216
227
|
/** Streams that carry speech text and so have a cloud route and an on-device route. */
|
|
217
228
|
const isSpeechTextStream = (stream: string): boolean =>
|
|
218
229
|
stream.startsWith("transcription:") || stream.startsWith("translation:")
|
|
@@ -1048,6 +1059,7 @@ class LocalMiniappRuntime {
|
|
|
1048
1059
|
subscriptions: new Set(),
|
|
1049
1060
|
forceLocalTranscriptionStreams: new Set(),
|
|
1050
1061
|
cloudTranscriptionStreams: new Set(),
|
|
1062
|
+
noFallbackTranscriptionStreams: new Set(),
|
|
1051
1063
|
sendMessage: sendFn,
|
|
1052
1064
|
lastPongAt: Date.now(),
|
|
1053
1065
|
unansweredPingRounds: 0,
|
|
@@ -2101,7 +2113,10 @@ class LocalMiniappRuntime {
|
|
|
2101
2113
|
if (!app) return
|
|
2102
2114
|
|
|
2103
2115
|
const rawStreams = (payload.subscriptions ?? payload.streams) as
|
|
2104
|
-
| (
|
|
2116
|
+
| (
|
|
2117
|
+
| string
|
|
2118
|
+
| {stream: string; rate?: string; forceLocal?: boolean; includeCloud?: boolean; localFallback?: boolean}
|
|
2119
|
+
)[]
|
|
2105
2120
|
| undefined
|
|
2106
2121
|
// Normalize: objects like {stream: "location_stream", rate: "realtime"} → extract stream name
|
|
2107
2122
|
// "location_stream" is the rate-bearing alias for "location_update".
|
|
@@ -2110,6 +2125,7 @@ class LocalMiniappRuntime {
|
|
|
2110
2125
|
let requestedLocationRate: string | null = null
|
|
2111
2126
|
const forceLocalTranscriptionStreams = new Set<string>()
|
|
2112
2127
|
const cloudTranscriptionStreams = new Set<string>()
|
|
2128
|
+
const noFallbackTranscriptionStreams = new Set<string>()
|
|
2113
2129
|
const streams = rawStreams?.map((s) => {
|
|
2114
2130
|
if (typeof s === "object" && s !== null) {
|
|
2115
2131
|
if (s.stream === "location_stream") {
|
|
@@ -2123,6 +2139,7 @@ class LocalMiniappRuntime {
|
|
|
2123
2139
|
}
|
|
2124
2140
|
if (isSpeechTextStream(s.stream) && (s.forceLocal !== true || s.includeCloud === true)) {
|
|
2125
2141
|
cloudTranscriptionStreams.add(s.stream)
|
|
2142
|
+
if (s.localFallback === false) noFallbackTranscriptionStreams.add(s.stream)
|
|
2126
2143
|
}
|
|
2127
2144
|
return s.stream
|
|
2128
2145
|
}
|
|
@@ -2190,6 +2207,9 @@ class LocalMiniappRuntime {
|
|
|
2190
2207
|
app.cloudTranscriptionStreams = new Set(
|
|
2191
2208
|
[...cloudTranscriptionStreams].filter((stream) => app.subscriptions.has(stream)),
|
|
2192
2209
|
)
|
|
2210
|
+
app.noFallbackTranscriptionStreams = new Set(
|
|
2211
|
+
[...noFallbackTranscriptionStreams].filter((stream) => app.subscriptions.has(stream)),
|
|
2212
|
+
)
|
|
2193
2213
|
this.replaceStreamSubscribers(packageName, previousSubscriptions, app.subscriptions)
|
|
2194
2214
|
// The overlay app presents phone notifications on the glasses while it
|
|
2195
2215
|
// listens to them; recorded for every dashboard so the role can move.
|
|
@@ -4863,6 +4883,7 @@ class LocalMiniappRuntime {
|
|
|
4863
4883
|
const previousSubscriptions = app.subscriptions
|
|
4864
4884
|
app.subscriptions = new Set(streams)
|
|
4865
4885
|
app.forceLocalTranscriptionStreams.clear()
|
|
4886
|
+
app.noFallbackTranscriptionStreams.clear()
|
|
4866
4887
|
app.cloudTranscriptionStreams = new Set(streams.filter(isSpeechTextStream))
|
|
4867
4888
|
this.replaceStreamSubscribers(packageName, previousSubscriptions, app.subscriptions)
|
|
4868
4889
|
|
|
@@ -4929,6 +4950,7 @@ class LocalMiniappRuntime {
|
|
|
4929
4950
|
let transcriptionLang: string | null = null
|
|
4930
4951
|
let forceLocalTranscriptionLang: string | null = null
|
|
4931
4952
|
let hasForceLocalTranscription = false
|
|
4953
|
+
let hasFallbackTranscription = false
|
|
4932
4954
|
const localTranslations: LocalTranslationSubscription[] = []
|
|
4933
4955
|
for (const [stream, subscribers] of this.activeStreamSubscribers()) {
|
|
4934
4956
|
if (subscribers.size === 0) continue
|
|
@@ -4960,6 +4982,7 @@ class LocalMiniappRuntime {
|
|
|
4960
4982
|
hasForceLocalTranscription = true
|
|
4961
4983
|
if (forceLocalTranscriptionLang === null) forceLocalTranscriptionLang = language
|
|
4962
4984
|
}
|
|
4985
|
+
if (routes.hasFallbackSubscriber) hasFallbackTranscription = true
|
|
4963
4986
|
if (routes.hasCloudSubscriber) cloudStreams.add(stream)
|
|
4964
4987
|
}
|
|
4965
4988
|
}
|
|
@@ -4974,6 +4997,7 @@ class LocalMiniappRuntime {
|
|
|
4974
4997
|
transcriptionLang !== null || localSourceLang !== null,
|
|
4975
4998
|
forceLocalTranscriptionLang ?? transcriptionLang ?? localSourceLang,
|
|
4976
4999
|
hasForceLocalTranscription || hasForceLocalTranslation,
|
|
5000
|
+
hasFallbackTranscription || localSourceLang !== null,
|
|
4977
5001
|
)
|
|
4978
5002
|
|
|
4979
5003
|
// Mirror the same set as typed AudioSubscription[]. When Soniox-direct is
|
|
@@ -5134,12 +5158,15 @@ class LocalMiniappRuntime {
|
|
|
5134
5158
|
if (q.feature !== "transcription") return
|
|
5135
5159
|
if (!q.blocked) {
|
|
5136
5160
|
this.lastSttQuotaNoticeAt = 0
|
|
5137
|
-
void localSttFallbackCoordinator.onSttQuotaBlockedChange(false)
|
|
5161
|
+
void localSttFallbackCoordinator.onSttQuotaBlockedChange(false).then(() => this.broadcastCloudStatus())
|
|
5138
5162
|
this.onQuotaRestored?.("transcription")
|
|
5139
5163
|
return
|
|
5140
5164
|
}
|
|
5141
5165
|
console.warn(`${LOG_TAG}: cloud transcription blocked by plan quota`)
|
|
5142
5166
|
void localSttFallbackCoordinator.onSttQuotaBlockedChange(true).then(() => {
|
|
5167
|
+
// Miniapps learn of the block through the status snapshot; the lens
|
|
5168
|
+
// notice below is for the wearer.
|
|
5169
|
+
this.broadcastCloudStatus()
|
|
5143
5170
|
const localSttActive = localSttFallbackCoordinator.isActive()
|
|
5144
5171
|
const now = Date.now()
|
|
5145
5172
|
if (now - this.lastSttQuotaNoticeAt >= STT_QUOTA_NOTICE_DEBOUNCE_MS) {
|
|
@@ -5383,7 +5410,7 @@ class LocalMiniappRuntime {
|
|
|
5383
5410
|
)
|
|
5384
5411
|
}
|
|
5385
5412
|
|
|
5386
|
-
private currentCloudStatus():
|
|
5413
|
+
private currentCloudStatus(): MiniappCloudStatus {
|
|
5387
5414
|
const base = cloudClientService.getStatus()
|
|
5388
5415
|
const fallbackActive =
|
|
5389
5416
|
(useSettingsStore.getState().getSetting(ISLAND_SETTINGS_KEYS.localSttFallbackActive) as boolean | undefined) ===
|
|
@@ -5394,6 +5421,7 @@ class LocalMiniappRuntime {
|
|
|
5394
5421
|
return {
|
|
5395
5422
|
status: base.status,
|
|
5396
5423
|
audioTransport: fallbackActive || sonioxDegraded ? "offline" : base.audioTransport,
|
|
5424
|
+
quotaBlocked: localSttFallbackCoordinator.isSttQuotaBlocked() ? ["transcription"] : [],
|
|
5397
5425
|
}
|
|
5398
5426
|
}
|
|
5399
5427
|
|
|
@@ -5543,7 +5571,7 @@ class LocalMiniappRuntime {
|
|
|
5543
5571
|
|
|
5544
5572
|
for (const packageName of matchedSubs) {
|
|
5545
5573
|
if (only !== undefined && packageName !== only) continue
|
|
5546
|
-
let transcriptionRoute:
|
|
5574
|
+
let transcriptionRoute: TranscriptionDeliveryRoute | undefined
|
|
5547
5575
|
if (normalizedStream.startsWith("transcription:") && transcriptionSource) {
|
|
5548
5576
|
const routes = this.appTranscriptionRoutesForEvent(packageName, normalizedStream)
|
|
5549
5577
|
transcriptionRoute =
|
|
@@ -5638,17 +5666,12 @@ class LocalMiniappRuntime {
|
|
|
5638
5666
|
})
|
|
5639
5667
|
}
|
|
5640
5668
|
|
|
5641
|
-
private appTranscriptionRoutesForSubscription(
|
|
5642
|
-
packageName: string,
|
|
5643
|
-
stream: string,
|
|
5644
|
-
): {
|
|
5645
|
-
cloud: boolean
|
|
5646
|
-
forceLocal: boolean
|
|
5647
|
-
} {
|
|
5669
|
+
private appTranscriptionRoutesForSubscription(packageName: string, stream: string): TranscriptionSubscriberRoutes {
|
|
5648
5670
|
const app = this.connectedApps.get(packageName)
|
|
5649
5671
|
return {
|
|
5650
5672
|
cloud: app?.cloudTranscriptionStreams.has(stream) === true,
|
|
5651
5673
|
forceLocal: app?.forceLocalTranscriptionStreams.has(stream) === true,
|
|
5674
|
+
localFallback: app?.noFallbackTranscriptionStreams.has(stream) !== true,
|
|
5652
5675
|
}
|
|
5653
5676
|
}
|
|
5654
5677
|
|
|
@@ -5666,21 +5689,20 @@ class LocalMiniappRuntime {
|
|
|
5666
5689
|
}
|
|
5667
5690
|
}
|
|
5668
5691
|
|
|
5669
|
-
private appTranscriptionRoutesForEvent(
|
|
5670
|
-
packageName: string,
|
|
5671
|
-
stream: string,
|
|
5672
|
-
): {
|
|
5673
|
-
cloud: boolean
|
|
5674
|
-
forceLocal: boolean
|
|
5675
|
-
} {
|
|
5692
|
+
private appTranscriptionRoutesForEvent(packageName: string, stream: string): TranscriptionSubscriberRoutes {
|
|
5676
5693
|
const app = this.connectedApps.get(packageName)
|
|
5677
5694
|
const autoStream = `${MiniappStreamType.TRANSCRIPTION}:auto`
|
|
5695
|
+
const cloudExact = app?.cloudTranscriptionStreams.has(stream) === true
|
|
5696
|
+
const cloudAuto = app?.cloudTranscriptionStreams.has(autoStream) === true
|
|
5678
5697
|
return {
|
|
5679
|
-
cloud:
|
|
5680
|
-
app?.cloudTranscriptionStreams.has(stream) === true || app?.cloudTranscriptionStreams.has(autoStream) === true,
|
|
5698
|
+
cloud: cloudExact || cloudAuto,
|
|
5681
5699
|
forceLocal:
|
|
5682
5700
|
app?.forceLocalTranscriptionStreams.has(stream) === true ||
|
|
5683
5701
|
app?.forceLocalTranscriptionStreams.has(autoStream) === true,
|
|
5702
|
+
// Fallback stays accepted while any matching default subscription takes it.
|
|
5703
|
+
localFallback:
|
|
5704
|
+
(cloudExact && app?.noFallbackTranscriptionStreams.has(stream) !== true) ||
|
|
5705
|
+
(cloudAuto && app?.noFallbackTranscriptionStreams.has(autoStream) !== true),
|
|
5684
5706
|
}
|
|
5685
5707
|
}
|
|
5686
5708
|
|
|
@@ -8,7 +8,8 @@ import sttModelManager from "./STTModelManager"
|
|
|
8
8
|
* Decides whether on-device Sherpa STT should produce transcripts for local
|
|
9
9
|
* miniapps, instead of (or in addition to) cloud transcription. Local STT is
|
|
10
10
|
* active when the cloud is unavailable (disconnected or refusing STT for plan
|
|
11
|
-
* quota) or any subscription
|
|
11
|
+
* quota) and some subscriber accepts fallback text, or any subscription
|
|
12
|
+
* requires `forceLocal`.
|
|
12
13
|
*
|
|
13
14
|
* Activation flips `localSttFallbackActive` in the settings store, which
|
|
14
15
|
* the bluetooth-sdk native side reads to decide whether to feed PCM into
|
|
@@ -37,6 +38,8 @@ class LocalSttFallbackCoordinator {
|
|
|
37
38
|
|
|
38
39
|
private hasTranscriptionSubscription = false
|
|
39
40
|
private hasForceLocalSubscription = false
|
|
41
|
+
/** Some default-route subscriber takes on-device text while the primary is down. */
|
|
42
|
+
private hasFallbackSubscription = false
|
|
40
43
|
private activeLanguage: string | null = null
|
|
41
44
|
/**
|
|
42
45
|
* Default to "cloud is up" so we never accidentally activate local STT before
|
|
@@ -123,10 +126,18 @@ class LocalSttFallbackCoordinator {
|
|
|
123
126
|
return this.localActive ? this.currentReason() : null
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
onSubscriptionChange(
|
|
127
|
-
|
|
129
|
+
onSubscriptionChange(
|
|
130
|
+
hasTranscription: boolean,
|
|
131
|
+
language: string | null,
|
|
132
|
+
hasForceLocal = false,
|
|
133
|
+
acceptsFallback = true,
|
|
134
|
+
): void {
|
|
135
|
+
this.log(
|
|
136
|
+
`onSubscriptionChange(hasTx=${hasTranscription}, lang=${language}, forceLocal=${hasForceLocal}, fallback=${acceptsFallback})`,
|
|
137
|
+
)
|
|
128
138
|
this.hasTranscriptionSubscription = hasTranscription
|
|
129
139
|
this.hasForceLocalSubscription = hasTranscription && hasForceLocal
|
|
140
|
+
this.hasFallbackSubscription = hasTranscription && acceptsFallback
|
|
130
141
|
this.activeLanguage = hasTranscription ? language : null
|
|
131
142
|
void this.reconcile()
|
|
132
143
|
}
|
|
@@ -256,7 +267,10 @@ class LocalSttFallbackCoordinator {
|
|
|
256
267
|
}
|
|
257
268
|
|
|
258
269
|
private shouldUseLocalStt(): boolean {
|
|
259
|
-
return
|
|
270
|
+
return (
|
|
271
|
+
this.hasTranscriptionSubscription &&
|
|
272
|
+
(this.hasForceLocalSubscription || (this.hasFallbackSubscription && !this.primaryUp()))
|
|
273
|
+
)
|
|
260
274
|
}
|
|
261
275
|
|
|
262
276
|
private log(msg: string): void {
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
export interface TranscriptionRouteSummary {
|
|
2
2
|
hasCloudSubscriber: boolean
|
|
3
3
|
hasForceLocalSubscriber: boolean
|
|
4
|
+
/** A default-route subscriber that takes on-device text while the primary is down. */
|
|
5
|
+
hasFallbackSubscriber: boolean
|
|
4
6
|
}
|
|
5
7
|
|
|
6
8
|
export interface TranscriptionSubscriberRoutes {
|
|
7
9
|
cloud: boolean
|
|
8
10
|
forceLocal: boolean
|
|
11
|
+
/** Default-route fallback acceptance; absent means accepted. */
|
|
12
|
+
localFallback?: boolean
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
/** "fallback" is on-device text serving the default route because the primary is down. */
|
|
16
|
+
export type TranscriptionDeliveryRoute = "default" | "forceLocal" | "fallback" | "all"
|
|
12
17
|
|
|
13
18
|
export function summarizeTranscriptionRoutes<T>(
|
|
14
19
|
subscribers: Iterable<T>,
|
|
@@ -16,12 +21,14 @@ export function summarizeTranscriptionRoutes<T>(
|
|
|
16
21
|
): TranscriptionRouteSummary {
|
|
17
22
|
let hasCloudSubscriber = false
|
|
18
23
|
let hasForceLocalSubscriber = false
|
|
24
|
+
let hasFallbackSubscriber = false
|
|
19
25
|
for (const subscriber of subscribers) {
|
|
20
26
|
const routes = routesFor(subscriber)
|
|
21
27
|
if (routes.cloud) hasCloudSubscriber = true
|
|
22
28
|
if (routes.forceLocal) hasForceLocalSubscriber = true
|
|
29
|
+
if (routes.cloud && routes.localFallback !== false) hasFallbackSubscriber = true
|
|
23
30
|
}
|
|
24
|
-
return {hasCloudSubscriber, hasForceLocalSubscriber}
|
|
31
|
+
return {hasCloudSubscriber, hasForceLocalSubscriber, hasFallbackSubscriber}
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
/**
|
|
@@ -41,7 +48,8 @@ export function transcriptionDeliveryRoute(
|
|
|
41
48
|
): TranscriptionDeliveryRoute | null {
|
|
42
49
|
if (source === "cloud") return routes.cloud ? "default" : null
|
|
43
50
|
if (primaryUp) return routes.forceLocal ? "forceLocal" : null
|
|
44
|
-
|
|
51
|
+
const fallback = routes.cloud && routes.localFallback !== false
|
|
52
|
+
if (fallback && routes.forceLocal) return "all"
|
|
45
53
|
if (routes.forceLocal) return "forceLocal"
|
|
46
|
-
return
|
|
54
|
+
return fallback ? "fallback" : null
|
|
47
55
|
}
|
package/src/stores/settings.ts
CHANGED
|
@@ -742,19 +742,6 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
742
742
|
saveOnServer: false,
|
|
743
743
|
persist: true,
|
|
744
744
|
},
|
|
745
|
-
// Debug: route the R1 ring through the glasses (advStart + RING_CONNECT_INFO,
|
|
746
|
-
// events forwarded by the CFW) instead of the phone link. OFF by default:
|
|
747
|
-
// the ring sends its touch reports to the glasses whenever it holds glasses
|
|
748
|
-
// targets, so the phone link only carries telemetry until those are erased.
|
|
749
|
-
// Synced to the Bluetooth SDK, which re-routes a connected ring on change.
|
|
750
|
-
// Device-local debug knob, so not saved on the server.
|
|
751
|
-
ring_via_glasses: {
|
|
752
|
-
key: "ring_via_glasses",
|
|
753
|
-
defaultValue: () => false,
|
|
754
|
-
writable: true,
|
|
755
|
-
saveOnServer: false,
|
|
756
|
-
persist: true,
|
|
757
|
-
},
|
|
758
745
|
// Maximum rate at which display updates leave the phone for the glasses.
|
|
759
746
|
// The Bluetooth SDK paces every scene frame and legacy layout to this,
|
|
760
747
|
// coalescing faster updates last-wins; clears bypass it. No effect on G2
|
|
@@ -1107,7 +1094,6 @@ export const BLUETOOTH_SETTING_KEYS: string[] = [
|
|
|
1107
1094
|
SETTINGS.cfw_scene_renderer.key,
|
|
1108
1095
|
SETTINGS.cfw_fast_ble.key,
|
|
1109
1096
|
SETTINGS.g2_perf_logs.key,
|
|
1110
|
-
SETTINGS.ring_via_glasses.key,
|
|
1111
1097
|
SETTINGS.display_max_fps.key,
|
|
1112
1098
|
SETTINGS.twelve_hour_time.key,
|
|
1113
1099
|
SETTINGS.metric_system.key,
|