/* catcrafts.net Copyright (C) 2026 Catcrafts The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ // Browser-side page chrome and content loading. // // This partition is the wasm half of the split: it owns the DOM and the VFS, // and calls into Catcrafts.Shared for every piece of markup. Nothing in here // builds HTML by hand — that all lives in Catcrafts.Shared:Views so the server // renders byte-identical pages. export module Catcrafts:Views; import Crafter.Graphics; import Catcrafts.Shared; import std; using namespace Crafter; export namespace Catcrafts { // Builds the persistent page chrome (header /
/ footer) // under . Owns the root element for the page's lifetime so its // children don't get yanked out of the DOM. Must be called from main() // — the Crafter.Graphics Dom bridge is only safe to use after wasm // instantiation hands control to user code. // // When SSR lands this becomes an adopt step rather than a build step: the // chrome will already be in the document and re-creating it would discard // server-rendered markup and flash. Keeping chrome construction separate // from route rendering is what keeps that change local to this function. void InitializePage(); // Per-render scratch ref to the
content container created by // InitializePage(). Looking it up fresh on every call costs one // document.getElementById; in return we don't have to thread a long-lived // reference across module boundaries. inline Dom::HtmlElementPtr MainContent() { return Dom::HtmlElementPtr("main"); } // Site content, parsed once at startup from the bundle. Crafter.Build // copies content/*.json to the bundle root and runtime.js fetches every // files.json entry into memory before _start, so the reads behind this are // plain synchronous ifstreams that never touch the network. const Views::SiteContent& SiteData(); // Read a file from the wasm VFS by bundle-root name. Returns empty on // failure: a missing content file degrades to an empty section rather than // a broken page. std::string ReadBundleFile(std::string_view name); // document.title is reachable only through Window::SetTitle, so the router // needs a Window to set a per-route title. Rather than thread one through // every render call, main() registers the live Window once and the router // calls SetDocumentTitle. A null window makes SetDocumentTitle a no-op, so // ordering mistakes degrade to a stale title instead of a crash. void SetActiveWindow(Window* window); void SetDocumentTitle(std::string_view title); // True when the document arrived server-rendered, i.e. InitializePage // adopted existing chrome rather than building it. // // The distinction matters: on an SSR'd page the DOM is already correct for // the URL and must not be re-rendered, while on the static fallback shell // (served when the backend is down) the app has to render everything // itself. Both paths exist, and this is how the router tells them apart. bool AdoptedSsr(); }