Skip to main content

mbr_core/
assets.rs

1//! Compiled-in default assets served under `/.mbr/*` and copied into static builds.
2//!
3//! Every asset is its own `pub const`, and [`DEFAULT_FILES`] is a table built
4//! from them. The split exists for the consumers that need only a few of these
5//! bytes: QuickLook inlines `theme.css` and mermaid into a preview and names
6//! exactly those two items, not the whole table, so nothing it does not use has
7//! a reason to be linked into it. The table is what the server's `/.mbr/*`
8//! fallback and the static build's `.mbr/` step walk.
9//!
10//! Highlight.js and the default Pico stylesheet are owned by
11//! [`crate::embedded_hljs`] and [`crate::embedded_pico`]; the table reuses their
12//! constants rather than embedding the same files a second time.
13
14use crate::{embedded_hljs as hljs, embedded_pico};
15
16/// [`DEFAULT_FILES`] route of the lazy task-browser chunk.
17///
18/// Named because `build.rs` has to skip exactly this entry: the task browser is
19/// server/GUI only (the index is built from live files), so shipping the chunk
20/// into a static site would be dead weight behind a button that cannot exist.
21pub const TASKS_CHUNK_ROUTE: &str = "/components/mbr-tasks.min.js";
22
23/// [`DEFAULT_FILES`] route of the lazy review-notes chunk.
24///
25/// Skipped by `build.rs` for the same reason as [`TASKS_CHUNK_ROUTE`]: review
26/// notes anchor to the `data-mbr-line` attributes only a server/GUI render
27/// emits, so `<mbr-review>` never renders in a static site and the chunk would
28/// be an unreachable payload in every generated page.
29pub const REVIEW_CHUNK_ROUTE: &str = "/components/mbr-review.min.js";
30
31/// [`DEFAULT_FILES`] route of the lazy search-panel extras chunk (folder picker,
32/// note-type list).
33///
34/// Skipped by `build.rs` like [`TASKS_CHUNK_ROUTE`]: the controls it serves —
35/// the scope select and the folder scope — render only in server/GUI mode,
36/// because static search is Pagefind, which has neither facets nor folders.
37pub const SEARCH_EXTRAS_CHUNK_ROUTE: &str = "/components/mbr-search-extras.min.js";
38
39const JS: &str = "application/javascript";
40const CSS: &str = "text/css";
41
42pub const FAVICON_PNG: &[u8] = include_bytes!("../templates/favicon.png");
43pub const THEME_CSS: &[u8] = include_bytes!("../templates/theme.css");
44pub const USER_CSS: &[u8] = include_bytes!("../templates/user.css");
45
46/// Main components bundle: every always-on element plus the lazy-chunk triggers.
47pub const COMPONENTS_JS: &[u8] = include_bytes!("../templates/components-js/mbr-components.min.js");
48/// Heavy Milkdown/Crepe editor chunk, lazy-loaded by `<mbr-editor>`.
49pub const EDITOR_JS: &[u8] = include_bytes!("../templates/components-js/mbr-editor.min.js");
50/// Sidebar mini force-graph chunk (d3-force), lazy-loaded by `<mbr-info>` when
51/// the info panel first opens.
52pub const GRAPH_JS: &[u8] = include_bytes!("../templates/components-js/mbr-graph.min.js");
53/// Relationship-charts chunk (family-chart, timeline tree, org chart),
54/// lazy-loaded by `<mbr-genealogy>` on person/organization pages only.
55pub const GENEALOGY_JS: &[u8] = include_bytes!("../templates/components-js/mbr-genealogy.min.js");
56/// Task-browser panel chunk, lazy-loaded by `<mbr-tasks>` the first time the
57/// panel is opened. Excluded from static builds — see [`TASKS_CHUNK_ROUTE`].
58pub const TASKS_JS: &[u8] = include_bytes!("../templates/components-js/mbr-tasks.min.js");
59/// Review-notes panel and form, lazy-loaded by `<mbr-review>` the first time a
60/// note is written or the list is opened. Excluded from static builds — see
61/// [`REVIEW_CHUNK_ROUTE`].
62pub const REVIEW_JS: &[u8] = include_bytes!("../templates/components-js/mbr-review.min.js");
63/// Search-panel folder picker and note-type list, lazy-loaded by `<mbr-search>`
64/// the first time the modal opens. Excluded from static builds — see
65/// [`SEARCH_EXTRAS_CHUNK_ROUTE`].
66pub const SEARCH_EXTRAS_JS: &[u8] =
67    include_bytes!("../templates/components-js/mbr-search-extras.min.js");
68/// Flashcard review overlay (+ ts-fsrs), lazy-loaded by `<mbr-flashcards>` when
69/// a deck is opened. Ships in static builds: In order / Random review needs no
70/// server, only spaced repetition does.
71pub const FLASHCARDS_JS: &[u8] = include_bytes!("../templates/components-js/mbr-flashcards.min.js");
72/// Flashcard reading view (collapsed review-history summaries), imported by
73/// `<mbr-flashcards>` at idle on `type: flashcard` pages only. Separate from
74/// the deck so reading a note never fetches ts-fsrs.
75pub const FLASHCARDS_READING_JS: &[u8] =
76    include_bytes!("../templates/components-js/mbr-flashcards-reading.min.js");
77
78pub const MERMAID_JS: &[u8] = include_bytes!("../templates/mermaid.12.1.0.min.js");
79
80// Reveal.js presentation framework
81pub const REVEAL_JS: &[u8] = include_bytes!("../templates/reveal.6.0.2.js");
82pub const REVEAL_CSS: &[u8] = include_bytes!("../templates/reveal.6.0.2.css");
83pub const REVEAL_THEME_BLANK_CSS: &[u8] =
84    include_bytes!("../templates/reveal.theme.blank.5.2.1.css");
85pub const REVEAL_THEME_BLACK_CSS: &[u8] =
86    include_bytes!("../templates/reveal.theme.black.5.2.1.css");
87pub const REVEAL_THEME_WHITE_CSS: &[u8] =
88    include_bytes!("../templates/reveal.theme.white.5.2.1.css");
89pub const REVEAL_SLIDES_CSS: &[u8] = include_bytes!("../templates/reveal-slides.css");
90pub const REVEAL_NOTES_JS: &[u8] = include_bytes!("../templates/reveal.notes.6.0.2.js");
91
92/// Every compiled-in default asset as `(route, bytes, mime type)`.
93///
94/// Routes are relative to `/.mbr`. The order is the one the static build
95/// writes them in.
96pub const DEFAULT_FILES: &[(&str, &[u8], &str)] = &[
97    ("/favicon.png", FAVICON_PNG, "image/png"),
98    ("/theme.css", THEME_CSS, CSS),
99    ("/user.css", USER_CSS, CSS),
100    ("/pico.min.css", embedded_pico::PICO_DEFAULT, CSS),
101    ("/components/mbr-components.min.js", COMPONENTS_JS, JS),
102    ("/components/mbr-editor.min.js", EDITOR_JS, JS),
103    ("/components/mbr-graph.min.js", GRAPH_JS, JS),
104    ("/components/mbr-genealogy.min.js", GENEALOGY_JS, JS),
105    (TASKS_CHUNK_ROUTE, TASKS_JS, JS),
106    (REVIEW_CHUNK_ROUTE, REVIEW_JS, JS),
107    (SEARCH_EXTRAS_CHUNK_ROUTE, SEARCH_EXTRAS_JS, JS),
108    ("/components/mbr-flashcards.min.js", FLASHCARDS_JS, JS),
109    (
110        "/components/mbr-flashcards-reading.min.js",
111        FLASHCARDS_READING_JS,
112        JS,
113    ),
114    ("/hljs.dark.css", hljs::HLJS_DARK_CSS, CSS),
115    ("/hljs.atom-one-dark.css", hljs::HLJS_ATOM_ONE_DARK_CSS, CSS),
116    ("/hljs.js", hljs::HLJS_JS, JS),
117    ("/hljs.lang.css.js", hljs::HLJS_LANG_CSS, JS),
118    ("/hljs.lang.javascript.js", hljs::HLJS_LANG_JAVASCRIPT, JS),
119    ("/hljs.lang.typescript.js", hljs::HLJS_LANG_TYPESCRIPT, JS),
120    ("/hljs.lang.rust.js", hljs::HLJS_LANG_RUST, JS),
121    ("/hljs.lang.python.js", hljs::HLJS_LANG_PYTHON, JS),
122    ("/hljs.lang.bash.js", hljs::HLJS_LANG_BASH, JS),
123    ("/hljs.lang.java.js", hljs::HLJS_LANG_JAVA, JS),
124    ("/hljs.lang.scala.js", hljs::HLJS_LANG_SCALA, JS),
125    ("/hljs.lang.go.js", hljs::HLJS_LANG_GO, JS),
126    ("/hljs.lang.ruby.js", hljs::HLJS_LANG_RUBY, JS),
127    ("/hljs.lang.nix.js", hljs::HLJS_LANG_NIX, JS),
128    ("/hljs.lang.json.js", hljs::HLJS_LANG_JSON, JS),
129    ("/hljs.lang.yaml.js", hljs::HLJS_LANG_YAML, JS),
130    ("/hljs.lang.xml.js", hljs::HLJS_LANG_XML, JS),
131    ("/hljs.lang.sql.js", hljs::HLJS_LANG_SQL, JS),
132    ("/hljs.lang.dockerfile.js", hljs::HLJS_LANG_DOCKERFILE, JS),
133    ("/hljs.lang.markdown.js", hljs::HLJS_LANG_MARKDOWN, JS),
134    ("/mermaid.min.js", MERMAID_JS, JS),
135    ("/reveal.js", REVEAL_JS, JS),
136    ("/reveal.css", REVEAL_CSS, CSS),
137    ("/reveal-theme-blank.css", REVEAL_THEME_BLANK_CSS, CSS),
138    ("/reveal-theme-black.css", REVEAL_THEME_BLACK_CSS, CSS),
139    ("/reveal-theme-white.css", REVEAL_THEME_WHITE_CSS, CSS),
140    ("/reveal-slides.css", REVEAL_SLIDES_CSS, CSS),
141    ("/reveal-notes.js", REVEAL_NOTES_JS, JS),
142];
143
144/// Looks up a compiled-in default asset by its route (relative to `/.mbr`).
145///
146/// Returns `(bytes, mime type)`, or `None` when no default exists for `route`.
147#[must_use]
148pub fn default_file(route: &str) -> Option<(&'static [u8], &'static str)> {
149    DEFAULT_FILES
150        .iter()
151        .find(|(name, _, _)| *name == route)
152        .map(|(_, bytes, mime)| (*bytes, *mime))
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use std::collections::HashSet;
159
160    #[test]
161    fn routes_are_unique_and_rooted() {
162        let mut seen = HashSet::new();
163        for (route, _, _) in DEFAULT_FILES {
164            assert!(route.starts_with('/'), "route {route} must start with /");
165            assert!(seen.insert(*route), "duplicate route {route}");
166        }
167    }
168
169    #[test]
170    fn default_file_finds_every_table_entry() {
171        for (route, bytes, mime) in DEFAULT_FILES {
172            assert_eq!(default_file(route), Some((*bytes, *mime)));
173        }
174        assert_eq!(default_file("/nope.js"), None);
175    }
176
177    #[test]
178    fn chunk_routes_are_in_the_table() {
179        for route in [
180            TASKS_CHUNK_ROUTE,
181            REVIEW_CHUNK_ROUTE,
182            SEARCH_EXTRAS_CHUNK_ROUTE,
183        ] {
184            assert!(default_file(route).is_some(), "{route} missing");
185        }
186    }
187
188    #[test]
189    fn quicklook_assets_match_their_routes() {
190        assert_eq!(default_file("/theme.css").map(|(b, _)| b), Some(THEME_CSS));
191        assert_eq!(
192            default_file("/mermaid.min.js").map(|(b, _)| b),
193            Some(MERMAID_JS)
194        );
195    }
196}