From 1c0ef315ff1ec905ca3f4422193e892272832900 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Tue, 18 Aug 2026 06:20:38 +0000 Subject: [PATCH] Refine dateline format; fix stale page caching breaking theme hot swap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dateline is now '1 Jan 2026', with ' – edited 3 Jan 2026' appended only when the last edit came at least 24h after publishing. The Last-Modified header added heuristic browser caching of pages (no Cache-Control was sent), so the site editor's re-fetch after a theme change served the cached page with the old stylesheet link. Pages now send 'Cache-Control: no-cache' — always revalidate, still cheap via the ETag. --- pagerite/app.py | 16 +++++++++++++--- pagerite/markdown.py | 15 +++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/pagerite/app.py b/pagerite/app.py index e67e821..fc53edb 100644 --- a/pagerite/app.py +++ b/pagerite/app.py @@ -668,13 +668,20 @@ async def show_page(request: Request, path: str) -> HTMLResponse | Response: node = chain[-1] if chain else None if node is not None and node.published and node.content is not None: # ETag on content + render version; clients revalidate cheaply, - # which keeps prefetched pages warm and current. + # which keeps prefetched pages warm and current. no-cache forces + # that revalidation: with Last-Modified but no Cache-Control, + # browsers would otherwise cache heuristically and serve stale + # pages (e.g. after a theme change) without asking us at all. etag = f'"{path}@{node.modified.timestamp()}v{data.version}"' if request.headers.get("if-none-match") == etag: return Response(status_code=304) return HTMLResponse( views.render_page(data.menu, path, data.brand, data.custom_css, data.theme, data.favicon), - headers={"etag": etag, "last-modified": _http_date(node.modified)}, + headers={ + "etag": etag, + "last-modified": _http_date(node.modified), + "cache-control": "no-cache", + }, ) if node is not None and node.published and node.content is None: # Category label without a landing page: placeholder with the pen @@ -682,7 +689,10 @@ async def show_page(request: Request, path: str) -> HTMLResponse | Response: return HTMLResponse( views.render_category(data.menu, path, data.brand, data.custom_css, data.theme, data.favicon), 404, - headers={"last-modified": _http_date(node.modified)}, + headers={ + "last-modified": _http_date(node.modified), + "cache-control": "no-cache", + }, ) if node is None and not path: # No front page (no top-level node with slug ""): "/" opens the diff --git a/pagerite/markdown.py b/pagerite/markdown.py index e96828f..f829c00 100644 --- a/pagerite/markdown.py +++ b/pagerite/markdown.py @@ -20,7 +20,7 @@ classes, e.g. `![alt](photo.avif "Caption"){.right}`. """ import re -from datetime import datetime +from datetime import datetime, timedelta from markdown_it import MarkdownIt from markdown_it.common.utils import escapeHtml @@ -156,13 +156,12 @@ def render( def _dateline(created: datetime, modified: datetime | None) -> str: - """Published/updated line for the ``{dates}`` tag. The updated date is - shown only when it falls on a later day than the publication.""" - pub = f'' - out = f"Published {pub}" - if modified is not None and modified.date() > created.date(): - upd = f'' - out += f", updated {upd}" + """Dateline for the ``{dates}`` tag: "1 Jan 2026", plus + " – edited 3 Jan 2026" when the last edit came >= 24h after + publishing (quick fixes right after posting stay unmentioned).""" + out = f'' + if modified is not None and modified - created >= timedelta(hours=24): + out += f' – edited ' return f'

{out}

'