From 319163ee7e2ddcc57b39baba781a516450b0aaf4 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Sat, 22 Aug 2026 14:04:34 +0000 Subject: [PATCH] Page caching and zstd compression. Avoid useless fetching. Mobile layouts of navigation menus improved. --- docs/analytics.md | 5 +- docs/backend.md | 4 +- docs/content-model.md | 2 +- docs/editing.md | 2 +- docs/frontend-runtime.md | 8 +- docs/themes-and-assets.md | 2 +- frontend/src/AnalyticsView.vue | 22 ++-- frontend/src/SiteEditor.vue | 45 +++++--- frontend/src/analytics-main.js | 20 ++-- frontend/src/assets/pagerite.css | 4 +- frontend/src/pagerite.js | 177 ++++++++++++++++++++++++------- frontend/src/swapdoc.js | 45 ++++---- pagerite/app.py | 149 ++++++++++++++++++++------ pagerite/data.py | 6 ++ pagerite/views.py | 143 +++++++++++++++++++------ pyproject.toml | 1 + 16 files changed, 479 insertions(+), 156 deletions(-) diff --git a/docs/analytics.md b/docs/analytics.md index 0dd1eff..17c3be5 100644 --- a/docs/analytics.md +++ b/docs/analytics.md @@ -197,8 +197,9 @@ Because it is a real page, fetch-navigation handles it like any other internal link: clicking the 📊 pen (or any link to `/_a`) fetches the server-rendered HTML, swaps the dynamic regions and mounts the Vue analytics app in place. The range selector updates the URL hash (`#week` etc.) so links to a specific -range can be shared. When the URL has no hash, the server defaults to `day` -if analytics history spans less than 24 hours, otherwise `week`. +range can be shared. When the URL has no hash, the client derives the +default from the first analytics snapshot: `day` if the recorded history +spans less than 24 hours, otherwise `week`. `AnalyticsView.vue` is no longer a full-screen overlay; the `body.analytics-open` page-chrome hiding and `#/analytics/` hash routing have been removed. diff --git a/docs/backend.md b/docs/backend.md index 4cbb54d..3fea8db 100644 --- a/docs/backend.md +++ b/docs/backend.md @@ -8,6 +8,8 @@ The FastAPI app. FastAPI's built-in API docs are disabled (`docs_url`/`redoc_url The build mirrors the URL space — hashed immutable assets under `/_assets/`, `favicon.ico` at the site root — and an `index.html` in the build would become a `/` route, so leave it out of the build to keep `/` ours. +Generated HTML pages (content pages, category/404 placeholders, `/_a`) go through `_html_response`: zstd-compressed per request at level 9 when the client sends `accept-encoding: zstd` (no gzip fallback; static assets are pre-compressed by the `Frontend`), with `vary: accept-encoding` set and the ETag kept identical across encodings so `if-none-match` revalidation still works. In production the rendered bodies are cached in an LRU keyed by everything the output depends on — page kind, path, the site origin (social meta), encoding, and `data.version`, which bumps on every content/settings change and so transparently invalidates the whole cache. The cache is bypassed in dev, where theme/design CSS is re-read from disk per request. Content pages carry an ETag built from the node's modified timestamp and `data.version`; `/_a` instead gets a blake3 hash of the rendered body (it has no Node), with matching `if-none-match` revalidations answered by a 304. + ## `data.py` msgspec Structs for the kanta database. See `docs/content-model.md` for the full data model. @@ -20,7 +22,7 @@ markdown-it-py renderer (html passthrough + attrs, footnote, deflist, tasklists, The shared page layout as an html5tagger `Template` with placeholders (`Title`, `Brand`, `Banner`, `Nav`, `Sidebar`, `Main`), nav rendering straight from the `Data.menu` tree (siblings sorted by `Node.order`; nav links to content-less labels point at their first child via `first_leaf`, the first published descendant with content), and page/404 rendering. -Content pages get SEO/social meta (description, canonical link, Open Graph + twitter card) from heuristics over the rendered article: the description is the first paragraph's text, the share image prefers a `{.hero}`-classed image, then the first raster ``, then the first SVG; the first `