Standardize poet attributions and reformat blog template

Convert all poet attribution lines to use an em dash, English
name format, and consistent spacing. Re-indent the blog single
page template for readability and adjust the typewriter element
layout.
This commit is contained in:
2026-05-14 02:29:10 +08:00
parent c73add9a58
commit 1fb7959071
10 changed files with 155 additions and 83 deletions
+146 -74
View File
@@ -1,82 +1,154 @@
{{ define "main" }}
<div class="hx:mx-auto hx:flex hextra-max-page-width">
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
{{ partial "toc.html" . }}
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-0 hx:md:px-12">
{{ partial "breadcrumb.html" (dict "page" . "enable" true) }}
{{ if .Title }}
<div class="hx:flex hx:flex-col hx:sm:flex-row hx:items-start hx:sm:items-center hx:sm:justify-between hx:gap-4 hx:mt-2">
<h1 class="hx:mb-0 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>
{{ partial "components/page-context-menu" . }}
</div>
{{ end }}
<div class="hx:mt-4 hx:mb-16 hx:text-gray-500 hx:dark:text-gray-400 hx:text-sm hx:flex hx:items-center hx:flex-wrap hx:gap-y-2">
{{- with $date := .Date }}<span class="hx:mr-1">{{ partial "utils/format-date" $date }}</span>{{ end -}}
{{- $lazyLoading := site.Params.enableImageLazyLoading | default true -}}
{{ if and .Date .Params.authors }}<span class="hx:mx-1">·</span>{{ end -}}
{{- with $.Params.authors -}}
{{- range $i, $author := . -}}
{{- if reflect.IsMap $author -}}
{{- if and $i (not $author.image) }}<span class="hx:mr-1">,</span>{{ end -}}
<a
{{ with $author.link }}href="{{ . }}" target="_blank"{{ end }}
class="hx:group hx:inline-flex hx:items-center hx:text-current hx:gap-x-1.5 hx:mx-1"
{{ with $author.name }}title="{{ . }}"{{ end }}
<div class="hx:mx-auto hx:flex hextra-max-page-width">
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true
"displayPlaceholder" true) }} {{ partial "toc.html" . }}
<article
class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]"
>
<main
id="content"
class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-0 hx:md:px-12"
>
{{ partial "breadcrumb.html" (dict "page" . "enable" true) }} {{ if
.Title }}
<div
class="hx:flex hx:flex-col hx:sm:flex-row hx:items-start hx:sm:items-center hx:sm:justify-between hx:gap-4 hx:mt-2"
>
<h1
class="hx:mb-0 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100"
>
{{- with $image := $author.image }}
{{- $isLocal := not (urls.Parse $image).Scheme -}}
{{- $startsWithSlash := hasPrefix $image "/" -}}
{{- if and $isLocal $startsWithSlash }}
{{- $image = (relURL (strings.TrimPrefix "/" $image)) -}}
{{ .Title }}
</h1>
{{ partial "components/page-context-menu" . }}
</div>
{{ end }}
<div
class="hx:mt-4 hx:mb-16 hx:text-gray-500 hx:dark:text-gray-400 hx:text-sm hx:flex hx:items-center hx:flex-wrap hx:gap-y-2"
>
{{- with $date := .Date }}<span class="hx:mr-1"
>{{ partial "utils/format-date" $date }}</span
>{{ end -}} {{- $lazyLoading :=
site.Params.enableImageLazyLoading | default true -}} {{ if and
.Date .Params.authors }}<span class="hx:mx-1">·</span>{{ end -}}
{{- with $.Params.authors -}} {{- range $i, $author := . -}} {{-
if reflect.IsMap $author -}} {{- if and $i (not $author.image)
}}<span class="hx:mr-1">,</span>{{ end -}}
<a
{{
with
$author.link
}}href="{{ . }}"
target="_blank"
{{
end
}}
class="hx:group hx:inline-flex hx:items-center hx:text-current hx:gap-x-1.5 hx:mx-1"
{{
with
$author.name
}}title="{{ . }}"
{{
end
}}
>
{{- with $image := $author.image }} {{- $isLocal := not
(urls.Parse $image).Scheme -}} {{- $startsWithSlash :=
hasPrefix $image "/" -}} {{- if and $isLocal
$startsWithSlash }} {{- $image = (relURL (strings.TrimPrefix
"/" $image)) -}} {{ end -}}
<img
src="{{ $image | safeURL }}"
alt="{{ $author.name }}"
class="hx:inline-block hx:h-4 hx:w-4 hx:rounded-full"
{{
if
$lazyLoading
}}loading="lazy"
{{
end
}}
/>
{{ end -}}
<img src="{{ $image | safeURL }}" alt="{{ $author.name }}" class="hx:inline-block hx:h-4 hx:w-4 hx:rounded-full" {{ if $lazyLoading }}loading="lazy"{{ end }} />
{{ end -}}
<div class="hx:group-hover:underline">{{ $author.name }}</div>
<div class="hx:group-hover:underline">
{{ $author.name }}
</div>
</a>
{{- else -}}
{{- if $i }}<span class="hx:mr-1">,</span>{{ end -}}<span class="hx:mx-1">{{ $author }}</span>
{{- end -}}
{{- end -}}
{{- end -}}
</div>
<div class="content">
{{ .Content }}
{{- else -}} {{- if $i }}<span class="hx:mr-1">,</span>{{ end
-}}<span class="hx:mx-1">{{ $author }}</span>
{{- end -}} {{- end -}} {{- end -}}
</div>
<div class="content">
{{ .Content }} {{- with .Params.poet }}
<div
id="typewriter-box"
style="opacity:0;transition:opacity .6s;margin-top:1.5em"
>
<p id="typewriter-text"></p>
</div>
<script>
(function () {
var all = `{{ . }}`;
var i = 0,
del = false,
dc = 0;
var te = document.getElementById("typewriter-text");
var box = document.getElementById("typewriter-box");
if (!te || !box) return;
var obs = new IntersectionObserver(
function (e) {
if (e[0].isIntersecting) {
box.style.opacity = "1";
obs.disconnect();
go();
}
},
{ rootMargin: "120px" },
);
obs.observe(box);
function go() {
setTimeout(
function () {
if (
!del &&
Math.random() < 0.1 &&
i > 8 &&
all.charAt(i - 1) !== "\n"
) {
del = true;
dc = Math.floor(Math.random() * 4) + 1;
}
if (del) {
i = Math.max(0, i - 1);
dc--;
if (
dc <= 0 ||
i === 0 ||
all.charAt(i - 1) === "\n"
)
del = false;
} else {
if (i < all.length) i++;
}
te.textContent = all.substring(0, i);
if (i < all.length || del) go();
},
del
? 55 + Math.random() * 30
: 70 + Math.random() * 100,
);
}
})();
</script>
{{- with .Params.poet }}
<div id="typewriter-box" style="opacity:0;transition:opacity .6s">
<p id="typewriter-text"></p>
</div>
<script>
(function(){
var all=`{{ . }}`;
var i=0,del=false,dc=0;
var te=document.getElementById("typewriter-text");
var box=document.getElementById("typewriter-box");
if(!te||!box)return;
var obs=new IntersectionObserver(function(e){
if(e[0].isIntersecting){box.style.opacity="1";obs.disconnect();go()}
},{rootMargin:"120px"});obs.observe(box);
function go(){setTimeout(function(){
if(!del&&Math.random()<0.1&&i>8&&all.charAt(i-1)!=="\n"){del=true;dc=Math.floor(Math.random()*4)+1}
if(del){i=Math.max(0,i-1);dc--;if(dc<=0||i===0||all.charAt(i-1)==="\n")del=false}
else{if(i<all.length)i++}
te.textContent=all.substring(0,i);
if(i<all.length||del)go()
},del?55+Math.random()*30:70+Math.random()*100)}
})();
</script>
{{- end }}
</div>
{{- end }}
</div>
{{- partial "components/last-updated.html" . -}}
{{- if (site.Params.blog.article.displayPagination | default true) -}}
{{- .Store.Set "reversePagination" (.Params.reversePagination | default true) -}}
{{- partial "components/pager.html" . -}}
{{ end }}
{{- partial "components/comments.html" . -}}
</main>
{{- partial "components/last-updated.html" . -}} {{- if
(site.Params.blog.article.displayPagination | default true) -}} {{-
.Store.Set "reversePagination" (.Params.reversePagination | default
true) -}} {{- partial "components/pager.html" . -}} {{ end }} {{-
partial "components/comments.html" . -}}
</main>
</article>
</div>
</div>
{{ end }}