Compare commits

...

13 commits

Author SHA1 Message Date
e7ba0bad7b wip: generate RSS feed
Some checks failed
/ test-build (push) Failing after 33s
2025-07-16 23:49:24 -04:00
ac8c88e2af ci: updated publish job to use cache
All checks were successful
/ test-build (push) Successful in 20s
2025-07-16 01:37:50 -04:00
59948ec517 updated changelog
All checks were successful
/ publish (push) Successful in 15s
/ test-build (push) Successful in 18s
2025-07-16 01:35:19 -04:00
dea02e3a4e ci: updated test to check version
All checks were successful
/ test-build (push) Successful in 21s
2025-07-16 01:33:53 -04:00
3dc623f1c7 added --version flag to cli
All checks were successful
/ test-build (push) Successful in 37s
2025-07-16 01:32:59 -04:00
4cef77e4e0 ci: added test with cache
All checks were successful
/ test-build (push) Successful in 21s
2025-07-16 01:17:23 -04:00
8ad8bad438 updated styling of lists 2025-07-15 23:24:06 -04:00
a0fb62ac7a release 1.2.0
All checks were successful
/ publish (push) Successful in 22s
- Improved the appearance and semantics of post navigation buttons.
  - Navigation now follows "newer/older" logic.
- Added hover symbols to page titles.
- Improved the styling of hover symbols and links.
2025-07-15 18:46:25 -04:00
d35d3f61fb updated documentation 2025-07-15 18:42:32 -04:00
4ea80a33f8 improved styling of hover symbols 2025-07-15 18:35:13 -04:00
c33acfa1c8 added hover symbols to page titles 2025-07-15 17:53:05 -04:00
bafe70ed37 fix post-nav button order
Navigation now follows newer/older logic. click right to go older, left
to go newer.
2025-07-15 17:18:09 -04:00
fa10a813f2 add to .kakrc 2025-07-15 16:48:03 -04:00
17 changed files with 351 additions and 121 deletions

View file

@ -7,6 +7,9 @@ jobs:
runs-on: based-alpine
steps:
- uses: actions/checkout@v4
- name: setup cache
id: uv-cache
uses: https://git.ficd.sh/ficd/uv-cache@v1
- name: build
run: |
uv sync

View file

@ -0,0 +1,18 @@
# this workflow checks if the project can be built successfully.
# it also serves to test whether based-alpine and uv-cache are working properly.
# Unit tests will be added here eventually
on: [push]
jobs:
test-build:
runs-on: based-alpine
steps:
- name: checkout source
uses: actions/checkout@v4
- name: setup cache
id: uv-cache
uses: https://git.ficd.sh/ficd/uv-cache@v1
- name: sync and build
run: |
uv sync
uv build
uv run zona --version

4
.kakrc
View file

