Files
andwati.com/templates/page.html
T
andwati e992baf2d2 Migrate templates to Zola 0.23 / Tera v2
bin/update-zola always installs the latest release, which as of
v0.23.0 rewrote the template engine to Tera v2. That broke the whole
site: removed filters (slice, concat, escape), test calls now require
named args, shortcodes are gone in favor of Tera components, and
content itself is now Tera-templated before markdown runs.

- slice(end=n) -> native [:n] slicing; concat(with=x) -> spread [...arr, x]
- escape -> escape_html; starting_with/containing -> named `pat` arg
- get_taxonomy_url `name` -> `term` (old name is deprecated)
- %+ date format no longer recognized; replaced with explicit RFC3339 pattern
- shortcodes (note/tip/warning/danger/youtube) rewritten as components;
  content call sites updated to component invocation syntax
- {% include %} no longer inherits for-loop-local variables (only ambient
  page/section context) - post-card.html converted to a component that
  takes page/index explicitly instead of relying on implicit `page`/`loop`
- two content code blocks with literal Jinja-style {{ }} syntax (Netlify
  CMS config, a Flask template snippet) wrapped in {% raw %} so Tera
  doesn't try to render them, since content is now templated too
- fixed a real content corruption bug along the way: the youtube
  shortcode's multi-line <iframe> tag had a conditional that could
  collapse to a whitespace-only line, which prematurely closed the
  HTML block under CommonMark rules and got the rest of the tag
  syntax-highlighted as code instead of rendered

Verified with a clean `zola build` and `zola serve` on v0.23.1, plus a
spot-check of the rendered HTML for both callouts and the video embed.
`zola check` only reports pre-existing external link rot, unrelated to
this change.
2026-08-07 10:46:09 +03:00

