Relax automatic large-card pick: w > 600, h > 400, aspect > 1.05
The old 1.4-2.5 aspect window sent big near-square images (e.g. 1350x1200) to the compact layout despite ample resolution. The new rule keeps square and portrait images compact (the compact box fits them) while letting anything big and clearly wider than tall render as a full-card cover.
This commit is contained in:
+1
-1
@@ -34,7 +34,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.
|
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 card image is the node's own `Node.image` when set, otherwise mined from the article (preferring a `{.hero}`-classed image, then the first raster `<img>`, then the first SVG), and only when the article yields none the inherited one (nearest ancestor, front page last — see docs/content-model.md); the first `<video>` yields `og:video`; URLs are made absolute with the site origin (`SITE_URL` — `https://<hostname>` from the CLI hostname argument; on localhost the request's own base URL is the fallback); `article:published/modified_time` come from `Node.created`/`modified`. Additionally `twitter:image` pins extension-less `/_f/{hash}` card images to the `.webp` variant — X only honors WebP via twitter:image (not og:image) and its scraper cannot be trusted to negotiate via Accept. `twitter:card` is `summary_large_image` when the image's probed store dimensions suit a large card (>= 600px wide, aspect between 1.4 and 2.5; dimensions are read from the `<hash>.webp` derivative via pyvips, cached per hash) and `summary` for small or portrait images — external or unprobeable images keep the presence-based default (large when an image exists). The node's `Node.large` setting overrides that pick per article (False = small, True = large, the default None = automatic; NOT inherited like `Node.image`). The page title is injected as `# {title}` when the markdown has no h1 of its own, so it never appears twice (it always supplies `<title>` and nav labels).
|
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 card image is the node's own `Node.image` when set, otherwise mined from the article (preferring a `{.hero}`-classed image, then the first raster `<img>`, then the first SVG), and only when the article yields none the inherited one (nearest ancestor, front page last — see docs/content-model.md); the first `<video>` yields `og:video`; URLs are made absolute with the site origin (`SITE_URL` — `https://<hostname>` from the CLI hostname argument; on localhost the request's own base URL is the fallback); `article:published/modified_time` come from `Node.created`/`modified`. Additionally `twitter:image` pins extension-less `/_f/{hash}` card images to the `.webp` variant — X only honors WebP via twitter:image (not og:image) and its scraper cannot be trusted to negotiate via Accept. `twitter:card` is `summary_large_image` when the image's probed store dimensions suit a large card (wider than 600px, taller than 400px and clearly wider than tall — aspect > 1.05, so square and portrait images keep the compact layout; dimensions are read from the `<hash>.webp` derivative via pyvips, cached per hash) and `summary` otherwise — external or unprobeable images keep the presence-based default (large when an image exists). The node's `Node.large` setting overrides that pick per article (False = small, True = large, the default None = automatic; NOT inherited like `Node.image`). The page title is injected as `# {title}` when the markdown has no h1 of its own, so it never appears twice (it always supplies `<title>` and nav labels).
|
||||||
|
|
||||||
The navbar holds top-level items only; the current section's subitems go to a left `#sidebar` as a nested list (the section's direct children plain, deeper levels indented with article-list-style markers), rendered only from the second level down — main-level pages list their children as cards after the content instead. Below that, the sidebar renders when the section offers at least two published items, or exactly one while viewing anything other than that only page — the section index, a 404, a grandchild (so those pages can reach the child), and also on that only page itself when it has published children of its own; no aside element at all on the front page, main-level pages, leaf pages and the sole childless page of a one-page section. Also, category labels are nodes without content — None *or* empty markdown — and their nav links point at their first child page. Dynamic regions have stable ids (`#page-banner`, `#nav`, `#sidebar`, `#main`) for fetch-navigation swaps (`#sidebar` may be absent on either side of a swap).
|
The navbar holds top-level items only; the current section's subitems go to a left `#sidebar` as a nested list (the section's direct children plain, deeper levels indented with article-list-style markers), rendered only from the second level down — main-level pages list their children as cards after the content instead. Below that, the sidebar renders when the section offers at least two published items, or exactly one while viewing anything other than that only page — the section index, a 404, a grandchild (so those pages can reach the child), and also on that only page itself when it has published children of its own; no aside element at all on the front page, main-level pages, leaf pages and the sole childless page of a one-page section. Also, category labels are nodes without content — None *or* empty markdown — and their nav links point at their first child page. Dynamic regions have stable ids (`#page-banner`, `#nav`, `#sidebar`, `#main`) for fetch-navigation swaps (`#sidebar` may be absent on either side of a swap).
|
||||||
|
|
||||||
|
|||||||
+10
-8
@@ -1045,20 +1045,22 @@ def _walk(node: Node, path: str):
|
|||||||
def _card_large(node: Node, image: str) -> bool:
|
def _card_large(node: Node, image: str) -> bool:
|
||||||
"""Whether the card renders large (True) or small (False).
|
"""Whether the card renders large (True) or small (False).
|
||||||
|
|
||||||
Automatic: large when the image's probed store dimensions suit a large
|
Automatic: large when the image is big and wide enough for the
|
||||||
card (>= 600px wide, landscape-ish aspect 1.4–2.5), small for
|
full-card cover — wider than 600px, taller than 400px and clearly
|
||||||
small/portrait images — and, when dimensions are unknown or the image
|
wider than tall (aspect > 1.05, so square and portrait images keep
|
||||||
is external, for any present image. The node's ``large`` setting
|
the compact layout whose box they fit). When dimensions are unknown
|
||||||
(per-article, not inherited) overrides the automatic pick; None
|
or the image is external, any present image defaults to large. The
|
||||||
means automatic. Shared by twitter:card (_social_meta, which maps it
|
node's ``large`` setting (per-article, not inherited) overrides the
|
||||||
to "summary_large_image"/"summary") and the site's own cards (_card).
|
automatic pick; None means automatic. Shared by twitter:card
|
||||||
|
(_social_meta, which maps it to "summary_large_image"/"summary") and
|
||||||
|
the site's own cards (_card).
|
||||||
"""
|
"""
|
||||||
large = bool(image)
|
large = bool(image)
|
||||||
if (m := re.search(r"/_f/([0-9a-f]{12})$", image)) and (
|
if (m := re.search(r"/_f/([0-9a-f]{12})$", image)) and (
|
||||||
dims := _image_dims(m.group(1))
|
dims := _image_dims(m.group(1))
|
||||||
):
|
):
|
||||||
w, h = dims
|
w, h = dims
|
||||||
large = w >= 600 and h > 0 and 1.4 <= w / h <= 2.5
|
large = w > 600 and h > 400 and w / h > 1.05
|
||||||
if node.large is not None:
|
if node.large is not None:
|
||||||
large = node.large
|
large = node.large
|
||||||
return large
|
return large
|
||||||
|
|||||||
Reference in New Issue
Block a user