refactor(build): place the views in the page instead of expanding templates

index.html now lists each view's host and the loading screen itself, so
the gev:template expansion plugin, its view list, the loading-screen
template and the ./build/html export are gone. createApplication takes
omitViews for views an application leaves out; their hosts are removed,
and a view an earlier call rendered is unmounted.
This commit is contained in:
Sameh Khamis committed 2026-10-31 17:52:47 -07:00
1 parent f279200af5
commit 5bf797ef71
18 files changed
+176 -174

No files matched your search

-11
View File
@@ -1,11 +0,0 @@
<!-- Intelligence HUD Overlay -->
<div id="intel-hud"></div>
<!-- Loading screen -->
<div id="loading-screen">
<div class="loader-content">
<span class="loader-logo brand-logo" data-logo-gaze data-logo-src="/logo.svg" aria-hidden="true"><img src="/logo.svg" alt="" /></span>
<h2 translate="no">GOD'S EYE <span class="title-accent">VIEW</span></h2>
<p class="loader-status" data-i18n>Initializing photorealistic world...</p>
</div>
</div>
+28 -10
View File
@@ -10,8 +10,9 @@ 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.
* Application views by name. A page places each one with a host element,
* `<div class="gev-view" data-gev-view="NAME"></div>` (`display: contents`),
* where the view's markup belongs.
*/
export const APPLICATION_VIEWS = Object.freeze({
'provider-settings': ProviderSettingsView,
@@ -24,21 +25,38 @@ export const APPLICATION_VIEWS = Object.freeze({
welcome: WelcomeView,
});
const mounted = new WeakSet();
const mounted = new WeakMap();
/**
* Render every application view into its host. Views belong to the page, as
* their static markup did, so they are never unmounted. Call before anything
* their static markup did, so they are unmounted only when a later call omits
* them; omit views before any control code is wired to them. Call before anything
* reads or translates the page; repeated calls are harmless.
* @param {Document} [doc] Page document.
* @param {{omit?: string[]}} [options] Views this page leaves out; their
* hosts are removed, unmounting a view an earlier call rendered.
*/
export function mountApplicationViews(doc = globalThis.document) {
export function mountApplicationViews(
doc = globalThis.document,
{ omit = [] } = {},
) {
for (const name of omit) {
if (!Object.hasOwn(APPLICATION_VIEWS, name))
throw new Error(`Unknown application view: ${name}`);
}
for (const host of doc?.querySelectorAll?.('[data-gev-view]') ?? []) {
if (mounted.has(host)) continue;
const name = host.getAttribute('data-gev-view');
const view = APPLICATION_VIEWS[name];
if (!view) throw new Error(`Unknown application view: ${name}`);
createApp(view).use(applicationPinia()).mount(host);
mounted.add(host);
if (omit.includes(name)) {
mounted.get(host)?.unmount();
mounted.delete(host);
host.remove();
continue;
}
if (mounted.has(host)) continue;
if (!Object.hasOwn(APPLICATION_VIEWS, name))
throw new Error(`Unknown application view: ${name}`);
const app = createApp(APPLICATION_VIEWS[name]).use(applicationPinia());
app.mount(host);
mounted.set(host, app);
}
}
@@ -0,0 +1,77 @@
// src/ui/views/mountApplicationViews.test.mjs — a page's view hosts render
// their views, and an application can leave views out, over happy-dom.
import '../../testSupport/registerDom.mjs';
import test from 'node:test';
import assert from 'node:assert/strict';
import { createApplication } from '../../app/application.js';
import { mountApplicationViews } from './index.js';
function page(names) {
document.body.replaceChildren();
for (const name of names) {
const host = document.createElement('div');
host.className = 'gev-view';
host.setAttribute('data-gev-view', name);
document.body.append(host);
}
}
const hostNames = () =>
[...document.querySelectorAll('[data-gev-view]')].map((host) =>
host.getAttribute('data-gev-view'),
);
test('omitted views lose their hosts and the rest render', () => {
page(['welcome', 'provider-settings']);
mountApplicationViews(document, { omit: ['provider-settings'] });
assert.deepEqual(hostNames(), ['welcome']);
assert.equal(document.getElementById('key-setup-chip'), null);
assert.ok(
document.querySelector('[data-gev-view="welcome"]').children.length,
);
});
test('a view an earlier call rendered is unmounted when a later call omits it', () => {
page(['welcome', 'provider-settings']);
mountApplicationViews(document);
assert.ok(document.getElementById('key-setup-chip'));
mountApplicationViews(document, { omit: ['provider-settings'] });
assert.deepEqual(hostNames(), ['welcome']);
assert.equal(document.getElementById('key-setup-chip'), null);
});
test('unknown view names are refused, in the page or in the omit list', () => {
page([]);
for (const name of ['toString', 'constructor'])
assert.throws(
() => mountApplicationViews(document, { omit: [name] }),
/Unknown application view/,
);
page(['toString']);
assert.throws(
() => mountApplicationViews(document),
/Unknown application view/,
);
assert.throws(
() => mountApplicationViews(document, { omit: ['nope'] }),
/Unknown application view: nope/,
);
page(['nope']);
assert.throws(
() => mountApplicationViews(document),
/Unknown application view: nope/,
);
});
test('createApplication leaves out the views its omitViews names', () => {
page(['welcome', 'provider-settings']);
const noop = () => ({});
createApplication({
createScene: noop,
createControls: noop,
createData: noop,
createTools: noop,
omitViews: ['provider-settings'],
});
assert.deepEqual(hostNames(), ['welcome']);
});