diff --git a/README.md b/README.md index 9ad13df..d66c2c2 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,38 @@ -

Zona

+

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. +[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. -**Note:** This project is in early development, and there are no versioned -releases yet. For an example of a website built with Zona, please see -[ficd.ca](https://ficd.ca) +**What do I mean by opinionated?** I built zona primarily for myself. I've tried +making it flexible by exposing 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. + +For an example of a website built with zona, please see +[ficd.ca](https://ficd.ca). - - [Features](#features) +- [Installation](#installation) +- [Usage](#usage) + - [Getting Started](#getting-started) + - [Site Layout](#site-layout) - [Internal Link Resolution](#internal-link-resolution) - [Syntax Highlighting](#syntax-highlighting) - [Image Labels](#image-labels) -- [Installation](#installation) -- [Usage](#usage) - - [Site Layout](#site-layout) - [Frontmatter](#frontmatter) - - [Configuration](#configuration) - + - [Post List](#post-list) +- [Configuration](#configuration) + - [Sitemap](#sitemap) + - [Ignore List](#ignore-list) + - [Drafts](#drafts) ## Features @@ -33,16 +45,78 @@ releases yet. 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 automatically discovered and rendered accordingly (can be + - 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. + +## Installation + +zona is not yet packaged on PyPI. You may use `uv` to install it from this +repository: + +```sh +uv tool install 'git+https://git.sr.ht/~ficd/zona' +``` + +## Usage + +_Note: you may provide the `--help` option to any subcommand to see the +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 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 start a live preview session, execute `zona serve`. The server will run until +it's killed by the user, and the website is rebuilt if any source files are +modified. _Note: if you change `config.yml` or any templates, you will need to +restart the preview server_. + +### Site Layout + +The following demonstrates a simple zona project layout: + +``` +config.yml +content/ +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 `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/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. ### Internal Link Resolution -When Zona encounters links in Markdown documents, it attempts to resolve them as +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 @@ -51,11 +125,12 @@ 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. +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 +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) @@ -65,8 +140,9 @@ following Pygments plugins are included: - 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): +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): ```yaml # config.yml @@ -75,16 +151,26 @@ markdown: theme: catppucin-mocha ``` -Then, run Zona with the following `uv` command: +Then, run zona with the following `uv` command: ```sh 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: + +``` +`#!python print(f"I love {foobar}!", end="")` +will be rendered as +`print(f"I love {foobar}!", end="")` +(the #!lang is stripped) +``` ### Image Labels -A feature unique to Zona is **image labels**. They make it easy to annotate +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: @@ -101,57 +187,131 @@ 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. - -## Installation - -Zona is not yet packaged on PyPI. You may use `uv` to install it from this -repository: - -```sh -uv tool install 'git+https://git.sr.ht/~ficd/zona' -``` - -## Usage - -_Note: you may provide the `--help` option to any subcommand to see the -available options and arguments. - -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 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 start a live preview session, execute `zona serve`. The server will run until -it's killed by the user, and the website is rebuilt if any source files are -modified. - -### Site Layout - -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. 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.** - -Markdown files inside a certain directory (`content/blog` by default) are -automatically treated as _blog posts_. This means they are rendered with the -`page.html` template, and included in the `post_list`, which can be included in -your site using the `post_list.html` template. +default stylesheet centers the label under the image. Note: _links_ inside image +captions are not currently supported. I am looking into a solution. ### Frontmatter -WIP +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: -### Configuration +| Key | Type & Default | Description | +| ------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------ | +| `title` | `str` = title-cased filename. | Title of the page. | +| `date` | Date string = file modified time. | Displayed on blog posts and used for post_list sorting. | +| `show_title` | `bool` = `true` | Whether `metadata.title` should be included in the template. | +| `header` | `bool` = `true` | Whether the header sitemap should be rendered. | +| `footer` | `bool` = `true` | Whether the footer should be rendered. | +| `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. | -WIP +**Note**: you can specify the date in any format that can be parsed by +[`python-dateutil`](https://pypi.org/project/python-dateutil/). + +### Post List + +Suppose you want `example.com/blog` to be a _post list_ page, and you want +`example.com/blog/my-post` to be a post. You would first create +`content/blog/index.md` and add the following frontmatter: + +```markdown +--- +title: Blog +post: false +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. + +Then, you'd create `content/blog/my-post.md` and populate it: + +```markdown +--- +title: My First Post +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 +`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`. + +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. + +Please see the default configuration: + +```yaml +sitemap: + Home: / +ignore: + - .marksman.toml +markdown: + image_labels: true + tab_length: 2 + syntax_highlighting: + enabled: true + theme: ashen + wrap: false +blog: + dir: blog +``` + +| Name | Description | +| -------------------------------------- | --------------------------------------------------------------------------------------------- | +| `sitemap` | Sitemap dictionary. See [Sitemap](#sitemap). | +| `ignore` | List of paths to ignore. See [Ignore List](#ignore-list). | +| `markdown.tab_length` | How many spaces should be considered an indentation level. | +| `markdown.syntax_highlighting.enabled` | Whether code should be highlighted. | +| `markdown.syntax_highlighting.theme` | [Pygments] style for highlighting. | +| `markdown.syntax_highlighting.wrap` | Whether the resulting code block should be word wrapped. | +| `blog.dir` | Name of a directory relative to `content/` whose children are automatically considered posts. | + +### 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: + +```yaml +sitemap: + Home: / + About: /about + Blog: /blog + Git: https://git.sr.ht/~ficd +``` + +### 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. + +### 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. + +[Ashen]: https://sr.ht/~ficd/ashen +[Pygments]: https://pygments.org/