211 lines
8.3 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
{% block description %}{{ page.description | default(value=config.description) }}{% endblock description %}
{% block canonical %}{{ page.permalink }}{% endblock canonical %}
{# Open Graph overrides for article pages #}
{% block og_title %}{{ page.title }}{% endblock og_title %}
{% block og_description %}{{ page.description | default(value=config.description) }}{% endblock og_description %}
{% block og_url %}{{ page.permalink }}{% endblock og_url %}
{% block og_type %}{% if "posts/_index.md" in page.ancestors %}article{% else %}website{% endif %}{% endblock og_type %}
{% block og_image %}{{ config.base_url }}{{ page.extra.image | default(value="/images/og-image.png") }}{% endblock og_image %}
{% block og_extra %}
{% if "posts/_index.md" in page.ancestors %}
{% if page.date %}<meta property="article:published_time" content="{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}">{% endif %}
{% if page.updated %}<meta property="article:modified_time" content="{{ page.updated | date(format="%Y-%m-%dT%H:%M:%S%:z") }}">{% endif %}
<meta property="article:author" content="{{ config.extra.author }}">
{% if page.taxonomies.tags %}
{% for tag in page.taxonomies.tags %}
<meta property="article:tag" content="{{ tag }}">
{% endfor %}
{% endif %}
{% endif %}
{% endblock og_extra %}
{# Twitter Card overrides #}
{% block twitter_title %}{{ page.title }}{% endblock twitter_title %}
{% block twitter_description %}{{ page.description | default(value=config.description) }}{% endblock twitter_description %}
{% block twitter_image %}{{ config.base_url }}{{ page.extra.image | default(value="/images/og-image.png") }}{% endblock twitter_image %}
{% block head %}
{% if "posts/_index.md" in page.ancestors %}
<!-- SEO: BlogPosting JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"@id": "{{ page.permalink | safe }}#article",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{{ page.permalink | safe }}"
},
"headline": {{ page.title | json_encode | safe }},
"description": {% if page.description %}{{ page.description | json_encode | safe }}{% else %}{{ config.description | json_encode | safe }}{% endif %},
"image": "{{ config.base_url | safe }}{% if page.extra.image %}{{ page.extra.image | safe }}{% else %}/images/og-image.png{% endif %}",
"url": "{{ page.permalink | safe }}",
"inLanguage": "en-US",
{% if page.date %}"datePublished": "{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}",{% endif %}
{% if page.updated %}"dateModified": "{{ page.updated | date(format="%Y-%m-%dT%H:%M:%S%:z") }}",{% else %}{% if page.date %}"dateModified": "{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}",{% endif %}{% endif %}
"author": {
"@type": "Person",
"@id": "{{ config.base_url | safe }}/#author",
"name": {{ config.extra.author | json_encode | safe }},
"url": "{{ config.base_url | safe }}/about/"
},
"publisher": {
"@type": "Person",
"@id": "{{ config.base_url | safe }}/#author",
"name": {{ config.extra.author | json_encode | safe }},
"url": "{{ config.base_url | safe }}/about/"
},
"isPartOf": {
"@type": "Blog",
"@id": "{{ config.base_url | safe }}/#blog",
"name": {{ config.title | json_encode | safe }},
"url": "{{ config.base_url | safe }}/"
}
{% if page.taxonomies.tags %}
,"keywords": [{% for tag in page.taxonomies.tags %}{{ tag | json_encode | safe }}{% if not loop.last %},{% endif %}{% endfor %}]
{% endif %}
}
</script>
<!-- SEO: BreadcrumbList JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "{{ config.base_url | safe }}/" },
{ "@type": "ListItem", "position": 2, "name": "Posts", "item": "{{ config.base_url | safe }}/posts/" },
{ "@type": "ListItem", "position": 3, "name": {{ page.title | json_encode | safe }}, "item": "{{ page.permalink | safe }}" }
]
}
</script>
{% else %}
<!-- SEO: WebPage JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"@id": "{{ page.permalink | safe }}",
"name": {{ page.title | json_encode | safe }},
"description": {% if page.description %}{{ page.description | json_encode | safe }}{% else %}{{ config.description | json_encode | safe }}{% endif %},
"url": "{{ page.permalink | safe }}",
"inLanguage": "en-US",
"isPartOf": {
"@type": "WebSite",
"@id": "{{ config.base_url | safe }}/#website"
}
}
</script>
<!-- SEO: BreadcrumbList JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "{{ config.base_url | safe }}/" },
{ "@type": "ListItem", "position": 2, "name": {{ page.title | json_encode | safe }}, "item": "{{ page.permalink | safe }}" }
]
}
</script>
{% endif %}
{% if page.extra.math %}
<link rel="stylesheet" href="{{ get_url(path="css/katex/katex.min.css") }}">
<script defer src="{{ get_url(path="js/katex/katex.min.js") }}"></script>
<script defer src="{{ get_url(path="js/katex/auto-render.min.js") }}"
onload="renderMathInElement(document.body);"></script>
{% endif %}
{% if page.extra.mermaid %}
<script type="module">
import mermaid from '{{ get_url(path="js/mermaid.min.js") }}';
mermaid.initialize({ startOnLoad: true, theme: 'dark' });
</script>
{% endif %}
<!-- Reading Progress Bar -->
<script defer src="{{ get_url(path="js/progress.js") }}"></script>
<!-- Footnote backlinks -->
<script defer src="{{ get_url(path="js/footnotes.js") }}"></script>
<!-- Heading anchor links -->
<script defer src="{{ get_url(path="js/heading-anchors.js") }}"></script>
<!-- Sticky TOC active-section highlighting -->
{% if page.toc %}
<script defer src="{{ get_url(path="js/toc.js") }}"></script>
{% endif %}
{% endblock head %}
{% block content %}
<div class="reading-progress-container" aria-hidden="true">
<div class="reading-progress-bar" id="reading-progress-bar"></div>
</div>
<div class="post-layout">
<article class="post-detail">
<header class="post-header">
<h1 class="post-title">{{ page.title }}</h1>
<div class="post-meta">
{% if page.date %}
<span class="post-meta-item">
<time datetime="{{ page.date }}">{{ page.date | date(format="%B %e, %Y") }}</time>
</span>
{% endif %}
{% if page.updated and page.updated != page.date %}
<span class="post-meta-sep" aria-hidden="true">·</span>
<span class="post-meta-item post-meta-updated">
<span class="post-meta-label">Updated</span>
<time datetime="{{ page.updated }}">{{ page.updated | date(format="%B %e, %Y") }}</time>
</span>
{% endif %}
{% if page.reading_time %}
<span class="post-meta-sep" aria-hidden="true">·</span>
<span class="post-meta-item">
{{ page.reading_time }} min read
</span>
{% endif %}
</div>
</header>
{% include "partials/series-nav.html" %}
{% include "partials/toc.html" %}
<div class="post-body">
{{ page.content | safe }}
</div>
{% include "partials/post-nav.html" %}
{% include "partials/related-posts.html" %}
<footer class="post-footer">
<div class="post-tags-share">
<div class="post-tags">
{% if page.taxonomies.tags %}
{% for tag in page.taxonomies.tags %}
<a href="{{ get_taxonomy_url(kind="tags", term=tag) }}" class="tag">#{{ tag }}</a>
{% endfor %}
{% endif %}
</div>
{% include "partials/social-share.html" %}
</div>
<!-- Utterances comments (posts only) -->
{% include "partials/comments.html" %}
</footer>
</article>
{% include "partials/toc-sidebar.html" %}
</div>
{% endblock content %}