// Serves a build over HTTP for testing in a browser. // // npm run serve # build/web-mobile on http://localhost:8123 // npm run serve -- 9000 # a different port // // Also serves one route of its own, `/_aspects`, which loads the game in fixed // size iframes at the portrait extremes docs/UI.md asks for (4:3 to 21:9). The // engine resizes itself to fill whatever window it is in, so an iframe is the // only straightforward way to see the layout at a shape the desktop window is // not — and the harness lives here rather than in the build folder because a // rebuild wipes everything inside it. import { createServer } from 'node:http'; import { readFile } from 'node:fs/promises'; import { extname, join, normalize, resolve } from 'node:path'; const ROOT = resolve(process.argv[3] ?? 'build/web-mobile'); const PORT = Number(process.argv[2] ?? 8123); const TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.mjs': 'text/javascript', '.json': 'application/json', '.css': 'text/css', '.png': 'image/png', '.jpg': 'image/jpeg', '.webp': 'image/webp', '.wasm': 'application/wasm', '.bin': 'application/octet-stream', }; /** width:height as a portrait phone reports it, from the widest to the tallest. */ const RATIOS = [ ['4:3 — widest', 3 / 4], ['16:9', 9 / 16], ['21:9 — tallest', 9 / 21], ]; function aspectsPage(height, only) { // Booting three engines at once is slow enough to be painful; `?only=16:9` // renders a single viewport when that is all you need. const wanted = only ? RATIOS.filter(([label]) => label.toLowerCase().startsWith(only.toLowerCase())) : RATIOS; const cells = (wanted.length > 0 ? wanted : RATIOS).map(([label, ratio]) => { const width = Math.round(height * ratio); return `