clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

main
atlas / render / boot.ts
boot.ts 3.2K
1// The boot module: the only page JavaScript the seam contributes.2//3// It does three things and nothing else:4//5// REGISTERS the three custom elements. Until this runs, `<atlas-viewport>`6// is an unknown tag that renders nothing and breaks nothing — which is what7// makes the seam deletable rather than merely small.8//9// OPENS the diagnostics seams a headless driver reads (docs/render-seam.md §8).10//11// HOOKS the one after-swap rescan issue #5 §4.3 allows. A morph swap can12// replace the scene node whole; the seam has to be told to look again. That13// is the entire glue budget: one listener, no `hx-on` anywhere, and no14// other behaviour on the page belongs to this lane.15//16// Note what is NOT here: no interaction handlers, no route knowledge, no17// session writes. Everything a reader does that is discrete goes to the18// server as an ordinary request, and comes back as a new scene.1920import { logger } from "./log.ts";21import { AtlasViewport } from "./viewport.ts";22import { AtlasChart } from "./chart/element.ts";23import { AtlasGlobe } from "./globe/element.ts";24import { expose } from "./diagnostics.ts";2526const log = logger("boot");2728/** Register the elements. Safe to call twice; the second call does nothing. */29export function register(): void {30 if (customElements.get("atlas-viewport")) return;31 customElements.define("atlas-chart", AtlasChart);32 customElements.define("atlas-globe", AtlasGlobe);33 customElements.define("atlas-viewport", AtlasViewport);34}3536/** Every viewport on the page. There is one, but nothing here assumes it. */37function viewports(): AtlasViewport[] {38 return [...document.querySelectorAll("atlas-viewport")]39 .filter((element): element is AtlasViewport => element instanceof AtlasViewport);40}4142/**43 * Start the seam.44 *45 * The after-swap hook listens for htmx's own settle event and for a plain46 * custom event, so a page that swaps by some other means — a test, a future47 * framework, a hand-written fetch — has one documented way to say "look48 * again" without this module knowing anything about htmx.49 */50export function boot(): void {51 register();52 const rescan = () => {53 for (const viewport of viewports()) viewport.rescan();54 };55 // Both spellings of the same two events. htmx 4 separates the words with56 // colons -- `htmx:after:swap` -- where htmx 2 camel-cased them, and a seam57 // listening only for the old names is a seam that is never told a swap58 // happened: the scene node is replaced whole, the watcher's observer goes59 // with it, and everything this lane draws quietly stops answering the page.60 // `atlas:rescan` is the plain custom event a page that swaps by some other61 // means can raise without this module knowing anything about htmx.62 const swapped = [63 "htmx:after:swap", "htmx:after:settle",64 "htmx:afterSwap", "htmx:afterSettle",65 "atlas:rescan",66 ];67 for (const name of swapped) document.body.addEventListener(name, rescan);68 for (const viewport of viewports()) expose(viewport);69 log.info("the seam is up", { op: "render", viewports: viewports().length });70}7172if (typeof document !== "undefined") {73 if (document.readyState === "loading") {74 document.addEventListener("DOMContentLoaded", boot, { once: true });75 } else {76 boot();77 }78}