added config option for preview scroll tolerance
This commit is contained in:
parent
fe0f338803
commit
10d1772a2d
5 changed files with 217 additions and 175 deletions
328
README.md
328
README.md
|
@ -1,24 +1,22 @@
|
||||||
<h1>zona</h1>
|
<h1>zona</h1>
|
||||||
|
|
||||||
[zona](https://sr.ht/~ficd/zona) is an _opinionated_ static site generator
|
[zona](https://git.ficd.sh/ficd/zona) is an _opinionated_ static site generator
|
||||||
written in Python. From a structured directory of Markdown content, zona
|
written in Python. From a structured directory of Markdown content, zona builds
|
||||||
builds a simple static website. It's designed to get out of your way and
|
a simple static website. It's designed to get out of your way and let you focus
|
||||||
let you focus on writing.
|
on writing.
|
||||||
|
|
||||||
**What do I mean by opinionated?** I built zona primarily for myself. I've
|
**What do I mean by opinionated?** I built zona primarily for myself. I've tried
|
||||||
tried making it flexible by exposing as many variables as possible to the
|
making it flexible by exposing as many variables as possible to the template
|
||||||
template engine. However, if you're looking for something stable,
|
engine. However, if you're looking for something stable, complete, and fully
|
||||||
complete, and fully configurable, zona may not be for you. If you want a
|
configurable, zona may not be for you. If you want a minimal Markdown blog and
|
||||||
minimal Markdown blog and are comfortable with modifying `jinja2`
|
are comfortable with modifying `jinja2` templates and CSS, then you're in luck.
|
||||||
templates and CSS, then you're in luck.
|
|
||||||
|
|
||||||
**Note:** This project is in early development, there are no versioned
|
**Note:** This project is in early development, there are no versioned releases
|
||||||
releases yet, and breaking changes are likely. Versioned releases will be
|
yet, and breaking changes are likely. Versioned releases will be made and zona
|
||||||
made and zona will be published to PyPI once it's stable. zona was
|
will be published to PyPI once it's stable. zona was previously implemented in
|
||||||
previously implemented in Go; I decided to rewrite the project in Python.
|
Go; I decided to rewrite the project in Python. If you're interested in seeing
|
||||||
If you're interested in seeing the previous codebase (which is feature
|
the previous codebase (which is feature incomplete), visit the
|
||||||
incomplete), visit the [~ficd/zona-go](https://git.sr.ht/~ficd/zona-go)
|
[zona-go](https://git.ficd.sh/ficd/zona-go) repository.
|
||||||
repository.
|
|
||||||
|
|
||||||
For an example of a website built with zona, please see
|
For an example of a website built with zona, please see
|
||||||
[ficd.sh](https://ficd.sh).
|
[ficd.sh](https://ficd.sh).
|
||||||
|
@ -31,6 +29,7 @@ For an example of a website built with zona, please see
|
||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
- [Building](#building)
|
- [Building](#building)
|
||||||
- [Live Preview](#live-preview)
|
- [Live Preview](#live-preview)
|
||||||
|
- [Live Reload](#live-reload)
|
||||||
- [How It Works](#how-it-works)
|
- [How It Works](#how-it-works)
|
||||||
- [Site Layout](#site-layout)
|
- [Site Layout](#site-layout)
|
||||||
- [Templates](#templates)
|
- [Templates](#templates)
|
||||||
|
@ -60,15 +59,15 @@ For an example of a website built with zona, please see
|
||||||
- Easily configurable sitemap header.
|
- Easily configurable sitemap header.
|
||||||
- Site footer written in Markdown.
|
- Site footer written in Markdown.
|
||||||
- Smart site layout discovery.
|
- Smart site layout discovery.
|
||||||
- Blog posts are automatically discovered and rendered accordingly (can
|
- Blog posts are automatically discovered and rendered accordingly (can be
|
||||||
be overridden in frontmatter).
|
overridden in frontmatter).
|
||||||
- Extended Markdown renderer:
|
- Extended Markdown renderer:
|
||||||
- Smart internal link resolution.
|
- Smart internal link resolution.
|
||||||
- Syntax highlighting.
|
- Syntax highlighting.
|
||||||
- Includes Kakoune syntax and [Ashen] highlighting.
|
- Includes Kakoune syntax and [Ashen] highlighting.
|
||||||
- [Image labels](#image-labels).
|
- [Image labels](#image-labels).
|
||||||
- Many `python-markdown` extensions enabled, including footnotes,
|
- Many `python-markdown` extensions enabled, including footnotes, tables,
|
||||||
tables, abbreviations, etc.
|
abbreviations, etc.
|
||||||
- LaTeX support.
|
- LaTeX support.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -77,7 +76,7 @@ zona is not yet packaged on PyPI. You may use `uv` to install it from this
|
||||||
repository:
|
repository:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
uv tool install 'git+https://git.sr.ht/~ficd/zona'
|
uv tool install 'git+https://git.ficd.sh/ficd/zona'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -87,71 +86,78 @@ available options and arguments._
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
|
|
||||||
To set up a new website, create a new directory and run `zona init` inside
|
To set up a new website, create a new directory and run `zona init` inside of
|
||||||
of it. This creates the required directory structure and writes the
|
it. This creates the required directory structure and writes the default
|
||||||
default configuration file. The default templates and default stylesheet
|
configuration file. The default templates and default stylesheet are also
|
||||||
are also written.
|
written.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
To build the website, run `zona build`. The project root is discovered
|
To build the website, run `zona build`. The project root is discovered according
|
||||||
according to the location of `config.yml`. By default, the output
|
to the location of `config.yml`. By default, the output directory is called
|
||||||
directory is called `public`, and saved inside the root directory.
|
`public`, and saved inside the root directory.
|
||||||
|
|
||||||
If you don't want discovery, you can specify the project root as the first
|
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
|
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/-o` flag. The `--draft/-d` flag includes draft posts in the output.
|
||||||
output.
|
|
||||||
|
|
||||||
_Note: the previous build is _not_ cleaned before the new site is built.
|
_Note: the previous build is _not_ cleaned before the new site is built. If
|
||||||
If you've deleted some pages, you may need to remove the output directory
|
you've deleted some pages, you may need to remove the output directory before
|
||||||
before rebuilding._
|
rebuilding._
|
||||||
|
|
||||||
### Live Preview
|
### Live Preview
|
||||||
|
|
||||||
To make the writing process as frictionless as possible, zona ships with a
|
To make the writing process as frictionless as possible, zona ships with a live
|
||||||
live preview server. It spins up an HTTP server, meaning that internal
|
preview server. It spins up an HTTP server, meaning that internal links work
|
||||||
links work properly (this is not the case if you simply open the `.html`
|
properly (this is not the case if you simply open the `.html` files in your
|
||||||
files in your browser.)
|
browser.)
|
||||||
|
|
||||||
Additionally, the server watches for changes to all source files, and
|
Additionally, the server watches for changes to all source files, and rebuilds
|
||||||
rebuilds the website when they're modified. _Note: the entire website is
|
the website when they're modified. _Note: the entire website is rebuilt — this
|
||||||
rebuilt — this ensures that links are properly resolved._
|
ensures that links are properly resolved._
|
||||||
|
|
||||||
Optionally, live reloading of the browser is also provided. With this
|
Drafts are enabled by default in live preview. Use `--final/-f` to disable them.
|
||||||
feature (enabled by default), your browser will automatically refresh open
|
By default, the build outputs to a temporary directory. Use `-o/--output` to
|
||||||
pages whenever the site is rebuilt. The live reloading requires JavaScript
|
override this.
|
||||||
support from the browser — this is why the feature is optional.
|
|
||||||
|
|
||||||
To start a preview server, use `zona serve`. You can specify the root
|
**Note**: if the live preview isn't working as expected, try restarting the
|
||||||
directory as its first argument. Use the `--host` to specify a host name
|
server. If you change the configuration or any templates, the server must also
|
||||||
(`localhost` by default) and `--port/-p` to specify a port (default:
|
be restarted. The live preview uses the same function as `zona build`
|
||||||
`8000`). The `--no-live-reload/-n` disables the live browser reloading
|
internally; this means that the output is also written to disk.
|
||||||
(_automatic site rebuilds are not disabled_).
|
|
||||||
|
|
||||||
Drafts are enabled by default in live preview. Use `--final/-f` to disable
|
#### Live Reload
|
||||||
them. By default, the build outputs to a temporary directory. Use
|
|
||||||
`-o/--output` to override this.
|
|
||||||
|
|
||||||
**Note**: if the live preview isn't working as expected, try restarting
|
Optionally, live reloading of the browser is also provided. With this feature
|
||||||
the server. If you change the configuration or any templates, the server
|
(enabled by default), your browser will automatically refresh open pages
|
||||||
must also be restarted. The live preview uses the same function as
|
whenever the site is rebuilt. The live reloading requires JavaScript support
|
||||||
`zona build` internally; this means that the output is also written to
|
from the browser — this is why the feature is optional.
|
||||||
disk.
|
|
||||||
|
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 `--live-reload/--no-live-reload` option overrides the value set in the
|
||||||
|
[config](#configuration) (`true` by default). _Automatic site rebuilds are not
|
||||||
|
affected_.
|
||||||
|
|
||||||
|
If you are scrolled to the bottom of the page in the browser, and you extend the
|
||||||
|
height of the page by adding new content, you will automatically be scrolled to
|
||||||
|
the _new_ bottom after reloading. You may tune the tolerance threshold in the
|
||||||
|
[configuration](#configuration).
|
||||||
|
|
||||||
#### How It Works
|
#### How It Works
|
||||||
|
|
||||||
The basic idea is this: after a rebuild, the server needs to notify your
|
The basic idea is this: after a rebuild, the server needs to notify your browser
|
||||||
browser to refresh the open pages. We implement this using a small amount
|
to refresh the open pages. We implement this using a small amount of JavaScript.
|
||||||
of JavaScript. The server injects a tiny script into any HTML page it
|
The server injects a tiny script into any HTML page it serves; which causes your
|
||||||
serves; which causes your browser to open a WebSocket connection with the
|
browser to open a WebSocket connection with the server. When the site is
|
||||||
server. When the site is rebuilt, the server notifies your browser via the
|
rebuilt, the server notifies your browser via the WebSocket, which reloads the
|
||||||
WebSocket, which reloads the page.
|
page.
|
||||||
|
|
||||||
Unfortunately, there is no way to implement this feature without using
|
Unfortunately, there is no way to implement this feature without using
|
||||||
JavaScript. **JavaScript is _only_ used for the live preview feature. The
|
JavaScript. **JavaScript is _only_ used for the live preview feature. The script
|
||||||
script is injected by the server, and never written to the HTML files in
|
is injected by the server, and never written to the HTML files in the output
|
||||||
the output directory.**
|
directory.**
|
||||||
|
|
||||||
### Site Layout
|
### Site Layout
|
||||||
|
|
||||||
|
@ -164,32 +170,30 @@ templates/
|
||||||
public/
|
public/
|
||||||
```
|
```
|
||||||
|
|
||||||
The **root** of the zona **project** _must_ contain the configuration
|
The **root** of the zona **project** _must_ contain the configuration file,
|
||||||
file, `config.yml`, and a directory called `content`. A directory called
|
`config.yml`, and a directory called `content`. A directory called `templates`
|
||||||
`templates` is optional, and prioritized if it exists. `public` is the
|
is optional, and prioritized if it exists. `public` is the built site output —
|
||||||
built site output — it's recommended to add this path to your
|
it's recommended to add this path to your `.gitignore`.
|
||||||
`.gitignore`.
|
|
||||||
|
|
||||||
The `content` directory is the **root of the website**. Think of it as the
|
The `content` directory is the **root of the website**. Think of it as the
|
||||||
**content root**. For example, suppose your website is hosted at
|
**content root**. For example, suppose your website is hosted at `example.com`.
|
||||||
`example.com`. `content/blog/index.md` corresponds to `example.com/blog`,
|
`content/blog/index.md` corresponds to `example.com/blog`,
|
||||||
`content/blog/my-post.md` becomes `example.com/blog/my-post`, etc.
|
`content/blog/my-post.md` becomes `example.com/blog/my-post`, etc.
|
||||||
|
|
||||||
- Internal links are resolved **relative to the `content` directory.**
|
- Internal links are resolved **relative to the `content` directory.**
|
||||||
- Templates are resolved relative to the `template` directory.
|
- Templates are resolved relative to the `template` directory.
|
||||||
|
|
||||||
Markdown files inside a certain directory (`content/blog` by default) are
|
Markdown files inside a certain directory (`content/blog` by default) are
|
||||||
automatically treated as _blog posts_. This means they are rendered with
|
automatically treated as _blog posts_. This means they are rendered with the
|
||||||
the `page` template, and included in the `post_list`, which can be
|
`page` template, and included in the `post_list`, which can be included in your
|
||||||
included in your site using the `post_list` template.
|
site using the `post_list` template.
|
||||||
|
|
||||||
### Templates
|
### Templates
|
||||||
|
|
||||||
The `templates` directory may contain any `jinja2` template files. You may
|
The `templates` directory may contain any `jinja2` template files. You may
|
||||||
modify the existing templates or create your own. To apply a certain
|
modify the existing templates or create your own. To apply a certain template to
|
||||||
template to a page, set the `template` option in its
|
a page, set the `template` option in its [frontmatter](#frontmatter). The
|
||||||
[frontmatter](#frontmatter). The following public variables are made
|
following public variables are made available to the template engine:
|
||||||
available to the template engine:
|
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| ---------- | ------------------------------------------------------ |
|
| ---------- | ------------------------------------------------------ |
|
||||||
|
@ -201,43 +205,40 @@ available to the template engine:
|
||||||
|
|
||||||
#### Markdown Footer
|
#### Markdown Footer
|
||||||
|
|
||||||
The `templates` directory can contain a file called `footer.md`. If it
|
The `templates` directory can contain a file called `footer.md`. If it exists,
|
||||||
exists, it's parsed and rendered into HTML, then made available to other
|
it's parsed and rendered into HTML, then made available to other templates as
|
||||||
templates as the `footer` variable. If `footer.md` is missing but
|
the `footer` variable. If `footer.md` is missing but `footer.html` exists, then
|
||||||
`footer.html` exists, then it's used instead. **Note: links are _not_
|
it's used instead. **Note: links are _not_ resolved in the footer.**
|
||||||
resolved in the footer.**
|
|
||||||
|
|
||||||
### Internal Link Resolution
|
### Internal Link Resolution
|
||||||
|
|
||||||
When zona encounters links in Markdown documents, it attempts to resolve
|
When zona encounters links in Markdown documents, it attempts to resolve them as
|
||||||
them as internal links. Links beginning with `/` are resolved relative to
|
internal links. Links beginning with `/` are resolved relative to the content
|
||||||
the content root; otherwise, they are resolved relative to the Markdown
|
root; otherwise, they are resolved relative to the Markdown file. If the link
|
||||||
file. If the link resolves to an existing file that is part of the
|
resolves to an existing file that is part of the website, it's replaced with an
|
||||||
website, it's replaced with an appropriate web-server-friendly link.
|
appropriate web-server-friendly link. Otherwise, the link isn't changed.
|
||||||
Otherwise, the link isn't changed.
|
|
||||||
|
|
||||||
For example, suppose the file `blog/post1.md` has a link `./post2.md`. The
|
For example, suppose the file `blog/post1.md` has a link `./post2.md`. The HTML
|
||||||
HTML output will contain the link `/blog/post2` (which corresponds to
|
output will contain the link `/blog/post2` (which corresponds to
|
||||||
`/blog/post2/index.html`). Link resolution is applied to _all_ internal
|
`/blog/post2/index.html`). Link resolution is applied to _all_ internal links,
|
||||||
links, including those pointing to static resources like images. Links are
|
including those pointing to static resources like images. Links are only
|
||||||
only modified if they point to a real file that's not included in the
|
modified if they point to a real file that's not included in the ignore list.
|
||||||
ignore list.
|
|
||||||
|
|
||||||
### Syntax Highlighting
|
### Syntax Highlighting
|
||||||
|
|
||||||
Zona uses [Pygments] to provide syntax highlighting for fenced code
|
Zona uses [Pygments] to provide syntax highlighting for fenced code blocks. The
|
||||||
blocks. The following Pygments plugins are included:
|
following Pygments plugins are included:
|
||||||
|
|
||||||
- [pygments-kakoune](https://git.sr.ht/~ficd/pygments-kakoune)
|
- [pygments-kakoune](https://codeberg.com/ficd/pygments-kakoune)
|
||||||
- A lexer providing for highlighting Kakoune code. Available under the
|
- A lexer providing for highlighting Kakoune code. Available under the `kak`
|
||||||
`kak` and `kakrc` aliases.
|
and `kakrc` aliases.
|
||||||
- [pygments-ashen](https://git.sr.ht/~ficd/ashen/tree/main/item/pygments/README.md)
|
- [pygments-ashen](https://codeberg.com/ficd/ashen/tree/main/item/pygments/README.md)
|
||||||
- An implementation of the [Ashen](https://git.sr.ht/~ficd/ashen) theme
|
- An implementation of the [Ashen](https://codeberg.com/ficd/ashen) theme for
|
||||||
for Pygments.
|
Pygments.
|
||||||
|
|
||||||
If you want to use any external Pygments styles or lexers, they must be
|
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
|
available in zona's Python environment. For example, you can give zona access to
|
||||||
access to [Catppucin](https://github.com/catppuccin/python):
|
[Catppucin](https://github.com/catppuccin/python):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# config.yml
|
# config.yml
|
||||||
|
@ -252,9 +253,9 @@ Then, run zona with the following `uv` command:
|
||||||
uvx --with catppucin zona build
|
uvx --with catppucin zona build
|
||||||
```
|
```
|
||||||
|
|
||||||
Inline syntax highlighting is also provided via a `python-markdown`
|
Inline syntax highlighting is also provided via a `python-markdown` extension.
|
||||||
extension. If you prefix inline code with a shebang followed by the
|
If you prefix inline code with a shebang followed by the language identifier, it
|
||||||
language identifier, it will be highlighted. For example:
|
will be highlighted. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
`#!python print(f"I love {foobar}!", end="")`
|
`#!python print(f"I love {foobar}!", end="")`
|
||||||
|
@ -280,10 +281,9 @@ will be rendered as
|
||||||
|
|
||||||
### Image Labels
|
### Image Labels
|
||||||
|
|
||||||
A feature unique to zona is **image labels**. They make it easy to
|
A feature unique to zona is **image labels**. They make it easy to annotate
|
||||||
annotate images in your Markdown documents. The alt text Markdown element
|
images in your Markdown documents. The alt text Markdown element is rendered as
|
||||||
is rendered as the label — with support for inline Markdown. Consider this
|
the label — with support for inline Markdown. Consider this example:
|
||||||
example:
|
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||

|

|
||||||
|
@ -298,14 +298,14 @@ The above results in the following HTML:
|
||||||
```
|
```
|
||||||
|
|
||||||
The `image-container` class is provided as a convenience for styling. The
|
The `image-container` class is provided as a convenience for styling. The
|
||||||
default stylesheet centers the label under the image. Note: _links_ inside
|
default stylesheet centers the label under the image. Note: _links_ inside image
|
||||||
image captions are not currently supported. I am looking into a solution.
|
captions are not currently supported. I am looking into a solution.
|
||||||
|
|
||||||
### Frontmatter
|
### Frontmatter
|
||||||
|
|
||||||
YAML frontmatter can be used to configure the metadata of documents. All
|
YAML frontmatter can be used to configure the metadata of documents. All of them
|
||||||
of them are optional. `none` is used when the option is unset. The
|
are optional. `none` is used when the option is unset. The following options are
|
||||||
following options are available:
|
available:
|
||||||
|
|
||||||
| Key | Type & Default | Description |
|
| Key | Type & Default | Description |
|
||||||
| ------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
| ------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||||
|
@ -338,10 +338,9 @@ template: post_list
|
||||||
Welcome to my blog! Please find a list of my posts below.
|
Welcome to my blog! Please find a list of my posts below.
|
||||||
```
|
```
|
||||||
|
|
||||||
Setting `post: false` is necessary because, by default, all documents
|
Setting `post: false` is necessary because, by default, all documents inside
|
||||||
inside `content/blog` are considered to be posts unless explicitly
|
`content/blog` are considered to be posts unless explicitly disabled in the
|
||||||
disabled in the frontmatter. We don't want the post list to list _itself_
|
frontmatter. We don't want the post list to list _itself_ as a post.
|
||||||
as a post.
|
|
||||||
|
|
||||||
Then, you'd create `content/blog/my-post.md` and populate it:
|
Then, you'd create `content/blog/my-post.md` and populate it:
|
||||||
|
|
||||||
|
@ -352,32 +351,32 @@ date: July 5, 2025
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Because `my-post` is inside the `blog` directory, `post: true` is implied.
|
Because `my-post` is inside the `blog` directory, `post: true` is implied. If
|
||||||
If you wanted to put it somewhere outside `blog`, you would need to set
|
you wanted to put it somewhere outside `blog`, you would need to set
|
||||||
`post: true` for it to be included in the post list.
|
`post: true` for it to be included in the post list.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Zona is configured in YAML format. The configuration file is called
|
Zona is configured in YAML format. The configuration file is called `config.yml`
|
||||||
`config.yml` and it **must** be located in the root of the project — in
|
and it **must** be located in the root of the project — in the same directory as
|
||||||
the same directory as `content` and `templates`.
|
`content` and `templates`.
|
||||||
|
|
||||||
Your configuration will be merged with the defaults. `zona init` also
|
Your configuration will be merged with the defaults. `zona init` also writes a
|
||||||
writes a copy of the default configuration to the correct location. If it
|
copy of the default configuration to the correct location. If it exists, you'll
|
||||||
exists, you'll be prompted before overwriting it.
|
be prompted before overwriting it.
|
||||||
|
|
||||||
**Note:** Currently, not every configuration value is actually used. Only
|
**Note:** Currently, not every configuration value is actually used. Only the
|
||||||
the useful settings are listed here.
|
useful settings are listed here.
|
||||||
|
|
||||||
Please see the default configuration:
|
Please see the default configuration:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
base_url: /
|
||||||
sitemap:
|
sitemap:
|
||||||
Home: /
|
Home: /
|
||||||
ignore:
|
ignore:
|
||||||
- .marksman.toml
|
- .marksman.toml
|
||||||
markdown:
|
markdown:
|
||||||
image_labels: true
|
|
||||||
tab_length: 2
|
tab_length: 2
|
||||||
syntax_highlighting:
|
syntax_highlighting:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -385,49 +384,60 @@ markdown:
|
||||||
wrap: false
|
wrap: false
|
||||||
links:
|
links:
|
||||||
external_new_tab: true
|
external_new_tab: true
|
||||||
|
build:
|
||||||
|
clean_output_dir: true
|
||||||
|
include_drafts: false
|
||||||
blog:
|
blog:
|
||||||
dir: blog
|
dir: blog
|
||||||
|
server:
|
||||||
|
reload:
|
||||||
|
enabled: true
|
||||||
|
scroll_tolerance: 100
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| -------------------------------------- | --------------------------------------------------------------------------------------------- |
|
| -------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||||
| `sitemap` | Sitemap dictionary. See [Sitemap](#sitemap). |
|
| `sitemap` | Sitemap dictionary. See [Sitemap](#sitemap). |
|
||||||
| `ignore` | List of paths to ignore. See [Ignore List](#ignore-list). |
|
| `ignore` | List of paths to ignore. See [Ignore List](#ignore-list). |
|
||||||
| `markdown.tab_length` | How many spaces should be considered an indentation level. |
|
| `markdown.tab_length` | How many spaces should be considered an indentation level. |
|
||||||
| `markdown.syntax_highlighting.enabled` | Whether code should be highlighted. |
|
| `markdown.syntax_highlighting.enabled` | Whether code should be highlighted. |
|
||||||
| `markdown.syntax_highlighting.theme` | [Pygments] style for highlighting. |
|
| `markdown.syntax_highlighting.theme` | [Pygments] style for highlighting. |
|
||||||
| `markdown.syntax_highlighting.wrap` | Whether the resulting code block should be word wrapped. |
|
| `markdown.syntax_highlighting.wrap` | Whether the resulting code block should be word wrapped. |
|
||||||
| `markdown.links.external_new_tab` | Whether external links should be opened in a new tab. |
|
| `markdown.links.external_new_tab` | Whether external links should be opened in a new tab. |
|
||||||
| `blog.dir` | Name of a directory relative to `content/` whose children are automatically considered posts. |
|
| `build.clean_output_dir` | Whether previous build artifacts should be cleared when building. Recommended to leave this on. |
|
||||||
|
| `build.include_drafts` | Whether drafts should be included by default. |
|
||||||
|
| `blog.dir` | Name of a directory relative to `content/` whose children are automatically considered posts. |
|
||||||
|
| `server.reload.enabled` | Whether the preview server should use [live reload](#live-preview). |
|
||||||
|
| `server.reload.scroll_tolerance` | The distance, in pixels, from the bottom to still count as "scrolled to bottom". |
|
||||||
|
|
||||||
### Sitemap
|
### Sitemap
|
||||||
|
|
||||||
You can define a sitemap in the configuration file. This is a list of
|
You can define a sitemap in the configuration file. This is a list of links that
|
||||||
links that will be rendered at the top of every page. The `sitemap` is a
|
will be rendered at the top of every page. The `sitemap` is a dictionary of
|
||||||
dictionary of `string` to `string` pairs, where each key is the displayed
|
`string` to `string` pairs, where each key is the displayed text of the link,
|
||||||
text of the link, and the value if the `href`. Consider this example:
|
and the value if the `href`. Consider this example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
sitemap:
|
sitemap:
|
||||||
Home: /
|
Home: /
|
||||||
About: /about
|
About: /about
|
||||||
Blog: /blog
|
Blog: /blog
|
||||||
Git: https://git.sr.ht/~ficd
|
Git: https://git.ficd.sh/ficd
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ignore List
|
### Ignore List
|
||||||
|
|
||||||
You can set a list of glob patterns in the [configuration](#configuration)
|
You can set a list of glob patterns in the [configuration](#configuration) that
|
||||||
that should be ignored by zona. This is useful because zona makes a copy
|
should be ignored by zona. This is useful because zona makes a copy of _every_
|
||||||
of _every_ file it encounters inside the `content` directory, regardless
|
file it encounters inside the `content` directory, regardless of its type. The
|
||||||
of its type. The paths must be relative to the `content` directory.
|
paths must be relative to the `content` directory.
|
||||||
|
|
||||||
### Drafts
|
### Drafts
|
||||||
|
|
||||||
zona allows you to begin writing content without including it in the final
|
zona allows you to begin writing content without including it in the final build
|
||||||
build output. If you set `draft: true` in a page's frontmatter, it will be
|
output. If you set `draft: true` in a page's frontmatter, it will be marked as a
|
||||||
marked as a draft. Drafts are completely excluded from `zona build` and
|
draft. Drafts are completely excluded from `zona build` and `zona serve` unless
|
||||||
`zona serve` unless the `--draft` flag is specified.
|
the `--draft` flag is specified.
|
||||||
|
|
||||||
[Ashen]: https://sr.ht/~ficd/ashen
|
[Ashen]: https://codeberg.com/ficd/ashen
|
||||||
[Pygments]: https://pygments.org/
|
[Pygments]: https://pygments.org/
|
||||||
|
|
|
@ -95,14 +95,15 @@ def serve(
|
||||||
bool,
|
bool,
|
||||||
typer.Option("--final", "-f", help="Don't include drafts."),
|
typer.Option("--final", "-f", help="Don't include drafts."),
|
||||||
] = False,
|
] = False,
|
||||||
no_live_reload: Annotated[
|
live_reload: Annotated[
|
||||||
bool,
|
bool | None,
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--no-live-reload",
|
"--live-reload/--no-live-reload",
|
||||||
"-n",
|
"-l/-L",
|
||||||
help="Don't automatically reload web preview.",
|
help="Automatically reload web preview. Overrides config.",
|
||||||
|
show_default=False,
|
||||||
),
|
),
|
||||||
] = False,
|
] = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Build the website and start a live preview server.
|
Build the website and start a live preview server.
|
||||||
|
@ -115,13 +116,17 @@ def serve(
|
||||||
print("Preview without drafts.")
|
print("Preview without drafts.")
|
||||||
else:
|
else:
|
||||||
print("Preview with drafts.")
|
print("Preview with drafts.")
|
||||||
|
if live_reload is None:
|
||||||
|
reload = None
|
||||||
|
else:
|
||||||
|
reload = live_reload
|
||||||
server.serve(
|
server.serve(
|
||||||
root=root,
|
root=root,
|
||||||
output=output,
|
output=output,
|
||||||
draft=not final,
|
draft=not final,
|
||||||
host=host,
|
host=host,
|
||||||
port=port,
|
port=port,
|
||||||
live_reload=not no_live_reload,
|
user_reload=reload,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,17 @@ class BuildConfig:
|
||||||
include_drafts: bool = False
|
include_drafts: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ReloadConfig:
|
||||||
|
enabled: bool = True
|
||||||
|
scroll_tolerance: int = 100
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ServerConfig:
|
||||||
|
reload: ReloadConfig = field(default_factory=ReloadConfig)
|
||||||
|
|
||||||
|
|
||||||
IGNORELIST = [".marksman.toml"]
|
IGNORELIST = [".marksman.toml"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,6 +82,7 @@ class ZonaConfig:
|
||||||
markdown: MarkdownConfig = field(default_factory=MarkdownConfig)
|
markdown: MarkdownConfig = field(default_factory=MarkdownConfig)
|
||||||
build: BuildConfig = field(default_factory=BuildConfig)
|
build: BuildConfig = field(default_factory=BuildConfig)
|
||||||
blog: BlogConfig = field(default_factory=BlogConfig)
|
blog: BlogConfig = field(default_factory=BlogConfig)
|
||||||
|
server: ServerConfig = field(default_factory=ServerConfig)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_file(cls, path: Path) -> "ZonaConfig":
|
def from_file(cls, path: Path) -> "ZonaConfig":
|
||||||
|
|
|
@ -9,12 +9,13 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const ws = new WebSocket("__SOCKET_ADDRESS_");
|
const ws = new WebSocket("__SOCKET_ADDRESS__");
|
||||||
|
const tol = __SCROLL_TOLERANCE__;
|
||||||
ws.onmessage = event => {
|
ws.onmessage = event => {
|
||||||
if (event.data === "reload") {
|
if (event.data === "reload") {
|
||||||
// store flag if user currently at bottom
|
// store flag if user currently at bottom
|
||||||
const nearBottom = window.innerHeight + window.scrollY
|
const nearBottom = window.innerHeight + window.scrollY
|
||||||
>= document.body.scrollHeight - 100;
|
>= document.body.scrollHeight - tol;
|
||||||
if (nearBottom) {
|
if (nearBottom) {
|
||||||
localStorage.setItem("wasAtBottom", "1");
|
localStorage.setItem("wasAtBottom", "1");
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,17 +21,22 @@ from zona.websockets import WebSocketServer
|
||||||
logger = get_logger()
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
def make_reload_script(host: str, port: int) -> str:
|
def make_reload_script(
|
||||||
|
host: str, port: int, scroll_tolerance: int
|
||||||
|
) -> str:
|
||||||
"""Generates the JavaScript that must be injected into HTML pages for the live reloading to work."""
|
"""Generates the JavaScript that must be injected into HTML pages for the live reloading to work."""
|
||||||
js = util.get_resource("server/inject.js").contents
|
js = util.get_resource("server/inject.js").contents
|
||||||
js = util.minify_js(js)
|
js = util.minify_js(js)
|
||||||
address = f"ws://{host}:{port}"
|
address = f"ws://{host}:{port}"
|
||||||
for placeholder, value in (("__SOCKET_ADDRESS_", address),):
|
for placeholder, value in (
|
||||||
|
("__SOCKET_ADDRESS__", address),
|
||||||
|
("__SCROLL_TOLERANCE__", scroll_tolerance),
|
||||||
|
):
|
||||||
if placeholder not in js:
|
if placeholder not in js:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"{placeholder} missing from reload script template!"
|
f"{placeholder} missing from reload script template!"
|
||||||
)
|
)
|
||||||
js = js.replace(placeholder, value)
|
js = js.replace(placeholder, str(value))
|
||||||
return f"<script>{js}</script>"
|
return f"<script>{js}</script>"
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,12 +177,13 @@ def serve(
|
||||||
draft: bool = True,
|
draft: bool = True,
|
||||||
host: str = "localhost",
|
host: str = "localhost",
|
||||||
port: int = 8000,
|
port: int = 8000,
|
||||||
live_reload: bool = True,
|
user_reload: bool | None = None,
|
||||||
):
|
):
|
||||||
"""Serve preview website with live reload and automatic rebuild."""
|
"""Serve preview website with live reload and automatic rebuild."""
|
||||||
# create temp dir, automatic cleanup
|
# create temp dir, automatic cleanup
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
builder = ZonaBuilder(root, Path(tmp), draft)
|
builder = ZonaBuilder(root, Path(tmp), draft)
|
||||||
|
config = builder.config
|
||||||
# initial site build
|
# initial site build
|
||||||
builder.build()
|
builder.build()
|
||||||
# use discovered paths if none provided
|
# use discovered paths if none provided
|
||||||
|
@ -186,13 +192,21 @@ def serve(
|
||||||
if root is None:
|
if root is None:
|
||||||
root = builder.layout.root
|
root = builder.layout.root
|
||||||
|
|
||||||
# spin up websocket server for live reloading
|
# use config value unless overridden by user
|
||||||
if live_reload:
|
reload = config.server.reload.enabled
|
||||||
|
if user_reload is not None:
|
||||||
|
reload = user_reload
|
||||||
|
if reload:
|
||||||
|
print("Live reloading is enabled.")
|
||||||
|
# spin up websocket server for live reloading
|
||||||
ws_port = port + 1
|
ws_port = port + 1
|
||||||
ws_server = WebSocketServer(host, ws_port)
|
ws_server = WebSocketServer(host, ws_port)
|
||||||
ws_server.start()
|
ws_server.start()
|
||||||
# generate reload script for injection
|
# generate reload script for injection
|
||||||
reload_script = make_reload_script(host, ws_port)
|
scroll_tolerance = config.server.reload.scroll_tolerance
|
||||||
|
reload_script = make_reload_script(
|
||||||
|
host, ws_port, scroll_tolerance
|
||||||
|
)
|
||||||
# generate handler with reload script as attribute
|
# generate handler with reload script as attribute
|
||||||
handler = make_handler_class(reload_script)
|
handler = make_handler_class(reload_script)
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue