meine
meine 🌒 - A CLI file manager and system utility built with Textual. It combines intuitive command parsing with rich t…
A Python-based static site generator using Jinja templates.
git clone https://github.com/jdeanwallace/jinjabread.gitjdeanwallace/jinjabreadA Python-based static site generator using Jinja templates.
Inspired by staticjinja, jekyll, and hugo.
Requires Python 3.11 or newer.
pip install jinjabread
python -m jinjabread new mysite
python -m jinjabread build mysite
python -m jinjabread serve mysite # Visit http://127.0.0.1:8000 in your browser.
index.html) can list all other pages in the same directory.With prettify_html enabled (the default), jinjabread re-indents every generated page for readability. It is a rendering-preserving reformatter rather than a byte-preserving one, and it makes a specific contract:
pre, textarea, script, and style content is emitted verbatim. Only insignificant whitespace between block-level elements changes.\n, following the POSIX convention that a text file's last line ends in a newline. Empty input stays empty.<p> elements or plain text, is emitted as-is and is never wrapped in a <div> or <span>.<script> or <head>, is emitted as a complete document with an HTML5 doctype; a body-level fragment is emitted without a doctype or wrapper.| Name | Description | Example path |
|---|---|---|
| Project directory | The project root | mysite |
| Content directory | Contains site content where each file becomes a site page | mysite/content |
| Layouts directory | Contains page layouts that gets used by the site content | mysite/layouts |
| Static directory | Contains static media that gets copied to the output directory | mysite/static |
| Output directory | The complete generated site, ready to be hosted | mysite/public |
| Config file | Custom site configurations in TOML format | mysite/jinjabread.toml |
mysite/
├── content
│ ├── about.html
│ ├── index.html
│ └── posts
│ ├── index.html
│ ├── my-journey
│ │ ├── index.html
│ │ └── profile-photo.jpg
│ └── my-story.html
├── jinjabread.toml
├── layouts
│ └── markdown.html
├── public
│ ├── about.html
│ ├── index.html
│ ├── posts
│ │ ├── index.html
│ │ ├── my-journey
│ │ │ ├── index.html
│ │ │ └── profile-photo.jpg
│ │ └── my-story.html
│ └── static
│ └── style.css
└── static
└── style.css
| File path | URL path |
|---|---|
public/index.html |
/ |
public/about.html |
/about |
public/posts/index.html |
/posts/ |
public/posts/my-story.html |
/posts/my-story |
public/posts/my-journey/index.html |
/posts/my-journey/ |
public/posts/my-journey/profile-photo.jpg |
/posts/my-journey/profile-photo.jpg |
public/static/style.css |
/static/style.css |
content_dir = "content" layouts_dir = "layouts" static_dir = "static" output_dir = "public" prettify_html = true [context] [[pages]] type = "jinjabread.MarkdownPage" glob_pattern = "**/*.md" layout_name = "markdown.html" [[pages]] type = "jinjabread.Page" glob_pattern = "**/*"
# jinjabread.toml output_dir = "dist"
# jinjabread.toml [context] site_name = "My site" url_origin = "https://mysite.com"
# jinjabread.toml [[pages]] type = "jinjabread.MarkdownPage" glob_pattern = "**/*.md" layout_name = "post.html" [[pages]] type = "jinjabread.Page" glob_pattern = "**/*"
# jinjabread.toml
[[pages]]
type = "jinjabread.MarkdownPage"
glob_pattern = "**/*.md"
layout_name = "markdown.html"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*.txt"
[pages.context]
foo = "bar"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*"
| Page type | Keyword arguments |
|---|---|
jinjabread.Page |
- glob_pattern |
jinjabread.MarkdownPage |
- glob_pattern - layout_name |
Markdown content supports full YAML metadata.
For example, given the following content and layout:
--- # content/my-blog-post.md title: My blog post author: John Smith description: A very nice story. keywords: - thrilling - must-read --- It was a cold stormy night...
<!-- layouts/markdown.html -->
<h1>{{ title }}</h1>
<h2>{{ description }}</h2>
<h3>Written by {{ author }}</h3>
{{ content }}
Results in the following output:
<!-- public/my-blog-post.html --> <h1> My blog post </h1> <h2> A very nice story. </h2> <h3> Written by John Smith </h3> <p> It was a cold stormy night... </p>
All pages have these context variables:
| Name | Description | Example value |
|---|---|---|
url_path |
The URL path of the current page | / |
file_path |
The file path of the current page, relative to the output directory | index.html |
Page-specific context variables override global site context variables.
For example:
# jinjabread.toml
[context]
fee = "fie"
foe = "fum"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*.txt"
[pages.context]
foe = "foo"
[[pages]]
type = "jinjabread.Page"
glob_pattern = "**/*.html"
[pages.context]
foe = "bar"
.txt pages will have the following extra context variables:
fee = "fie" foe = "foo"
.html pages have the following extra context variables:
fee = "fie" foe = "bar"
All index pages (i.e., index.*) has an extra context variable named pages which is list of dictionaries of context variables from its sibling files.
For example, given the following file structure:
mysite/content/posts/
├── index.html
├── post1.md
└── post2.md
You can list all the pages in the posts directory using:
<!-- mysite/content/posts/index.html -->
{% for page in pages %}
<a href="{{ page.url_path }}">
{{ page.url_path.split('/') | last | title }}
</a>
{% endfor %}
Resulting in:
<!-- mysite/public/posts/index.html --> <a href="/posts/post1">Post1</a> <a href="/posts/post2">Post2</a>
Install uv (https://docs.astral.sh/uv/getting-started/installation/), then:
uv sync
uv run python -m unittest
uv build
export TWINE_USERNAME='__token__' export TWINE_PASSWORD='secret-token' uv run twine upload dist/*
more like this
meine 🌒 - A CLI file manager and system utility built with Textual. It combines intuitive command parsing with rich t…
Create and add progress bars, Office365 Files and Dropbox Paper papers by labels and link them to your Todoist tasks.
search projects, people, and tags