migrate to decoupled Blowfish layout, fix TOC scrollspy, and update docs

- Theme Decoupling & Cleanup:
      - Fully decoupled and vendored the Blowfish theme directly into root directories (layouts/,
  assets/, static/, data/, i18n/).
      - De-registered and cleaned up legacy Git submodules (themes/blowfish, themes/hugo-admonitions)
  from the Git index to protect the build from upstream breakages.
      - Configured .gitignore to ignore development preview symlinks (content, static,
  config/_default/menus.en.toml), build folders (public/, resources/), and locks.

    - TOC & Layout Fixes:
      - Replaced the custom layouts/_default/single.html template with the Blowfish default to restore
  native container widths and the side-by-side flex layout.
      - Restored TOC to float on the right sidebar on desktop viewports and render inline on mobile
  viewports.
      - Fixed a parameter resolution bug in layouts/partials/toc.html by checking both .Site.Params.
  smartTOC and .Site.Params.article.smartTOC (where it is defined in params.toml).
      - Upgraded the TOC scrollspy JavaScript to target all '#TableOfContents' containers on the page,
  enabling highlight tracking on both desktop and mobile layouts.

    - Documentation Updates (README.md):
      - Restored YAML front matter metadata block at the top.
      - Added detailed notes on the CI/CD build process and Gitea runner deployment targets
  (/srv/www/docs-public and /srv/www/docs-private).
      - Added a categorized and sorted "Shortcodes Quick Reference" table at the top of the shortcodes
  section (ordered by Custom, Hugo Default, and Blowfish).
      - Documented custom shortcodes (Include, Screenshot, Raw HTML) and missing Hugo default
  shortcodes (Highlight, Instagram, Param, Ref, Relref, Twitter, Vimeo, Youtube).
      - Restructured headings to ensure all sub-shortcodes are H3, removed block break tags (<br/>),
  and appended horizontal separators (---) after H2 sections.
This commit is contained in:
2026-06-06 19:26:33 +00:00
Unverified
parent d329fe9dfb
commit 835b118d1a
443 changed files with 33405 additions and 1220 deletions

View File

