Compare commits

..

1 Commits

Author SHA1 Message Date
ca8718da3c docs: add documentation versioning 2026-03-11 15:04:20 -04:00
5 changed files with 257 additions and 5 deletions

View File

@@ -5,7 +5,7 @@ on:
types: [published] types: [published]
permissions: permissions:
contents: read contents: write
pages: write pages: write
id-token: write id-token: write
@@ -16,9 +16,14 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }} url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/configure-pages@v5
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v7 uses: astral-sh/setup-uv@v7
@@ -26,9 +31,22 @@ jobs:
- name: Set up Python - name: Set up Python
run: uv python install 3.13 run: uv python install 3.13
- run: uv sync --group dev - run: uv sync --group docs
- run: uv run zensical build --clean - name: Install mkdocs shim
run: cp scripts/mkdocs .venv/bin/mkdocs && chmod +x .venv/bin/mkdocs
- name: Deploy docs version
run: |
VERSION="${GITHUB_REF_NAME#v}"
MINOR_VERSION="${VERSION%.*}"
uv run mike deploy --push --update-aliases "$MINOR_VERSION" latest
uv run mike set-default --push latest
- name: Prepare site artifact
run: git worktree add site gh-pages
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v4 - uses: actions/upload-pages-artifact@v4
with: with:

View File

