diff --git a/.forgejo/workflows/publish.yml b/.forgejo/workflows/publish.yml index 53dd92f..fde3206 100644 --- a/.forgejo/workflows/publish.yml +++ b/.forgejo/workflows/publish.yml @@ -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 diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml new file mode 100644 index 0000000..2a05ee6 --- /dev/null +++ b/.forgejo/workflows/test.yml @@ -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 diff --git a/.kakrc b/.kakrc index 7a60b1f..688bcbe 100644 --- a/.kakrc +++ b/.kakrc @@ -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} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e65c79..104815a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/README.md b/README.md index bd7eb9b..c3facfb 100644 --- a/README.md +++ b/README.md @@ -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 @@ -196,13 +193,17 @@ the packaged defaults. 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 | -| ---------- | ------------------------------------------------------ | -| `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`. | +| 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 diff --git a/pyproject.toml b/pyproject.toml index a66fa54..f0c6b14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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"] diff --git a/src/zona/builder.py b/src/zona/builder.py index 27ff2ca..bd2812b 100644 --- a/src/zona/builder.py +++ b/src/zona/builder.py @@ -123,6 +123,8 @@ class ZonaBuilder: 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 @@ -130,17 +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 - return post_list - - def _build(self): - post_list = self._get_post_list() + # 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, diff --git a/src/zona/cli.py b/src/zona/cli.py index 4b48d3a..da15a22 100644 --- a/src/zona/cli.py +++ b/src/zona/cli.py @@ -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( diff --git a/src/zona/data/content/static/style.css b/src/zona/data/content/static/style.css index f02717e..caa0eef 100644 --- a/src/zona/data/content/static/style.css +++ b/src/zona/data/content/static/style.css @@ -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; -} diff --git a/src/zona/data/templates/post_list.html b/src/zona/data/templates/post_list.html index 85e0be8..6b0bdca 100644 --- a/src/zona/data/templates/post_list.html +++ b/src/zona/data/templates/post_list.html @@ -1,20 +1,18 @@ -{% 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 %}
{{ content | safe }}
{% if post_list %} - +
+ +
{% endif %} {% endblock %} - diff --git a/src/zona/data/templates/post_nav.html b/src/zona/data/templates/post_nav.html index 86ac330..1e96ff5 100644 --- a/src/zona/data/templates/post_nav.html +++ b/src/zona/data/templates/post_nav.html @@ -1,9 +1,11 @@
- <{% if previous %}prev{% endif %}{% if previous and next %}|{% endif %}{% if next %}next{% endif - %}> + <{% if newer %}newr{% + else %}null{% endif %}{% if older %}oldr{% else %}null{% endif %}>
diff --git a/src/zona/data/templates/title.html b/src/zona/data/templates/title.html index 9753365..1541095 100644 --- a/src/zona/data/templates/title.html +++ b/src/zona/data/templates/title.html @@ -1,2 +1,2 @@ -

{{ metadata.title }}

+

{{ metadata.title }}

diff --git a/src/zona/models.py b/src/zona/models.py index d009476..7f67015 100644 --- a/src/zona/models.py +++ b/src/zona/models.py @@ -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 diff --git a/src/zona/templates.py b/src/zona/templates.py index d5e0f22..4529143 100644 --- a/src/zona/templates.py +++ b/src/zona/templates.py @@ -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, ) diff --git a/uv.lock b/uv.lock index c00e364..c81743b 100644 --- a/uv.lock +++ b/uv.lock @@ -509,7 +509,7 @@ wheels = [ [[package]] name = "zona" -version = "1.1.0" +version = "1.2.1" source = { editable = "." } dependencies = [ { name = "dacite" },