diff --git a/README.md b/README.md index e1f210d..d11c3b2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,30 @@

zona

[zona](https://sr.ht/~ficd/zona) is an _opinionated_ static site generator -written in Python. From a structured directory of Markdown content, zona builds -a simple static website. It's designed to get out of your way and let you focus -on writing. +written in Python. From a structured directory of Markdown content, zona +builds a simple static website. It's designed to get out of your way and +let you focus on writing. -**What do I mean by opinionated?** I built zona primarily for myself. I've tried -making it flexible by exposing as many variables as possible to the template -engine. However, if you're looking for something stable, complete, and fully -configurable, zona may not be for you. If you want a minimal Markdown blog and -are comfortable with modifying `jinja2` templates and CSS, then you're in luck. +**What do I mean by opinionated?** I built zona primarily for myself. I've +tried making it flexible by exposing as many variables as possible to the +template engine. However, if you're looking for something stable, +complete, and fully configurable, zona may not be for you. If you want a +minimal Markdown blog and are comfortable with modifying `jinja2` +templates and CSS, then you're in luck. -**Note:** This project is in early development, there are no versioned releases -yet, and breaking changes are likely. Versioned releases will be made and zona -will be published to PyPI once it's stable. zona was previously implemented in -Go; I decided to rewrite the project in Python. If you're interested in seeing -the previous codebase (which is feature incomplete), visit the -[~ficd/zona-go](https://git.sr.ht/~ficd/zona-go) repository. +**Note:** This project is in early development, there are no versioned +releases yet, and breaking changes are likely. Versioned releases will be +made and zona will be published to PyPI once it's stable. zona was +previously implemented in Go; I decided to rewrite the project in Python. +If you're interested in seeing the previous codebase (which is feature +incomplete), visit the [~ficd/zona-go](https://git.sr.ht/~ficd/zona-go) +repository. For an example of a website built with zona, please see [ficd.ca](https://ficd.ca). + - [Features](#features) - [Installation](#installation) - [Usage](#usage) @@ -41,6 +44,7 @@ For an example of a website built with zona, please see - [Sitemap](#sitemap) - [Ignore List](#ignore-list) - [Drafts](#drafts) + ## Features @@ -55,15 +59,16 @@ For an example of a website built with zona, please see - Easily configurable sitemap header. - Site footer written in Markdown. - Smart site layout discovery. - - Blog posts are automatically discovered and rendered accordingly (can be - overridden in frontmatter). + - Blog posts are automatically discovered and rendered accordingly (can + be overridden in frontmatter). - Extended Markdown renderer: - Smart internal link resolution. - Syntax highlighting. - Includes Kakoune syntax and [Ashen] highlighting. - [Image labels](#image-labels). - - Many `python-markdown` extensions enabled, including footnotes, tables, - abbreviations, etc. + - Many `python-markdown` extensions enabled, including footnotes, + tables, abbreviations, etc. + - LaTeX support. ## Installation @@ -81,66 +86,68 @@ available options and arguments._ ### Getting Started -To set up a new website, create a new directory and run `zona init` inside of -it. This creates the required directory structure and writes the default -configuration file. The default templates and default stylesheet are also -written. +To set up a new website, create a new directory and run `zona init` inside +of it. This creates the required directory structure and writes the +default configuration file. The default templates and default stylesheet +are also written. ### Building -To build the website, run `zona build`. The project root is discovered according -to the location of `config.yml`. By default, the output directory is called -`public`, and saved inside the root directory. +To build the website, run `zona build`. The project root is discovered +according to the location of `config.yml`. By default, the output +directory is called `public`, and saved inside the root directory. If you don't want discovery, you can specify the project root as the first argument to `zona build`. You may specify a path for the output using the -`--output/-o` flag. The `--draft/-d` flag includes draft posts in the output. +`--output/-o` flag. The `--draft/-d` flag includes draft posts in the +output. -_Note: the previous build is _not_ cleaned before the new site is built. If -you've deleted some pages, you may need to remove the output directory before -rebuilding._ +_Note: the previous build is _not_ cleaned before the new site is built. +If you've deleted some pages, you may need to remove the output directory +before rebuilding._ ### Live Preview -To make the writing process as frictionless as possible, zona ships with a live -preview server. It spins up an HTTP server, meaning that internal links work -properly (this is not the case if you simply open the `.html` files in your -browser.) +To make the writing process as frictionless as possible, zona ships with a +live preview server. It spins up an HTTP server, meaning that internal +links work properly (this is not the case if you simply open the `.html` +files in your browser.) -Additionally, the server watches for changes to all source files, and rebuilds -the website when they're modified. _Note: the entire website is rebuilt — this -ensures that links are properly resolved._ +Additionally, the server watches for changes to all source files, and +rebuilds the website when they're modified. _Note: the entire website is +rebuilt — this ensures that links are properly resolved._ -Optionally, live reloading of the browser is also provided. With this feature -(enabled by default), your browser will automatically refresh open pages -whenever the site is rebuilt. The live reloading requires JavaScript support -from the browser — this is why the feature is optional. +Optionally, live reloading of the browser is also provided. With this +feature (enabled by default), your browser will automatically refresh open +pages whenever the site is rebuilt. The live reloading requires JavaScript +support from the browser — this is why the feature is optional. -To start a preview server, use `zona serve`. You can specify the root directory -as its first argument. Use the `--host` to specify a host name (`localhost` by -default) and `--port/-p` to specify a port (default: `8000`). The `--output/-o` -and `--draft/-d` options from `zona build` are also supported. Finally, the -`--no-live-reload/-n` disables the live browser reloading. _Automatic site -rebuilds are not disabled._ +To start a preview server, use `zona serve`. You can specify the root +directory as its first argument. Use the `--host` to specify a host name +(`localhost` by default) and `--port/-p` to specify a port (default: +`8000`). The `--output/-o` and `--draft/-d` options from `zona build` are +also supported. Finally, the `--no-live-reload/-n` disables the live +browser reloading. _Automatic site rebuilds are not disabled._ -**Note**: if the live preview isn't working as expected, try restarting the -server. If you change the configuration or any templates, the server must also -be restarted. The live preview uses the same function as `zona build` -internally; this means that the output is also written to disk. +**Note**: if the live preview isn't working as expected, try restarting +the server. If you change the configuration or any templates, the server +must also be restarted. The live preview uses the same function as +`zona build` internally; this means that the output is also written to +disk. #### How It Works -The basic idea is this: after a rebuild, the server needs to notify your browser -to refresh the open pages. We implement this using a small amount of JavaScript. -The server injects a tiny script into any HTML page it serves; which causes your -browser to open a WebSocket connection with the server. When the site is -rebuilt, the server notifies your browser via the WebSocket, which reloads the -page. +The basic idea is this: after a rebuild, the server needs to notify your +browser to refresh the open pages. We implement this using a small amount +of JavaScript. The server injects a tiny script into any HTML page it +serves; which causes your browser to open a WebSocket connection with the +server. When the site is rebuilt, the server notifies your browser via the +WebSocket, which reloads the page. Unfortunately, there is no way to implement this feature without using -JavaScript. **JavaScript is _only_ used for the live preview feature. The script -is injected by the server, and never written to the HTML files in the output -directory.** +JavaScript. **JavaScript is _only_ used for the live preview feature. The +script is injected by the server, and never written to the HTML files in +the output directory.** ### Site Layout @@ -153,30 +160,32 @@ templates/ public/ ``` -The **root** of the zona **project** _must_ contain the configuration file, -`config.yml`, and a directory called `content`. A directory called `templates` -is optional, and prioritized if it exists. `public` is the built site output — -it's recommended to add this path to your `.gitignore`. +The **root** of the zona **project** _must_ contain the configuration +file, `config.yml`, and a directory called `content`. A directory called +`templates` is optional, and prioritized if it exists. `public` is the +built site output — it's recommended to add this path to your +`.gitignore`. The `content` directory is the **root of the website**. Think of it as the -**content root**. For example, suppose your website is hosted at `example.com`. -`content/blog/index.md` corresponds to `example.com/blog`, +**content root**. For example, suppose your website is hosted at +`example.com`. `content/blog/index.md` corresponds to `example.com/blog`, `content/blog/my-post.md` becomes `example.com/blog/my-post`, etc. - Internal links are resolved **relative to the `content` directory.** - Templates are resolved relative to the `template` directory. Markdown files inside a certain directory (`content/blog` by default) are -automatically treated as _blog posts_. This means they are rendered with the -`page` template, and included in the `post_list`, which can be included in your -site using the `post_list` template. +automatically treated as _blog posts_. This means they are rendered with +the `page` template, and included in the `post_list`, which can be +included in your site using the `post_list` template. ### Templates The `templates` directory may contain any `jinja2` template files. You may -modify the existing templates or create your own. To apply a certain template to -a page, set the `template` option in its [frontmatter](#frontmatter). The -following public variables are made available to the template engine: +modify the existing templates or create your own. To apply a certain +template to a page, set the `template` option in its +[frontmatter](#frontmatter). The following public variables are made +available to the template engine: | Name | Description | | ---------- | ------------------------------------------------------ | @@ -188,40 +197,43 @@ following public variables are made available to the template engine: #### Markdown Footer -The `templates` directory can contain a file called `footer.md`. If it exists, -it's parsed and rendered into HTML, then made available to other templates as -the `footer` variable. If `footer.md` is missing but `footer.html` exists, then -it's used instead. **Note: links are _not_ resolved in the footer.** +The `templates` directory can contain a file called `footer.md`. If it +exists, it's parsed and rendered into HTML, then made available to other +templates as the `footer` variable. If `footer.md` is missing but +`footer.html` exists, then it's used instead. **Note: links are _not_ +resolved in the footer.** ### Internal Link Resolution -When zona encounters links in Markdown documents, it attempts to resolve them as -internal links. Links beginning with `/` are resolved relative to the content -root; otherwise, they are resolved relative to the Markdown file. If the link -resolves to an existing file that is part of the website, it's replaced with an -appropriate web-server-friendly link. Otherwise, the link isn't changed. +When zona encounters links in Markdown documents, it attempts to resolve +them as internal links. Links beginning with `/` are resolved relative to +the content root; otherwise, they are resolved relative to the Markdown +file. If the link resolves to an existing file that is part of the +website, it's replaced with an appropriate web-server-friendly link. +Otherwise, the link isn't changed. -For example, suppose the file `blog/post1.md` has a link `./post2.md`. The HTML -output will contain the link `/blog/post2` (which corresponds to -`/blog/post2/index.html`). Link resolution is applied to _all_ internal links, -including those pointing to static resources like images. Links are only -modified if they point to a real file that's not included in the ignore list. +For example, suppose the file `blog/post1.md` has a link `./post2.md`. The +HTML output will contain the link `/blog/post2` (which corresponds to +`/blog/post2/index.html`). Link resolution is applied to _all_ internal +links, including those pointing to static resources like images. Links are +only modified if they point to a real file that's not included in the +ignore list. ### Syntax Highlighting -Zona uses [Pygments] to provide syntax highlighting for fenced code blocks. The -following Pygments plugins are included: +Zona uses [Pygments] to provide syntax highlighting for fenced code +blocks. The following Pygments plugins are included: - [pygments-kakoune](https://git.sr.ht/~ficd/pygments-kakoune) - - A lexer providing for highlighting Kakoune code. Available under the `kak` - and `kakrc` aliases. + - A lexer providing for highlighting Kakoune code. Available under the + `kak` and `kakrc` aliases. - [pygments-ashen](https://git.sr.ht/~ficd/ashen/tree/main/item/pygments/README.md) - - An implementation of the [Ashen](https://git.sr.ht/~ficd/ashen) theme for - Pygments. + - An implementation of the [Ashen](https://git.sr.ht/~ficd/ashen) theme + for Pygments. If you want to use any external Pygments styles or lexers, they must be -available in zona's Python environment. For example, you can give zona access to -[Catppucin](https://github.com/catppuccin/python): +available in zona's Python environment. For example, you can give zona +access to [Catppucin](https://github.com/catppuccin/python): ```yaml # config.yml @@ -236,9 +248,9 @@ Then, run zona with the following `uv` command: uvx --with catppucin zona build ``` -Inline syntax highlighting is also provided via a `python-markdown` extension. -If you prefix inline code with a shebang followed by the language identifier, it -will be highlighted. For example: +Inline syntax highlighting is also provided via a `python-markdown` +extension. If you prefix inline code with a shebang followed by the +language identifier, it will be highlighted. For example: ``` `#!python print(f"I love {foobar}!", end="")` @@ -247,11 +259,27 @@ will be rendered as (the #!lang is stripped) ``` +### Markdown Extensions + +- [BetterEm](https://facelessuser.github.io/pymdown-extensions/extensions/betterem/) +- [SuperFences](https://facelessuser.github.io/pymdown-extensions/extensions/superfences/) + - `disable_indented_code_blocks=True` +- [Extra](https://python-markdown.github.io/extensions/extra/) + - Excluding Fenced Code Blocks. +- [Caret](https://facelessuser.github.io/pymdown-extensions/extensions/caret/) +- [Tilde](https://facelessuser.github.io/pymdown-extensions/extensions/tilde/) +- [Sane Lists](https://python-markdown.github.io/extensions/sane_lists/) +- [EscapeAll](https://facelessuser.github.io/pymdown-extensions/extensions/escapeall/) + - `hardbreak=True` +- [LaTeX2MathML4Markdown](https://gitlab.com/parcifal/l2m4m/-/tree/develop?ref_type=heads) + - Disable per-file with the `math: false` frontmatter option. + ### Image Labels -A feature unique to zona is **image labels**. They make it easy to annotate -images in your Markdown documents. The alt text Markdown element is rendered as -the label — with support for inline Markdown. Consider this example: +A feature unique to zona is **image labels**. They make it easy to +annotate images in your Markdown documents. The alt text Markdown element +is rendered as the label — with support for inline Markdown. Consider this +example: ```markdown ![This **image** has _markup_.](static/markdown.png) @@ -266,14 +294,14 @@ The above results in the following HTML: ``` The `image-container` class is provided as a convenience for styling. The -default stylesheet centers the label under the image. Note: _links_ inside image -captions are not currently supported. I am looking into a solution. +default stylesheet centers the label under the image. Note: _links_ inside +image captions are not currently supported. I am looking into a solution. ### Frontmatter -YAML frontmatter can be used to configure the metadata of documents. All of them -are optional. `none` is used when the option is unset. The following options are -available: +YAML frontmatter can be used to configure the metadata of documents. All +of them are optional. `none` is used when the option is unset. The +following options are available: | Key | Type & Default | Description | | ------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------ | @@ -285,6 +313,7 @@ available: | `template` | `str \| none` = `none` | Template to use for this page. Relative to `templates/`, `.html` extension optional. | | `post` | `bool \| none` = `none` | Whether this page is a **post**. `true`/`false` is _absolute_. Leave it unset for automatic detection. | | `draft` | `bool` = `false` | Whether this page is a draft. See [drafts](#drafts) for more. | +| `math` | `bool` = `true` | Whether the LaTeX extension should be enabled for this page. | **Note**: you can specify the date in any format that can be parsed by [`python-dateutil`](https://pypi.org/project/python-dateutil/). @@ -305,9 +334,10 @@ template: post_list Welcome to my blog! Please find a list of my posts below. ``` -Setting `post: false` is necessary because, by default, all documents inside -`content/blog` are considered to be posts unless explicitly disabled in the -frontmatter. We don't want the post list to list _itself_ as a post. +Setting `post: false` is necessary because, by default, all documents +inside `content/blog` are considered to be posts unless explicitly +disabled in the frontmatter. We don't want the post list to list _itself_ +as a post. Then, you'd create `content/blog/my-post.md` and populate it: @@ -318,22 +348,22 @@ date: July 5, 2025 --- ``` -Because `my-post` is inside the `blog` directory, `post: true` is implied. If -you wanted to put it somewhere outside `blog`, you would need to set +Because `my-post` is inside the `blog` directory, `post: true` is implied. +If you wanted to put it somewhere outside `blog`, you would need to set `post: true` for it to be included in the post list. ## Configuration -Zona is configured in YAML format. The configuration file is called `config.yml` -and it **must** be located in the root of the project — in the same directory as -`content` and `templates`. +Zona is configured in YAML format. The configuration file is called +`config.yml` and it **must** be located in the root of the project — in +the same directory as `content` and `templates`. -Your configuration will be merged with the defaults. `zona init` also writes a -copy of the default configuration to the correct location. If it exists, you'll -be prompted before overwriting it. +Your configuration will be merged with the defaults. `zona init` also +writes a copy of the default configuration to the correct location. If it +exists, you'll be prompted before overwriting it. -**Note:** Currently, not every configuration value is actually used. Only the -useful settings are listed here. +**Note:** Currently, not every configuration value is actually used. Only +the useful settings are listed here. Please see the default configuration: @@ -365,10 +395,10 @@ blog: ### Sitemap -You can define a sitemap in the configuration file. This is a list of links that -will be rendered at the top of every page. The `sitemap` is a dictionary of -`string` to `string` pairs, where each key is the displayed text of the link, -and the value if the `href`. Consider this example: +You can define a sitemap in the configuration file. This is a list of +links that will be rendered at the top of every page. The `sitemap` is a +dictionary of `string` to `string` pairs, where each key is the displayed +text of the link, and the value if the `href`. Consider this example: ```yaml sitemap: @@ -380,17 +410,17 @@ sitemap: ### Ignore List -You can set a list of glob patterns in the [configuration](#configuration) that -should be ignored by zona. This is useful because zona makes a copy of _every_ -file it encounters inside the `content` directory, regardless of its type. The -paths must be relative to the `content` directory. +You can set a list of glob patterns in the [configuration](#configuration) +that should be ignored by zona. This is useful because zona makes a copy +of _every_ file it encounters inside the `content` directory, regardless +of its type. The paths must be relative to the `content` directory. ### Drafts -zona allows you to begin writing content without including it in the final build -output. If you set `draft: true` in a page's frontmatter, it will be marked as a -draft. Drafts are completely excluded from `zona build` and `zona serve` unless -the `--draft` flag is specified. +zona allows you to begin writing content without including it in the final +build output. If you set `draft: true` in a page's frontmatter, it will be +marked as a draft. Drafts are completely excluded from `zona build` and +`zona serve` unless the `--draft` flag is specified. [Ashen]: https://sr.ht/~ficd/ashen [Pygments]: https://pygments.org/