mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
IntelHUD no longer builds #intel-hud with innerHTML or writes its readouts by id: it publishes its readouts, typing summary, REC blink, visibility, layout variant and style colors through its publish option to the gev-hud store, and the intel-hud application view renders them. The corners render from mount so the panel layout controllers register them as obstacles. The vessel layer presents the selected vessel's readout through src/hudReadouts.js, which IntelHUD follows, instead of writing into the HUD. The Cyber sonar state answers a HUD toggle from IntelHUD's visibility rather than the class the view renders on its next update.
181 lines
5.3 KiB
JavaScript
181 lines
5.3 KiB
JavaScript
// The HUD's place lookups are paid Google requests made only to feed the AI
|
|
// summary, so a server without one must not be asked to make them.
|
|
//
|
|
// hud.js imports `mgrs`, which Node's ESM loader cannot read named exports
|
|
// from, so a module hook swaps it for a stub before the import (the same
|
|
// setup as hudPageVisibility.test.mjs).
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
|
import { registerHooks } from 'node:module';
|
|
|
|
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
|
registerHooks({
|
|
resolve(specifier, context, next) {
|
|
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
|
return next(specifier, context);
|
|
},
|
|
load(url, context, next) {
|
|
if (url === MGRS_STUB_URL) {
|
|
return {
|
|
format: 'module',
|
|
shortCircuit: true,
|
|
source:
|
|
'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
|
};
|
|
}
|
|
return next(url, context);
|
|
},
|
|
});
|
|
|
|
const { IntelHUD } = await import('./hud.js');
|
|
|
|
function installPage() {
|
|
const { elements, publish } = hudReadouts();
|
|
const previous = { document: globalThis.document, window: globalThis.window };
|
|
globalThis.document = {
|
|
hidden: false,
|
|
visibilityState: 'visible',
|
|
getElementById: (id) => elements.get(id) ?? null,
|
|
querySelectorAll: () => [],
|
|
addEventListener() {},
|
|
removeEventListener() {},
|
|
};
|
|
globalThis.window ??= {
|
|
setTimeout,
|
|
clearTimeout,
|
|
setInterval,
|
|
clearInterval,
|
|
};
|
|
const viewer = {
|
|
camera: {
|
|
pitch: -Math.PI / 2,
|
|
positionCartographic: { latitude: 0.53, longitude: -1.7, height: 600 },
|
|
computeViewRectangle: () => undefined,
|
|
moveStart: { addEventListener() {}, removeEventListener() {} },
|
|
moveEnd: { addEventListener() {}, removeEventListener() {} },
|
|
},
|
|
};
|
|
return {
|
|
viewer,
|
|
publish,
|
|
restore() {
|
|
for (const [key, value] of Object.entries(previous)) {
|
|
if (value === undefined) delete globalThis[key];
|
|
else globalThis[key] = value;
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
/** A HUD whose place lookup and summary service only count their calls. */
|
|
function createHud(t, summaryService) {
|
|
t.mock.timers.enable({ apis: ['setInterval'] });
|
|
const page = installPage();
|
|
const hud = new IntelHUD(page.viewer, {
|
|
summaryService,
|
|
publish: page.publish,
|
|
});
|
|
t.after(() => {
|
|
hud.destroy();
|
|
page.restore();
|
|
});
|
|
const calls = { context: 0 };
|
|
hud._latestMetrics = { altM: 600 };
|
|
hud._summaryContext = async () => {
|
|
calls.context += 1;
|
|
return { placeLabels: ['Austin'], streetLabels: [], nearbyPlaceLabels: [] };
|
|
};
|
|
return { hud, calls };
|
|
}
|
|
|
|
test('a server that reports no AI summary gets no place lookups', async (t) => {
|
|
const service = {
|
|
calls: 0,
|
|
configured: async () => false,
|
|
async summarize() {
|
|
this.calls += 1;
|
|
return { ok: true, status: 200, data: { summary: 'never' } };
|
|
},
|
|
};
|
|
const { hud, calls } = createHud(t, service);
|
|
await hud._updateSummary(false, true);
|
|
hud._markSummaryDirty();
|
|
await hud._updateSummary(false);
|
|
assert.equal(calls.context, 0);
|
|
assert.equal(service.calls, 0);
|
|
});
|
|
|
|
test('an unconfigured reply stops the lookups for the rest of the page', async (t) => {
|
|
const service = {
|
|
calls: 0,
|
|
async summarize() {
|
|
this.calls += 1;
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
data: {
|
|
configured: false,
|
|
code: 'OPENAI_NOT_CONFIGURED',
|
|
error: null,
|
|
summary: null,
|
|
},
|
|
};
|
|
},
|
|
};
|
|
const { hud, calls } = createHud(t, service);
|
|
await hud._updateSummary(false, true);
|
|
assert.equal(calls.context, 1, 'a server without the probe is asked once');
|
|
hud._markSummaryDirty();
|
|
await hud._updateSummary(false, true);
|
|
assert.equal(calls.context, 1);
|
|
assert.equal(service.calls, 1);
|
|
});
|
|
|
|
test('a configured or unknown summary keeps looking places up', async (t) => {
|
|
for (const configured of [true, null, undefined]) {
|
|
await t.test(String(configured), async (st) => {
|
|
const service = {
|
|
configured:
|
|
configured === undefined ? undefined : async () => configured,
|
|
summarize: async () => ({
|
|
ok: true,
|
|
status: 200,
|
|
data: { summary: 'Austin capitol district live' },
|
|
}),
|
|
};
|
|
const { hud, calls } = createHud(st, service);
|
|
await hud._updateSummary(false, true);
|
|
assert.equal(calls.context, 1);
|
|
});
|
|
}
|
|
});
|
|
|
|
test('an unchanged context restores its accepted reply over the fallback line', async (t) => {
|
|
const service = {
|
|
calls: 0,
|
|
configured: async () => true,
|
|
async summarize() {
|
|
this.calls += 1;
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
data: { summary: 'Austin capitol district live' },
|
|
};
|
|
},
|
|
};
|
|
const { hud } = createHud(t, service);
|
|
const shown = () =>
|
|
globalThis.document.getElementById('hud-summary').textContent;
|
|
await hud._updateSummary(false, true);
|
|
assert.equal(shown(), 'Austin capitol district live');
|
|
|
|
// A small pan settles: the fallback line is painted and the reused labels
|
|
// give the same context, so no new request is made.
|
|
hud._setSummaryText('fallback line', false);
|
|
hud._markSummaryDirty();
|
|
await hud._updateSummary(false);
|
|
assert.equal(service.calls, 1);
|
|
assert.equal(shown(), 'Austin capitol district live');
|
|
});
|