Extend NavItem struct with children field to support nested navigation.
discover_nav() now populates children for section items by collecting
section pages via Section::collect_items() and mapping them to child
NavItem entries. Children are sorted by weight then alphabetically.
Add responsive table rules: horizontal scroll within container,
word-break for inline code, and constrained max-width. Fixes
hamburger menu being pushed off-screen on pages with wide tables.
Implement pure CSS mobile navigation with the following features:
- Collapsible sidebar nav hidden behind hamburger toggle on mobile
- Animated hamburger → X icon morph using CSS transforms
- Smooth slide-down transition for nav reveal (max-height/opacity)
- Hamburger positioned on right side of header via absolute positioning
- Uses :has() selector to detect checkbox state without JavaScript
- Overflow fixes to prevent horizontal scroll on mobile devices
The implementation follows suckless principles with zero JavaScript.
pulldown-cmark emits alt text as Text events between Image start/end
tags. Previously, images were rendered immediately on the Start event
with empty alt text, losing the user-provided description.
Refactored to accumulate alt text using the same pattern as code block
handling: state variables track image attributes and alt content, then
the full <img> tag is rendered on the End event.
Also omits the title attribute entirely when no title is provided,
producing cleaner HTML output.
Fixes Lighthouse "Image elements have [alt] attributes" audit issue.
Add docs/content/features/sitemap.md documenting the automatic
XML sitemap generation feature:
- Output location and contents
- Auto-generation behavior
- lastmod date handling
- Linking and validation tips
Follows established pattern from feeds.md.
Eliminate redundant Content::from_path call by using the pre-discovered
manifest.homepage content. Also uses manifest.nav directly.
- Removes dead_code warning on SiteManifest.homepage field
- Reduces I/O by reusing discovered content
- Simplifies function signature (4 args vs 5)
Replace separate discover_nav + discover_sections calls with single
SiteManifest::discover() in run() pipeline:
- Update generate_feed() to accept &SiteManifest
- Update generate_sitemap_file() to accept &SiteManifest
- Simplify process_pages() to return Result<()>
- Remove redundant all_posts collection (now in manifest.posts)
- Remove unused discover_nav and discover_sections imports
All 49 tests pass, site builds identically.
Introduce SiteManifest struct that aggregates all site content from a
single discovery pass:
- homepage: content/_index.md
- sections: directories with _index.md
- pages: top-level standalone .md files
- posts: blog section items (sorted by date for feed)
- nav: navigation menu items
Add discover_pages() helper and 5 unit tests covering homepage,
sections, pages, posts, and nav discovery.
Not yet integrated into main.rs pipeline.
Wire generate_sitemap() into main.rs pipeline:
- Refactor process_pages() to return discovered pages
- Add generate_sitemap_file() helper function
- Generate sitemap.xml after homepage (Step 5 in pipeline)
- All 44 tests pass, sitemap contains 12 URLs
The sitemap includes homepage, section indices, section items,
and standalone pages following XML Sitemap 1.0 protocol.
Implement src/sitemap.rs with generate_sitemap() for SEO-compliant
XML sitemap generation. Follows the feed.rs pattern:
- SitemapEntry struct for URL metadata
- build_sitemap_xml() for XML construction
- xml_escape() for special character handling
- 5 unit tests covering single/multiple entries, lastmod, escaping
Module declared in main.rs but not yet integrated into pipeline.
- Add sidebar footer with GitHub repo link and MIT copyright
- Generate geometric logo for project branding
- Add logo as favicon in browser tab
- Add logo to sidebar header (32x32) next to "sukr" text
- Add centered logo (128px) at top of README
- Add sidebar footer with GitHub repo link and MIT copyright
- Position footer at bottom of sidebar using flexbox
- Generate geometric logo and add as favicon
- Update base.html template with footer and favicon link
KaTeX renders square roots using a 400em-wide SVG that relies on CSS
`overflow: hidden` to clip to the correct length. Without the KaTeX
stylesheet, this SVG extends across the entire page.
Changes:
- Add KaTeX CDN stylesheet link to base.html for fonts and layout
- Add .katex-display local margin/overflow overrides
- Add .hide-tail svg max-width fallback for file:// CORS blocking
The CDN provides full KaTeX styling when accessible. The SVG constraint
serves as a fallback when CDN is blocked (e.g., local file:// access).
Use tree-sitter-toml-ng v0.7.0 from tree-sitter-grammars, which is
compatible with tree-sitter 0.26 (unlike the older tree-sitter-toml).
- Add tree-sitter-toml-ng dependency
- Add Toml variant to Language enum
- Add TOML_CONFIG with crate's HIGHLIGHTS_QUERY
Fix markdown code block highlighting to properly support both markdown
structure (headings, frontmatter) and language injection (rust, bash).
The key fix uses `#set! injection.include-children` in the injection
query to override tree-sitter-md's internal tokenization, enabling
proper highlighting of embedded languages within code fences.
Changes:
- Use crate's HIGHLIGHT_QUERY_BLOCK for base markdown highlighting
- Add custom injection query with include-children directive
- Add YAML/TOML frontmatter and HTML block injection rules
- Add text.* highlight names (title, literal, uri, reference)
- Add string.escape highlight name
- Add CSS styles for new highlight classes
- Remove unused custom md-highlights.scm
- Add Atom feed generation docs (feeds.md)
- Add CSS minification docs (css.md)
- Fix em dash usage in css.md for natural prose
Completes AI audit (4 layers passed) and documentation gap analysis.
All 39 tests pass, docs site builds successfully.
Self-documenting docs site built with sukr itself (dogfooding):
Core changes:
- Rename package from nrd-sh to sukr
- Move personal site to sites/nrd.sh/
- Update AGENTS.md and README.md
Documentation site (docs/):
- Add site.toml with sukr.io base URL
- Create docs-specific templates with sidebar navigation
- Add dark theme CSS with syntax highlighting colors
- Document all features: templates, sections, syntax highlighting,
mermaid diagrams, and LaTeX math rendering
Bug fixes:
- Render individual pages for all sections (not just blog type)
- Add #[source] error chaining for Tera template errors
- Print full error chain in main() for better debugging
Self-documenting docs site built with sukr itself (dogfooding):
- docs/site.toml with sukr.io base URL
- docs-specific templates with sidebar navigation
- Dark theme CSS, responsive layout
- Documentation: getting-started, configuration, features
Also: improved error chaining for better template debugging
Enable monorepo support with CLI configuration:
- Add PathsConfig struct with serde defaults for content, output,
static, and templates directories
- Add optional [paths] table to site.toml (backward compatible)
- Add -c/--config <FILE> flag to specify config file path
- Add -h/--help flag with usage information
- Resolve all paths relative to config file location
Users can now run multiple sites from a single repo:
nrd-sh # uses ./site.toml
nrd-sh -c sites/blog/site.toml # looks in sites/blog/
Includes 2 new unit tests for path configuration parsing.
Fully migrate from compile-time maud templates to runtime Tera:
- Rewrote main.rs to use TemplateEngine and discover_sections()
- Replaced hardcoded blog/projects with generic section loop
- Added Clone derive to Frontmatter and Content
- Fixed section_type dispatch via Section struct
- Deleted src/templates.rs, removed maud dependency
Users can now add sections without code changes.
Enable generic section processing by adding:
- Section struct with index, name, section_type, path
- Section::collect_items() to gather content in a section
- discover_sections() to find all directories with _index.md
Section type is determined from frontmatter `section_type` field,
falling back to directory name. Includes 5 unit tests.
Lay groundwork for user-editable templates by adding Tera as a
runtime template engine alongside the existing maud templates.
Changes:
- Add tera dependency
- Create TemplateEngine struct with render methods
- Add TemplateLoad/TemplateRender error variants
- Add section_type/template fields to Frontmatter
- Create templates/ directory with base, page, section, and content templates
Dead code warnings are expected; TemplateEngine will be wired
in to replace maud in subsequent commits.
Replace hardcoded "/blog/{slug}.html" URL pattern with dynamic
path derivation using Content.output_path(). This ensures feed
URLs work correctly for any content location, not just blog posts.
Update all template functions to accept nav parameter and iterate
over discovered NavItem slice instead of hardcoded links.
Refactor main.rs:
- Call discover_nav() early in run()
- Thread nav to all template renders
- Replace hardcoded page list with dynamic discovery
Navigation is now fully driven by filesystem structure.
Add NavItem struct and discover_nav() function to scan content
directory and automatically build navigation from:
- Top-level .md files (pages)
- Directories with _index.md (sections)
Navigation is sorted by frontmatter weight, then alphabetically.
Custom nav_label field allows overriding title in nav menu.
Includes 5 unit tests covering page/section discovery, weight
ordering, and nav_label support.
Switch mermaid-rs-renderer from git dependency to crates.io 0.1.
Vendor and patch dagre_rust 0.0.5 to fix upstream panic in
remove_edge_label_proxies() where unwrap() is called on None
when processing edge label proxies without edge references.
This enables sequence diagrams and state diagrams that previously
crashed the build.
Intercept 'mermaid' code blocks in render.rs and call
mermaid::render_diagram() to convert to inline SVG.
- Use catch_unwind to handle upstream dagre_rust panics gracefully
- Graceful fallback: show raw code with mermaid-error class on failure
- Flowcharts render correctly; some diagram types hit upstream bugs
Add src/mermaid.rs with render_diagram() wrapper around
mermaid-rs-renderer for build-time Mermaid-to-SVG conversion.
- Use mermaid-rs-renderer git dependency (SVG-only, no PNG)
- Configure with RenderOptions::modern() theme
- Include unit tests for flowchart and sequence diagrams
Enable ENABLE_MATH in pulldown-cmark options and implement
InlineMath/DisplayMath event handlers in the render pipeline.
- Inline math ($...$) renders via render_math(_, false)
- Display math ($$...$$) wrapped in <div class="math-display">
- Graceful degradation: errors logged to stderr, raw LaTeX shown
with class="math-error" for styling
Add src/math.rs with render_math() wrapper around katex-rs for
build-time LaTeX to HTML conversion. Wire module into main.rs.
- Use KatexContext::default() for standard LaTeX function registry
- Settings: display_mode toggle, throw_on_error=false for graceful
degradation
- Include unit tests for inline/display math rendering
Adapted Helix nix/injections.scm for tree-sitter-highlight:
- Removed Helix-specific predicates
- Recognizes bash in buildPhase/installPhase, python in testScript,
json in fromJSON, etc.
- queries/nix-highlights.scm: Adapted from Helix editor queries.
Properly identifies functions, properties, variable parameters,
escape sequences, and string interpolation. Much richer than
the bundled tree-sitter-nix query (99 lines vs 113 lines).
- src/highlight.rs: Load custom Nix query via include_str!
instead of using bundled HIGHLIGHTS_QUERY.
- static/style.css: Fix variable (#e36209) and punctuation (#586069)
colors to be distinct from text color.
Results: hl-function now appears (6 occurrences), hl-property
visible (17), hl-variable reduced from 43 to 17. Functions like
mkShell now render in purple/blue instead of orange.
- src/highlight.rs: Add 7 new captures to HIGHLIGHT_NAMES:
embedded, escape, function, punctuation.special, string.special,
string.special.path, string.special.uri. Now 27 total captures.
- static/style.css: Add CSS variables and class rules for new
captures in both light and dark themes. escape gets bold
styling, string-special uses distinct color.
These captures are used by tree-sitter-nix but were previously
missing, causing sparse highlighting. Now functions, escapes,
and paths are properly highlighted.