feat(hud): Cyber HUD theme with GPU contact sonar (#706)

Add the opt-in Cyber HUD layout with coordinated map and cockpit panel
styling, GPU-only contact sonar (rings, range, power, opacity, sector) with
native-rendering fallback, and set_cyber_sonar voice controls. Sonar visual
style inspired by kk376's tactical naval sonar (#214).

Co-authored-by: Sameh Khamis <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-09-23 20:31:56 -07:00
1 parent f913066cf2
commit 513dc87a0e
74 files changed
+7320 -90

No files matched your search

+35
View File
@@ -4,8 +4,43 @@ import {
collectKeyUpdates,
keySetupChipLabel,
stripKeylessBasemapFromHash,
bindKeySetupPlacement,
} from './keySetup.js';
test('setup placement moves the same button only in Cyber and disconnects on teardown', () => {
const home = {};
const chip = { parentNode: home, hidden: true };
const root = { parentNode: home, before: (node) => { node.parentNode = home; } };
let moves = 0;
const toolbar = { append: (node) => { moves++; node.parentNode = toolbar; } };
const theme = { dataset: { uiTheme: 'tactical' } };
let notify, disconnected = false;
class Observer {
constructor(callback) { notify = callback; }
observe(target, options) {
assert.equal(target, theme);
assert.deepEqual(options.attributeFilter, ['data-ui-theme']);
}
disconnect() { disconnected = true; }
}
const dispose = bindKeySetupPlacement({
documentElement: theme,
getElementById: () => toolbar,
defaultView: { MutationObserver: Observer },
}, chip, root);
assert.equal(chip.parentNode, home);
for (const variant of ['cyber', 'operator', 'cyber', 'minimal']) {
theme.dataset.uiTheme = variant;
notify();
assert.equal(chip.parentNode, variant === 'cyber' ? toolbar : home);
notify();
}
assert.equal(moves, 2, 'identical theme sync does not reparent again');
assert.equal(chip.hidden, true, 'placement cannot reveal a retired button');
dispose();
assert.equal(disconnected, true);
});
test('the chip counts what is missing, and retires the count at zero', () => {
assert.equal(keySetupChipLabel({ setCount: 0, total: 8 }), 'POWER UP · 8 KEYS WAITING');
assert.equal(keySetupChipLabel({ setCount: 7, total: 8 }), 'POWER UP · 1 KEY WAITING');