Web Renderer

Code for static site generator used to render this website. Vibecoded (created by prompting LLMs) with ChatGPT in Python.

  • Last commit at:
  • 2025-04-27 17:02:53: “Use attr_list in markdown”
  • Clone the repo with:
  • git clone TBD
  • Last commit zip archive

Web Renderer Manual

This web renderer is a Python-based static site generator that processes Markdown files with frontmatter and renders them using Jinja2 templates. It combines global and local defaults, converts Markdown content to HTML, and supports both “collocated content” (copying assets) and dynamic section listings. The renderer can build both global and local section lists from your content.

This piece of software and the documentation were created by prompting ChatGPT o3-mini-high model (now known as “vibe coding”). Commits in this GIT repository correspond to the iterative edits done by the LLM.


Table of Contents


Project Structure

The renderer expects a project structure similar to the following:

.
├── content/                   # Markdown source files and defaults
│   ├── defaults.json          # Global defaults (optional)
│   ├── blog/                  # A section directory
│   │   ├── defaults.json      # Local defaults for this section (must include "is_section": true)
│   │   ├── index.md           # Section index page (renders to www/blog/index.html)
│   │   ├── post1.md           # Blog post (renders to www/blog/post1/index.html)
│   │   ├── post2.md           # Blog post (renders to www/blog/post2/index.html)
│   │   └── images/            # Collocated assets (copied to www/blog/images/)
│   └── about/
│       └── index.md           # Regular page (renders to www/about/index.html)
├── templates/                 # Jinja2 templates
│   ├── default.html           # Default template
│   └── blog.html              # Custom template for the blog section
└── www/                       # Output directory (generated HTML and copied assets)

Key Features

  • Markdown with Frontmatter:
    Uses python-frontmatter to parse TOML/YAML frontmatter and markdown to convert content to HTML.

  • Defaults Handling:

  • Global Defaults: Loaded from content/defaults.json (if present).
  • Local Defaults: Loaded from a defaults.json in the same directory as a Markdown file.
    Frontmatter metadata always overrides these defaults.

  • Custom Templating:
    Uses Jinja2 templates from the templates/ directory. A frontmatter key template can specify a template; otherwise, default.html is used.

  • Pretty URL Generation:
    Maps input Markdown files under content/ to “clean” output paths in www/:

  • For non-index files: content/path/page.mdwww/path/page/index.html
  • For index files: content/path/index.mdwww/path/index.html (no extra subdirectory named “index”)

  • Collocated Content:
    When processing an index.md, non-Markdown and non-default files from its directory (and subdirectories) are copied to the corresponding output directory (for assets like images, code files, etc.). This behavior can be disabled via the -n or --no-copy command-line flag.

  • Dynamic Section Listings:

  • Global Sections:
    The renderer scans the entire content/ directory for “section” directories (those with a defaults.json containing "is_section": true). For each such section, it gathers entries from:

    • Markdown files (excluding index.md)
    • Subdirectories that contain an index.md
      Each entry is built from its frontmatter (without the actual content) plus a "link" key (filename or directory name without the .md extension). These entries are assembled into a dictionary (keyed by the section’s relative path) and added to the context as sections (available on every page).
  • Local Section List:
    When rendering an index.md in a section directory, a local list (section_list) is built from both the Markdown files and the subdirectories (if they contain an index.md) within that same directory. This list is built from frontmatter (plus a "link" key) and is available only on that index page.


Installation and Dependencies

Ensure you have Python 3.6 or later. Install the required packages using pip:

pip install python-frontmatter markdown jinja2

The script also uses built-in modules like os, json, argparse, and shutil.


Usage

Run the renderer from the project root (where the content/, templates/, and www/ directories reside).

Process a Single Markdown File

python renderer.py content/blog/post1.md

Process All Markdown Files Recursively

python renderer.py

Disable Copying of Collocated Files

To render pages without copying non-Markdown assets, use the --no-copy flag:

python renderer.py --no-copy

