# DocEngine Configurable markdown-to-HTML rendering library with sanitization presets. Built on pulldown-cmark (GFM) and ammonia. Used by MNW (site docs, blog posts, user-generated content), Multithreaded (forum posts), and the desktop apps (descriptions, notes). ## Presets Four rendering presets, each with different security/feature tradeoffs: | Preset | Use case | Tables | Images | Raw HTML | Dangerous scheme filter | Sanitization | |--------|----------|:------:|:------:|:--------:|:-----------------------:|--------------| | **Permissive** | Docs, blog posts (trusted) | Y | Y | Y | N | Default ammonia | | **Standard** | App text fields (descriptions) | Y | N | Y | N | Default ammonia | | **Strict** | User-generated content (forums) | N | N | N | Y | nofollow on links | | **Sanitize-only** | External HTML (RSS feeds) | -- | -- | -- | -- | Default ammonia, no markdown parsing | ```rust use docengine::{render_permissive, render_standard, render_strict, sanitize_html}; // Convenience functions let html = render_permissive("# Hello\n\n**Bold** text"); let html = render_standard("A description with [link](https://example.com)"); let html = render_strict("User post with @mentions and `code`"); let html = sanitize_html("

Pre-rendered

"); // Builder pattern for custom configurations use docengine::{Renderer, SanitizePreset}; let html = Renderer::permissive() .with_strip_images(true) // override: strip images even in permissive .with_footnotes(false) .render("# Custom config"); // Render with metadata (word count, reading time) let result = Renderer::standard().render_with_meta("Some article text..."); println!("{} words, ~{} min read", result.word_count, result.reading_time_minutes); ``` ## Feature Flags All optional features are off by default. Enable what you need: | Flag | Dependencies | Provides | |------|-------------|----------| | `doc-loader` | regex | `DocLoader` -- load a directory of `.md` files into an in-memory page store | | `directives` | regex-lite | `post_process_directives` -- `[!NOTE]`/`[!TIP]`/`[!TABS]` blockquote alerts and code tabs | | `frontmatter` | toml | `parse_frontmatter` -- extract TOML frontmatter delimited by `+++` | | `mentions` | regex-lite | `extract_mentions`, `resolve_mentions` -- `@username` parsing and linking | | `quotes` | regex-lite, uuid | `post_process_quotes` -- replace `[quote:POST_ID:HASH]` markers with author attribution | | `media-urls` | regex-lite | `rewrite_media_paths`, `img_to_video` -- CDN path rewriting and video tag conversion | | `assumptions` | toml, regex-lite | `Assumptions` -- load a TOML source-of-truth file, compute derived values, validate, substitute `{{ dotted.path \| filter(args) }}` markers in markdown with an extensible filter pipeline (built-ins: `int`, `ceil`, `floor`, `round`, `money`, `percent`, `upper`, `lower`) | | `full` | all of the above | Enable everything | ```toml # In Cargo.toml docengine = { path = "../shared/docengine" } # Core only (from MNW/server/) docengine = { path = "../../MNW/shared/docengine" } # From Apps/ ``` ## Core API ### Types - **`Renderer`** -- configurable markdown renderer with builder pattern - **`RenderResult`** -- rendered HTML plus `word_count` and `reading_time_minutes` - **`SanitizePreset`** -- `Permissive`, `Standard`, `Strict`, `Minimal` - **`TocEntry`** -- heading level, text, and anchor for table of contents ### Functions | Function | Description | |----------|-------------| | `render_permissive(md)` | Render with full GFM features | | `render_standard(md)` | Render without images | | `render_strict(md)` | Render with all restrictions (UGC-safe) | | `sanitize_html(html)` | Clean pre-rendered HTML without markdown parsing | | `word_count(text)` | Count words in raw text | | `reading_time_minutes(wc)` | Estimate reading time (200 wpm) | | `extract_title(md)` | Pull the first `# Heading` from markdown | | `strip_first_heading(md)` | Remove the first `# Heading` (for template-rendered titles) | | `extract_toc(md)` | Build a `Vec` from all headings | | `render_toc_html(entries)` | Render TOC entries as a `