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:
Sameh Khamis committed 2026-10-30 17:47:52 -07:00
1 parent 9d24945143
commit 6802c9c8b0
46 files changed
+258 -97

No files matched your search

+10 -1
View File
@@ -4,4 +4,13 @@
* `mountApplicationViews()` renders them before the page is wired. The
* loading screen stays static so it paints before any script runs.
*/
export const APPLICATION_VIEW_NAMES = Object.freeze([]);
export const APPLICATION_VIEW_NAMES = Object.freeze([
'provider-settings',
'scene-chrome',
'cockpit',
'display-controls',
'command-dock',
'layer-panels',
'context',
'welcome',
]);
+5
View File
@@ -0,0 +1,5 @@
import { register } from 'node:module';
// Lets Node import single-file components: `node --import=<this file>`.
// Hosts reach it as `<core package>/build/register-vue`.
register('./vueLoader.js', import.meta.url);
+49
View File
@@ -0,0 +1,49 @@
import { readFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { compileScript, compileTemplate, parse } from 'vue/compiler-sfc';
import { VUE_TEMPLATE_OPTIONS } from './vueOptions.js';
/**
* Compile a single-file component to an ES module for Node, so unit tests (the
* application's and a host's) can import components without a bundler.
* Styles are dropped: tests read the global stylesheet separately.
*/
export function compileVueModule(source, filename) {
const { descriptor, errors } = parse(source, { filename });
if (errors.length) throw errors[0];
const id = filename;
if (descriptor.scriptSetup) {
return compileScript(descriptor, {
id,
inlineTemplate: true,
templateOptions: VUE_TEMPLATE_OPTIONS,
}).content;
}
const script = descriptor.script
? compileScript(descriptor, { id, genDefaultAs: '__sfc__' }).content
: 'const __sfc__ = {};';
let template = '';
if (descriptor.template) {
const compiled = compileTemplate({
source: descriptor.template.content,
filename,
id,
...VUE_TEMPLATE_OPTIONS,
});
if (compiled.errors.length) throw compiled.errors[0];
template = compiled.code;
}
return `${script}\n${template}\nif (typeof render === 'function') __sfc__.render = render;\nexport default __sfc__;\n`;
}
/** Node module hook: load `.vue` files as compiled modules. */
export async function load(url, context, nextLoad) {
if (!url.endsWith('.vue')) return nextLoad(url, context);
const filename = fileURLToPath(url);
const source = await readFile(filename, 'utf8');
return {
format: 'module',
source: compileVueModule(source, filename),
shortCircuit: true,
};
}
+13 -4
View File
@@ -1,11 +1,20 @@
/**
* Template compiler options shared by the browser build, the boundary check
* and the unit-test loader. Whitespace is preserved so a view renders exactly
* the DOM its static markup did: inline layouts depend on text between nodes.
* and the unit-test loader. Whitespace between nodes is kept so a view renders
* as its static markup did: inline layouts depend on text between nodes.
*/
export const VUE_COMPILER_OPTIONS = Object.freeze({ whitespace: 'preserve' });
const VUE_COMPILER_OPTIONS = Object.freeze({ whitespace: 'preserve' });
/**
* SFC template options. Asset URLs such as `/pin.svg` stay literal, as they
* were in the static markup, instead of becoming module imports.
*/
export const VUE_TEMPLATE_OPTIONS = Object.freeze({
compilerOptions: VUE_COMPILER_OPTIONS,
transformAssetUrls: false,
});
/** `@vitejs/plugin-vue` options for application builds. */
export const VUE_PLUGIN_OPTIONS = Object.freeze({
template: { compilerOptions: VUE_COMPILER_OPTIONS },
template: VUE_TEMPLATE_OPTIONS,
});