Merge remote-tracking branch 'origin/main' into authoring-panel

# Conflicts:
#	docs/CURRENT-STATE.md
This commit is contained in:
Said Turksever committed 2026-10-29 18:40:02 +00:00
commit 2b05461232
142 files changed
+20740 -1917

No files matched your search

-2
View File
@@ -212,7 +212,6 @@
"src/styles/thermal.js",
"src/styles/anime.js",
"src/styles/noir.js",
"src/styles/snow.js",
"scripts/qa-visual-effects.mjs",
"scripts/qa-draw-tool.mjs",
"scripts/qa-geo-import.mjs",
@@ -703,7 +702,6 @@
"src/ui/shareRestoration.js",
"src/ui/visualSettings.js",
"src/ui/panelChrome.js",
"src/ui/panelChrome.radioOwnership.test.mjs",
"src/testSupport/readShellSource.mjs",
"src/ui/stateOwners.test.mjs",
"src/layers/flights/records.js",
+5 -2
View File
@@ -125,8 +125,11 @@ export function extractMessages() {
const html = readFileSync(path.join(ROOT, file), 'utf8');
for (const [, tag, attrs, text] of html.matchAll(TAG)) {
const where = `${file} <${tag}>`;
if (/\sdata-i18n(\s|=|$)/.test(attrs))
add(normalize(decodeHtml(text)), where);
if (/\sdata-i18n(\s|=|$)/.test(attrs)) {
const context = attrs.match(/\sdata-i18n-context="([^"]*)"/);
const message = normalize(decodeHtml(text));
add(context ? `${decodeHtml(context[1])}|${message}` : message, where);
}
for (const [, name] of attrs.matchAll(
/\sdata-i18n-(title|aria-label|placeholder)\b/g,
)) {
+272 -12
View File
@@ -439,8 +439,14 @@
"src/ui/slider.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -463,7 +469,6 @@
"src/styles/thermal.js",
"src/styles/anime.js",
"src/styles/noir.js",
"src/styles/snow.js",
"src/i18n/marker.js"
],
"external": ["cesium"]
@@ -479,8 +484,14 @@
"src/localRequestGate.mjs",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -509,8 +520,14 @@
"src/data/layerSnapshot.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js",
@@ -531,8 +548,14 @@
"src/app/stateChannel.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -548,8 +571,14 @@
"src/ui/radioTunerModel.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -568,8 +597,14 @@
"src/ui/cctvPresentation.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js",
@@ -595,8 +630,14 @@
"src/ui/panelRailGeometry.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/layerNames.js"
@@ -630,8 +671,14 @@
"src/ui/panelRailGeometry.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
@@ -647,8 +694,14 @@
"src/ui/actionButton.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
@@ -697,7 +750,6 @@
"src/styles/anime.js",
"src/styles/noir.js",
"src/styles/retro.js",
"src/styles/snow.js",
"src/styles/surveillance.js",
"src/styles/thermal.js",
"src/ui/applicationShell.js",
@@ -806,8 +858,14 @@
"src/data/cameraArrival.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js",
@@ -815,7 +873,8 @@
"src/ui/layerNames.js",
"src/ui/statusText.js",
"src/ui/statusBadge.js",
"src/scopeMaskPolicy.js"
"src/scopeMaskPolicy.js",
"src/visualStylePolicy.js"
],
"external": ["cesium"]
},
@@ -928,11 +987,18 @@
"src/data/cameraArrival.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
"src/ui/unitLabels.js",
"src/visualStylePolicy.js"
],
"external": ["cesium"]
},
@@ -996,8 +1062,14 @@
"src/data/modelEligibility.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
@@ -1045,8 +1117,14 @@
"src/data/modelEligibility.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1108,8 +1186,14 @@
"src/data/inputOwnership.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1138,8 +1222,14 @@
"src/maps/imageryHost.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js",
@@ -1169,8 +1259,14 @@
"src/data/inputOwnership.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1200,8 +1296,14 @@
"src/data/inputOwnership.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
@@ -1234,8 +1336,14 @@
"src/data/inputOwnership.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1277,8 +1385,14 @@
"src/data/infrastructureOverlayEntry.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/layers/installations/classLabel.js"
@@ -1314,8 +1428,14 @@
"src/ui/panelRailGeometry.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
@@ -1365,8 +1485,14 @@
"src/layers/traffic/roadModes.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1395,8 +1521,14 @@
"src/data/cameraSensitivity.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1444,8 +1576,14 @@
"src/layers/cctv/sourcePolicy.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1508,8 +1646,14 @@
"scripts/qa-street-level.mjs",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/layers/streetLevel/providers/mapillary/policy.js"
@@ -1538,8 +1682,14 @@
"src/layers/alpr/floors.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1579,8 +1729,14 @@
"src/data/inputOwnership.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1602,8 +1758,14 @@
"src/data/inputOwnership.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1659,8 +1821,14 @@
"src/maps/imageryHost.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -1722,8 +1890,14 @@
"src/voice/pointerReticle.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/statusText.js",
@@ -2240,7 +2414,6 @@
"src/styles/anime.js",
"src/styles/noir.js",
"src/styles/retro.js",
"src/styles/snow.js",
"src/styles/surveillance.js",
"src/styles/thermal.js",
"src/ui/applicationShell.js",
@@ -2606,8 +2779,14 @@
"src/app/embed.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js",
@@ -2630,7 +2809,8 @@
"src/voice/geminiCaptureWorklet.js",
"src/ui/statusBadge.js",
"src/sceneCommandRelease.js",
"src/scopeMaskPolicy.js"
"src/scopeMaskPolicy.js",
"src/visualStylePolicy.js"
],
"external": [
"hls.js",
@@ -3191,8 +3371,14 @@
"src/sources/infrastructureData.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js",
@@ -3302,8 +3488,14 @@
"src/app/stateChannel.js",
"src/data/cameraArrival.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/i18n/index.js"
@@ -3326,8 +3518,14 @@
"src/ui/uiLifetime.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -3347,7 +3545,6 @@
"src/styles/anime.js",
"src/styles/noir.js",
"src/styles/retro.js",
"src/styles/snow.js",
"src/styles/surveillance.js",
"src/styles/thermal.js",
"src/ui/applicationShortcuts.js",
@@ -3363,12 +3560,19 @@
"src/i18n/marker.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/scopeMaskPolicy.js",
"src/ui/displayControls.js"
"src/ui/displayControls.js",
"src/visualStylePolicy.js"
],
"external": ["cesium"]
},
@@ -3390,8 +3594,14 @@
"src/ui/uiLifetime.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -3482,8 +3692,14 @@
"src/data/routeSteps.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
@@ -3538,8 +3754,14 @@
"src/voice/pointerReticle.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/statusText.js",
@@ -3793,8 +4015,14 @@
"src/sources/capability.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -3837,8 +4065,14 @@
"src/sources/transitHistory.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -3891,8 +4125,14 @@
"src/layers/perimeters/inciweb.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
],
@@ -3908,8 +4148,14 @@
"src/layers/gnss/records.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/statusText.js",
@@ -3937,7 +4183,6 @@
"src/styles/anime.js",
"src/styles/noir.js",
"src/styles/retro.js",
"src/styles/snow.js",
"src/styles/surveillance.js",
"src/styles/thermal.js",
"src/view/index.js",
@@ -3946,7 +4191,8 @@
"src/data/idSwitches.js",
"src/data/detectionPolicy.js",
"src/scopeMaskPolicy.js",
"src/i18n/marker.js"
"src/i18n/marker.js",
"src/visualStylePolicy.js"
],
"external": []
},
@@ -3968,8 +4214,14 @@
"src/i18n/marker.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/data/geoImportImages.js",
@@ -4050,7 +4302,8 @@
"src/tools/views.js",
"src/tools/globePanel.js",
"src/tools/queries/panelRequest.js",
"src/geoDistance.js"
"src/geoDistance.js",
"src/visualStylePolicy.js"
],
"external": ["satellite.js"]
},
@@ -4151,7 +4404,8 @@
"src/view/index.js",
"src/data/idSwitches.js",
"src/data/layerState.js",
"src/data/layerStateTokenReservations.json"
"src/data/layerStateTokenReservations.json",
"src/visualStylePolicy.js"
],
"external": []
},
@@ -4165,8 +4419,14 @@
"src/i18n/marker.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
"src/i18n/locales/it.json",
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/statusText.js",
+19 -19
View File
@@ -1603,7 +1603,7 @@ try {
cockpit.setVisionMode('optical');
current.focus({ preventScroll: true });
const states = [read()];
for (let index = 0; index < 7; index += 1) {
for (let index = 0; index < 6; index += 1) {
current.click();
states.push(read());
}
@@ -1637,36 +1637,36 @@ try {
reentered,
};
});
const cycleModes = ['optical', 'crt', 'nvg', 'thermal', 'anime', 'noir', 'snow', 'optical'];
const cycleLabels = ['NORMAL', 'CRT', 'NVG', 'FLIR', 'ANIME', 'NOIR', 'SNOW', 'NORMAL'];
const cycleModes = ['optical', 'crt', 'nvg', 'thermal', 'anime', 'noir', 'optical'];
const cycleLabels = ['NORMAL', 'CRT', 'NVG', 'FLIR', 'ANIME', 'NOIR', 'NORMAL'];
check(
'Cockpit vision control exposes Normal, CRT, NVG, FLIR, Anime, Noir and Snow before wrapping',
'Cockpit vision control exposes Normal, CRT, NVG, FLIR, Anime and Noir before wrapping',
JSON.stringify(visionCycle.states.map((state) => state.mode)) === JSON.stringify(cycleModes)
&& JSON.stringify(visionCycle.states.map((state) => state.label)) === JSON.stringify(cycleLabels)
&& visionCycle.states[5]?.aria === 'Current cockpit vision style: Noir. Activate for next style.'
&& visionCycle.states[7]?.aria === 'Current cockpit vision style: Normal. Activate for next style.',
&& visionCycle.states[6]?.aria === 'Current cockpit vision style: Normal. Activate for next style.',
JSON.stringify(visionCycle.states.map(({ mode, label, aria }) => ({ mode, label, aria }))),
);
check(
'user vision changes open Display for every temporary treatment and retain selector focus',
visionCycle.states.slice(1, 7).every((state) => state.displayOpen === 'true')
&& visionCycle.states.slice(1, 7).every((state) => state.radioOpen === 'false')
&& visionCycle.states.slice(1, 7).every((state) => state.parametersActive)
&& visionCycle.states.slice(1, 7).every((state) => !state.parametersCollapsed)
&& visionCycle.states.slice(1, 7).every((state) => state.signalCollapsed)
visionCycle.states.slice(1, 6).every((state) => state.displayOpen === 'true')
&& visionCycle.states.slice(1, 6).every((state) => state.radioOpen === 'false')
&& visionCycle.states.slice(1, 6).every((state) => state.parametersActive)
&& visionCycle.states.slice(1, 6).every((state) => !state.parametersCollapsed)
&& visionCycle.states.slice(1, 6).every((state) => state.signalCollapsed)
&& visionCycle.states.every((state) => state.focusRetained),
JSON.stringify(visionCycle.states),
);
check(
'each Cockpit vision mode drives only its own shader and Normal clears them all',
visionCycle.states.slice(1, 7).every((state) => {
visionCycle.states.slice(1, 6).every((state) => {
const target = {
crt: 'retro', nvg: 'surveillance', thermal: 'thermal', anime: 'anime', noir: 'noir', snow: 'snow',
crt: 'retro', nvg: 'surveillance', thermal: 'thermal', anime: 'anime', noir: 'noir',
}[state.mode];
return Object.entries(state.intensities).every(([name, value]) =>
name === target ? value > 0 : value === 0);
})
&& Object.values(visionCycle.states[7].intensities).every((value) => value === 0),
&& Object.values(visionCycle.states[6].intensities).every((value) => value === 0),
JSON.stringify(visionCycle.states.map(({ mode, intensities }) => ({ mode, intensities }))),
);
check(
@@ -1751,7 +1751,7 @@ try {
return state;
}, { variantName: variant, kind: openKind });
for (const variant of ['off', 'minimal', 'tactical', 'operator']) {
for (const variant of ['off', 'minimal', 'tactical']) {
for (const kind of ['display', 'radio']) {
const state = await desktopState(variant, kind);
check(
@@ -2081,7 +2081,7 @@ try {
await page.screenshot({ path: path.join(shotsDir, `cyber-resize-${height}.png`) });
}
}
await page.evaluate(() => window.__godsEyeView.styleManager._setHudVariant('operator'));
await page.evaluate(() => window.__godsEyeView.styleManager._setHudVariant('tactical'));
const signalTransition = await page.evaluate(() => {
const manager = window.__godsEyeView.styleManager;
@@ -2317,13 +2317,13 @@ try {
const styleManager = window.__godsEyeView.styleManager;
const styleBefore = styleManager.activeStyle;
// A Cockpit-only choice that Reset must undo.
styleManager.cockpitView.setVisionMode('snow');
styleManager.cockpitView.setVisionMode('anime');
window.__qaCockpitReset = {
calls: 0,
original: manager.resetToGlobeView,
subjectId: awareness?.getContextSnapshot?.()?.subject?.id || null,
styleBefore,
snowDuringCockpit: styleManager.stages?.snow?.uniforms?.intensity,
animeDuringCockpit: styleManager.stages?.anime?.uniforms?.intensity,
};
manager.resetToGlobeView = function qaCountedCockpitReset(...args) {
window.__qaCockpitReset.calls += 1;
@@ -2360,7 +2360,7 @@ try {
height,
subjectPreserved: Boolean(qa.subjectId && subjectId === qa.subjectId),
styleRestored: gev.styleManager.activeStyle === qa.styleBefore,
snowDuringCockpit: qa.snowDuringCockpit,
animeDuringCockpit: qa.animeDuringCockpit,
// Cockpit-time intensities are Cockpit's own, so check against the map preset.
visionRestored: Object.entries(gev.styleManager.stages || {}).every(([name, stage]) =>
name === gev.styleManager.activeStyle
@@ -2376,7 +2376,7 @@ try {
&& resetState.resetHidden
&& Math.abs(resetState.height - 18_000_000) < 150_000
&& resetState.subjectPreserved
&& resetState.snowDuringCockpit > 0
&& resetState.animeDuringCockpit > 0
&& resetState.styleRestored
&& resetState.visionRestored,
JSON.stringify(resetState),
+5 -5
View File
@@ -175,12 +175,12 @@ const MUTATIONS = [
to: ' /* belt removed */',
},
{
// Anchored past the call so it cannot land on the cockpit disclosure's own
// stopImmediatePropagation() a few lines below and prove nothing about this.
defect: 'the radio disclosure returns to stopPropagation, so one ESC does two things',
// Anchored past the call so it lands on the Cockpit utility disclosure's
// Escape claim and nowhere else.
defect: 'a Cockpit utility disclosure returns to stopPropagation, so later document listeners also act on its ESC',
file: 'radio',
from: ' event.stopImmediatePropagation();\n const escapedFromDisclosure =\n event.target === this._contextRadioToggleBtn ||',
to: ' event.stopPropagation();\n const escapedFromDisclosure =\n event.target === this._contextRadioToggleBtn ||',
from: " event.stopImmediatePropagation();\n const kind = displayOpen ? 'display' : 'radio';",
to: " event.stopPropagation();\n const kind = displayOpen ? 'display' : 'radio';",
},
{
defect: 'the "no timer" decision is deleted, so the next editor re-litigates it blind',
-47
View File
@@ -357,53 +357,6 @@ async function runArbitrationSection(page, { shots, consoleErrors }) {
`classVisible=${afterUncoveredEsc.classVisible} session=${afterUncoveredEsc.session}`,
);
// ── Blocker repro: a control that claims only the KEY ───────────────────
// The compact Radio disclosure closes on ESC from a capture listener bound
// long before this module exists. It called stopPropagation(), which does
// NOT stop later listeners on the same document — so one key closed the
// disclosure AND dismissed the launcher. The card is not hiding behind this
// one and must not yield to it: the key is simply already spoken for.
await open(page, { query: "?welcome=1", errorSink: consoleErrors });
const beforeRadio = await launcherState();
record('the launcher is up before the radio disclosure opens', beforeRadio.onScreen);
const radioOpened = await page.evaluate(() => {
document.getElementById('context-radio-toggle-btn')?.click();
return !!document.getElementById('context-radio-dock')?.classList.contains('disclosure-open');
});
await sleep(250);
record('the compact Radio disclosure opens', radioOpened);
const withRadio = await launcherState();
record(
'a small disclosure is a key contest, not a screen takeover — no yield',
withRadio.classVisible && withRadio.topmost,
`classVisible=${withRadio.classVisible} topmost=${withRadio.topmost}`,
);
await page.keyboard.press('Escape');
await sleep(400);
const radioAfterEsc = await page.evaluate(() => (
!!document.getElementById('context-radio-dock')?.classList.contains('disclosure-open')
));
const launcherAfterRadioEsc = await launcherState();
record('ESC closes the disclosure', radioOpened && !radioAfterEsc,
`disclosure still open=${radioAfterEsc}`);
record(
'that SAME ESC does not also dismiss the launcher',
beforeRadio.onScreen && launcherAfterRadioEsc.classVisible
&& launcherAfterRadioEsc.session !== 'dismissed',
`classVisible=${launcherAfterRadioEsc.classVisible} session=${launcherAfterRadioEsc.session} — one key, one action`,
);
// ...and the NEXT press is the launcher's, so nothing was permanently taken.
await page.keyboard.press('Escape');
await sleep(600);
const escAfterRadioClosed = await launcherState();
record(
'the next ESC belongs to the launcher again',
beforeRadio.onScreen && !escAfterRadioClosed.classVisible
&& escAfterRadioClosed.session === 'dismissed',
`classVisible=${escAfterRadioClosed.classVisible} session=${escAfterRadioClosed.session}`,
);
// ── Deferred reveal: a surface already up when the launcher would show ──
// The class goes on as soon as the app exists, which is well before the
// launcher's reveal (~T+1.9s), so init genuinely sees a screen it does not
+8 -8
View File
@@ -200,8 +200,8 @@ const checkResponsiveStyleFocus = async (width) => {
await page.focus('#control-panel-toggle');
await pressTabs(2); // disclosure -> pin -> Normal
}
for (const [index, style] of ['normal', 'thermal', 'snow'].entries()) {
if (index) await pressTabs(3);
for (const [tabs, style] of [[0, 'normal'], [3, 'thermal'], [2, 'noir']]) {
if (tabs) await pressTabs(tabs);
const state = await styleFocusMetrics();
check(`${width} px ${style} keyboard ring is visible and unclipped`,
hasVisibleStyleFocus(state, style) && state.selectedStyle === 'normal'
@@ -567,13 +567,13 @@ try {
// Visual Styles precede the five Map Source tiles. Keep the existing opening
// handoff to selected OSM, then use the user's real Shift+Tab path into styles.
await pressTabs(5, true);
const snowFocus = await styleFocusMetrics();
check('Shift+Tab reaches Snow with a visible ring while Normal and OSM stay selected',
hasVisibleStyleFocus(snowFocus, 'snow') && !snowFocus.selected
&& snowFocus.selectedStyle === 'normal' && snowFocus.selectedMap === 'osm',
JSON.stringify(snowFocus));
const noirFocus = await styleFocusMetrics();
check('Shift+Tab reaches Noir with a visible ring while Normal and OSM stay selected',
hasVisibleStyleFocus(noirFocus, 'noir') && !noirFocus.selected
&& noirFocus.selectedStyle === 'normal' && noirFocus.selectedMap === 'osm',
JSON.stringify(noirFocus));
await page.screenshot({ path: path.join(shotsDir, 'keyboard-style-focus.png') });
await pressTabs(3, true);
await pressTabs(2, true);
const middleFocus = await styleFocusMetrics();
check('Shift+Tab traverses the middle FLIR style without activating it',
hasVisibleStyleFocus(middleFocus, 'thermal') && !middleFocus.selected
+76 -330
View File
@@ -81,6 +81,32 @@ function check(name, ok, detail = '') {
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// Radio lives only inside Context; a user reaches its controls by expanding
// Context and then Radio. Returns the prior collapse state so a step can put
// the panels back for the scenarios that follow.
async function revealRadioControls(page, targetId) {
const prior = await page.evaluate(async (id) => {
const manager = window.__godsEyeView.styleManager;
const collapsed = (panelId) => document.getElementById(panelId).classList.contains('collapsed');
const state = { context: collapsed('global-context-panel'), radio: collapsed('radio-panel') };
manager.setPanelCollapsed('global-context-panel', false, { explicit: true });
manager.setPanelCollapsed('radio-panel', false, { explicit: true });
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
document.getElementById(id).scrollIntoView({ block: 'nearest' });
return state;
}, targetId);
await page.waitForSelector(`#${targetId}`, { visible: true });
return prior;
}
async function restoreRadioPanels(page, prior) {
await page.evaluate(({ context, radio }) => {
const manager = window.__godsEyeView.styleManager;
manager.setPanelCollapsed('radio-panel', radio);
manager.setPanelCollapsed('global-context-panel', context);
}, prior);
}
// Native pick buffers and clustered overlay entries settle on rendered frames,
// not on wall-clock sleeps, particularly with software rendering.
async function settleRadioFrames(page) {
@@ -285,31 +311,16 @@ async function main() {
window.__godsEyeView.styleManager.setPanelCollapsed('global-context-panel', true);
});
const enablePanels = await revealRadioControls(page, 'radio-enable-btn');
const enableStartedAt = Date.now();
const initialDisclosure = await page.evaluate(() => {
const button = document.getElementById('context-radio-toggle-btn');
const mini = document.getElementById('context-radio-mini');
const enabledBefore = window.__godsEyeView.dataManager.isEnabled('radio');
button.click();
return {
enabledBefore,
enabledAfterDisclosure: window.__godsEyeView.dataManager.isEnabled('radio'),
expanded: button.getAttribute('aria-expanded'),
miniHidden: mini.hidden,
};
});
check(
'Radio launcher discloses compact controls without changing power',
!initialDisclosure.enabledBefore && !initialDisclosure.enabledAfterDisclosure
&& initialDisclosure.expanded === 'true' && !initialDisclosure.miniHidden,
JSON.stringify(initialDisclosure),
);
await page.$eval('#context-radio-mini-enable-btn', (button) => button.click());
await page.click('#radio-enable-btn');
await page.waitForFunction(() => {
const state = window.__godsEyeView.dataManager.layers.get('radio').module.getUIState();
return state.enabled && !state.loading && state.stationCount === 750;
});
const enableElapsedMs = Date.now() - enableStartedAt;
// Later scenarios start from the collapsed rail this step began with.
await restoreRadioPanels(page, enablePanels);
const initial = await page.evaluate(() => {
const module = window.__godsEyeView.dataManager.layers.get('radio').module;
const state = module.getUIState();
@@ -328,9 +339,6 @@ async function main() {
color: entry.dataset.radioColor,
})),
enablePressed: document.getElementById('radio-enable-btn').getAttribute('aria-pressed'),
launcherExpanded: document.getElementById('context-radio-toggle-btn').getAttribute('aria-expanded'),
launcherActive: document.getElementById('context-radio-toggle-btn').classList.contains('active'),
compactEnablePressed: document.getElementById('context-radio-mini-enable-btn').getAttribute('aria-pressed'),
fullPlayEnabled: !document.getElementById('radio-play-btn').disabled,
fullPlayLabel: document.getElementById('radio-play-btn').getAttribute('aria-label'),
tunerVisible: !document.getElementById('radio-tuner').hidden,
@@ -341,9 +349,8 @@ async function main() {
check('750-station catalog renders within the browser budget', initial.state.stationCount === 750 && enableElapsedMs < 5000, `${enableElapsedMs}ms`);
check('enable does not autoplay', initial.plays === 0 && initial.state.audioState === 'stopped');
check(
'compact and detailed Radio power controls reflect the layer manager independently of disclosure',
initial.enablePressed === 'true' && initial.compactEnablePressed === 'true'
&& initial.launcherExpanded === 'true' && initial.launcherActive,
'Radio power control reflects the layer manager after the panels collapse again',
initial.enablePressed === 'true',
);
check('dynamic canonical and genre categories reach the UI', initial.filterOptions >= 10, `${initial.filterOptions} options`);
const musicOption = initial.filterPalette.find((entry) => entry.id === 'music');
@@ -419,14 +426,10 @@ async function main() {
prevDisabled: document.getElementById('radio-prev-btn').disabled,
playDisabled: document.getElementById('radio-play-btn').disabled,
volumeDisabled: document.getElementById('radio-volume').disabled,
disclosureLabel: document.getElementById('context-radio-toggle-btn').getAttribute('aria-label'),
compactPowerLabel: document.getElementById('context-radio-mini-enable-btn').getAttribute('aria-label'),
compactPowerText: document.getElementById('context-radio-mini-enable-btn').textContent,
fullLabel: document.getElementById('radio-enable-btn').getAttribute('aria-label'),
fullText: document.getElementById('radio-enable-btn').textContent,
layerState: document.getElementById('radio-layer-state').textContent,
playbackMessage: document.getElementById('radio-playback-state').textContent,
miniStation: document.getElementById('context-radio-mini-station').textContent,
dataRailText: document.querySelector('[data-layer-id="radio"] .data-toggle-btn')?.textContent,
dataRailState: document.querySelector('[data-layer-id="radio"] .data-toggle-btn')?.dataset.feedState,
dataRailLabel: document.querySelector('[data-layer-id="radio"] .data-toggle-btn')?.getAttribute('aria-label'),
@@ -468,14 +471,10 @@ async function main() {
&& uncertainPresentation.prevDisabled
&& uncertainPresentation.playDisabled
&& uncertainPresentation.volumeDisabled
&& /compact Radio controls/i.test(uncertainPresentation.disclosureLabel)
&& /uncertain/i.test(uncertainPresentation.compactPowerLabel)
&& uncertainPresentation.compactPowerText === 'RECONCILE'
&& /uncertain/i.test(uncertainPresentation.fullLabel)
&& uncertainPresentation.fullText === 'RECONCILE'
&& uncertainPresentation.layerState === 'UNCERTAIN'
&& /uncertain/i.test(uncertainPresentation.playbackMessage)
&& uncertainPresentation.miniStation === 'RADIO STATE UNCERTAIN'
&& uncertainPresentation.dataRailText === 'UNCERTAIN'
&& uncertainPresentation.dataRailState === 'uncertain'
&& /uncertain/i.test(uncertainPresentation.dataRailLabel)
@@ -517,10 +516,6 @@ async function main() {
presentationActive: window.__godsEyeView.dataManager.layers.get('radio').module.getUIState().presentationActive,
markerSourceVisible: window.__godsEyeView.viewer.dataSources.getByName('Radio stations')[0]?.show,
full: document.getElementById('radio-enable-btn').textContent,
disclosure: document.getElementById('context-radio-toggle-btn').getAttribute('aria-label'),
disclosureControls: document.getElementById('context-radio-toggle-btn').getAttribute('aria-controls'),
disclosureExpanded: document.getElementById('context-radio-toggle-btn').getAttribute('aria-expanded'),
compactPower: document.getElementById('context-radio-mini-enable-btn').textContent,
layerState: document.getElementById('radio-layer-state').textContent,
message: document.getElementById('radio-playback-state').textContent,
stopDisabled: document.getElementById('radio-stop-btn').disabled,
@@ -534,8 +529,6 @@ async function main() {
&& disablingPresentation.presentationActive === false
&& disablingPresentation.markerSourceVisible === false
&& disablingPresentation.full === 'DISABLING'
&& /compact Radio controls/i.test(disablingPresentation.disclosure)
&& disablingPresentation.compactPower === 'DISABLING'
&& disablingPresentation.layerState === 'DISABLING'
&& /disabling/i.test(disablingPresentation.message)
&& disablingPresentation.stopDisabled
@@ -592,10 +585,6 @@ async function main() {
presentationActive: window.__godsEyeView.dataManager.layers.get('radio').module.getUIState().presentationActive,
markerSourceVisible: window.__godsEyeView.viewer.dataSources.getByName('Radio stations')[0]?.show,
full: document.getElementById('radio-enable-btn').textContent,
disclosure: document.getElementById('context-radio-toggle-btn').getAttribute('aria-label'),
disclosureControls: document.getElementById('context-radio-toggle-btn').getAttribute('aria-controls'),
disclosureExpanded: document.getElementById('context-radio-toggle-btn').getAttribute('aria-expanded'),
compactPower: document.getElementById('context-radio-mini-enable-btn').textContent,
layerState: document.getElementById('radio-layer-state').textContent,
message: document.getElementById('radio-playback-state').textContent,
tunerHidden: document.getElementById('radio-tuner').hidden,
@@ -610,10 +599,6 @@ async function main() {
&& enablingPresentation.presentationActive === false
&& enablingPresentation.markerSourceVisible === false
&& enablingPresentation.full === 'ENABLING'
&& enablingPresentation.disclosure === 'Go to expanded Radio section'
&& enablingPresentation.disclosureControls === 'radio-panel'
&& enablingPresentation.disclosureExpanded === 'true'
&& enablingPresentation.compactPower === 'ENABLING'
&& enablingPresentation.layerState === 'ENABLING'
&& /enabling/i.test(enablingPresentation.message)
&& enablingPresentation.tunerHidden
@@ -1581,112 +1566,9 @@ async function main() {
JSON.stringify(directoryRefreshContinuity),
);
await page.select('#radio-filter', 'news');
check('launcher enables Radio without forcing Context open', !initial.panelExpanded && !initial.contextExpanded && initial.nestedInContext);
check('enabled Radio stays nested in Context and leaves both panels collapsed', !initial.panelExpanded && !initial.contextExpanded && initial.nestedInContext);
check('full Play is available before a station is selected', !initial.state.selected && initial.fullPlayEnabled && initial.fullPlayLabel === 'Play nearest radio station');
const hoverOnlyDisclosure = await page.evaluate(() => {
const button = document.getElementById('context-radio-toggle-btn');
if (button.getAttribute('aria-expanded') === 'true') button.click();
return {
expanded: button.getAttribute('aria-expanded'),
hidden: document.getElementById('context-radio-mini').hidden,
};
});
await page.hover('#context-radio-toggle-btn');
await sleep(100);
const hoverOnlyVisible = await page.$eval('#context-radio-mini', (mini) => (
!mini.hidden && getComputedStyle(mini).display !== 'none'
));
check(
'hover alone does not disclose compact Radio controls',
hoverOnlyDisclosure.expanded === 'false' && hoverOnlyDisclosure.hidden && !hoverOnlyVisible,
JSON.stringify({ hoverOnlyDisclosure, hoverOnlyVisible }),
);
await page.click('#context-radio-toggle-btn');
await page.waitForFunction(() => {
const button = document.getElementById('context-radio-toggle-btn');
const mini = document.getElementById('context-radio-mini');
return button.getAttribute('aria-expanded') === 'true'
&& !mini.hidden && getComputedStyle(mini).display !== 'none';
});
const compact = await page.evaluate(() => {
const mini = document.getElementById('context-radio-mini');
const toggle = document.getElementById('context-radio-toggle-btn');
const dock = document.getElementById('context-radio-dock');
const miniRect = mini.getBoundingClientRect();
const toggleRect = toggle.getBoundingClientRect();
const miniStyle = getComputedStyle(mini);
return {
visible: miniStyle.display !== 'none' && miniRect.width > 0 && miniRect.height > 0,
display: miniStyle.display,
visibility: miniStyle.visibility,
opacity: miniStyle.opacity,
miniRect: { top: miniRect.top, left: miniRect.left, width: miniRect.width, height: miniRect.height },
toggleRect: { top: toggleRect.top, left: toggleRect.left, width: toggleRect.width, height: toggleRect.height },
dockRect: (() => {
const rect = dock.getBoundingClientRect();
return { top: rect.top, left: rect.left, width: rect.width, height: rect.height };
})(),
offsetParent: mini.offsetParent?.id || mini.offsetParent?.className || null,
prevEnabled: !document.getElementById('context-radio-mini-prev-btn').disabled,
playEnabled: !document.getElementById('context-radio-mini-play-btn').disabled,
playLabel: document.getElementById('context-radio-mini-play-btn').getAttribute('aria-label'),
nextEnabled: !document.getElementById('context-radio-mini-next-btn').disabled,
volumeEnabled: !document.getElementById('context-radio-mini-volume').disabled,
offsetBelowToggle: Math.round(miniRect.top - toggleRect.bottom),
};
});
check(
'explicit launcher disclosure reveals enabled compact Radio controls',
compact.visible && compact.prevEnabled && compact.playEnabled && compact.nextEnabled && compact.volumeEnabled,
JSON.stringify(compact),
);
check('compact Play is available before a station is selected', compact.playLabel === 'Play nearest radio station');
check('compact Radio controls sit 20px below the launcher', compact.offsetBelowToggle === 20, `${compact.offsetBelowToggle}px`);
const compactCloseDisclosure = await page.evaluate(() => {
const launcher = document.getElementById('context-radio-toggle-btn');
const close = document.getElementById('context-radio-mini-close-btn');
close.focus();
close.click();
const result = {
launcherExpanded: launcher.getAttribute('aria-expanded'),
compactHidden: document.getElementById('context-radio-mini').hidden,
focusRestored: document.activeElement === launcher,
radioCollapsed: document.getElementById('radio-panel').classList.contains('collapsed'),
};
launcher.click();
return result;
});
check(
'dedicated compact close hides only the compact Radio disclosure and restores launcher focus',
compactCloseDisclosure.launcherExpanded === 'false'
&& compactCloseDisclosure.compactHidden
&& compactCloseDisclosure.focusRestored
&& compactCloseDisclosure.radioCollapsed,
JSON.stringify(compactCloseDisclosure),
);
const compactTouchDisclosure = await page.evaluate(() => {
const button = document.getElementById('context-radio-toggle-btn');
button.click();
const closed = button.getAttribute('aria-expanded') === 'false'
&& document.getElementById('context-radio-mini').hidden;
button.dispatchEvent(new PointerEvent('pointerdown', {
bubbles: true,
pointerType: 'touch',
isPrimary: true,
}));
button.click();
return {
closed,
reopened: button.getAttribute('aria-expanded') === 'true'
&& !document.getElementById('context-radio-mini').hidden,
};
});
check(
'touch/coarse-pointer activation can close and reopen compact Radio controls',
compactTouchDisclosure.closed && compactTouchDisclosure.reopened,
JSON.stringify(compactTouchDisclosure),
);
const multiExpandedPanelLayout = await page.evaluate(async () => {
const manager = window.__godsEyeView.styleManager;
const ids = ['data-panel', 'scene-panel', 'pp-toggles', 'cctv-panel', 'global-context-panel'];
@@ -2062,7 +1944,6 @@ async function main() {
hudVariant: manager.hud.getVariant(),
displayExpanded: display.getAttribute('aria-expanded') === 'true',
radioExpanded: radio.getAttribute('aria-expanded') === 'true',
contextRadioExpanded: document.getElementById('context-radio-toggle-btn')?.getAttribute('aria-expanded') === 'true',
focusId: document.activeElement?.id || null,
detectionMode: manager.getDetectionState().detectionMode,
models3dEnabled: manager._models3dEnabled,
@@ -2158,11 +2039,13 @@ async function main() {
for (let index = 0; index < 5; index += 1) {
recordLayoutStep();
if (index < 4) {
if (index < 2) {
hudLayout.value = index === 0 ? 'operator' : 'minimal';
hudLayout.dispatchEvent(new Event('change', { bubbles: true }));
} else {
// Off and back on in Minimal, so the toggle must restore a
// non-default layout before the cycle returns to Tactical.
if (index === 1 || index === 2) {
hudToggle.click();
} else {
hudLayout.value = index === 0 ? 'minimal' : 'tactical';
hudLayout.dispatchEvent(new Event('change', { bubbles: true }));
}
await waitForHudSettle();
}
@@ -2287,8 +2170,7 @@ async function main() {
document.getElementById('cockpit-radio-next-btn'),
].every((button) => button?.isConnected && !button.disabled);
const detailsExcluded = !document.querySelector(
'#cockpit-radio-panel #context-radio-details-btn, '
+ '#cockpit-radio-panel #radio-filter, #cockpit-radio-panel #radio-tuner',
'#cockpit-radio-panel #radio-filter, #cockpit-radio-panel #radio-tuner',
);
const scenesHidden = getComputedStyle(scene).display === 'none';
radio.click();
@@ -2331,7 +2213,7 @@ async function main() {
scenesHidden,
mapProviderUtilityStable,
hudInteractionsExact: layoutSteps.map((step) => step.hud).join('>')
=== 'TACTICAL>OPERATOR>MINIMAL>OFF>MINIMAL',
=== 'TACTICAL>MINIMAL>OFF>MINIMAL>TACTICAL',
// The accordion clears the topline, Cockpit cards, and Cesium credits
// across every HUD layout. Its list owns any overflow internally.
leftLaneClearAcrossHudCycle: layoutSteps.every((step) => (
@@ -2381,7 +2263,6 @@ async function main() {
manager.setPanelCollapsed('global-context-panel', prior.contextCollapsed);
if (display.getAttribute('aria-expanded') !== String(prior.displayExpanded)) display.click();
if (radio.getAttribute('aria-expanded') !== String(prior.radioExpanded)) radio.click();
manager._setRadioDisclosure(prior.contextRadioExpanded);
hud.hidden = prior.hudHidden;
signal.hidden = prior.signalHidden;
if (prior.utilityTop) hud.style.setProperty('--cockpit-utility-top', prior.utilityTop);
@@ -2416,7 +2297,6 @@ async function main() {
&& document.getElementById('global-context-panel').classList.contains('collapsed') === prior.contextCollapsed
&& display.getAttribute('aria-expanded') === String(prior.displayExpanded)
&& radio.getAttribute('aria-expanded') === String(prior.radioExpanded)
&& document.getElementById('context-radio-toggle-btn')?.getAttribute('aria-expanded') === String(prior.contextRadioExpanded)
&& hud.hidden === prior.hudHidden
&& signal.hidden === prior.signalHidden
&& intelHud.style.getPropertyValue('transition') === priorHudTransition
@@ -2472,21 +2352,20 @@ async function main() {
check('first Play scenario starts without a leaked Radio selection', firstPlayPrecondition === null,
`selected=${firstPlayPrecondition}`);
await page.select('#radio-filter', 'all');
await page.$eval('#context-radio-toggle-btn', (button) => {
if (button.getAttribute('aria-expanded') !== 'true') button.click();
});
await page.evaluate(() => {
const camera = window.__godsEyeView.viewer.camera;
window.__qaFirstPlayFlyToCalls = [];
window.__qaFirstPlayOriginalFlyTo = camera.flyTo;
camera.flyTo = (options) => window.__qaFirstPlayFlyToCalls.push(options);
document.getElementById('context-radio-toggle-btn').focus();
});
await page.$eval('#context-radio-mini-play-btn', (button) => button.click());
const firstPlayPanels = await revealRadioControls(page, 'radio-play-btn');
await page.focus('#radio-play-btn');
await page.click('#radio-play-btn');
await page.waitForFunction(() => {
const state = window.__godsEyeView.dataManager.layers.get('radio').module.getUIState();
return state.audioState === 'playing' || state.audioState === 'error';
});
await restoreRadioPanels(page, firstPlayPanels);
const postMicroDragPlayback = await page.evaluate(async () => {
const mod = window.__godsEyeView.dataManager.layers.get('radio').module;
let state = mod.getUIState();
@@ -2593,59 +2472,19 @@ async function main() {
);
await page.select('#radio-filter', 'news');
await page.$eval('#context-radio-mini-volume', (input) => {
await page.$eval('#radio-volume', (input) => {
input.value = '35';
input.dispatchEvent(new Event('input', { bubbles: true }));
});
const compactVolume = await page.evaluate(() => ({
const radioVolume = await page.evaluate(() => ({
volume: window.__godsEyeView.dataManager.layers.get('radio').module.getUIState().volume,
full: document.getElementById('radio-volume').value,
mini: document.getElementById('context-radio-mini-volume').value,
label: document.getElementById('radio-volume-value').textContent,
}));
check('compact volume shares the full Radio player state', Math.abs(compactVolume.volume - 0.35) < 0.001 && compactVolume.full === '35' && compactVolume.mini === '35');
const compactDisclosureContinuity = await page.evaluate(async () => {
const radio = window.__godsEyeView.dataManager.layers.get('radio').module;
const manager = window.__godsEyeView.styleManager;
manager.setPanelCollapsed('global-context-panel', true);
manager._setRadioDisclosure(true);
await radio.play({ origin: 'user' });
const before = radio.getUIState();
const disclosure = document.getElementById('context-radio-toggle-btn');
if (disclosure.getAttribute('aria-expanded') !== 'true') disclosure.click();
disclosure.click();
const closed = {
expanded: disclosure.getAttribute('aria-expanded'),
hidden: document.getElementById('context-radio-mini').hidden,
state: radio.getUIState(),
};
disclosure.click();
return {
before,
closed,
reopened: disclosure.getAttribute('aria-expanded'),
miniHidden: document.getElementById('context-radio-mini').hidden,
after: radio.getUIState(),
};
});
check(
'compact disclosure preserves playback, station, filter, and volume continuity',
compactDisclosureContinuity.closed.expanded === 'false'
&& compactDisclosureContinuity.closed.hidden
&& compactDisclosureContinuity.reopened === 'true'
&& !compactDisclosureContinuity.miniHidden
&& compactDisclosureContinuity.before.audioState === 'playing'
&& compactDisclosureContinuity.closed.state.audioState === 'playing'
&& compactDisclosureContinuity.after.audioState === 'playing'
&& compactDisclosureContinuity.after.selected?.id === compactDisclosureContinuity.before.selected?.id
&& compactDisclosureContinuity.after.filter === compactDisclosureContinuity.before.filter
&& compactDisclosureContinuity.after.volume === compactDisclosureContinuity.before.volume,
JSON.stringify({
before: compactDisclosureContinuity.before,
closed: compactDisclosureContinuity.closed,
reopened: compactDisclosureContinuity.reopened,
after: compactDisclosureContinuity.after,
}),
'full Radio volume drives the Radio player state',
Math.abs(radioVolume.volume - 0.35) < 0.001 && radioVolume.full === '35' && radioVolume.label === '35%',
JSON.stringify(radioVolume),
);
const voicePause = await page.evaluate(async () => {
@@ -2709,11 +2548,13 @@ async function main() {
idleVoice.audioState === 'paused',
idleVoice.audioState,
);
const manualPlayTarget = await page.evaluate(() => {
const manualPlayTarget = await page.evaluate(async () => {
const manager = window.__godsEyeView.styleManager;
manager.setPanelCollapsed('global-context-panel', true);
manager._setRadioDisclosure(true);
const button = document.getElementById('context-radio-mini-play-btn');
manager.setPanelCollapsed('global-context-panel', false);
manager.setPanelCollapsed('radio-panel', false);
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
const button = document.getElementById('radio-play-btn');
button.scrollIntoView({ block: 'nearest' });
const rect = button.getBoundingClientRect();
const style = getComputedStyle(button);
return {
@@ -2728,12 +2569,12 @@ async function main() {
};
});
check(
'manual voice takeover targets the visible compact Radio Play control',
'manual voice takeover targets the visible full Radio Play control',
manualPlayTarget.visible && manualPlayTarget.enabled,
JSON.stringify(manualPlayTarget),
);
if (manualPlayTarget.visible && manualPlayTarget.enabled) {
await page.click('#context-radio-mini-play-btn');
await page.click('#radio-play-btn');
await page.waitForFunction(() => (
window.__godsEyeView.dataManager.layers.get('radio').module.getUIState().audioState === 'playing'
&& window.__godsEyeView.voiceCommands.status === 'idle'
@@ -2781,7 +2622,6 @@ async function main() {
await page.evaluate(() => window.__godsEyeView.voiceCommands.setStatus('idle', 'QA complete'));
await page.evaluate(async () => {
const manager = window.__godsEyeView.styleManager;
manager._setRadioDisclosure(false);
manager.setPanelCollapsed('global-context-panel', false);
manager.setPanelCollapsed('radio-panel', false);
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
@@ -3835,22 +3675,22 @@ async function main() {
gev.dataManager.layers.get('flights').enabled = true;
});
await sleep(500);
await page.evaluate(() => window.__godsEyeView.styleManager.setPanelCollapsed('global-context-panel', true));
await page.evaluate(() => {
const button = document.getElementById('context-radio-toggle-btn');
if (button.getAttribute('aria-expanded') !== 'true') button.click();
const manager = window.__godsEyeView.styleManager;
manager.setPanelCollapsed('global-context-panel', false);
manager.setPanelCollapsed('radio-panel', false);
});
await page.waitForSelector('#context-radio-mini', { visible: true });
await page.waitForSelector('#radio-next-btn', { visible: true });
await page.evaluate(() => {
window.__qaRadioCameraHeight = window.__godsEyeView.viewer.camera.positionCartographic.height;
});
const radioOnlyNextDispatched = await page.evaluate(() => {
const button = document.getElementById('context-radio-mini-next-btn');
const button = document.getElementById('radio-next-btn');
if (!button) return false;
button.click();
return true;
});
if (!radioOnlyNextDispatched) throw new Error('Radio compact next button unavailable');
if (!radioOnlyNextDispatched) throw new Error('Radio next button unavailable');
await page.waitForFunction(() => typeof window.__qaRejectRadioPlay === 'function');
await page.evaluate(() => {
const viewer = window.__godsEyeView.viewer;
@@ -3885,7 +3725,7 @@ async function main() {
flyToCalls: window.__qaRadioFlyToCalls.length,
flightTargets,
cameraHeight: window.__qaRadioCameraHeight,
compactPlayLabel: document.getElementById('context-radio-mini-play-btn').getAttribute('aria-label'),
playLabel: document.getElementById('radio-play-btn').getAttribute('aria-label'),
regularMarkerSize: Array.from({ length: window.__godsEyeView.viewer.dataSources.length }, (_, index) => window.__godsEyeView.viewer.dataSources.get(index))
.find((item) => item.name === 'Radio stations')?.entities.values[0]?.point?.pixelSize?.getValue(),
selectedMarkerSize: selectedEntity?.point?.pixelSize?.getValue(),
@@ -3898,7 +3738,7 @@ async function main() {
};
});
check(
'compact Next follows stable directory order and retries its next available station',
'Radio Next follows stable directory order and retries its next available station',
playing.state.selected?.id === playing.expectedFallback?.id
&& playing.calls[0] === playing.expectedPrimary?.streamUrl
&& playing.calls[1] === playing.expectedFallback?.streamUrl,
@@ -3909,9 +3749,9 @@ async function main() {
fallback: playing.expectedFallback?.id,
}),
);
check('compact next is explicit playback and updates play/pause state', playing.calls.length === 2 && playing.compactPlayLabel.startsWith('Pause'));
check('Radio next is explicit playback and updates play/pause state', playing.calls.length === 2 && playing.playLabel.startsWith('Pause'));
check(
'compact next rotates toward its station without changing zoom, including fallback',
'Radio next rotates toward its station without changing zoom, including fallback',
playing.flyToCalls === 2
&& Math.abs(playing.flightTargets[0]?.lat - playing.expectedPrimary?.lat) < 0.001
&& Math.abs(playing.flightTargets[0]?.lon - playing.expectedPrimary?.lon) < 0.001
@@ -3950,11 +3790,11 @@ async function main() {
};
return [
await run('flights', 'radio-next-btn'),
await run('military', 'context-radio-mini-prev-btn'),
await run('military', 'radio-prev-btn'),
];
});
check(
'full and compact Radio navigation preserve Flights/Military camera ownership',
'full Radio Next and Prev navigation preserve Flights/Military camera ownership',
trackedRadioControls.every((sample) => sample.afterStationId
&& sample.afterStationId !== sample.beforeStationId
&& sample.flyToDelta === 0
@@ -3968,7 +3808,7 @@ async function main() {
viewer.trackedEntity = undefined;
window.__qaRadioDelayNextFailure = true;
const beforeFlyToCalls = window.__qaRadioFlyToCalls.length;
document.getElementById('context-radio-mini-next-btn')?.click();
document.getElementById('radio-next-btn')?.click();
return { beforeFlyToCalls };
});
await page.waitForFunction(() => typeof window.__qaRejectRadioPlay === 'function');
@@ -4244,7 +4084,7 @@ async function main() {
window.__godsEyeView.dataManager.layers.get('flights').enabled = false;
});
const radioOnlyNextClicked = await page.evaluate(() => {
const button = document.getElementById('context-radio-mini-next-btn');
const button = document.getElementById('radio-next-btn');
if (!(button instanceof HTMLButtonElement)) return false;
button.click();
return true;
@@ -4508,106 +4348,12 @@ async function main() {
&& companion.radioCollapsed && !companion.contextCollapsed && companion.nested,
);
const expandedContextRadioBefore = await page.evaluate(() => {
const gev = window.__godsEyeView;
const scroller = document.querySelector('#global-context-panel .global-context-panel-inner');
scroller.scrollTop = 0;
const camera = gev.viewer.camera.positionWC;
return {
scrollTop: scroller.scrollTop,
pageY: window.scrollY,
camera: { x: camera.x, y: camera.y, z: camera.z },
plays: window.__qaRadioPlayCalls.length,
};
});
await page.click('#context-radio-toggle-btn');
await page.waitForFunction(() => !document.getElementById('radio-panel').classList.contains('collapsed'));
await page.waitForFunction((priorScroll) => (
document.activeElement?.getAttribute('data-collapse-target') === 'radio-panel'
&& document.querySelector('#global-context-panel .global-context-panel-inner').scrollTop > priorScroll
), { timeout: 10_000 }, expandedContextRadioBefore.scrollTop);
const expandedContextRadioAfter = await page.evaluate(() => {
const gev = window.__godsEyeView;
gev.styleManager._renderRadioState(gev.dataManager.layers.get('radio').module.getUIState());
const launcher = document.getElementById('context-radio-toggle-btn');
const radio = document.getElementById('radio-panel');
const scroller = document.querySelector('#global-context-panel .global-context-panel-inner');
const camera = gev.viewer.camera.positionWC;
return {
ariaControls: launcher.getAttribute('aria-controls'),
expanded: launcher.getAttribute('aria-expanded'),
label: launcher.getAttribute('aria-label'),
radioCollapsed: radio.classList.contains('collapsed'),
compactHidden: document.getElementById('context-radio-mini').hidden,
scrollTop: scroller.scrollTop,
focusTarget: document.activeElement?.getAttribute('data-collapse-target'),
pageY: window.scrollY,
camera: { x: camera.x, y: camera.y, z: camera.z },
plays: window.__qaRadioPlayCalls.length,
state: gev.dataManager.layers.get('radio').module.getUIState().audioState,
};
});
const expandedContextRadioCameraDelta = Math.hypot(
expandedContextRadioAfter.camera.x - expandedContextRadioBefore.camera.x,
expandedContextRadioAfter.camera.y - expandedContextRadioBefore.camera.y,
expandedContextRadioAfter.camera.z - expandedContextRadioBefore.camera.z,
);
check(
'expanded Context Radio icon reveals and scrolls to embedded Radio without an overlay or playback/camera theft',
expandedContextRadioAfter.ariaControls === 'radio-panel'
&& expandedContextRadioAfter.expanded === 'true'
&& expandedContextRadioAfter.label === 'Go to expanded Radio section'
&& !expandedContextRadioAfter.radioCollapsed
&& expandedContextRadioAfter.compactHidden
&& expandedContextRadioAfter.scrollTop > expandedContextRadioBefore.scrollTop
&& expandedContextRadioAfter.focusTarget === 'radio-panel'
&& expandedContextRadioAfter.pageY === expandedContextRadioBefore.pageY
&& expandedContextRadioCameraDelta < 0.01
&& expandedContextRadioAfter.plays === expandedContextRadioBefore.plays
&& expandedContextRadioAfter.state === 'playing',
JSON.stringify({
before: expandedContextRadioBefore,
after: expandedContextRadioAfter,
cameraDelta: expandedContextRadioCameraDelta,
}),
);
await page.evaluate(() => {
const manager = window.__godsEyeView.styleManager;
manager.setPanelCollapsed('global-context-panel', true);
manager._setRadioDisclosure(true);
manager.setPanelCollapsed('global-context-panel', false);
manager.setPanelCollapsed('radio-panel', false);
});
await page.waitForFunction(() => {
const close = document.getElementById('context-radio-mini-close-btn');
const rect = close.getBoundingClientRect();
return !document.getElementById('context-radio-mini').hidden && rect.width > 0 && rect.height > 0;
});
await page.click('#context-radio-mini-close-btn');
const closedCompact = await page.evaluate(() => {
const launcher = document.getElementById('context-radio-toggle-btn');
return {
radioCollapsed: document.getElementById('radio-panel').classList.contains('collapsed'),
compactHidden: document.getElementById('context-radio-mini').hidden,
focusRestored: document.activeElement === launcher,
};
});
await page.click('#context-radio-toggle-btn');
await page.waitForFunction(() => !document.getElementById('context-radio-mini').hidden);
await page.click('#context-radio-details-btn');
const repeatedOpen = await page.evaluate(() => ({
radioCollapsed: document.getElementById('radio-panel').classList.contains('collapsed'),
compactHidden: document.getElementById('context-radio-mini').hidden,
}));
const splitDetailActions = { closedCompact, repeatedOpen };
check(
'collapsed Context keeps compact close and full-panel open as separate stable actions',
!splitDetailActions.closedCompact.radioCollapsed
&& splitDetailActions.closedCompact.compactHidden
&& splitDetailActions.closedCompact.focusRestored
&& !splitDetailActions.repeatedOpen.radioCollapsed
&& splitDetailActions.repeatedOpen.compactHidden,
JSON.stringify(splitDetailActions),
);
await page.waitForFunction(() => !document.getElementById('radio-panel').classList.contains('collapsed'));
await page.click('#global-context-flights-btn');
await page.waitForFunction(() => !document.getElementById('context-flights-view').hidden);
+5 -12
View File
@@ -345,7 +345,7 @@ try {
(await readScope()).feather === 2,
);
await page.click('#scope-toggle');
for (const layout of ['minimal', 'operator', 'cyber', 'tactical']) {
for (const layout of ['minimal', 'cyber', 'tactical']) {
await page.select('#hud-layout-select', layout);
const state = await readScope();
check(
@@ -395,7 +395,7 @@ try {
restored,
);
for (const layout of ['minimal', 'operator', 'tactical']) {
for (const layout of ['minimal', 'tactical']) {
await page.select('#hud-layout-select', layout);
await page.evaluate(() =>
window.__godsEyeView.styleManager.applyVisualState({
@@ -429,20 +429,13 @@ try {
wrappersTransparent: wrappers.every(
(el) => getComputedStyle(el).display === 'contents',
),
operatorFrame:
hud.dataset.variant !== 'operator' ||
(getComputedStyle(hud.querySelector('.hud-corner'))
.backgroundColor === 'rgba(0, 0, 0, 0.36)' &&
getComputedStyle(hud.querySelector('.hud-top-bar'))
.backgroundImage !== 'none'),
};
});
check(
`${layout}: Scope ON retains the original presentation without new backplates`,
original.markerAbsent &&
original.unbacked &&
original.wrappersTransparent &&
original.operatorFrame,
original.wrappersTransparent,
original,
);
check(
@@ -571,7 +564,7 @@ try {
);
});
const broadBackings = [
...hud.querySelectorAll('.hud-corner, .hud-top-bar, .hud-bottom-bar'),
...hud.querySelectorAll('.hud-corner, .hud-bottom-bar'),
].filter(
(el) =>
el.offsetWidth > 0 &&
@@ -656,7 +649,7 @@ try {
}),
);
check('narrow HUD text backings stay within the viewport', narrow);
for (const layout of ['minimal', 'operator', 'tactical']) {
for (const layout of ['minimal', 'tactical']) {
await page.select('#hud-layout-select', layout);
await page.evaluate(() =>
window.__godsEyeView.styleManager.applyVisualState({
-1
View File
@@ -80,7 +80,6 @@ try {
'thermal',
'anime',
'noir',
'snow',
'normal',
]) {
await page.evaluate(