How It Works

  1. Loading Defaults and Frontmatter:
  2. Global defaults are loaded from content/defaults.json (if it exists).
  3. Local defaults are loaded from a defaults.json in the same directory as the Markdown file.
  4. The Markdown file’s frontmatter is parsed and merged with these defaults (frontmatter takes highest priority).

  5. Markdown Conversion:
    The Markdown content is converted to HTML and stored in the context under the key content_html.

  6. Template Selection:
    The renderer selects a template from the templates/ directory based on the template key in the context (defaulting to default.html if not specified).

  7. Output Path Generation:

  8. For non-index Markdown files, the output is generated at www/<relative-path>/page/index.html.
  9. For index.md files, the output is generated as www/<relative-path>/index.html.

  10. Collocated Content Copying:
    For an index.md, the renderer copies all non-Markdown and non-default files from its directory (and recursively, with filtering) into the output directory. This facilitates "collocated content" such as images or code files.

  11. Section Listings:

  12. Global Sections:
    The renderer scans the entire content/ directory for section directories (those whose local defaults.json contains "is_section": true) and builds a dictionary of sections. Each section’s value is a list of entries (from Markdown files, excluding index.md, and subdirectories containing an index.md) constructed from their frontmatter plus a "link" key. This dictionary is added to the context as sections (available on every page).

  13. Local Section List:
    When an index.md is rendered in a section directory, the renderer also builds a local list (section_list) of entries from the current directory, including entries for both Markdown files (excluding index.md) and subdirectories that contain an index.md.

  14. Rendering and Output:
    The Jinja2 template is rendered using the assembled context. The output HTML is saved in the appropriate location in www/, and if enabled, collocated files are copied.


Template Variables

Within your Jinja2 templates, the following variables (and any keys from your defaults/frontmatter) are available:

  • Standard Keys:
  • title, author, date, etc. (from frontmatter or defaults)
  • content_html — the HTML-converted content of the Markdown file.

  • Section Listings:

  • sections — a global dictionary mapping section names (relative to content/) to lists of entries (each entry is a dictionary with frontmatter attributes and a "link" key).
  • section_list — a local list (only on index.md files in a section) of entries from the current directory, including entries for Markdown files and subdirectories with an index.md.

  • Template Selection:
    The key template (if present in frontmatter or defaults) determines which template is used.


Customization

  • Templates:
    Create or modify templates in the templates/ directory. To select a custom template for a page, include a template key in its frontmatter (e.g., "template": "blog.html").

  • Defaults:
    Adjust global defaults in content/defaults.json and per-section defaults in each section’s defaults.json.

  • Section Identification:
    A directory is marked as a section if its defaults.json contains "is_section": true. The renderer will use this to build both global sections (sections) and local section lists (section_list).

  • Collocated Files:
    Collocated assets (files other than Markdown and defaults.json) are automatically copied to the corresponding output directory. Use the --no-copy option if you wish to disable this behavior.


Example

Assume you have a blog section at content/blog/ with the following files:

  • content/blog/defaults.json:
{
  "is_section": true,
  "template": "blog.html",
  "author": "John Doe"
}
  • content/blog/index.md:
+++
title: "Blog Home"
+++
# Welcome to the Blog
This is the blog homepage.
  • content/blog/post1.md:
+++
title: "First Post"
date: "2025-01-01"
+++
Content of the first post.
  • content/blog/post2.md:
    (similar structure to post1.md)
  • content/blog/images/
    (contains image files)

When rendering content/blog/index.md, the renderer will: - Load the global defaults from content/defaults.json and the local defaults from content/blog/defaults.json. - Merge these with the frontmatter from index.md. - Convert the Markdown content to HTML. - Build a global sections object (sections) that includes a blog key with entries from post1.md, post2.md, and any subdirectories containing an index.md. - Since the blog directory is marked as a section, a local section_list is also built from entries in the blog directory (from both Markdown files and subdirectories that have an index.md). - Render using the blog.html template. - Output the page to www/blog/index.html and copy the images/ directory (and other collocated assets) to www/blog/images/.


Troubleshooting

  • Directory Structure:
    Verify that your project structure follows the expected layout (i.e. content/, templates/, and www/ directories exist).

  • Defaults Files:
    Ensure that your defaults.json files are valid JSON and contain the expected keys (e.g., "is_section": true for sections).

  • Collocated Assets:
    If unexpected files are copied, double-check that your Markdown files and defaults files are correctly named (case-insensitive checks are performed).

  • Command-Line Usage:
    Use the --no-copy flag if you wish to disable asset copying for debugging purposes.

  • Debugging:
    You can add debug print statements in the Python script (for example, in the ignore function for copying or while building section lists) to inspect the internal state.


Conclusion

This web renderer software offers a flexible, Python-based solution for generating static websites. It merges global and local defaults with Markdown frontmatter, supports custom Jinja2 templating, generates pretty output paths, copies collocated assets, and dynamically builds both global and local section listings. By adjusting defaults and templates, you can tailor the renderer to fit a wide range of static site projects.

Feel free to modify and extend this software to suit your specific needs!


Happy rendering!