Preview: on-screen error overlay and non-fatal ghost build failures

This commit is contained in:
2026-07-24 15:45:53 +10:00
parent 2e37ca1001
commit fc3d035b20
+23 -1
View File
@@ -34,6 +34,22 @@ document.head.insertAdjacentHTML('beforeend', `<style>${styles}
</style>`);
await requireAuth();
// Surface client-side errors on screen — a silent throw here used to leave the
// panel stuck on "Waiting for server…" with no clue why.
function showError(msg) {
let el = document.getElementById('perr');
if (!el) {
el = document.createElement('div');
el.id = 'perr';
el.style.cssText = 'position:fixed;bottom:0;left:0;right:0;max-height:35vh;overflow:auto;' +
'background:rgba(120,0,0,.9);color:#fff;font:12px monospace;padding:8px;z-index:9999;white-space:pre-wrap';
document.body.appendChild(el);
}
el.textContent += msg + '\n';
}
window.addEventListener('error', e => showError('[error] ' + e.message + (e.filename ? ` (${e.filename}:${e.lineno})` : '')));
window.addEventListener('unhandledrejection', e => showError('[rejection] ' + (e.reason?.message || e.reason)));
const COLORS = { Red: '#ff2678', Yellow: '#fff35d', Blue: '#51eaf1' };
const app = document.getElementById('app');
app.innerHTML = `${nav('preview')}
@@ -135,7 +151,13 @@ const clockSamples = []; let clockOffset = 0;
async function addGhost(rec) {
if (active.has(rec.uid)) return;
const group = await buildGhost(rec, gradients, modelManifest, characters);
let group;
try {
group = await buildGhost(rec, gradients, modelManifest, characters);
} catch (e) {
showError(`ghost "${rec.name}" failed to build: ${e.message}`);
return;
}
group.userData.setHeight(ghostHeight);
const label = textSprite(rec.name, COLORS[rec.color] || '#fff');
label.scale.set(20, 5, 1);