diff --git a/frontend/src/assets/pagerite.css b/frontend/src/assets/pagerite.css index c9882b3..6f3ae92 100644 --- a/frontend/src/assets/pagerite.css +++ b/frontend/src/assets/pagerite.css @@ -847,7 +847,7 @@ article.multicol { article.multicol .margin, article.multicol .aside, - article.multicol figure:has(.margin) { + article.multicol figure.margin { position: absolute; left: 0; width: 14rem; @@ -898,7 +898,7 @@ article.multicol { body:has(#sidebar):has(.multicol):not(.editing) article.multicol .margin, body:has(#sidebar):has(.multicol):not(.editing) article.multicol .aside, - body:has(#sidebar):has(.multicol):not(.editing) article.multicol figure:has(.margin) { + body:has(#sidebar):has(.multicol):not(.editing) article.multicol figure.margin { position: absolute; /* Attached to the article's left border (1.25rem gap), hanging into the left lane and growing leftward with it: 12rem when the lane is @@ -916,7 +916,7 @@ article.multicol { /* A shrink-wrapped figure (explicit image width) centers in the plain layout; inside a column the centering looks adrift — left-align. Floated figures keep their own margins (the text gap). */ -.multicol .colseg.cols figure:has(img[width]):not(:has(.left), :has(.right), :has(.margin)) { +.multicol .colseg.cols figure:has(img[width]):not(:has(.left), :has(.right), .margin) { margin-inline: 0; } @@ -1094,7 +1094,8 @@ blockquote p + p { } /* Side boxes: ::: aside is a muted floated box; {.margin} / ::: margin - is a plainer margin note, and figures take {.margin} like {.left}. + is a plainer margin note, and figures take the class directly on the +
(the renderer moves it off the img), floating like {.left}. Where the layout has room for a side zone — multicol pages, the sidebar's track, the wide single-column gutter (see the article section and the figure rules below) — the boxes are taken out of flow @@ -1287,7 +1288,8 @@ dd { Markdown images standing alone in a paragraph render as a block
(with
when the image has a title); the brace-attribute positioning class ({.left}, {.right}, {.wide}) lives - on the img inside, but only the figure is ever positioned, so the + on the img inside — except {.margin}, which the renderer moves onto + the figure itself — but only the figure is ever positioned, so the caption stays below the image. Raw HTML written by the author stays inline and unstyled beyond these defaults. */ img { @@ -1353,9 +1355,10 @@ figure:has(img[width]) { width: fit-content; } -/* {.margin} figures float left like {.left} ones — until they fall into +/* {.margin} figures (the class moves onto the figure wrapper at render — + see markdown.py) float left like {.left} ones — until they fall into the side zone (see the composition rules up in the article section). */ -figure:has(.margin) { +figure.margin { float: left; width: 30%; max-width: 50%; @@ -1445,7 +1448,7 @@ article figure img { @media (min-width: 104rem) { body:not(.editing):not(:has(.multicol)) article .margin, body:not(.editing):not(:has(.multicol)) article .aside, - body:not(.editing):not(:has(.multicol)) article figure:has(.margin) { + body:not(.editing):not(:has(.multicol)) article figure.margin { position: absolute; --box-w: min(18rem, (100vw - 78rem) / 2 - 1.25rem); width: var(--box-w); @@ -1668,7 +1671,7 @@ article h2 { figure:has(.right), figure:has(.left), - figure:has(.margin) { + figure.margin { float: none; width: 100%; max-width: none; diff --git a/pagerite/app.py b/pagerite/app.py index 94c79d6..29c76f4 100644 --- a/pagerite/app.py +++ b/pagerite/app.py @@ -30,6 +30,7 @@ walking the tree (``resolve``), moves are slot detach/attach import asyncio import logging +from collections.abc import AsyncIterator from contextlib import asynccontextmanager from fastapi import FastAPI, Request @@ -39,12 +40,12 @@ from pagerite import api, files, pages, tracking, translate from pagerite.__main__ import DEVMODE from pagerite.files import file_store from pagerite.state import HOSTNAME, analytics_store, data, frontend, kanta - +from collections.abc import AsyncGenerator logger = logging.getLogger(__name__) @asynccontextmanager -async def lifespan(_app: FastAPI): +async def lifespan(_app: FastAPI) -> AsyncGenerator: """Open the database (migrations run inside kanta.open), load assets, load GeoIP.""" async with kanta: translate.log_service_urls(data.translate_keys, HOSTNAME) diff --git a/pagerite/markdown.py b/pagerite/markdown.py index fb12103..b331630 100644 --- a/pagerite/markdown.py +++ b/pagerite/markdown.py @@ -156,14 +156,27 @@ def _image_rule( page = env.get("page_path", "") token.attrs["src"] = f"/{page}/{src}" if page else f"/{src}" token.attrs["alt"] = self.renderInlineAsText(token.children, options, env) - img = self.renderToken(tokens, idx, options, env) if len(tokens) == 1: # The only inline content of its paragraph: render as a block # figure, captioned when titled. (The

wrapper is dropped by - # _unwrap_lone_figures below.) + # _unwrap_lone_figures below.) {.margin} positions the whole + # figure, so it moves from the img onto the figure wrapper — left + # on the img, the margin-breakout CSS would pull the image out of + # the figure (and mostly off-screen), leaving the caption behind. + classes = (token.attrs.get("class") or "").split() + figure_class = "" + if "margin" in classes: + classes.remove("margin") + if classes: + token.attrs["class"] = " ".join(classes) + else: + del token.attrs["class"] + figure_class = ' class="margin"' + img = self.renderToken(tokens, idx, options, env) title = token.attrs.get("title") caption = f"

{escapeHtml(title)}
" if title else "" - return f"
{img}{caption}
" + return f"{img}{caption}
" + img = self.renderToken(tokens, idx, options, env) # Inline with other content: a plain inline image. return img