-
Notifications
You must be signed in to change notification settings - Fork 1
html rendering
- src/repodocs/render.py
- src/repodocs/cli.py
The html command bundles the Markdown pages produced by repodocs generate into a single self-contained HTML file, <out>/wiki.html. The page is a client-side viewer: page Markdown is embedded inline as JSON and rendered in the browser with marked, sanitized with DOMPurify, highlighted with highlight.js, and diagrammed with mermaid. It works from file:// and needs no server.
Sources: src/repodocs/cli.py:L94-L107, src/repodocs/render.py:L413-L461
cmd_html (src/repodocs/cli.py:348-358) resolves the repo path and --out directory (defaulting to <repo>/repo-docs), checks for --vendor, and calls build_html(repo, out, vendor). It reports the number of .md pages bundled (excluding index.md) and, if --vendor was passed, notes that assets/ was written.
| Flag | Effect |
|---|---|
[repo] |
Repository path to derive the GitHub breadcrumb/link from (positional, parsed by parse_repo_and_flags) |
--out DIR |
Directory containing generated *.md pages and where wiki.html is written (default <repo>/repo-docs) |
--vendor |
Downloads the CDN JS/CSS libs into <out>/assets/ and rewrites the page to use relative paths, making it fully offline |
Sources: src/repodocs/cli.py:L348-L358, src/repodocs/cli.py:L94-L107
If --out points at a translated subdirectory (e.g. <out>/pt), the UI labels are localized based on the directory name via lang_labels, since repodocs translate writes translated pages under <out>/<lang>/ and then invokes repodocs html <repo> --out <out>/<lang>.
Sources: src/repodocs/cli.py:L110-L121, src/repodocs/render.py:L68-L70
build_html(repo, out, vendor=False) (src/repodocs/render.py:413-461) is the driver:
- Globs
<out>/*.md(excludingindex.md); dies with exit 1 if none exist, instructing the user to runrepodocs generatefirst. - Reads
<out>/plan.json, if present, to recover each page's declaredtitleand the plan's pageorder; malformed or missing plan.json is tolerated (OSError/json.JSONDecodeErrorare swallowed). - Derives the breadcrumb and GitHub link from
github_base(repo): if the repo has a GitHub remote,slugis the<owner>/<repo>extracted from the blob URL,breadcrumbis that slug (else the local directory name), andghrootishttps://github.com/<slug>. - Determines
cite_base— citation links only get rewritten toblob/<sha>GitHub URLs when the working tree is clean and HEAD is pushed to a remote branch, as verified bycitations_safe(); otherwise citations stay relative and a warning is printed to stderr. - Reads every Markdown page, resolving each page's title (plan title, else the first
# H1extracted bymd_title(), else the slug) and rewriting its citation links viarewrite_citation_links(). - Orders pages:
plan.jsonorder first (filtered to pages that exist), then any remaining pages alphabetically. - Groups pages into nav sections with
group_pages(). - If
vendoris set, callsvendor_assets(out), converting network failures into a fatal error withdie(). - Infers UI labels from
out.namevialang_labels(). - Renders the template with
render_html()and writes it to<out>/wiki.html.
Sources: src/repodocs/render.py:L413-L461, src/repodocs/render.py:L464-L469
flowchart TD
A["cmd_html(args)"] --> B["build_html(repo, out, vendor)"]
B --> C["glob out/*.md"]
B --> D["read plan.json (titles, order)"]
B --> E["github_base(repo) -> breadcrumb, ghroot"]
B --> F["citations_safe() -> cite_base or None"]
B --> G["read each .md, rewrite_citation_links()"]
B --> H["group_pages(ordered) -> nav groups"]
B --> I{vendor?}
I -- yes --> J["vendor_assets(out) -> assets/*"]
I -- no --> K[skip]
B --> L["lang_labels(out.name)"]
B --> M["render_html(...) -> wiki.html"]
Sources: src/repodocs/render.py:L413-L461
group_pages(slugs) (src/repodocs/render.py:73-88) buckets page slugs deterministically into four fixed nav groups — Overview (overview, installation, limitations, changelog), Development (development, testing, contributing, security, dev-setup), Reference (slug is or contains architecture, or contains interop), and Features (everything else) — preserving plan order within each bucket and omitting empty groups.
Sources: src/repodocs/render.py:L73-L88
The sidebar is built client-side in the embedded <script>: for each group in GROUPS, a .navgroup div with a .navhead label (localized via LABELS.groups) is created, and a .navlink anchor (href="#" + slug) is added per page. A #filter text input performs live, case-insensitive filtering over .navlink text, hiding empty groups' headers.
Sources: src/repodocs/render.py:L271-L295
The right-hand "On this page" TOC is built by buildToc(content), which selects all h2/h3 elements in the rendered article, assigns each a sec-<i> id, and appends an anchor per heading (.toc-3 styling for h3) that smooth-scrolls to the heading on click. If a page has no headings, the TOC <aside> is hidden.
Sources: src/repodocs/render.py:L312-L325
The "Relevant source files" section — always the page's first <h2> followed by a <ul> — is collapsed into a <details>/<summary> element by collapseSources(content). The match is purely structural (first h2 whose next sibling is a ul), not text-based, so it works across languages/models.
Sources: src/repodocs/render.py:L296-L311
Prev/next page footer links are generated by pageFooter(content, slug) using the page's index in ORDER (the flattened, grouped slug order computed in build_html), sanitized through DOMPurify.sanitize() before insertion.
Sources: src/repodocs/render.py:L326-L336, src/repodocs/render.py:L448-L450
On page navigation, show(slug) looks up PAGES[slug], and — guarding against a failed DOMPurify load by rendering a plain-text fallback message instead of unsanitized HTML — parses the page's Markdown with marked.parse() and sanitizes the result with DOMPurify.sanitize(html, { ADD_ATTR: ["target"] }) before injecting it into #content. code.language-mermaid blocks are converted into .mermaid divs and re-rendered by mermaid.run(). pre code blocks are syntax-highlighted via hljs.highlightElement(). Both hljs and mermaid calls are wrapped in try/catch since a rendering failure in either library should not break the rest of the page.
Sources: src/repodocs/render.py:L337-L362
Mermaid is initialized once at load with mermaid.initialize({ startOnLoad: false, theme: "dark", securityLevel: "strict" }), deferring diagram rendering to the explicit mermaid.run() call per page.
Sources: src/repodocs/render.py:L273-L273
The dark theme itself is a static <style> block embedded in HTML_TEMPLATE: fixed header bar, fixed-width sidebar (--side: 240px) and TOC (--toc: 220px) panes, a centered article (max-width 720px), styled code blocks/tables/<details>, and responsive breakpoints that hide the TOC below 1100px and the sidebar below 720px.
Sources: src/repodocs/render.py:L200-L256
LANG_LABELS (src/repodocs/render.py:53-62) defines UI strings (search, toc, github, prev, next, and nav groups labels) for en and pt (Brazilian Portuguese). lang_labels(name) looks up the label set by out-directory name, falling back to English for any unrecognized name.
Sources: src/repodocs/render.py:L53-L70
By default, five libraries are loaded from pinned jsDelivr CDN URLs defined in CDN_ASSETS: marked@12.0.2, mermaid@11.16.0, @highlightjs/cdn-assets@11.11.1 (JS and github-dark CSS), and dompurify@3.4.12. Each is paired with a sha384 Subresource Integrity hash in SRI, computed from the exact pinned CDN bytes, so that a tampered or compromised CDN response is rejected by the browser rather than silently executed. _asset_sri_attr(vendor, key) emits the integrity/crossorigin attribute text for CDN mode and returns "" for vendored mode, since vendored files' bytes don't match the CDN hash.
Sources: src/repodocs/render.py:L15-L34, src/repodocs/render.py:L372-L379
When --vendor is passed, vendor_assets(out) (src/repodocs/render.py:188-197) downloads each file in VENDOR_FILES (mapping local filenames to the same pinned CDN URLs) into <out>/assets/, and writes THIRD_PARTY_NOTICES — reproduced license texts for marked (MIT), mermaid (MIT), highlight.js (BSD-3-Clause), and DOMPurify (Apache-2.0 OR MPL-2.0) — to <out>/assets/THIRD-PARTY-NOTICES.txt, satisfying each library's attribution requirements in the published artifact. render_html() then selects VENDOR_ASSETS (relative paths like assets/marked.min.js) instead of CDN_ASSETS for the <script>/<link> src/href values, and omits the SRI attributes for those tags. build_html() treats a download failure (OSError) as fatal via die(), instructing the user to check connectivity and rerun repodocs html --vendor.
Sources: src/repodocs/render.py:L37-L50, src/repodocs/render.py:L91-L197, src/repodocs/render.py:L451-L457, src/repodocs/render.py:L382-L410
render_html() fills HTML_TEMPLATE by string replacement. Because breadcrumb/ghroot can originate from an untrusted repo directory name or configured remote slug, they are HTML-escaped (html.escape(..., quote=True)) before being placed in text nodes or the GitHub link's href/text, and JSON-escaped (with </ neutralized to <\/) before being embedded as JS string/object literals (REPO, PAGES). __PAGES__ — which embeds every page's raw Markdown — is replaced last, so that Markdown content containing template-token-like text (e.g. __GROUPS__) cannot clobber earlier substitutions.
Sources: src/repodocs/render.py:L382-L410
md_title(text) (src/repodocs/render.py:464-469) returns the first Markdown # H1 heading's text as a page's fallback title when plan.json supplies none.
Sources: src/repodocs/render.py:L464-L469
- Home
- Installation & Setup
- Architecture
- CLI Reference
- LLM Backend Selection
- Repository Scanning
- Wiki Page Planning
- Page Generation
- Source Citations
- Translation
- HTML Rendering
- Diagram Rendering
- Diagram Poster Tool
- Git Remote Link Resolution
- Publishing
- GitHub Wiki Integration
- Shared Utilities
- Environment Configuration
- Security & Trust Boundaries
- Limitations & Non-goals
- Testing
- Development
- Contributing
- Upgrading
- Changelog