Rust library to generate static websites
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: some docs (#36)

* docs: some docs

* fix: perf

* fix: wteak

authored by

Erika and committed by
GitHub
d34e3444 779bef7b

+513 -115
-89
website/content/docs/assets.md
··· 1 - --- 2 - title: "Assets" 3 - description: "Learn how to import and use assets in your Maudit site." 4 - section: "core-concepts" 5 - --- 6 - 7 - Maudit supports importing assets like images, stylesheets, and scripts into your project and pages. 8 - 9 - ### Images 10 - 11 - To import an image, add it anywhere in your project's directory, and use the `ctx.assets.add_image()` method to add it to a page's assets. 12 - 13 - Like other assets, images can be used directly in Maud templates. 14 - 15 - ```rs 16 - use maudit::page::prelude::*; 17 - use maud::html; 18 - 19 - #[route("/blog")] 20 - pub struct Blog; 21 - 22 - impl Page for Blog { 23 - fn render(&self, ctx: &mut RouteContext) -> RenderResult { 24 - let image = ctx.assets.add_image("logo.png"); 25 - 26 - html! { 27 - (image) // Generates <img src="IMAGE_URL" loading="lazy" decoding="async" /> 28 - }.into() 29 - } 30 - } 31 - ``` 32 - 33 - Alternatively, if not using Maud, the `url()` method on the image can be used to generate any necessary HTML or other output. 34 - 35 - ```rs 36 - fn render(&self, ctx: &mut RouteContext) -> RenderResult { 37 - let image = ctx.assets.add_image("logo.png"); 38 - 39 - RenderResult::Html(format!("<img src=\"{}\" loading=\"lazy\" decoding=\"async\" />", image.url().unwrap())) 40 - } 41 - ``` 42 - 43 - At this time, images are not automatically optimized or resized, but this will be added in the future. 44 - 45 - ### Scripts 46 - 47 - JavaScript and TypeScript files can be added to pages using the `ctx.assets.add_script()` method. 48 - 49 - ```rs 50 - use maudit::page::prelude::*; 51 - use maud::{html, Markup}; 52 - 53 - #[route("/blog")] 54 - pub struct Blog; 55 - 56 - impl Page<RouteParams, Markup> for Blog { 57 - fn render(&self, ctx: &mut RouteContext) -> Markup { 58 - let script = ctx.assets.add_script("script.js"); 59 - 60 - html! { 61 - (script) // Generates <script src="SCRIPT_URL" type="module"></script> 62 - } 63 - } 64 - } 65 - ``` 66 - 67 - The `include_script()` method can be used to automatically include the script in the page, which can be useful when using layouts or other shared templates. 68 - 69 - ```rs 70 - fn render(&self, ctx: &mut RouteContext) -> Markup { 71 - ctx.assets.include_script("script.js"); 72 - 73 - layout( 74 - html! { 75 - div { 76 - "Look ma, no explicit script tag!" 77 - } 78 - } 79 - ) 80 - } 81 - ``` 82 - 83 - When using `include_script()`, the script will be included inside the `head` tag with the `type="module"` attribute. [Note that this attribute implicitely means that your script will be deferred](https://v8.dev/features/modules#defer) after the page has loaded. At this time, pages without a `head` tag won't have the script included. 84 - 85 - ### Transformation & Bundling 86 - 87 - Maudit uses [Rolldown](https://rolldown.rs) to process and bundle scripts and styles. Rolldown will automatically chunk, minify, transpile, etc. your scripts and stylesheets, optimizing them for production. Features like tree shaking, minification, TypeScript support and more are all included out of the box. 88 - 89 - At this time, Maudit does not support customizing the transformation process, but this will be added in the future.
+290
website/content/docs/content.md
··· 1 + --- 2 + title: "Content" 3 + description: "Learn how to load and use content and data in your Maudit site." 4 + section: "core-concepts" 5 + --- 6 + 7 + Maudit lets you load content and data in different formats to use across your website. 8 + 9 + For example, you can configure a folder of Markdown (.md) files to be converted into HTML and included in your pages. You can also fetch a remote JSON file at build time and use its data within your site. 10 + 11 + In Maudit, this concept is called Content Sources. A content source is a collection of content or data, usually (but not necessarily) structured in a homogeneous way, that your website can use. 12 + 13 + ## Defining a content source 14 + 15 + Content sources are defined in the coronate entry point through the `content_sources!` macro. 16 + 17 + ```rs 18 + use maudit::content::content_sources; 19 + 20 + #[markdown_entry] 21 + pub struct BlogPost { 22 + pub title: String, 23 + pub description: Option<String>, 24 + } 25 + 26 + fn main() { 27 + coronate( 28 + routes![ 29 + // ... 30 + ], 31 + content_sources![ 32 + "source_name" => loader(...), 33 + "another_source" => glob_markdown<BlogPost>("path/to/files/*.md", None) 34 + ], 35 + Default::default() 36 + ); 37 + } 38 + ``` 39 + 40 + Where `loader` and `glob_markdown` are functions returning a Vec of `ContentEntry`. Typically, a loader also accepts a type argument specifying the shape of the data for each entries it returns, which will be used inside your pages to provide typed content. 41 + 42 + ## Using a content source in pages 43 + 44 + Once a content source is defined, it can be accessed in pages through the `RouteContext#content` property. 45 + 46 + ```rs 47 + use maudit::page::prelude::*; 48 + use maud::{html, PreEscaped}; 49 + 50 + #[route("/some-article")] 51 + pub struct SomeArticlePage; 52 + 53 + impl Page for SomeArticlePage { 54 + fn render(&self, ctx: &mut RouteContext) -> RenderResult { 55 + let entry = ctx 56 + .content 57 + .get_source::<BlogPost>("source_name") 58 + .get_entry("entry_id"); 59 + 60 + let entry_data = entry.data(ctx); 61 + 62 + html! { 63 + h1 { (entry_data.title) } 64 + @if let Some(description) = &entry_data.description { 65 + p { (description) } 66 + } 67 + 68 + (PreEscaped(entry.render(ctx))) 69 + }.into() 70 + } 71 + } 72 + ``` 73 + 74 + ## Loaders 75 + 76 + ### Built-in loaders 77 + 78 + #### `glob_markdown` 79 + 80 + The `glob_markdown` loader can be used to load one or multiple folders of Markdown (`.md`) files. 81 + 82 + ```rs 83 + use maudit::content::{glob_markdown, markdown_entry}; 84 + 85 + #[markdown_entry] 86 + pub struct DocsContent { 87 + pub title: String, 88 + pub description: Option<String>, 89 + pub section: Option<DocsSection>, 90 + } 91 + 92 + "docs" => glob_markdown::<DocsContent>("content/docs/*.md", None) 93 + ``` 94 + 95 + This loader take a glob pattern (compatible with [the `glob` crate](https://github.com/rust-lang/glob)) as its first argument, and an optional `MarkdownOptions` struct as its second argument to customise Markdown rendering. The frontmatter of each Markdown file will be deserialized using [Serde](https://serde.rs) into the type argument provided to `glob_markdown`, which can use the `#[markdown_entry]` macro to derive the necessary traits and add the necessary properties to the struct. Note that using this feature require the installation of Serde into your project as the macro uses Serde's derive macros. 96 + 97 + ### Custom loaders 98 + 99 + As said previously, a loader is simply a function returning a Vec of `ContentEntry`. This means you can create your own loaders to load content from any source you want, as long as you return the right type. 100 + 101 + For instance, you could create a loader that fetches a remote JSON file and deserializes it into a struct, producing a content source with a single entry: 102 + 103 + ```rs 104 + use maudit::content::{ContentEntry}; 105 + 106 + #[derive(serde::Deserialize)] 107 + pub struct MyType { 108 + pub id: u32, 109 + pub name: String, 110 + } 111 + 112 + pub fn my_loader(path: &str) -> Vec<ContentEntry<MyType>> { 113 + let response = reqwest::blocking::get(path).unwrap(); 114 + let data = response.json::<MyType>().unwrap(); 115 + 116 + vec![ContentEntry::new(data.id.into(), None, None, data, None)] 117 + } 118 + 119 + // Use it as a content source: 120 + use maudit::content::content_sources; 121 + 122 + content_sources![ 123 + "my_data" => my_loader("https://example.com/data.json") 124 + ]; 125 + ``` 126 + 127 + and then in pages, you could access the data like this: 128 + 129 + ```rs 130 + use maudit::page::prelude::*; 131 + 132 + #[route("/data")] 133 + pub struct DataPage; 134 + 135 + impl Page for DataPage { 136 + fn render(&self, ctx: &mut RouteContext) -> RenderResult { 137 + let entry = ctx 138 + .content 139 + .get_source::<MyType>("my_data") 140 + .get_entry("0"); 141 + 142 + let entry_data = entry.data(); 143 + 144 + format!( 145 + "<h1>Data</h1><p>ID: {}, Name: {}</p>", 146 + entry_data.id, entry_data.name 147 + ).into() 148 + } 149 + } 150 + ``` 151 + 152 + Content entries can also be rendered by passing a render function to the `render` method of `ContentEntry`. 153 + 154 + ```rs 155 + ContentEntry::new( 156 + data.id.into(), 157 + Some(Box::new(|content, ctx| { 158 + // render the content string into HTML 159 + maudit::render_markdown(content, markdown_options, None, ctx) 160 + })), 161 + None, 162 + data, 163 + None, 164 + ) 165 + ``` 166 + 167 + ## Markdown rendering 168 + 169 + Either through loaders or by using the `render_markdown` function directly, Maudit supports rendering local and remote Markdown and enriching it with shortcodes and custom components. 170 + 171 + ### Shortcodes 172 + 173 + Shortcodes provide a way to extend Markdown with custom functionality. They serve a similar role to [components in MDX](https://mdxjs.com) or [tags in Markdoc](https://markdoc.dev/docs/tags), allowing authors to define and reuse snippets throughout their content. Shortcodes can accept attributes and content, and can be self-closing or not. 174 + 175 + ```markdown 176 + --- 177 + title: { { enhance title="Super Title" / } } 178 + --- 179 + 180 + Here's an image with a caption: 181 + 182 + {{ image src="./image.png" }} 183 + This is a caption! 184 + {{ /image }} 185 + ``` 186 + 187 + This snippet could expand into something like this: 188 + 189 + ```markdown 190 + --- 191 + title: Very Cool Super Title 192 + --- 193 + 194 + Here's an image with a caption: 195 + 196 + <figure> 197 + <img src="/_maudit/image.hash.webp" width="200" height="200" loading="lazy" decoding="async" /> 198 + <figcaption>This is a caption!</figcaption> 199 + </figure> 200 + ``` 201 + 202 + To define a shortcode, create an instance of `MarkdownShortcodes` and use the `register` method to register shortcodes. This can then be passed as the `shortcodes` parameter of `MarkdownOptions`. 203 + 204 + ```rs 205 + use maudit::shortcodes::MarkdownShortcodes; 206 + 207 + fn main() { 208 + let create_markdown_options = || { 209 + let mut shortcodes = MarkdownShortcodes::default(); 210 + 211 + shortcodes.register("enhance", |attrs, _| { 212 + let title = attrs.get_required("title"); 213 + 214 + format!("Very Cool {}", title) 215 + }) 216 + 217 + MarkdownOptions { 218 + shortcodes, 219 + ..Default::default() 220 + } 221 + } 222 + 223 + coronate( 224 + routes![ 225 + // ... 226 + ], 227 + content_sources![ 228 + "blog" => glob_markdown::<BlogPost>("content/blog/**/*.md", Some(create_markdown_options())), 229 + ], 230 + ..Default::default() 231 + ); 232 + } 233 + ``` 234 + 235 + Note that shortcodes expand before Markdown is rendered, so you can use shortcodes to generate Markdown content as well as HTML. 236 + 237 + ### Components 238 + 239 + Maudit supports using custom components to render Markdown content. For instance, by default `# Title` will be rendered as `<h1>Title</h1>`, but you can override this behaviour by providing your own component for headings. 240 + 241 + To do so, create an instance of `MarkdownComponents` and use the various builder (`.heading`, `.link`, `.paragraph`, etc.) methods to register components. This can then be passed as the `components` parameter of `MarkdownOptions`. 242 + 243 + ```rs 244 + use maudit::components::MarkdownComponents; 245 + 246 + struct TestCustomHeading; 247 + 248 + impl HeadingComponent for TestCustomHeading { 249 + fn render_start(&self, level: u8, id: Option<&str>, classes: &[&str]) -> String { 250 + let id_attr = id.map(|i| format!(" id=\"{}\"", i)).unwrap_or_default(); 251 + let class_attr = if classes.is_empty() { 252 + String::new() 253 + } else { 254 + format!(" class=\"{}\"", classes.join(" ")) 255 + }; 256 + format!( 257 + "<h{}{}{}> This is a custom Heading: ", 258 + level, id_attr, class_attr 259 + ) 260 + } 261 + 262 + fn render_end(&self, level: u8) -> String { 263 + format!("</h{}>", level) 264 + } 265 + } 266 + 267 + fn main() { 268 + let create_markdown_options = || { 269 + let mut components = MarkdownComponents::new().heading(TestCustomHeading); 270 + 271 + MarkdownOptions { 272 + components, 273 + ..Default::default() 274 + } 275 + }; 276 + 277 + coronate( 278 + routes![ 279 + // ... 280 + ], 281 + content_sources![ 282 + "blog" => glob_markdown::<BlogPost>("content/blog/**/*.md", Some(create_markdown_options())), 283 + ], 284 + ..Default::default(), 285 + ); 286 + } 287 + 288 + ``` 289 + 290 + Unlike shortcodes, components are used during the Markdown rendering process, so they can only generate HTML, not Markdown.
+3 -3
website/content/docs/entrypoint.md
··· 17 17 } 18 18 ``` 19 19 20 - ### Registering Routes 20 + ## Registering Routes 21 21 22 22 All kinds of routes must be passed to the `coronate` function in order for them to be built. 23 23 ··· 35 35 36 36 See the [Routing](/docs/routing) documentation for more information on how to define routes. 37 37 38 - ### Content 38 + ## Content 39 39 40 - ### Options 40 + ## Options
+70
website/content/docs/images.md
··· 1 + --- 2 + title: "Images" 3 + description: "Learn how to import and use images in your Maudit site." 4 + section: "core-concepts" 5 + --- 6 + 7 + Maudit includes support using images in various contexts. In your pages as img tags, collocated next to and linked in your Markdown files, or inside JSON files, and more. 8 + 9 + Additionally, Maudit supports processing (i.e. resizing and converting) images at build time. 10 + 11 + ## Using images 12 + 13 + ### In pages 14 + 15 + To use an image in a page, add it anywhere in your project's directory, and use the `ctx.assets.add_image()` method to add it to a page's assets. 16 + 17 + ```rs 18 + use maudit::page::prelude::*; 19 + 20 + #[route("/blog")] 21 + pub struct Blog; 22 + 23 + impl Page for Blog { 24 + fn render(&self, ctx: &mut RouteContext) -> RenderResult { 25 + let image = ctx.assets.add_image("logo.png"); 26 + 27 + format!("", image.url).into() 28 + } 29 + } 30 + ``` 31 + 32 + Paths to image are resolved relative to the root of your project, not from the page's location. 33 + 34 + ### In Markdown 35 + 36 + To use an image in Markdown, link to it using standard Markdown syntax. 37 + 38 + ```markdown 39 + ![Description](./image.png) 40 + ``` 41 + 42 + Images can be collocated next to your content, or anywhere else in your project and are resolved relatively to your Markdown file. 43 + 44 + ## Processing images 45 + 46 + Images added to pages can be transformed by using `ctx.assets.add_image_with_options()`. 47 + 48 + ```rs 49 + use maudit::page::prelude::*; 50 + 51 + #[route("/image")] 52 + pub struct ImagePage; 53 + 54 + impl Page for ImagePage { 55 + fn render(&self, ctx: &mut RouteContext) -> RenderResult { 56 + let image = ctx.assets.add_image_with_options( 57 + "path/to/image.jpg", 58 + ImageOptions { 59 + width: Some(800), 60 + height: None, 61 + format: Some(ImageFormat::Png) 62 + }, 63 + )?; 64 + 65 + format!("<img src=\"{}\" alt=\"Processed Image\" />", image.url).into() 66 + } 67 + } 68 + ``` 69 + 70 + Processing images in Markdown files is currently not supported.
+3 -3
website/content/docs/installation.md
··· 4 4 section: "getting-started" 5 5 --- 6 6 7 - ### Prerequisites 7 + ## Prerequisites 8 8 9 9 - [Rust (1.83 or later)](https://www.rust-lang.org) 10 10 - A code editor (e.g. Visual Studio Code, RustRover, Helix, etc.) ··· 18 18 rustup default stable 19 19 ``` 20 20 21 - ### Installing Maudit 21 + ## Installing Maudit 22 22 23 23 Maudit provides a CLI tool for interacting with websites created using the library and generating new ones. To install the CLI tool, run the following command: 24 24 ··· 30 30 31 31 If you do not wish to use the CLI, or are integrating Maudit into an existing project, follow the instructions in the [manual installation guide](/docs/manual-install). 32 32 33 - ### Creating a new project 33 + ## Creating a new project 34 34 35 35 To create a new Maudit project, run the following command: 36 36
+49
website/content/docs/javascript.md
··· 1 + --- 2 + title: "Scripts" 3 + description: "Learn how to import and use JavaScript and TypeScript files in your Maudit site." 4 + section: "core-concepts" 5 + --- 6 + 7 + JavaScript and TypeScript files can be added to pages using the `ctx.assets.add_script()` method. 8 + 9 + ```rs 10 + use maudit::page::prelude::*; 11 + use maud::{html, Markup}; 12 + 13 + #[route("/blog")] 14 + pub struct Blog; 15 + 16 + impl Page<RouteParams, Markup> for Blog { 17 + fn render(&self, ctx: &mut RouteContext) -> Markup { 18 + let script = ctx.assets.add_script("script.js"); 19 + 20 + html! { 21 + (script) // Generates <script src="SCRIPT_URL" type="module"></script> 22 + } 23 + } 24 + } 25 + ``` 26 + 27 + The `include_script()` method can be used to automatically include the script in the page, which can be useful when using layouts or other shared templates. 28 + 29 + ```rs 30 + fn render(&self, ctx: &mut RouteContext) -> Markup { 31 + ctx.assets.include_script("script.js"); 32 + 33 + layout( 34 + html! { 35 + div { 36 + "Look ma, no explicit script tag!" 37 + } 38 + } 39 + ) 40 + } 41 + ``` 42 + 43 + When using `include_script()`, the script will be included inside the `head` tag with the `type="module"` attribute. [Note that this attribute implicitely means that your script will be deferred](https://v8.dev/features/modules#defer) after the page has loaded. At this time, pages without a `head` tag won't have the script included. 44 + 45 + ## Transformation & Bundling 46 + 47 + Maudit uses [Rolldown](https://rolldown.rs) to process and bundle scripts and styles. Rolldown will automatically chunk, minify, transpile, etc. your scripts and stylesheets, optimizing them for production. Features like tree shaking, minification, TypeScript support and more are all included out of the box. 48 + 49 + At this time, Maudit does not support customizing the transformation process, but this will be added in the future.
+52
website/content/docs/performance.md
··· 1 + --- 2 + title: "Performance" 3 + description: "Learn how to improve the build times and performance of your Maudit site." 4 + section: "guide" 5 + --- 6 + 7 + Maudit can generally [build websites pretty quickly](https://github.com/bruits/maudit/tree/main/benchmarks), but there are a few strategies you can use to improve build times and the performance of your site. 8 + 9 + A Maudit project is a normal Rust project, so [any performance optimizations that apply to Rust projects](https://nnethercote.github.io/perf-book/build-configuration.html#minimizing-compile-times) also apply to Maudit projects, but there are a few specific strategies that can help improve the performance of your Maudit site. 10 + 11 + ## In development 12 + 13 + ### Cargo settings 14 + 15 + We recommend using the following settings in your `Cargo.toml` to improve build times during development. This will increase the optimization level of your dependencies without making the compile time of your own crate longer. 16 + 17 + ```toml 18 + [profile.dev.package."*"] 19 + opt-level = 3 20 + ``` 21 + 22 + This is particularly relevant if you are processing a lot of images, as there is a large difference in performance between debug and release builds of the crates Maudit uses for image processing. 23 + 24 + ### Disabling heavy features during development 25 + 26 + When running through `maudit dev`, the `is_dev()` function will return `true`, allowing you to conditionally disable features that are slow to build or run during development. 27 + 28 + ```rs 29 + use maudit::is_dev; 30 + 31 + if !is_dev() { 32 + // Do something slow 33 + } 34 + ``` 35 + 36 + Running `cargo run` will show which pages of your site are slow to build, allowing you to identify bottlenecks in your build process. Note that it is not generally worth it to disable things such as image processing as Maudit will cache processed images between builds, even in development mode. 37 + 38 + ### Preventing build directory block 39 + 40 + As Maudit recompile your project on every change, it is possible to run into issues where the build directory is first blocked by another process, [most commonly `rust-analyzer` in your editor.](https://github.com/rust-lang/rust-analyzer/issues/4616), slowing down builds significantly. 41 + 42 + While this does improve the time it takes to get feedback on changes, note that changing `rust-analyzer` settings to use a different build directory will use a lot of disk space. 43 + 44 + ## In production 45 + 46 + ### Release builds 47 + 48 + If not using `maudit build`, ensure you are building your project in release mode using `cargo build --release` or `cargo run --release`. This will significantly improve the performance of your site and is a common pitfall for new users. 49 + 50 + ### Caching 51 + 52 + Make sure to cache the `target` directory between builds in your CI/CD pipeline. This will very significantly improve build times, especially if you have a lot of dependencies or processed images. Platforms like Netlify or Vercel will automatically cache the `target` directory for you.
+3 -3
website/content/docs/philosophy.md
··· 4 4 section: "getting-started" 5 5 --- 6 6 7 - ### Maudit is about making _static_ websites 7 + ## Maudit is about making _static_ websites 8 8 9 9 Many of the modern web frameworks have gained new output modes opposite to their original purpose, for instance Next.js, a SSR-first framework, has `output: "export"` to generate a static website and Astro, SSG-first, has `output: "server"` to do the reverse. 10 10 ··· 14 14 15 15 **Maudit is about making static websites**. It has no higher goals than that. It won't try to become a server-side rendering framework, a hybrid framework, or anything else. This focus allows us to make the best static website generator we can. 16 16 17 - ### Your website changes less often than its content 17 + ## Your website changes less often than its content 18 18 19 19 Many parts of Maudit projects are written in Rust, a compiled language, thus requiring recompilation for changes. This overhead is justified by the assumption that structural changes are less frequent than content updates. For example, in a blog, new articles are more common than layout changes. 20 20 21 21 Without delving into the complexities of incremental builds, this architecture allows Maudit projects to build very quickly, even for large websites, thanks to the raw performance of a compiled language. 22 22 23 - ### Maudit is a library, not a framework 23 + ## Maudit is a library, not a framework 24 24 25 25 Every feature in Maudit was designed to be able to be used outside of Maudit. You can access and load all of your content outside of Maudit. Maudit pages are normal Rust structs and can be handled outside of Maudit, and so on. 26 26
+5 -5
website/content/docs/routing.md
··· 4 4 section: "core-concepts" 5 5 --- 6 6 7 - ### Static Routes 7 + ## Static Routes 8 8 9 9 To create a new page in your Maudit project, create a struct and implement the `Page` trait for it, adding the `#[route]` attribute to the struct definition with the path of the route as an argument. The path can be any Rust expression, as long as it returns a `String`. 10 10 ··· 25 25 26 26 Finally, make sure to [register the page](#registering-routes) in the `coronate` function for it to be built. 27 27 28 - ### Ergonomic returns 28 + ## Ergonomic returns 29 29 30 30 The `Page` trait accepts a generic parameter in third position for the return type of the `render` method. This type must implement `Into<RenderResult>`, enabling more ergonomic returns in certain cases. 31 31 ··· 42 42 - `String`, `Vec<u8>`, `&str`, `&[u8]` 43 43 - [Various templating libraries](/docs/templating/) 44 44 45 - ### Dynamic Routes 45 + ## Dynamic Routes 46 46 47 47 Maudit supports creating dynamic routes with parameters. Allowing one to create many pages that share the same structure and logic, but with different content. For example, a blog where each post has a unique URL, e.g., `/posts/my-blog-post`. 48 48 ··· 106 106 107 107 Like static routes, dynamic routes must be [registered](#registering-routes) in the `coronate` function in order for them to be built. 108 108 109 - ### Endpoints 109 + ## Endpoints 110 110 111 111 Maudit supports returning other types of content besides HTML, such as JSON, plain text or binary data. To do this, simply add a file extension to the route path and return the content in the `render` method. 112 112 ··· 153 153 154 154 Endpoints must also be [registered](#registering-routes) in the `coronate` function in order for them to be built. 155 155 156 - ### Registering Routes 156 + ## Registering Routes 157 157 158 158 All kinds of routes must be passed to the `coronate` function in [the entrypoint](/docs/entrypoint) in order to be built. 159 159
+2 -2
website/content/docs/styling.md
··· 44 44 } 45 45 ``` 46 46 47 - #### Tailwind support 47 + ## Tailwind support 48 48 49 49 Maudit includes built-in support for [Tailwind CSS](https://tailwindcss.com/). To use it, use `add_style_with_options()` or `include_style_with_options()` with the `StyleOptions { tailwind: true }` option. 50 50 ··· 60 60 } 61 61 ``` 62 62 63 - Maudit will automatically run Tailwind (using the binary provided at [`BuildOptions#tailwind_binary_path`](https://docs.rs/maudit/0.3.2/maudit/struct.BuildOptions.html#structfield.tailwind_binary_path)) on the specified CSS file. 63 + Maudit will automatically run Tailwind (using the binary provided at [`BuildOptions#tailwind_binary_path`](https://docs.rs/maudit/latest/maudit/struct.BuildOptions.html#structfield.tailwind_binary_path)) on the specified CSS file. 64 64 65 65 Tailwind can then be configured normally, through native CSS in Tailwind 4.0, or through a `tailwind.config.js` file in earlier versions. 66 66
+2
website/src/content.rs
··· 8 8 pub enum DocsSection { 9 9 GettingStarted, 10 10 CoreConcepts, 11 + Guide, 11 12 Advanced, 12 13 } 13 14 ··· 16 17 match self { 17 18 DocsSection::GettingStarted => PreEscaped("Getting Started".to_string()), 18 19 DocsSection::CoreConcepts => PreEscaped("Core Concepts".to_string()), 20 + DocsSection::Guide => PreEscaped("Guide".to_string()), 19 21 DocsSection::Advanced => PreEscaped("Advanced".to_string()), 20 22 } 21 23 }
+34 -10
website/src/layout/docs_sidebars.rs
··· 22 22 match section { 23 23 DocsSection::GettingStarted => 0, 24 24 DocsSection::CoreConcepts => 1, 25 - DocsSection::Advanced => 2, 25 + DocsSection::Guide => 2, 26 + DocsSection::Advanced => 3, 26 27 } 27 28 }); 28 29 ··· 68 69 } 69 70 70 71 pub fn right_sidebar(headings: &[MarkdownHeading]) -> Markup { 71 - let html_headings: Vec<maud::PreEscaped<String>> = headings 72 - .iter() 73 - .map(|heading| { 74 - html! { 75 - li { 76 - a href=(format!("#{}", heading.id)) { (heading.title) } 77 - } 72 + let mut html_headings: Vec<maud::PreEscaped<String>> = Vec::new(); 73 + let mut i = 0; 74 + let mut seen_h2 = false; 75 + while i < headings.len() { 76 + let heading = &headings[i]; 77 + let (pad, border) = match heading.level { 78 + 2 => ("pl-0", ""), // h2 79 + 3 => ("pl-4", "border-l-2 border-borders"), // h3 80 + 4 => ("pl-8", "border-l-2 border-borders"), // h4 81 + 5 => ("pl-12", "border-l-2 border-borders"), // h5 82 + 6 => ("pl-16", "border-l-2 border-borders"), // h6 83 + _ => ("pl-0", ""), // fallback 84 + }; 85 + let next_level = if i + 1 < headings.len() { 86 + headings[i + 1].level 87 + } else { 88 + 0 89 + }; 90 + let margin_top = if heading.level == 2 && next_level > 2 && seen_h2 { 91 + "mt-4" 92 + } else { 93 + "" 94 + }; 95 + if heading.level == 2 { 96 + seen_h2 = true; 97 + } 98 + html_headings.push(html! { 99 + li.(pad).(border).(margin_top) { 100 + a href=(format!("#{}", heading.id)) { (heading.title) } 78 101 } 79 - }) 80 - .collect(); 102 + }); 103 + i += 1; 104 + } 81 105 82 106 html!( 83 107 h2.text-lg.font-bold { "On This Page" }