mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(ui): render every application template except the loading screen as a Vue view
The scene chrome, cockpit, Display controls, command dock, layer panels, Context rail, welcome card and Provider Settings move verbatim into src/ui/views and mount into their hosts before the page is translated and wired. Each view renders the same elements, attributes and text as its template; Vue keeps whitespace between elements as one space, and no view markup sits under a white-space: pre rule. Template asset URLs stay literal, view files are left out of Prettier so the markup is not reflowed, and the Node component loader moves to build/ and is exported as build/register-vue for host test runners. Hosts that do not initialize Core's locale pass localePicker: false instead of editing the markup.
This commit is contained in:
1 parent
9d24945143
commit
6802c9c8b0
46 files changed
+258
-97
No files matched your search
@@ -77,6 +77,8 @@ export class StyleManager extends ShellFacade {
|
||||
/**
|
||||
* @param {Cesium.Viewer} viewer - The CesiumJS viewer instance.
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.localePicker=true] - false removes the language
|
||||
* picker, for a host that does not initialize Core's locale.
|
||||
*/
|
||||
constructor(
|
||||
viewer,
|
||||
@@ -86,9 +88,11 @@ export class StyleManager extends ShellFacade {
|
||||
services,
|
||||
requestServices,
|
||||
initialShareCode = null,
|
||||
localePicker = true,
|
||||
} = {},
|
||||
) {
|
||||
super();
|
||||
this._localePickerEnabled = localePicker;
|
||||
const {
|
||||
IntelHUD,
|
||||
ShareLinkManager,
|
||||
@@ -763,6 +767,10 @@ export class StyleManager extends ShellFacade {
|
||||
];
|
||||
const root = globalThis.document?.getElementById?.('locale-picker');
|
||||
if (!root) return;
|
||||
if (this._localePickerEnabled === false) {
|
||||
root.remove();
|
||||
return;
|
||||
}
|
||||
this._localePicker = mountLocalePicker({
|
||||
root,
|
||||
current: getLocale(),
|
||||
|
||||
@@ -82,7 +82,7 @@ test('real runner and facade use visual settings, preserve visibility and return
|
||||
});
|
||||
|
||||
test('native sonar sliders represent every accepted integer without browser snapping', () => {
|
||||
const html = readFileSync(new URL('./templates/display-controls.html', import.meta.url), 'utf8');
|
||||
const html = readFileSync(new URL('./views/DisplayControlsView.vue', import.meta.url), 'utf8');
|
||||
for (const name of ['rings', 'range', 'intensity', 'opacity', 'sector']) {
|
||||
assert.match(html, new RegExp(`id="cyber-sonar-${name}"[^>]*step="1"`));
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ import { VisualSettings } from './visualSettings.js';
|
||||
import { STYLES } from './visualPresets.js';
|
||||
|
||||
test('CCTV and Context keep their headers outside Cyber-only scroll bodies', () => {
|
||||
for (const file of ['layer-panels.html', 'context.html']) {
|
||||
for (const file of ['LayerPanelsView.vue', 'ContextView.vue']) {
|
||||
const markup = readFileSync(
|
||||
new URL(`./templates/${file}`, import.meta.url),
|
||||
new URL(`./views/${file}`, import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(markup, /class="cyber-panel-body" data-rail-scroller/);
|
||||
@@ -647,7 +647,7 @@ test('Cyber visual defaults update the style, thermal palette and render state',
|
||||
});
|
||||
|
||||
test('the Display selector, voice schema and final stylesheet expose Cyber', () => {
|
||||
const display = read('./templates/display-controls.html');
|
||||
const display = read('./views/DisplayControlsView.vue');
|
||||
const stylesheet = read('../../style.css');
|
||||
const rightRail = read('./rightPanelRail.js');
|
||||
const hud = read('../hud.js');
|
||||
@@ -877,7 +877,7 @@ test('Cyber panels retain clean edges without the added footer band', () => {
|
||||
|
||||
test('Cyber keeps Sonar on/off without the removed CSS effect switch or dimmer', () => {
|
||||
const template = readFileSync(
|
||||
new URL('./templates/display-controls.html', import.meta.url),
|
||||
new URL('./views/DisplayControlsView.vue', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(template, /id="cyber-sonar-toggle"/);
|
||||
|
||||
@@ -439,7 +439,7 @@ test('the application composition wires the import and owns its teardown', () =>
|
||||
);
|
||||
assert.ok(tools.indexOf('defer(() => geoFileImport?.destroy());') > importAt);
|
||||
const markup = fs.readFileSync(
|
||||
new URL('./templates/display-controls.html', import.meta.url),
|
||||
new URL('./views/DisplayControlsView.vue', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
for (const id of [
|
||||
|
||||
@@ -136,3 +136,21 @@ test('the language already active neither asks nor reloads', () => {
|
||||
view.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('a host without Core locales removes the picker instead of mounting it', () => {
|
||||
const previous = globalThis.document;
|
||||
let removed = 0;
|
||||
const root = { remove: () => removed++ };
|
||||
globalThis.document = {
|
||||
getElementById: (id) => (id === 'locale-picker' ? root : null),
|
||||
};
|
||||
const shell = { services: {}, _localePickerEnabled: false };
|
||||
try {
|
||||
StyleManager.prototype._initLocaleSelector.call(shell);
|
||||
assert.equal(removed, 1);
|
||||
assert.equal(shell._localePicker, undefined);
|
||||
} finally {
|
||||
for (const unregister of shell._unsavedWorkChecks) unregister();
|
||||
globalThis.document = previous;
|
||||
}
|
||||
});
|
||||
@@ -11,8 +11,8 @@ const read = (path) => readFileSync(new URL(path, import.meta.url), 'utf8');
|
||||
test('every scroller the rail measuring pass lifts restores its scroll offset', () => {
|
||||
const css = read('./styles/layers.css');
|
||||
const templates = [
|
||||
read('./templates/context.html'),
|
||||
read('./templates/layer-panels.html'),
|
||||
read('./views/ContextView.vue'),
|
||||
read('./views/LayerPanelsView.vue'),
|
||||
].join('\n');
|
||||
const lifted = [
|
||||
...css.matchAll(
|
||||
|
||||
@@ -190,7 +190,7 @@ class FakeElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** The static rail panel skeleton from templates/context.html. */
|
||||
/** The static rail panel skeleton from views/ContextView.vue. */
|
||||
function fakeDocument() {
|
||||
const document = {
|
||||
documentElement: { clientWidth: 1200 },
|
||||
|
||||
@@ -988,7 +988,7 @@ test('a note under SINCE says what date it compares, only while one is given', (
|
||||
assert.equal(note().hidden, true);
|
||||
// The panel's own markup has it, read out with the slider.
|
||||
const template = fs.readFileSync(
|
||||
new URL('./templates/layer-panels.html', import.meta.url),
|
||||
new URL('./views/LayerPanelsView.vue', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(template, /<small id="sl-since-note"[^>]* hidden>/);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<!-- Aircraft first-person view. It can be entered from the visible view switcher. -->
|
||||
<section id="cockpit-hud" aria-label="Aircraft cockpit view" hidden data-i18n-aria-label>
|
||||
<!-- One optical coordinate system: every targeting mark shares this center.
|
||||
@@ -326,3 +327,4 @@
|
||||
<span data-i18n>EXIT COCKPIT</span>
|
||||
</button>
|
||||
</nav>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<!-- Unified bottom command dock: locations, voice (injected at runtime), visual presets -->
|
||||
<div id="command-dock" aria-label="Navigation, voice, and visual preset controls" data-i18n-aria-label>
|
||||
<!-- Control panel -->
|
||||
@@ -91,3 +92,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<!-- Global Context is deliberately independent from the left layer accordion. -->
|
||||
<aside id="right-context-rail">
|
||||
<div id="weather-panel" class="panel-collapsible" data-panel-id="weather-panel" hidden>
|
||||
@@ -214,3 +215,4 @@
|
||||
</aside>
|
||||
|
||||
<div id="scene-runtime"></div>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<!-- Post-processing toggles (top-right) -->
|
||||
<div id="pp-toggles" class="panel-collapsible collapsed" data-panel-id="pp-toggles">
|
||||
<div class="compact pp-header-row">
|
||||
@@ -184,3 +185,4 @@
|
||||
</div>
|
||||
</div>
|
||||
<button id="clean-view-exit" title="Return UI controls" data-i18n-title data-i18n>EXIT CLEAN VIEW</button>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<div id="left-panel-stack">
|
||||
<!-- Data Layer Toggle Panel -->
|
||||
<div id="data-panel" class="panel-collapsible collapsed active" data-panel-id="data-panel">
|
||||
@@ -185,3 +186,4 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<!-- POWER UP — in-app key setup. Dev-server only: src/keySetup.js removes
|
||||
both the chip and this dialog outright when /api/setup/status is not
|
||||
there to answer (prod builds, LAN visitors). Rows are rendered from
|
||||
@@ -28,3 +29,4 @@
|
||||
</div>
|
||||
<p class="key-setup-note" data-key-setup-status role="status" aria-live="polite" data-i18n>The Google Maps key buys the photorealistic planet — everything else stacks on top.</p>
|
||||
</aside>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<div id="cesiumContainer"></div>
|
||||
<div id="world-overlay-root" aria-hidden="true">
|
||||
<canvas id="world-overlay-canvas"></canvas>
|
||||
@@ -59,3 +60,4 @@
|
||||
<div id="safe-frame-overlay">
|
||||
<div id="safe-frame-box"></div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<!-- Explicit mission choice on a fresh session. Live feeds are not auto-enabled
|
||||
until the visitor asks; shared links skip this surface entirely. It returns
|
||||
every session until "Don't show this again" is ticked — see
|
||||
@@ -56,3 +57,4 @@
|
||||
a live region that also holds layout makes the row jump as it speaks. -->
|
||||
<p class="first-run-note" data-first-run-status role="status" aria-live="polite" data-i18n>Tip: the GEV MIC button in the dock lets you talk to the map.</p>
|
||||
</aside>
|
||||
</template>
|
||||
+18
-1
@@ -1,11 +1,28 @@
|
||||
import { createApp } from 'vue';
|
||||
import { applicationPinia } from '../vueRuntime.js';
|
||||
import WelcomeView from './WelcomeView.vue';
|
||||
import ContextView from './ContextView.vue';
|
||||
import LayerPanelsView from './LayerPanelsView.vue';
|
||||
import CommandDockView from './CommandDockView.vue';
|
||||
import DisplayControlsView from './DisplayControlsView.vue';
|
||||
import CockpitView from './CockpitView.vue';
|
||||
import SceneChromeView from './SceneChromeView.vue';
|
||||
import ProviderSettingsView from './ProviderSettingsView.vue';
|
||||
|
||||
/**
|
||||
* Application views by template name (build/applicationViews.js lists the
|
||||
* same names). Each renders the markup its static template held.
|
||||
*/
|
||||
export const APPLICATION_VIEWS = Object.freeze({});
|
||||
export const APPLICATION_VIEWS = Object.freeze({
|
||||
'provider-settings': ProviderSettingsView,
|
||||
'scene-chrome': SceneChromeView,
|
||||
cockpit: CockpitView,
|
||||
'display-controls': DisplayControlsView,
|
||||
'command-dock': CommandDockView,
|
||||
'layer-panels': LayerPanelsView,
|
||||
context: ContextView,
|
||||
welcome: WelcomeView,
|
||||
});
|
||||
|
||||
const mounted = new WeakSet();
|
||||
|
||||
|
||||
Reference in new issue
Block a user