//! The files a described document asks for that are not descriptions. //! //! The window loads from the `quasi` scheme, so the protocol handler sees every //! request the document makes: five stylesheets, three scripts and three fonts. //! Four of the stylesheets are embedded and one, the theme, is rendered at //! startup from the stored choice. //! None of them is a route, and [`quasi_tauri::Protocol::passthrough`] is where //! they belong. //! //! Embedded rather than read off disk. A Tauri bundle's frontend is inside the //! binary, so there is no directory to read from in a shipped app, and an app //! that served its own stylesheet from a path the installer chose would work in //! `cargo run` and break everywhere else. //! //! The address space here and the router's are kept apart on purpose: a //! passthrough runs first and wins, so a route under `/static` or `/fonts` //! would be shadowed rather than merged. use quasi_tauri::Served; /// The bytes at a path, if this is one of ours. /// /// Takes the path alone. A stylesheet is a static thing at an address, so the /// verb is not part of the question. pub fn get(path: &str) -> Option { // The one address here whose bytes are not in the binary: the theme is a // config value resolved at startup, so it is rendered rather than embedded. // See [`super::theming`]. if path == super::theming::ADDRESS { return Some(Served::new(CSS, super::theming::css().as_bytes())); } let (content_type, body): (&str, &[u8]) = match path { // The cascade, in the order index.html links it: geometry and // typography before the sheet that reads their custom properties. "/static/typography.css" => (CSS, include_bytes!("../../frontend/css/typography.css")), "/static/geometry.css" => (CSS, include_bytes!("../../frontend/css/geometry.css")), "/static/timing.css" => (CSS, include_bytes!("../../frontend/css/timing.css")), "/static/layout.css" => (CSS, include_bytes!("../../frontend/css/layout.css")), "/static/styles.css" => (CSS, include_bytes!("../../frontend/css/styles.css")), // htmx is vendored under `frontend/vendor/`, byte for byte the copy // the MNW server serves, so the two hosts running the same // descriptions run the same runtime. Out of `js/` because the // design-system lint polices that directory as our own code and a // minified library calls `confirm()` inside itself. // The rest are written out by `build.rs` from the renderer crate's // own consts, so they cannot fossilise against the markup they read. // Every one the shell links has to be here: an address the shell names // and this match does not is a 404 per document. "/static/htmx.min.js" => (JS, include_bytes!("../../frontend/vendor/htmx.min.js")), "/static/quasi-selection.js" => { (JS, include_bytes!("../../frontend/js/quasi-selection.js")) } "/static/quasi-clock.js" => (JS, include_bytes!("../../frontend/js/quasi-clock.js")), // The host half of `Action::by_host`: picking a file is not describable // and never was. Hand-written, and the only such file left after the // swap. See `frontend/js/host.js` for why it is allowed to exist. "/static/host.js" => (JS, include_bytes!("../../frontend/js/host.js")), "/static/quasi-download.js" => (JS, include_bytes!("../../frontend/js/quasi-download.js")), "/static/quasi-fill.js" => (JS, include_bytes!("../../frontend/js/quasi-fill.js")), "/static/quasi-reveal.js" => (JS, include_bytes!("../../frontend/js/quasi-reveal.js")), "/static/quasi-repeat.js" => (JS, include_bytes!("../../frontend/js/quasi-repeat.js")), // `/static/../fonts/X` off typography.css, which the URL parser has // already resolved by the time it reaches here. "/fonts/QuasiMono.woff2" => ( WOFF2, include_bytes!("../../frontend/fonts/QuasiMono.woff2"), ), "/fonts/QuasiBody.woff2" => ( WOFF2, include_bytes!("../../frontend/fonts/QuasiBody.woff2"), ), "/fonts/Reglo-Bold.woff2" => ( WOFF2, include_bytes!("../../frontend/fonts/Reglo-Bold.woff2"), ), _ => return None, }; Some(Served::new(content_type, body)) } const CSS: &str = "text/css; charset=utf-8"; const JS: &str = "text/javascript; charset=utf-8"; const WOFF2: &str = "font/woff2"; #[cfg(test)] mod tests { use super::get; #[test] fn every_asset_the_shell_names_is_served() { // The shell in `super::super::protocol` names these. A miss here is a // 404 in a running window, which reads as an unstyled screen rather // than as a missing file. for path in [ "/static/typography.css", "/static/geometry.css", "/static/timing.css", "/static/layout.css", "/static/styles.css", "/static/htmx.min.js", "/static/quasi-selection.js", "/static/quasi-clock.js", "/static/quasi-download.js", "/static/quasi-fill.js", "/static/quasi-reveal.js", "/static/quasi-repeat.js", "/fonts/QuasiMono.woff2", "/fonts/QuasiBody.woff2", "/fonts/Reglo-Bold.woff2", ] { let served = get(path).unwrap_or_else(|| panic!("{path} is not served")); assert!(!served.body.is_empty(), "{path} is empty"); } } /// The theme is the one address whose body is not in the binary, so it is /// held apart from the list above rather than added to it. /// /// Empty here, and that is the answer rather than a gap in the test. The /// sheet is rendered by `theming::install` from the stored choice, and no /// test process has an `AppState` to install from. A document served in /// that window renders on the stock intent tokens `css/styles.css` /// declares, which is what those are for. /// /// What this holds is the half that would otherwise fail silently: the /// address answers at all. A 404 here is an unstyled window, and the /// address is named in three places. #[test] fn the_theme_is_served_even_before_it_is_installed() { let path = crate::quasi::theming::ADDRESS; let served = get(path).unwrap_or_else(|| panic!("{path} is not served")); assert_eq!(served.content_type, super::CSS); assert_eq!( served.body, crate::quasi::theming::css().as_bytes(), "the body is whatever theming holds, installed or not" ); } #[test] fn a_route_address_is_not_an_asset_address() { // The passthrough runs before the router and wins, so anything the // router owns must miss here. for path in ["/", "/projects", "/tasks", "/static", "/static/"] { assert!(get(path).is_none(), "{path} must reach the router"); } } }