@@ -1,86 +1,140 @@
{{ define "main" }}
{{ .Scratch.Set "scope" "single" }}
<article class="w-full py-8">
<article>
{{/* Hero */}}
{{ if .Params.showHero | default (site.Params.article.showHero | default false) }}
{{ partial (printf "hero/%s.html" (.Params.heroStyle | default site.Params.article.heroStyle)) . }}
{{ $heroStyle := .Params.heroStyle }}
{{ if not $heroStyle }}{{ $heroStyle = site.Params.article.heroStyle }}{{ end }}
{{ $heroStyle := print "hero/" $heroStyle ".html" }}
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
{{ partial $heroStyle . }}
{{ else }}
{{ partial "hero/basic.html" . }}
{{ end }}
{{ end }}
<header id="single_header" class="mt-5 max-w-prose mx-auto px-4">
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title | emojify }}</h1>
{{/* Header */}}
<header id="single_header" class="mt-5 max-w-prose">
{{ if .Params.showBreadcrumbs | default (site.Params.article.showBreadcrumbs | default false) }}
{{ partial "breadcrumbs.html" . }}
{{ end }}
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
{{ .Title | emojify }}
</h1>
<div class="mt-1 mb-6 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
{{ partial "article-meta/basic.html" (dict "context" . "scope" "single") }}
<!-- Added Dates Section
<div class="mt-2 flex flex-wrap gap-x-4 italic opacity-80 text-sm">
<span>Published: {{ .Date.Format "Jan 2, 2006" }}</span>
{{ if ne (.Lastmod.Format "2006-01-02") (.Date.Format "2006-01-02") }}
<span class="before:content-['•'] before:mr-4">
Last updated: {{ .Lastmod.Format "Jan 2, 2006" }}
</span>
{{ end }}
</div> -->
</div>
{{ if not (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
{{ template "SingleAuthor" . }}
{{ end }}
</header>
{{/* The Content Wrapper: Standard Blowfish structure */}}
<div class="mt-8 px-4">
<div class="container mx-auto prose dark:prose-invert">
{{ if .Params.showTableOfContents | default site.Params.article.showTableOfContents }}
<div class="toc-container mb-8">
{{/* Body */}}
<section class="flex flex-col max-w-full mt-0 prose dark:prose-invert lg:flex-row">
{{ $enableToc := site.Params.article.showTableOfContents | default false }}
{{ $enableToc = .Params.showTableOfContents | default $enableToc }}
{{ $showToc := and $enableToc (in .TableOfContents "<ul") }}
{{ $topClass := cond (hasPrefix site.Params.header.layout "fixed") "lg:top-[140px]" "lg:top-10" }}
{{ if $showToc }}
<div class="order-first lg:ms-auto px-0 lg:order-last lg:ps-8 lg:max-w-2xs">
<div class="toc ps-5 print:hidden lg:sticky {{ $topClass }}">
{{ partial "toc.html" . }}
</div>
{{ end }}
<article class="article-content">
{{ partial "series/series.html" . }}
</div>
{{ end }}
<div class="min-w-0 min-h-0 max-w-fit">
{{ partial "series/series.html" . }}
<div class="article-content max-w-prose mb-20">
{{ .Content }}
{{ if .Params.replyByEmail | default site.Params.replyByEmail }}
{{ $defaultReplyByEmail := site.Params.replyByEmail }}
{{ $replyByEmail := default $defaultReplyByEmail .Params.replyByEmail }}
{{ if $replyByEmail }}
{{ $replySubject := i18n "article.reply_to" (dict "Title" .Title) | default (printf "Reply to %s" .Title) }}
<strong class="block mt-8">
<a target="_blank" href="mailto:{{ site.Params.Author.email }}">Reply by Email</a>
<a
class="email-link m-1 rounded bg-neutral-300 p-1.5 text-neutral-700 hover:bg-primary-500 hover:text-neutral dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-primary-400 dark:hover:text-neutral-800"
href="#"
data-email="{{ site.Params.Author.email | base64Encode }}"
data-subject="{{ replace $replySubject "\"" "'" }}">
{{ i18n "article.reply_by_email" | default "Reply by Email" }}
</a>
<noscript>
<span
class="m-1 rounded bg-neutral-300 p-1.5 text-neutral-700 dark:bg-neutral-700 dark:text-neutral-300">
{{ i18n "article.reply_by_email" | default "Reply by Email" }} (require JavaScript)
</span>
</noscript>
</strong>
{{ end }}
<div class="mt-10">
{{ if (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
{{ template "SingleAuthor" . }}
{{ end }}
{{ partial "series/series-closed.html" . }}
{{ partial "sharing-links.html" . }}
{{ partial "related.html" . }}
</div>
</article>
</div>
{{ if (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
{{ template "SingleAuthor" . }}
{{ end }}
{{ partial "series/series-closed.html" . }}
{{ partial "sharing-links.html" . }}
{{ partial "related.html" . }}
</div>
</div>
</section>
<footer class="pt-8 mt-12 border-t border-neutral-200 dark:border-neutral-800 max-w-prose mx-auto px-4">
{{/* Footer */}}
<footer class="pt-8 max-w-prose print:hidden">
{{ partial "article-pagination.html" . }}
{{ if .Params.showComments | default (site.Params.article.showComments | default false) }}
{{ if templates.Exists "partials/comments.html" }}
<div class="pt-3">
<hr class="border-dotted border-neutral-300 dark:border-neutral-600" />
<div class="pt-3">
{{ partial "comments.html" . }}
</div>
</div>
{{ else }}
{{ warnf "[BLOWFISH] Comments are enabled for %s but no comments partial exists." .File.Path }}
{{ end }}
{{ end }}
</footer>
</article>
{{ end }}
{{ define "SingleAuthor" }}
<div class="max-w-prose">
{{ $authorsData := site.Data.authors }}
{{ $baseURL := site.BaseURL }}
{{ $isAuthorBottom := (.Params.showAuthorBottom | default ( site.Params.article.showAuthorBottom | default false)) }}
{{ if not (strings.HasSuffix $baseURL "/") }}{{ $baseURL = delimit (slice $baseURL "/") "" }}{{ end }}
{{ $authorsData := hugo.Data.authors }}
{{ $taxonomies := site.Taxonomies.authors }}
{{ $baseURL := site.BaseURL }}
{{ $taxonomyLink := 0 }}
{{ $showAuthor := 0 }}
{{ $isAuthorBottom := (.Params.showAuthorBottom | default ( site.Params.article.showAuthorBottom | default false)) }}
{{ if .Params.showAuthor | default (site.Params.article.showAuthor | default true) }}
{{ partial "author.html" . }}
{{ end }}
{{ if not (strings.HasSuffix $baseURL "/") }}
{{ $baseURL = delimit (slice $baseURL "/") "" }}
{{ end }}
{{ range $author := .Page.Params.authors }}
{{ $authorData := index $authorsData $author }}
{{ if $authorData }}
{{ $link := printf "%sauthors/%s/" $baseURL $author }}
{{ partial "author-extra.html" (dict "context" . "data" $authorData "link" $link) }}
{{ if .Params.showAuthor | default (site.Params.article.showAuthor | default true) }}
{{ $showAuthor = 1 }}
{{ partial "author.html" . }}
{{ end }}
{{ range $author := .Page.Params.authors }}
{{ $authorData := index $authorsData $author }}
{{- if $authorData -}}
{{ range $taxonomyname, $taxonomy := $taxonomies }}
{{ if (eq $taxonomyname $author) }}
{{ $taxonomyLink = delimit (slice $baseURL "authors/" $author "/") "" }}
{{ end }}
{{ end }}
{{ end }}
{{ $finalLink := $taxonomyLink }}
{{ $currentLang := site.Language.Name }}
{{ if eq site.LanguagePrefix "" }}
{{ $finalLink = printf "%sauthors/%s/" $baseURL $author }}
{{ else }}
{{ $finalLink = printf "%s%s/authors/%s/" $baseURL $currentLang $author }}
{{ end }}
{{ partial "author-extra.html" (dict "context" . "data" $authorData "link" $finalLink) }}
{{- end -}}
{{ end }}
{{ if or $taxonomyLink $showAuthor }}
<div class="{{ cond $isAuthorBottom "mb-10" "mb-5" }}"></div>
</div>
{{ end }}
{{ end }}
{{ end }}