@ -15,6 +15,10 @@ define-command readme %{
root-edit README.md
}
define-command kakrc %{
root-edit .kakrc
}
# change working directory to the package
hook global -once BufCreate .* %{
change-directory %exp{%opt{project_root}/src/zona}

View file

@ -1,3 +1,14 @@
# 1.2.1
- Added `--version` flag to CLI.
# 1.2.0
- Improved the appearance and semantics of post navigation buttons.
- Navigation now follows "newer/older" logic.
- Added hover symbols to page titles.
- Improved the styling of hover symbols and links.
# 1.1.0
- Major improvements to default stylesheet.

View file

@ -101,10 +101,6 @@ 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.
_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
@ -121,9 +117,10 @@ 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 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.
server. If you change the configuration, 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 --- a temporary directory by default, unless
overridden with `-o/--output`.
#### Live Reload
@ -197,12 +194,16 @@ option in its [frontmatter](#frontmatter). The following public variables are
made available to the template engine:
| Name | Description |
| ---------- | ------------------------------------------------------ |
| ----------- | -------------------------------------------------------- |
| `content` | The content of this page. |
| `url` | The resolved URL of this page. |
| `metadata` | The frontmatter of this page (_merged with defaults_). |
| `header` | The sitemap header in HTML form. Can be `False`. |
| `footer` | The footer in HTML form. Can be `False`. |
| `is_post` | Whether this page is a post. |
| `newer` | URL of the newer post in the post list. |
| `older` | URL of the older post in the post list. |
| `post_list` | A sorted list of `Item` objects. Meant for internal use. |
#### Markdown Footer

View file

@ -1,6 +1,6 @@
[project]
name = "zona"
version = "1.1.0"
version = "1.2.1"
description = "Opinionated static site generator."
license = "BSD-3-Clause "
license-files = ["LICENSE"]
@ -11,6 +11,7 @@ authors = [
requires-python = ">=3.12"
dependencies = [
"dacite>=1.9.2",
"feedgen>=1.0.0",
"jinja2>=3.1.6",
"l2m4m>=1.0.4",
"markdown>=3.8.2",
@ -57,7 +58,7 @@ reportUnusedCallResult = false
reportCallInDefaultInitializer = false
enableTypeIgnoreComments = true
reportIgnoreCommentWithoutRule = false
allowedUntypedLibraries = ["frontmatter", "pygments", "pymdownx", "l2m4m"]
allowedUntypedLibraries = ["frontmatter", "pygments", "pymdownx", "l2m4m", "feedgen"]
[tool.ruff]
line-length = 70

View file

@ -1,7 +1,9 @@
import shutil
from datetime import date
from datetime import date, datetime
from pathlib import Path
from feedgen.feed import FeedGenerator
from zona import markdown as zmd
from zona import util
from zona.config import ZonaConfig
@ -49,9 +51,7 @@ class ZonaBuilder:
destination=destination,
url=str(destination.relative_to(layout.output)),
)
if path.name.endswith(
".md"
) and not path.is_relative_to(
if path.name.endswith(".md") and not path.is_relative_to(
layout.root / "content" / "static"
):
logger.debug(f"Parsing {path.name}.")
@ -72,13 +72,11 @@ class ZonaBuilder:
item.copy = False
name = destination.stem
if name == "index":
item.destination = (
item.destination.with_suffix(".html")
item.destination = item.destination.with_suffix(
".html"
)
else:
relative = path.relative_to(base).with_suffix(
""
)
relative = path.relative_to(base).with_suffix("")
name = relative.stem
item.destination = (
layout.output
@ -90,15 +88,43 @@ class ZonaBuilder:
layout.output
)
item.url = (
""
if rel_url == Path(".")
else rel_url.as_posix()
"" if rel_url == Path(".") else rel_url.as_posix()
)
items.append(item)
self.items = items
def _build(self):
def generate_feed(self) -> str:
post_list = self._get_post_list()
config = self.config.feed
fg = FeedGenerator()
fg.id(config.link)
fg.title(config.title)
author = {
"name": config.author.name,
"email": config.author.email,
}
fg.author(author)
fg.link(href=config.link, rel="self")
fg.language(config.language)
fg.description(config.description)
for post in post_list:
assert post.metadata
fe = fg.add_entry() # pyright: ignore[reportUnknownVariableType]
fe.id(f"{config.link}{util.normalize_url(post.url)}") # pyright: ignore[reportUnknownMemberType]
fe.title(post.metadata.title) # pyright: ignore[reportUnknownMemberType]
fe.author(author) # pyright: ignore[reportUnknownMemberType]
fe.description(post.metadata.description) # pyright: ignore[reportUnknownMemberType]
date = post.metadata.date
# ISSUE: lack of timezone causes an error, need to add TZ config option
dt = datetime.combine(date, datetime.min.time())
fe.pubDate(dt) # pyright: ignore[reportUnknownMemberType]
return fg.rss_str(pretty=True) # pyright: ignore[reportUnknownVariableType]
def _get_post_list(self) -> list[Item]:
assert self.items
# sort according to date
# descending order
post_list: list[Item] = sorted(
[item for item in self.items if item.post],
key=lambda item: item.metadata.date
@ -106,12 +132,17 @@ class ZonaBuilder:
else date.min,
reverse=True,
)
# number of posts
# generate RSS here
posts = len(post_list)
# link post chronology
for i, item in enumerate(post_list):
prev = post_list[i - 1] if i > 0 else None
next = post_list[i + 1] if i < posts - 2 else None
item.previous = prev
item.next = next
# prev: older post
older = post_list[i + 1] if i + 1 < posts else None
# next: newer post
newer = post_list[i - 1] if i > 0 else None
item.older = older
item.newer = newer
templater = Templater(
config=self.config,
@ -125,9 +156,7 @@ class ZonaBuilder:
# write code highlighting stylesheet
if self.config.markdown.syntax_highlighting.enabled:
pygments_style = zmd.get_style_defs(self.config)
pygments_path = (
self.layout.output / "static" / "pygments.css"
)
pygments_path = self.layout.output / "static" / "pygments.css"
util.ensure_parents(pygments_path)
pygments_path.write_text(pygments_style)
for item in self.item_map.values():
@ -175,3 +204,4 @@ class ZonaBuilder:
logger.debug("Building...")
self._build()
self.fresh = False
print(self.generate_feed())

View file

@ -1,3 +1,4 @@
from importlib.metadata import version as __version__
from pathlib import Path
from typing import Annotated
@ -134,8 +135,23 @@ def serve(
)
def version_callback(value: bool):
if value:
print(f"Zona version: {__version__('zona')}")
raise typer.Exit()
@app.callback()
def main_entry(
version: Annotated[ # pyright: ignore[reportUnusedParameter]
bool | None,
typer.Option(
"--version",
callback=version_callback,
is_eager=True,
help="Print version info and exit.",
),
] = None,
verbosity: Annotated[
str,
typer.Option(

View file

@ -69,16 +69,32 @@ class ServerConfig:
reload: ReloadConfig = field(default_factory=ReloadConfig)
@dataclass
class AuthorConfig:
name: str = "John Doe"
email: str = "john@doe.net"
@dataclass
class FeedConfig:
enabled: bool = True
path: str = "feed.xml"
link: str = "https://example.com"
title: str = "Zona Website"
description: str = "My zona website."
language: str = "en"
author: AuthorConfig = field(default_factory=AuthorConfig)
IGNORELIST = [".marksman.toml"]
@dataclass
class ZonaConfig:
base_url: str = "/"
feed: FeedConfig = field(default_factory=FeedConfig)
# dictionary where key is name, value is url
sitemap: SitemapConfig = field(
default_factory=lambda: {"Home": "/"}
)
sitemap: SitemapConfig = field(default_factory=lambda: {"Home": "/"})
# list of globs relative to content that should be ignored
ignore: list[str] = field(default_factory=lambda: IGNORELIST)
markdown: MarkdownConfig = field(default_factory=MarkdownConfig)

View file

@ -1,4 +1,5 @@
:root {
--main-placeholder-color: #b14242;
--main-text-color: #b4b4b4;
--main-text-opaque-color: rgba(180, 180, 180, 0.8);
--main-bg-color: #121212;
@ -33,27 +34,101 @@ header {
.post-nav {
font-family: monospace;
font-size: 0.95em;
white-space: nowrap;
}
.post-nav .bar {
position: relative;
bottom: 0.05em;
display: inline-block;
width: 1px;
height: 0.8em;
background-color: currentColor;
vertical-align: middle;
margin: 0 0.3em;
}
.post-nav .placeholder {
color: var(--main-placeholder-color);
}
.post-nav .symbol {
color: var(--main-bullet-color);
margin: 0;
padding: 0;
display: inline;
}
.post-nav a {
margin: 0 2px;
.site-logo.hover-symbol::before {
content: "~/";
}
.site-logo {
.title.hover-symbol::before {
content: "$";
}
.hover-symbol {
color: inherit;
position: relative;
font-weight: bold;
text-decoration: none;
/* font-size: 1.75rem;*/
transition: color 0.15s ease;
}
.hover-symbol::before {
font-family: monospace;
content: "#";
position: absolute;
right: 100%;
margin-right: 0.25em;
top: 50%;
transform: translateY(-50%);
opacity: 0;
transition: opacity 0.15s ease, color 0.15s ease;
color: var(--main-text-color);
}
.hover-symbol:hover::before {
opacity: 1;
color: var(--main-placeholder-color);
}
.hover-symbol:hover {
background-color: transparent;
}
.toc ul {
font-family: monospace;
text-transform: lowercase;
margin: auto;
width: 50%;
}
.toc ul ul {
padding-left: 1em;
margin-left: 1em;
/* list-style-type: " ";*/
}
.toc ul ul ul {
padding-left: 1em;
margin-left: 1em;
/* list-style-type: "-- ";*/
}
.toclink {
position: relative;
text-decoration: none;
color: inherit;
transition: color 0.15s ease;
text-transform: lowercase;
font-family: monospace;
}
.post-list a {
position: relative;
text-decoration: none;
transition: color 0.15s ease;
text-transform: lowercase;
font-family: monospace;
}
.toclink::before {
@ -64,7 +139,16 @@ header {
top: 50%;
transform: translateY(-50%);
opacity: 0;
transition: opacity 0.2s ease;
transition: opacity 0.15s ease, color 0.15s ease;
color: var(--main-link-color);
}
.toclink:hover::before {
opacity: 1;
color: var(--main-placeholder-color);
}
.toclink:hover {
background-color: transparent;
}
h1 .toclink::before {
@ -83,13 +167,6 @@ h4 .toclink::before {
content: "###";
}
.toclink:hover::before {
opacity: 1;
}
.toclink:hover {
background-color: transparent;
}
/* h1, */
h2,
h3,
@ -111,6 +188,11 @@ h1 {
font-family: monospace;
}
.title a {
color: inherit;
text-decoration: none;
}
article h1:first-of-type {
margin-block-start: 1.67rem;
}
@ -154,40 +236,55 @@ h6 {
font-weight: bold;
}
/*ul {*/
/* list-style-type: disc;*/
/*}*/
ul {
list-style-type: disc;
/* or any other list style */
list-style-type: " ";
}
ul ul {
padding-left: 1em;
margin-left: 1em;
list-style-type: "+ ";
}
ul ul ul {
list-style-type: "~ ";
}
ul ul ul ul {
list-style-type: "• ";
}
ul ul ul ul ul {
list-style-type: " ";
}
ul ul ul ul ul ul {
list-style-type: "+ ";
}
ul ul ul ul ul ul ul {
list-style-type: "~ ";
}
ul ul ul ul ul ul ul ul {
list-style-type: "• ";
}
li::marker {
color: var(--main-bullet-color);
/* Change this to your desired color */
}
a {
color: var(--main-link-color);
text-decoration: none;
position: relative;
text-decoration: underline;
text-decoration-color: rgba(0, 0, 0, 0);
text-underline-offset: 2px;
}
a::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
width: 100%;
height: 1px;
background-color: currentColor;
transform: scaleX(0);
transform-origin: center;
transition: transform 0.1s ease;
a {
transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
a:hover::after {
transform: scaleX(1);
}
a:has(> code)::after {
display: none;
a:hover {
text-decoration-color: var(--main-placeholder-color);
color: var(--main-bullet-color);
}
max-width: 100%;
@ -424,23 +521,3 @@ caption {
font-size: 0.8rem;
color: var(--main-small-text-color);
}
a > code {
text-decoration: none;
color: var(--main-link-color);
position: relative;
}
a:has(> code) {
text-decoration: none;
background: none;
/* position: static;*/
}
a:hover > code {
text-decoration: underline;
}
a:hover:has(> code) {
background: none;
}

View file

@ -1,13 +1,11 @@
{% extends "base.html" %} {% block content %}
{% if metadata.show_title %}
{% include "title.html" %}
{% endif %}
{% extends "base.html" %} {% block content %} {% if metadata.show_title %} {%
include "title.html" %} {% endif %}
<article>{{ content | safe }}</article>
{% if post_list %}
<ul>
<div class="post-list">
<ul>
{% for item in post_list %}
<li>
<time class="post-list-date" datetime="{{ item.metadata.date | safe }}"
@ -15,6 +13,6 @@
>{{ item.metadata.title }}</a>
</li>
{% endfor %}
</ul>
</ul>
</div>
{% endif %} {% endblock %}

View file

@ -1,9 +1,11 @@
<div class="post-nav">
<center>
<span class="symbol">&lt;</span>{% if previous %}<a
href="{{ previous }}"
>prev</a>{% endif %}{% if previous and next %}<span class="symbol"
>|</span>{% endif %}{% if next %}<a href="{{ next }}">next</a>{% endif
%}<span class="symbol">&gt;</span>
<span class="symbol">&lt;</span>{% if newer %}<a href="{{ newer }}">newr</a>{%
else %}<span class="placeholder">null</span>{% endif %}<span
class="symbol"
><span class="bar"></span></span>{% if older %}<a href="{{ older }}"
>oldr</a>{% else %}<span class="placeholder">null</span>{% endif %}<span
class="symbol"
>&gt;</span>
</center>
</div>

View file

@ -1,2 +1,2 @@
<center><h1 class="title">{{ metadata.title }}</h1></center>
<center><h1 class="title hover-symbol"><a id="" href="#">{{ metadata.title }}</a></h1></center>

View file

@ -30,6 +30,8 @@ class Metadata:
math: bool = True
# TODO: migrate to using datetime, where user can optionall specify
# a time as well. if only date is given, default to time.min
def parse_date(raw_date: str | date | object) -> date:
if isinstance(raw_date, date):
return raw_date

View file

@ -23,8 +23,8 @@ class Item:
type: ItemType | None = None
copy: bool = True
post: bool = False
next: Item | None = None
previous: Item | None = None
newer: Item | None = None
older: Item | None = None
# @dataclass

View file

@ -27,7 +27,6 @@ def get_footer(template_dir: Path) -> str | None:
return html_footer.read_text()
# TODO: add next/prev post button logic to posts
# TODO: add a recent posts element that can be included elsewhere?
class Templater:
def __init__(
@ -79,11 +78,11 @@ class Templater:
header=header,
footer=footer,
is_post=item.post,
next=util.normalize_url(item.next.url)
if item.next
newer=util.normalize_url(item.newer.url)
if item.newer
else None,
previous=util.normalize_url(item.previous.url)
if item.previous
older=util.normalize_url(item.older.url)
if item.older
else None,
post_list=self.post_list,
)

54
uv.lock generated
View file

@ -44,6 +44,16 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/94/35/386550fd60316d1e37eccdda609b074113298f23cef5bddb2049823fe666/dacite-1.9.2-py3-none-any.whl", hash = "sha256:053f7c3f5128ca2e9aceb66892b1a3c8936d02c686e707bee96e19deef4bc4a0", size = 16600, upload-time = "2025-02-05T09:27:24.345Z" },
]
[[package]]
name = "feedgen"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "lxml" },
{ name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6b/59/be0a6f852b5dfbf19e6c8e962c8f41407697f9f52a7902250ed98683ae89/feedgen-1.0.0.tar.gz", hash = "sha256:d9bd51c3b5e956a2a52998c3708c4d2c729f2fcc311188e1e5d3b9726393546a", size = 258496, upload-time = "2023-12-25T18:04:08.421Z" }
[[package]]
name = "iniconfig"
version = "2.1.0"
@ -87,6 +97,46 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/fd/aba08bb9e527168efad57985d7db9a853eb2384b1efa5ca5f3a3794c9cef/latex2mathml-3.78.0-py3-none-any.whl", hash = "sha256:1aeca3dc027b3006ad7b301b7f4a15ffbb4c1451e3dc8c3389e97b37b497e1d6", size = 73673, upload-time = "2025-05-03T16:51:51.991Z" },
]
[[package]]
name = "lxml"
version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c5/ed/60eb6fa2923602fba988d9ca7c5cdbd7cf25faa795162ed538b527a35411/lxml-6.0.0.tar.gz", hash = "sha256:032e65120339d44cdc3efc326c9f660f5f7205f3a535c1fdbf898b29ea01fb72", size = 4096938, upload-time = "2025-06-26T16:28:19.373Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/89/c3/d01d735c298d7e0ddcedf6f028bf556577e5ab4f4da45175ecd909c79378/lxml-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78718d8454a6e928470d511bf8ac93f469283a45c354995f7d19e77292f26108", size = 8429515, upload-time = "2025-06-26T16:26:06.776Z" },
{ url = "https://files.pythonhosted.org/packages/06/37/0e3eae3043d366b73da55a86274a590bae76dc45aa004b7042e6f97803b1/lxml-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:84ef591495ffd3f9dcabffd6391db7bb70d7230b5c35ef5148354a134f56f2be", size = 4601387, upload-time = "2025-06-26T16:26:09.511Z" },
{ url = "https://files.pythonhosted.org/packages/a3/28/e1a9a881e6d6e29dda13d633885d13acb0058f65e95da67841c8dd02b4a8/lxml-6.0.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2930aa001a3776c3e2601cb8e0a15d21b8270528d89cc308be4843ade546b9ab", size = 5228928, upload-time = "2025-06-26T16:26:12.337Z" },
{ url = "https://files.pythonhosted.org/packages/9a/55/2cb24ea48aa30c99f805921c1c7860c1f45c0e811e44ee4e6a155668de06/lxml-6.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:219e0431ea8006e15005767f0351e3f7f9143e793e58519dc97fe9e07fae5563", size = 4952289, upload-time = "2025-06-28T18:47:25.602Z" },
{ url = "https://files.pythonhosted.org/packages/31/c0/b25d9528df296b9a3306ba21ff982fc5b698c45ab78b94d18c2d6ae71fd9/lxml-6.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bd5913b4972681ffc9718bc2d4c53cde39ef81415e1671ff93e9aa30b46595e7", size = 5111310, upload-time = "2025-06-28T18:47:28.136Z" },
{ url = "https://files.pythonhosted.org/packages/e9/af/681a8b3e4f668bea6e6514cbcb297beb6de2b641e70f09d3d78655f4f44c/lxml-6.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:390240baeb9f415a82eefc2e13285016f9c8b5ad71ec80574ae8fa9605093cd7", size = 5025457, upload-time = "2025-06-26T16:26:15.068Z" },
{ url = "https://files.pythonhosted.org/packages/99/b6/3a7971aa05b7be7dfebc7ab57262ec527775c2c3c5b2f43675cac0458cad/lxml-6.0.0-cp312-cp312-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d6e200909a119626744dd81bae409fc44134389e03fbf1d68ed2a55a2fb10991", size = 5657016, upload-time = "2025-07-03T19:19:06.008Z" },
{ url = "https://files.pythonhosted.org/packages/69/f8/693b1a10a891197143c0673fcce5b75fc69132afa81a36e4568c12c8faba/lxml-6.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca50bd612438258a91b5b3788c6621c1f05c8c478e7951899f492be42defc0da", size = 5257565, upload-time = "2025-06-26T16:26:17.906Z" },
{ url = "https://files.pythonhosted.org/packages/a8/96/e08ff98f2c6426c98c8964513c5dab8d6eb81dadcd0af6f0c538ada78d33/lxml-6.0.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:c24b8efd9c0f62bad0439283c2c795ef916c5a6b75f03c17799775c7ae3c0c9e", size = 4713390, upload-time = "2025-06-26T16:26:20.292Z" },
{ url = "https://files.pythonhosted.org/packages/a8/83/6184aba6cc94d7413959f6f8f54807dc318fdcd4985c347fe3ea6937f772/lxml-6.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afd27d8629ae94c5d863e32ab0e1d5590371d296b87dae0a751fb22bf3685741", size = 5066103, upload-time = "2025-06-26T16:26:22.765Z" },
{ url = "https://files.pythonhosted.org/packages/ee/01/8bf1f4035852d0ff2e36a4d9aacdbcc57e93a6cd35a54e05fa984cdf73ab/lxml-6.0.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:54c4855eabd9fc29707d30141be99e5cd1102e7d2258d2892314cf4c110726c3", size = 4791428, upload-time = "2025-06-26T16:26:26.461Z" },
{ url = "https://files.pythonhosted.org/packages/29/31/c0267d03b16954a85ed6b065116b621d37f559553d9339c7dcc4943a76f1/lxml-6.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c907516d49f77f6cd8ead1322198bdfd902003c3c330c77a1c5f3cc32a0e4d16", size = 5678523, upload-time = "2025-07-03T19:19:09.837Z" },
{ url = "https://files.pythonhosted.org/packages/5c/f7/5495829a864bc5f8b0798d2b52a807c89966523140f3d6fa3a58ab6720ea/lxml-6.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36531f81c8214e293097cd2b7873f178997dae33d3667caaae8bdfb9666b76c0", size = 5281290, upload-time = "2025-06-26T16:26:29.406Z" },
{ url = "https://files.pythonhosted.org/packages/79/56/6b8edb79d9ed294ccc4e881f4db1023af56ba451909b9ce79f2a2cd7c532/lxml-6.0.0-cp312-cp312-win32.whl", hash = "sha256:690b20e3388a7ec98e899fd54c924e50ba6693874aa65ef9cb53de7f7de9d64a", size = 3613495, upload-time = "2025-06-26T16:26:31.588Z" },
{ url = "https://files.pythonhosted.org/packages/0b/1e/cc32034b40ad6af80b6fd9b66301fc0f180f300002e5c3eb5a6110a93317/lxml-6.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:310b719b695b3dd442cdfbbe64936b2f2e231bb91d998e99e6f0daf991a3eba3", size = 4014711, upload-time = "2025-06-26T16:26:33.723Z" },
{ url = "https://files.pythonhosted.org/packages/55/10/dc8e5290ae4c94bdc1a4c55865be7e1f31dfd857a88b21cbba68b5fea61b/lxml-6.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:8cb26f51c82d77483cdcd2b4a53cda55bbee29b3c2f3ddeb47182a2a9064e4eb", size = 3674431, upload-time = "2025-06-26T16:26:35.959Z" },
{ url = "https://files.pythonhosted.org/packages/79/21/6e7c060822a3c954ff085e5e1b94b4a25757c06529eac91e550f3f5cd8b8/lxml-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6da7cd4f405fd7db56e51e96bff0865b9853ae70df0e6720624049da76bde2da", size = 8414372, upload-time = "2025-06-26T16:26:39.079Z" },
{ url = "https://files.pythonhosted.org/packages/a4/f6/051b1607a459db670fc3a244fa4f06f101a8adf86cda263d1a56b3a4f9d5/lxml-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b34339898bb556a2351a1830f88f751679f343eabf9cf05841c95b165152c9e7", size = 4593940, upload-time = "2025-06-26T16:26:41.891Z" },
{ url = "https://files.pythonhosted.org/packages/8e/74/dd595d92a40bda3c687d70d4487b2c7eff93fd63b568acd64fedd2ba00fe/lxml-6.0.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:51a5e4c61a4541bd1cd3ba74766d0c9b6c12d6a1a4964ef60026832aac8e79b3", size = 5214329, upload-time = "2025-06-26T16:26:44.669Z" },
{ url = "https://files.pythonhosted.org/packages/52/46/3572761efc1bd45fcafb44a63b3b0feeb5b3f0066886821e94b0254f9253/lxml-6.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d18a25b19ca7307045581b18b3ec9ead2b1db5ccd8719c291f0cd0a5cec6cb81", size = 4947559, upload-time = "2025-06-28T18:47:31.091Z" },
{ url = "https://files.pythonhosted.org/packages/94/8a/5e40de920e67c4f2eef9151097deb9b52d86c95762d8ee238134aff2125d/lxml-6.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4f0c66df4386b75d2ab1e20a489f30dc7fd9a06a896d64980541506086be1f1", size = 5102143, upload-time = "2025-06-28T18:47:33.612Z" },
{ url = "https://files.pythonhosted.org/packages/7c/4b/20555bdd75d57945bdabfbc45fdb1a36a1a0ff9eae4653e951b2b79c9209/lxml-6.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f4b481b6cc3a897adb4279216695150bbe7a44c03daba3c894f49d2037e0a24", size = 5021931, upload-time = "2025-06-26T16:26:47.503Z" },
{ url = "https://files.pythonhosted.org/packages/b6/6e/cf03b412f3763d4ca23b25e70c96a74cfece64cec3addf1c4ec639586b13/lxml-6.0.0-cp313-cp313-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a78d6c9168f5bcb20971bf3329c2b83078611fbe1f807baadc64afc70523b3a", size = 5645469, upload-time = "2025-07-03T19:19:13.32Z" },
{ url = "https://files.pythonhosted.org/packages/d4/dd/39c8507c16db6031f8c1ddf70ed95dbb0a6d466a40002a3522c128aba472/lxml-6.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae06fbab4f1bb7db4f7c8ca9897dc8db4447d1a2b9bee78474ad403437bcc29", size = 5247467, upload-time = "2025-06-26T16:26:49.998Z" },
{ url = "https://files.pythonhosted.org/packages/4d/56/732d49def0631ad633844cfb2664563c830173a98d5efd9b172e89a4800d/lxml-6.0.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:1fa377b827ca2023244a06554c6e7dc6828a10aaf74ca41965c5d8a4925aebb4", size = 4720601, upload-time = "2025-06-26T16:26:52.564Z" },
{ url = "https://files.pythonhosted.org/packages/8f/7f/6b956fab95fa73462bca25d1ea7fc8274ddf68fb8e60b78d56c03b65278e/lxml-6.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1676b56d48048a62ef77a250428d1f31f610763636e0784ba67a9740823988ca", size = 5060227, upload-time = "2025-06-26T16:26:55.054Z" },
{ url = "https://files.pythonhosted.org/packages/97/06/e851ac2924447e8b15a294855caf3d543424364a143c001014d22c8ca94c/lxml-6.0.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0e32698462aacc5c1cf6bdfebc9c781821b7e74c79f13e5ffc8bfe27c42b1abf", size = 4790637, upload-time = "2025-06-26T16:26:57.384Z" },
{ url = "https://files.pythonhosted.org/packages/06/d4/fd216f3cd6625022c25b336c7570d11f4a43adbaf0a56106d3d496f727a7/lxml-6.0.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4d6036c3a296707357efb375cfc24bb64cd955b9ec731abf11ebb1e40063949f", size = 5662049, upload-time = "2025-07-03T19:19:16.409Z" },
{ url = "https://files.pythonhosted.org/packages/52/03/0e764ce00b95e008d76b99d432f1807f3574fb2945b496a17807a1645dbd/lxml-6.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7488a43033c958637b1a08cddc9188eb06d3ad36582cebc7d4815980b47e27ef", size = 5272430, upload-time = "2025-06-26T16:27:00.031Z" },
{ url = "https://files.pythonhosted.org/packages/5f/01/d48cc141bc47bc1644d20fe97bbd5e8afb30415ec94f146f2f76d0d9d098/lxml-6.0.0-cp313-cp313-win32.whl", hash = "sha256:5fcd7d3b1d8ecb91445bd71b9c88bdbeae528fefee4f379895becfc72298d181", size = 3612896, upload-time = "2025-06-26T16:27:04.251Z" },
{ url = "https://files.pythonhosted.org/packages/f4/87/6456b9541d186ee7d4cb53bf1b9a0d7f3b1068532676940fdd594ac90865/lxml-6.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:2f34687222b78fff795feeb799a7d44eca2477c3d9d3a46ce17d51a4f383e32e", size = 4013132, upload-time = "2025-06-26T16:27:06.415Z" },
{ url = "https://files.pythonhosted.org/packages/b7/42/85b3aa8f06ca0d24962f8100f001828e1f1f1a38c954c16e71154ed7d53a/lxml-6.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:21db1ec5525780fd07251636eb5f7acb84003e9382c72c18c542a87c416ade03", size = 3672642, upload-time = "2025-06-26T16:27:09.888Z" },
]
[[package]]
name = "markdown"
version = "3.8.2"
@ -459,10 +509,11 @@ wheels = [
[[package]]
name = "zona"
version = "1.1.0"
version = "1.2.1"
source = { editable = "." }
dependencies = [
{ name = "dacite" },
{ name = "feedgen" },
{ name = "jinja2" },
{ name = "l2m4m" },
{ name = "markdown" },
@ -489,6 +540,7 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "dacite", specifier = ">=1.9.2" },
{ name = "feedgen", specifier = ">=1.0.0" },
{ name = "jinja2", specifier = ">=3.1.6" },
{ name = "l2m4m", specifier = ">=1.0.4" },
{ name = "markdown", specifier = ">=3.8.2" },