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
- Key Features
- Installation and Dependencies
- Usage
- How It Works
- Template Variables
- Customization
- Example
- Troubleshooting
- Conclusion
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:
Usespython-frontmatter
to parse TOML/YAML frontmatter andmarkdown
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 thetemplates/
directory. A frontmatter keytemplate
can specify a template; otherwise,default.html
is used. -
Pretty URL Generation:
Maps input Markdown files undercontent/
to “clean” output paths inwww/
: - For non-index files:
content/path/page.md
→www/path/page/index.html
-
For index files:
content/path/index.md
→www/path/index.html
(no extra subdirectory named “index”) -
Collocated Content:
When processing anindex.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 entirecontent/
directory for “section” directories (those with adefaults.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 assections
(available on every page).
- Markdown files (excluding
-
Local Section List:
When rendering anindex.md
in a section directory, a local list (section_list
) is built from both the Markdown files and the subdirectories (if they contain anindex.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
- Loading Defaults and Frontmatter:
- Global defaults are loaded from
content/defaults.json
(if it exists). - Local defaults are loaded from a
defaults.json
in the same directory as the Markdown file. -
The Markdown file’s frontmatter is parsed and merged with these defaults (frontmatter takes highest priority).
-
Markdown Conversion:
The Markdown content is converted to HTML and stored in the context under the keycontent_html
. -
Template Selection:
The renderer selects a template from thetemplates/
directory based on thetemplate
key in the context (defaulting todefault.html
if not specified). -
Output Path Generation:
- For non-index Markdown files, the output is generated at
www/<relative-path>/page/index.html
. -
For index.md files, the output is generated as
www/<relative-path>/index.html
. -
Collocated Content Copying:
For anindex.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. -
Section Listings:
-
Global Sections:
The renderer scans the entirecontent/
directory for section directories (those whose localdefaults.json
contains"is_section": true
) and builds a dictionary of sections. Each section’s value is a list of entries (from Markdown files, excludingindex.md
, and subdirectories containing anindex.md
) constructed from their frontmatter plus a"link"
key. This dictionary is added to the context assections
(available on every page). -
Local Section List:
When anindex.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 (excludingindex.md
) and subdirectories that contain anindex.md
. -
Rendering and Output:
The Jinja2 template is rendered using the assembled context. The output HTML is saved in the appropriate location inwww/
, 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 tocontent/
) 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 anindex.md
. -
Template Selection:
The keytemplate
(if present in frontmatter or defaults) determines which template is used.
Customization
-
Templates:
Create or modify templates in thetemplates/
directory. To select a custom template for a page, include atemplate
key in its frontmatter (e.g.,"template": "blog.html"
). -
Defaults:
Adjust global defaults incontent/defaults.json
and per-section defaults in each section’sdefaults.json
. -
Section Identification:
A directory is marked as a section if itsdefaults.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 anddefaults.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/
, andwww/
directories exist). -
Defaults Files:
Ensure that yourdefaults.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!