//! Offline-only web UI. Everything the browser needs (HTML, CSS, JS, SVG //! logo) is embedded in the compiled binary via `include_str!` / //! `include_bytes!`. No CDN, no external fonts, no remote images — //! OpenPXE renders identically on an air-gapped network. //! //! Layout follows the Netbox Labs pattern: dark left sidebar with primary //! nav, top bar with secondary tabs, card-dense content panels. #![forbid(unsafe_code)] /// Render the top-level page. /// /// * `base_url` is interpolated into the footer so operators can see at /// a glance what URL clients are PXE-booting from. /// * `asset_version` is appended as `?v=…` to every asset URL so each /// release ships with brand-new asset URLs — browsers (and any /// intermediary proxy) can't keep serving last release's `app.js` /// when we know the new one is incompatible. Combined with /// `Cache-Control: no-cache, must-revalidate` on the asset handlers, /// the worst-case caching window is one version. #[must_use] pub fn index_html(base_url: &str, asset_version: &str) -> String { INDEX_HTML .replace("{{BASE_URL}}", base_url) .replace("{{ASSET_VERSION}}", asset_version) } #[must_use] pub fn app_js() -> &'static str { APP_JS } #[must_use] pub fn app_css() -> &'static str { APP_CSS } #[must_use] pub fn logo_svg() -> &'static str { LOGO_SVG } /// Larger, faster-cycling rainbow disc — used for the page-load /// transition and the imaging-progress widget on Dashboard / Queue. /// Pure SVG + SMIL, no JS, no GIF. #[must_use] pub fn loader_svg() -> &'static str { LOADER_SVG } const INDEX_HTML: &str = include_str!("index.html"); const APP_CSS: &str = include_str!("app.css"); const APP_JS: &str = include_str!("app.js"); const LOGO_SVG: &str = include_str!("logo.svg"); const LOADER_SVG: &str = include_str!("loader.svg");