@@ -0,0 +1,69 @@
{#-
Override of partials/header.html — adds version selector slot missing in Zensical.
-#}
{% set class = "md-header" %}
{% if "navigation.tabs.sticky" in features %}
{% set class = class ~ " md-header--shadow md-header--lifted" %}
{% elif "navigation.tabs" not in features %}
{% set class = class ~ " md-header--shadow" %}
{% endif %}
<header class="{{ class }}" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="{{ lang.t('header') }}">
<a href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}" title="{{ config.site_name | e }}" class="md-header__button md-logo" aria-label="{{ config.site_name }}" data-md-component="logo">
{% include "partials/logo.html" %}
</a>
<label class="md-header__button md-icon" for="__drawer">
{% set icon = config.theme.icon.menu or "material/menu" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
{{ config.site_name }}
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
{% if page.meta and page.meta.title %}
{{ page.meta.title }}
{% else %}
{{ page.title }}
{% endif %}
</span>
</div>
</div>
</div>
{% if config.theme.palette %}
{% if not config.theme.palette is mapping %}
{% include "partials/palette.html" %}
{% endif %}
{% endif %}
{% if not config.theme.palette is mapping %}
{% include "partials/javascripts/palette.html" %}
{% endif %}
{% if config.extra.alternate %}
{% include "partials/alternate.html" %}
{% endif %}
{% if "search" in config.plugins %}
{% set search = config.plugins["search"] | attr("config") %}
{% if search.enabled %}
<label class="md-header__button md-icon" for="__search">
{% set icon = config.theme.icon.search or "material/magnify" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</label>
{% include "partials/search.html" %}
{% endif %}
{% endif %}
<div class="md-header__source">
{% if config.repo_url %}
{% include "partials/source.html" %}
{% endif %}
</div>
</nav>
{% if "navigation.tabs.sticky" in features %}
{% if "navigation.tabs" in features %}
{% include "partials/tabs.html" %}
{% endif %}
{% endif %}
</header>

View File

@@ -0,0 +1,100 @@
/* Version selector styles for Zensical modern theme (backported from classic theme).
The JS appends .md-version into .md-header__topic, so we make that flex. */
.md-header__topic:has(.md-version) {
display: flex;
align-items: center;
}
:root {
--md-version-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M140.3 376.8c12.6 10.2 31.1 9.5 42.8-2.2l128-128c9.2-9.2 11.9-22.9 6.9-34.9S301.4 192 288.5 192h-256c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 7 34.8l128 128z"/></svg>');
}
.md-version {
flex-shrink: 0;
font-size: .8rem;
height: 2.4rem;
}
[dir=ltr] .md-version__current { margin-left: 1.4rem; margin-right: .4rem; }
[dir=rtl] .md-version__current { margin-left: .4rem; margin-right: 1.4rem; }
.md-version__current {
color: inherit;
cursor: pointer;
outline: none;
position: relative;
top: .05rem;
}
[dir=ltr] .md-version__current:after { margin-left: .4rem; }
[dir=rtl] .md-version__current:after { margin-right: .4rem; }
.md-version__current:after {
background-color: currentcolor;
content: "";
display: inline-block;
height: .6rem;
-webkit-mask-image: var(--md-version-icon);
mask-image: var(--md-version-icon);
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: contain;
mask-size: contain;
width: .4rem;
}
.md-version__alias {
margin-left: .3rem;
opacity: .7;
}
.md-version__list {
background-color: var(--md-default-bg-color);
border-radius: .1rem;
box-shadow: var(--md-shadow-z2);
color: var(--md-default-fg-color);
list-style-type: none;
margin: .2rem .8rem;
max-height: 0;
opacity: 0;
overflow: auto;
padding: 0;
position: absolute;
scroll-snap-type: y mandatory;
top: .15rem;
transition: max-height 0ms .5s, opacity .25s .25s;
z-index: 3;
}
.md-version:focus-within .md-version__list,
.md-version:hover .md-version__list {
max-height: 10rem;
opacity: 1;
transition: max-height 0ms, opacity .25s;
}
.md-version:hover .md-version__list { animation: hoverfix .25s forwards; }
.md-version:focus-within .md-version__list { animation: none; }
.md-version__item { line-height: 1.8rem; }
[dir=ltr] .md-version__link { padding-left: .6rem; padding-right: 1.2rem; }
[dir=rtl] .md-version__link { padding-left: 1.2rem; padding-right: .6rem; }
.md-version__link {
cursor: pointer;
display: block;
outline: none;
scroll-snap-align: start;
transition: color .25s, background-color .25s;
white-space: nowrap;
width: 100%;
}
.md-version__link:focus,
.md-version__link:hover { color: var(--md-accent-fg-color); }
.md-version__link:focus { background-color: var(--md-default-fg-color--lightest); }

5
mkdocs.yml Normal file
View File

@@ -0,0 +1,5 @@
# Minimal stub for mike compatibility.
# The actual build is handled by zensical (see scripts/mkdocs shim).
site_name: FastAPI Toolsets
docs_dir: docs
site_dir: site

60
scripts/mkdocs Executable file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""mkdocs shim for mike compatibility.
mike parses mkdocs.yml (valid YAML stub) for its Python internals, then calls
`mkdocs build --config-file <mike-injected-temp.yml>` as a subprocess.
This shim intercepts that subprocess call, ignores the temp config, and
delegates the actual build to `zensical build -f zensical.toml` instead.
"""
from __future__ import annotations
import os
import subprocess
import sys
def main() -> None:
args = sys.argv[1:]
# mike calls `mkdocs --version` to embed in the commit message
if args and args[0] == "--version":
print("mkdocs, version 1.0.0 (zensical shim)")
return
if not args or args[0] != "build":
result = subprocess.run(["python3", "-m", "mkdocs"] + args)
sys.exit(result.returncode)
config_file = "mkdocs.yml"
clean = False
i = 1
while i < len(args):
if args[i] in ("-f", "--config-file") and i + 1 < len(args):
config_file = args[i + 1]
i += 2
elif args[i] in ("-c", "--clean"):
clean = True
i += 1
elif args[i] == "--dirty":
i += 1
else:
i += 1
# mike creates a temp file prefixed with "mike-mkdocs"; always delegate
# the actual build to zensical regardless of which config was passed.
del config_file # unused — zensical auto-discovers zensical.toml
cmd = ["zensical", "build"]
if clean:
cmd.append("--clean")
env = os.environ.copy()
result = subprocess.run(cmd, env=env)
sys.exit(result.returncode)
if __name__ == "__main__":
main()