First commit
Some checks failed
Blowfish Docs Deploy / build (push) Has been cancelled
Blowfish Docs Deploy / deploy (push) Has been cancelled
Test Build / Build Example Site (push) Has been cancelled

Delete exampleSite
Add initial content, images & docker-compose.yml
Use extend-head.html for analytics
Set remote url to gitea.novicelab.io
This commit is contained in:
2026-03-14 14:37:54 +00:00
commit a85b9146ee
508 changed files with 36938 additions and 0 deletions

9
layouts/404.html Normal file
View File

@@ -0,0 +1,9 @@
{{ define "main" }}
<h1 class="mb-3 text-4xl font-extrabold">{{ i18n "error.404_title" | emojify }}</h1>
<p class="mt-8 mb-12 text-neutral-400 dark:text-neutral-500">
{{ i18n "error.404_error" | emojify }}
</p>
<div class="prose dark:prose-invert">
<p>{{ i18n "error.404_description" | emojify }}</p>
</div>
{{ end }}

View File

@@ -0,0 +1,93 @@
{{- /* To customize your own admonitions, you can do the following:
1. Change colors
Define your colors in `assets/css/custom.css` using the variables from:
https://github.com/nunocoracao/blowfish/blob/main/assets/css/components/admonition.css
Example:
```
:root {
--adm-note-bg: red;
}
html.dark {
--adm-note-bg: green;
}
```
This will update the background color for light and dark mode.
2. Change icons and type settings
You can override the default type and icon mappings by creating your own
`impls/hooks/admonition-maps.html` in your `layouts` folder.
This allows you to assign different icons or styles for each admonition type.
*/ -}}
{{- if eq .Type "alert" -}}
{{- $maps := partialCached "impls/hooks/admonition-maps.html" . -}}
{{- $typeMap := $maps.typeMap -}}
{{- $iconMap := $maps.iconMap -}}
{{- $rawType := .AlertType | lower -}}
{{- $normalizedType := index $typeMap $rawType | default $rawType -}}
{{- $iconName := .Attributes.icon | default (index $iconMap $normalizedType) | default "circle-info" -}}
{{- $admonitionTitle := .AlertTitle | default ((i18n (printf "admonition.%s" $normalizedType) | default $normalizedType) | title) -}}
{{- $containerClass := "admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" -}}
{{- $headerClass := "flex items-center gap-2 font-semibold text-inherit" -}}
{{- $contentClass := "admonition-content mt-3 text-base leading-relaxed text-inherit" -}}
{{- $isCollapsible := in (slice "+" "-") .AlertSign -}}
{{- if $isCollapsible -}}
<details
class="{{ $containerClass }} group"
data-type="{{ $normalizedType }}"
{{ if eq .AlertSign "+" }}open{{ end }}>
<summary class="{{ $headerClass }} cursor-pointer">
<div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg">
{{- partial "icon.html" $iconName -}}
</div>
<div class="grow">
{{ $admonitionTitle }}
</div>
<div
class="ms-auto flex h-5 w-5 items-center justify-center transition-transform ease-in-out -rotate-90 group-open:rotate-0 print:hidden">
{{- partial "icon.html" "chevron-down" -}}
</div>
</summary>
{{- if .Text -}}
<div class="{{ $contentClass }}">
{{- .Text | safeHTML -}}
</div>
{{- end -}}
</details>
{{- else -}}
<div class="{{ $containerClass }}" data-type="{{ $normalizedType }}">
<div class="{{ $headerClass }}">
<div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg">
{{- partial "icon.html" $iconName -}}
</div>
<div class="grow">
{{ $admonitionTitle }}
</div>
</div>
{{- if .Text -}}
<div class="{{ $contentClass }}">
{{- .Text | safeHTML -}}
</div>
{{- end -}}
</div>
{{- end -}}
{{- else -}}
<blockquote
{{- range $k, $v := .Attributes -}}
{{- if $v -}}
{{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr -}}
{{- end -}}
{{- end -}}>
{{- .Text | safeHTML -}}
</blockquote>
{{- end -}}

View File

@@ -0,0 +1,10 @@
{{- $title := or .Attributes.title "" -}}
{{- $lang := or .Type "text" -}}
<div class="highlight-wrapper">
{{- with $title -}}
<div class="codeblock-title">
{{- $title -}}
</div>
{{- end -}}
{{- transform.Highlight .Inner $lang .Options -}}
</div>

View File

@@ -0,0 +1,10 @@
{{ $anchor := anchorize .Anchor }}
<h{{ .Level }} class="relative group">{{ .Text | safeHTML }}
<div id="{{ $anchor }}" class="anchor"></div>
{{ if .Page.Params.showHeadingAnchors | default (.Page.Site.Params.article.showHeadingAnchors | default true) }}
<span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
<a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#{{ $anchor }}" aria-label="{{ i18n "article.anchor_label" }}">#</a>
</span>
{{ end }}
</h{{ .Level }}>

View File

@@ -0,0 +1,114 @@
{{- define "RenderImageSimple" -}}
{{- $imgObj := .imgObj -}}
{{- $src := .src -}}
{{- $alt := .alt -}}
<img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="{{ $alt }}"
src="{{ $src }}"
{{ with $imgObj -}}
{{ with $imgObj.Width }}width="{{ . }}"{{ end }}
{{ with $imgObj.Height }}height="{{ . }}"{{ end }}
{{- end }}>
{{- end -}}
{{- define "RenderImageResponsive" -}}
{{/* Responsive Image
The current setting sizes="(min-width: 768px) 50vw, 65vw" makes the iPhone 16 and 16 Pro
select a smaller image, while the iPhone 16 Pro Max selects a larger image.
Steps:
1. Check the media queries in the `sizes` property.
2. Find the first matching value. For example, on a mobile device with a CSS pixel width
of 390px and DPR = 3 (iPhone 13), given setting sizes="(min-width: 768px) 50vw, 100vw",
it matches the `100vw` option.
3. Calculate the optimal image size: 390 × 3 × 100% (100vw) = 1170.
4. Find the corresponding match in the `srcset`.
To make the browser select a smaller image on mobile devices
override the template and change the `sizes` property to "(min-width: 768px) 50vw, 30vw"
The sizes="auto" is valid only when loading="lazy".
*/}}
{{- $imgObj := .imgObj -}}
{{- $alt := .alt -}}
{{- $originalWidth := $imgObj.Width -}}
{{- $img800 := $imgObj -}}
{{- $img1280 := $imgObj -}}
{{- if gt $originalWidth 800 -}}
{{- $img800 = $imgObj.Resize "800x" -}}
{{- end -}}
{{- if gt $originalWidth 1280 -}}
{{- $img1280 = $imgObj.Resize "1280x" -}}
{{- end -}}
{{- $srcset := printf "%s 800w, %s 1280w" $img800.RelPermalink $img1280.RelPermalink -}}
<img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="{{ $alt }}"
{{ with $imgObj.Width }}width="{{ . }}"{{ end }}
{{ with $imgObj.Height }}height="{{ . }}"{{ end }}
src="{{ $img800.RelPermalink }}"
srcset="{{ $srcset }}"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="{{ $imgObj.RelPermalink }}">
{{- end -}}
{{- define "RenderImageCaption" -}}
{{- with .caption -}}
<figcaption>{{ . | markdownify }}</figcaption>
{{- end -}}
{{- end -}}
{{- $disableImageOptimizationMD := .Page.Site.Params.disableImageOptimizationMD | default false -}}
{{- $urlStr := .Destination | safeURL -}}
{{- $url := urls.Parse $urlStr -}}
{{- $altText := .Text -}}
{{- $caption := .Title -}}
{{- $isRemote := findRE "^(https?|data)" $url.Scheme -}}
{{- $resource := "" -}}
{{- if not $isRemote -}}
{{- $resource = or ($.Page.Resources.GetMatch $urlStr) (resources.Get $urlStr) -}}
{{- end -}}
<figure
{{- range $k, $v := .Attributes -}}
{{- if $v -}}
{{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr -}}
{{- end -}}
{{- end -}}>
{{- if $isRemote -}}
{{- template "RenderImageSimple" (dict "imgObj" "" "src" $urlStr "alt" $altText) -}}
{{- else if $resource -}}
{{- $isSVG := eq $resource.MediaType.SubType "svg" -}}
{{- $shouldOptimize := and (not $disableImageOptimizationMD) (not $isSVG) -}}
{{- if $shouldOptimize -}}
{{- template "RenderImageResponsive" (dict "imgObj" $resource "alt" $altText) -}}
{{- else -}}
{{/* Not optimize image
If it is an SVG file, pass the permalink
Otherwise, pass the resource to allow width and height attributes
*/}}
{{- if $isSVG -}}
{{- template "RenderImageSimple" (dict "imgObj" "" "src" $resource.RelPermalink "alt" $altText) -}}
{{- else -}}
{{- template "RenderImageSimple" (dict "imgObj" $resource "src" $resource.RelPermalink "alt" $altText) -}}
{{- end -}}
{{- end -}}
{{- else -}}
{{- template "RenderImageSimple" (dict "imgObj" "" "src" $urlStr "alt" $altText) -}}
{{- end -}}
{{- template "RenderImageCaption" (dict "caption" $caption) -}}
</figure>

View File

@@ -0,0 +1,38 @@
{{/* From Hugo, Apache v2 license
https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_markup/render-link.html
*/}}
{{- $u := urls.Parse .Destination -}}
{{- $href := $u.String -}}
{{- if strings.HasPrefix $u.String "#" -}}
{{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment -}}
{{- else if and $href (not $u.IsAbs) -}}
{{- $path := strings.TrimPrefix "./" $u.Path -}}
{{- with or
($.PageInner.GetPage $path)
($.PageInner.Resources.Get $path)
(resources.Get $path)
-}}
{{- $href = .RelPermalink -}}
{{- with $u.RawQuery -}}
{{- $href = printf "%s?%s" $href . -}}
{{- end -}}
{{- with $u.Fragment -}}
{{- $href = printf "%s#%s" $href . -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/* Open external links in a new tab if
configured to do so by the page param externalLinkForceNewTab
or site param article.externalLinkForceNewTab
and the link starts with http: or https:
*/}}
{{- $forceNewTab := .Page.Params.externalLinkForceNewTab | default (.Page.Site.Params.article.externalLinkForceNewTab | default true) -}}
{{- $isExternal := or (strings.HasPrefix .Destination "http:") (strings.HasPrefix .Destination "https:") -}}
{{- $newTab := and $forceNewTab $isExternal -}}
<a href="{{ $href }}" {{ with .Title }}title="{{ . }}"{{ end }}
{{- if $newTab }} target="_blank" rel="noreferrer"{{ end }}>
{{- .Text | safeHTML -}}
</a>
{{- /* Trim EOF */ -}}

View File

@@ -0,0 +1,54 @@
<!doctype html>
<html
lang="{{ with site.Params.isoCode | default (site.LanguageCode | default `en`) }}{{- . -}}{{ end }}"
dir="{{ cond (site.Params.rtl | default false) `rtl` `ltr` }}"
class="scroll-smooth"
data-default-appearance="{{ site.Params.defaultAppearance | default `light` }}"
data-auto-appearance="{{ site.Params.autoSwitchAppearance | default `true` }}">
{{- partial "head.html" . -}}
{{- partialCached "init.html" . -}}
{{ $bodyLayout := "flex flex-col h-screen m-auto leading-7 max-w-7xl px-6 sm:px-14 md:px-24 lg:px-32" }}
{{ $bodyColor := "text-lg bg-neutral text-neutral-900 dark:bg-neutral-800 dark:text-neutral" }}
<body class="{{ $bodyLayout }} {{ $bodyColor }} {{ if site.Params.enableStyledScrollbar | default true }}bf-scrollbar{{ end }}">
<div id="the-top" class="absolute flex self-center">
<a
class="px-3 py-1 text-sm -translate-y-8 rounded-b-lg bg-primary-200 focus:translate-y-0 dark:bg-neutral-600"
href="#main-content">
<span class="font-bold text-primary-600 pe-2 dark:text-primary-400">&darr;</span>
{{ i18n "nav.skip_to_main" }}
</a>
</div>
{{ $header := print "header/" site.Params.header.layout ".html" }}
{{ if templates.Exists ( printf "partials/%s" $header ) }}
{{ partial $header . }}
{{ else }}
{{ partial "header/basic.html" . }}
{{ end }}
<div class="relative flex flex-col grow">
<main id="main-content" class="grow">
{{ block "main" . }}{{ end }}
{{ if and (site.Params.footer.showScrollToTop | default true) }}
{{- partial "scroll-to-top.html" . -}}
{{ end }}
</main>
{{- partial "footer.html" . -}}
{{ if site.Params.enableSearch | default false }}
{{- partial "search.html" . -}}
{{ end }}
</div>
</body>
{{ if site.Params.buymeacoffee.globalWidget | default false }}
<script
data-name="BMC-Widget"
data-cfasync="false"
src="https://cdnjs.buymeacoffee.com/1.0.0/widget.prod.min.js"
data-id="{{ site.Params.buymeacoffee.identifier }}"
data-description="Support me on Buy me a coffee!"
{{ with site.Params.buymeacoffee.globalWidgetMessage }}data-message="{{ . }}"{{ end }}
{{ with site.Params.buymeacoffee.globalWidgetColor | default `#FFDD00` }}data-color="{{ . }}"{{ end }}
{{ with site.Params.buymeacoffee.globalWidgetPosition }}data-position="{{ . }}"{{ end }}
data-x_margin="18"
data-y_margin="18"></script>
{{ end }}
</html>

View File

@@ -0,0 +1,33 @@
{{- $index := slice -}}
{{ $pages := .Site.Pages }}
{{ range .Site.Home.Translations }}
{{ $pages = $pages | lang.Merge .Site.Pages }}
{{ end }}
{{- range $pages -}}
{{- if not .Params.excludeFromSearch -}}
{{- $section := .Site.GetPage "section" .Section -}}
{{- if .Date -}}
{{- $index = $index | append (dict
"date" (.Date | time.Format (.Site.Language.Params.dateFormat | default ":date_long"))
"title" (.Title | emojify | safeJS)
"section" ($section.Title | emojify | safeJS)
"summary" (.Summary | plainify | htmlUnescape | safeJS)
"content" (.Plain | safeJS)
"permalink" .RelPermalink
"externalUrl" .Params.externalUrl
"type" .Type
) -}}
{{- else -}}
{{- $index = $index | append (dict
"title" (.Title | emojify | safeJS)
"section" ($section.Title | emojify | safeJS)
"summary" (.Summary | plainify | htmlUnescape | safeJS)
"content" (.Plain | safeJS)
"permalink" .RelPermalink
"externalUrl" .Params.externalUrl
"type" .Type
) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $index | jsonify -}}

141
layouts/_default/list.html Normal file
View File

@@ -0,0 +1,141 @@
{{ define "main" }}
{{ .Scratch.Set "scope" "list" }}
{{ $enableToc := .Params.showTableOfContents | default (site.Params.list.showTableOfContents | default false) }}
{{ $showToc := and $enableToc (in .TableOfContents "<ul") }}
{{/* Hero */}}
{{ if site.Params.list.showHero | default false }}
{{ $heroStyle := print "hero/" site.Params.list.heroStyle ".html" }}
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
{{ partial $heroStyle . }}
{{ else }}
{{ partial "hero/basic.html" . }}
{{ end }}
{{ end }}
{{/* Header */}}
<header>
{{ if .Params.showBreadcrumbs | default (site.Params.list.showBreadcrumbs | default false) }}
{{ partial "breadcrumbs.html" . }}
{{ end }}
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title }}</h1>
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
{{ partial "article-meta/list.html" (dict "context" . "scope" "single") }}
</div>
</header>
{{/* Description (markdown content) */}}
{{ $tocMargin := cond $showToc "mt-12" "mt-0" }}
{{ $topClass := cond (hasPrefix site.Params.header.layout "fixed") "lg:top-[140px]" "lg:top-10" }}
<section class="{{ $tocMargin }} prose flex max-w-full flex-col dark:prose-invert lg:flex-row mb-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>
</div>
{{ end }}
<div class="min-w-0 min-h-0 max-w-prose w-full">
{{ .Content }}
</div>
</section>
{{/* Article Grid */}}
{{ if gt .Pages 0 }}
{{ $cardView := .Params.cardView | default (site.Params.list.cardView | default false) }}
{{ $cardViewScreenWidth := .Params.cardViewScreenWidth | default (site.Params.list.cardViewScreenWidth | default false) }}
{{ $groupByYear := .Params.groupByYear | default (site.Params.list.groupByYear | default false) }}
{{ $orderByWeight := .Params.orderByWeight | default (site.Params.list.orderByWeight | default false) }}
{{ $groupByYear := and (not $orderByWeight) $groupByYear }}
{{ if not $cardView }}
<section class="space-y-10 w-full">
{{ if not $orderByWeight }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
{{ if $groupByYear }}
<h2 class="mt-12 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
{{ end }}
{{ range .Pages }}
{{ partial "article-link/simple.html" . }}
{{ end }}
{{ end }}
{{ else }}
{{ range (.Paginate (.Pages.ByWeight)).Pages }}
{{ partial "article-link/simple.html" . }}
{{ end }}
{{ end }}
</section>
{{/* else: is cardView */}}
{{ else }}
{{ if $groupByYear }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
{{ if $cardViewScreenWidth }}
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
</section>
</div>
{{ else }}
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
</section>
{{ end }}
{{ end }}
{{/* else: not groupByYear */}}
{{ else }}
{{ if $cardViewScreenWidth }}
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
{{ if not $orderByWeight }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
{{ end }}
{{ else }}
{{ range (.Paginate (.Pages.ByWeight)).Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
{{ end }}
</section>
</div>
{{ else }}
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{{ if not $orderByWeight }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
{{ end }}
{{ else }}
{{ range (.Paginate (.Pages.ByWeight)).Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
{{ end }}
</section>
{{ end }}{{/* End of cardViewScreenWidth */}}
{{ end }}{{/* End of groupByYear */}}
{{ end }}{{/* End of cardView */}}
{{/* else: .Pages not greater to zero */}}
{{ else }}
<section class="mt-10 prose dark:prose-invert">
<p class="py-8 border-t">
<em>{{ i18n "list.no_articles" | emojify }}</em>
</p>
</section>
{{ end }}
{{ partial "pagination.html" . }}
{{ end }}

55
layouts/_default/rss.xml Normal file
View File

@@ -0,0 +1,55 @@
{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if or $.IsHome $.IsSection -}}
{{- $pages = $pctx.RegularPages -}}
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
<link>{{ .Permalink }}</link>
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
<generator>Hugo -- gohugo.io</generator>
<language>{{ site.LanguageCode }}</language>{{ with .Site.Params.Author.email }}
<managingEditor>{{.}}{{ with $.Site.Params.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Params.Author.email }}
<webMaster>{{.}}{{ with $.Site.Params.Author.name }} ({{.}}){{end}}</webMaster>{{end}}
{{ if .Site.Params.footer.showCopyright | default true -}}
<copyright>{{ with replace .Site.Params.copyright "{ year }" now.Year }}{{.}}{{ else }}© {{ now.Format "2006" }} {{ .Site.Params.Author.name }}{{- end }}</copyright>
{{- end }}
{{ if not .Date.IsZero }}<lastBuildDate>{{ (index $pages.ByLastmod.Reverse 0).Lastmod.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
{{- with .OutputFormats.Get "RSS" -}}
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
{{- end -}}
{{ if and (.Site.Params.rssnext.feedId) (.Site.Params.rssnext.userId) }}
<follow_challenge>
<feedId>{{ .Site.Params.rssnext.feedId }}</feedId>
<userId>{{ .Site.Params.rssnext.userId }}</userId>
</follow_challenge>
{{ end }}
{{ range $pages }}
<item>
<title>{{ .Title }}</title>
<link>{{ .Permalink }}</link>
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
{{ with .Site.Params.Author.email }}<author>{{.}}{{ with $.Site.Params.Author.name }} ({{.}}){{end}}</author>{{end}}
<guid>{{ .Permalink }}</guid>
<description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>
{{ range ( where .Site.RegularPages ".RelPermalink" .RelPermalink | first 1 ) }}
{{- $images := .Resources.ByType "image" -}}
{{- $featured := $images.GetMatch "*feature*" -}}
{{- if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end -}}
{{- with $featured -}}
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="{{ $featured.Permalink }}" />
{{- end -}}
{{ end }}
</item>
{{ end }}
</channel>
</rss>

View File

@@ -0,0 +1,18 @@
{{ define "main" }}
<article class="max-w-full">
<header>
{{ 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>
</header>
<section class="max-w-full mt-6 prose dark:prose-invert">
{{ .Content }}
</section>
<footer class="pt-8">
{{ partial "sharing-links.html" . }}
</footer>
</article>
{{ end }}

View File

@@ -0,0 +1,132 @@
{{ define "main" }}
{{ .Scratch.Set "scope" "single" }}
<article>
{{/* Hero */}}
{{ if .Params.showHero | default (site.Params.article.showHero | default false) }}
{{ $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 */}}
<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") }}
</div>
{{ if not (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
{{ template "SingleAuthor" . }}
{{ end }}
</header>
{{/* 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>
</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 }}
{{ $defaultReplyByEmail := site.Params.replyByEmail }}
{{ $replyByEmail := default $defaultReplyByEmail .Params.replyByEmail }}
{{ if $replyByEmail }}
<strong class="block mt-8">
<a
target="_blank"
class="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="mailto:{{ site.Params.Author.email }}?subject={{ replace (printf "Reply to %s" .Title) "\"" "'" }}">
{{ i18n "article.reply_by_email" | default "Reply by Email" }}
</a>
</strong>
{{ end }}
</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>
</section>
{{/* 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" }}
{{ $authorsData := site.Data.authors }}
{{ $taxonomies := site.Taxonomies.authors }}
{{ $baseURL := site.BaseURL }}
{{ $taxonomyLink := 0 }}
{{ $showAuthor := 0 }}
{{ $isAuthorBottom := (.Params.showAuthorBottom | default ( site.Params.article.showAuthorBottom | default false)) }}
{{ if not (strings.HasSuffix $baseURL "/") }}
{{ $baseURL = delimit (slice $baseURL "/") "" }}
{{ end }}
{{ 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 }}
{{ $finalLink := $taxonomyLink }}
{{ $currentLang := site.Language.Lang }}
{{ 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>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,23 @@
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
{{ range .Data.Pages }}{{ if and (not .Sitemap.Disable) (not (in .Site.Params.sitemap.excludedKinds .Kind)) }}{{ if and (.Param "xml" | default true) (or (not .Params.externalUrl) (and (.Params.externalUrl) (and (not (hasPrefix .Params.externalUrl "http://")) (not (hasPrefix .Params.externalUrl "https://"))))) (not .Params.excludeFromSearch) }}
{{- if .Permalink -}}{{ $link := .Permalink }}{{ if .Params.externalUrl }}{{ $link = printf "%s%s" .Site.BaseURL (substr .Params.externalUrl 1) }}{{ end }}
<url>
<loc>{{ $link }}</loc>{{ if not .Lastmod.IsZero }}
<lastmod>{{ safeHTML ( .Lastmod.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
<changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
<priority>{{ .Sitemap.Priority }}</priority>{{ end }}{{ if .IsTranslated }}{{ range .Translations }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.LanguageCode }}"
href="{{ .Permalink }}"
/>{{ end }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.LanguageCode }}"
href="{{ $link }}"
/>{{ end }}
</url>
{{- end -}}{{ end }}{{ end }}{{ end }}
</urlset>

118
layouts/_default/term.html Normal file
View File

@@ -0,0 +1,118 @@
{{ define "main" }}
{{ .Scratch.Set "scope" "term" }}
{{ $showHero := .Params.showHero | default site.Params.term.showHero | default false }}
{{ if $showHero }}
{{ $heroStyle := print "hero/" site.Params.term.heroStyle ".html" }}
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
{{ partial $heroStyle . }}
{{ else }}
{{ partial "hero/basic.html" . }}
{{ end }}
{{- end -}}
<header class="mt-5">
{{ if .Params.showBreadcrumbs | default (site.Params.term.showBreadcrumbs | default false) }}
{{ partial "breadcrumbs.html" . }}
{{ end }}
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title }}</h1>
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
{{ partial "article-meta/term.html" (dict "context" . "scope" "single") }}
</div>
</header>
<section class="flex flex-col max-w-full mt-0 mb-5 prose dark:prose-invert lg:flex-row">
{{ if .Content }}
<div class="min-w-0 min-h-0 max-w-prose">
{{ .Content }}
</div>
{{ end }}
</section>
{{ if gt .Pages 0 }}
{{ $cardView := .Params.cardView | default (site.Params.term.cardView | default false) }}
{{ $cardViewScreenWidth := site.Params.term.cardViewScreenWidth | default false }}
{{ $groupByYear := .Params.groupByYear | default (site.Params.term.groupByYear | default false) }}
{{ if not $cardView }}
<section class="space-y-10 w-full">
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
{{ if $groupByYear }}
<h2 class="mt-12 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
{{ end }}
{{ range .Pages }}
{{ partial "article-link/simple.html" . }}
{{ end }}
{{ end }}
</section>
{{ else if and $cardView (not $cardViewScreenWidth) }}
{{ if $groupByYear }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
</section>
{{ end }}
{{ else }}
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
{{ end }}
</section>
{{ end }}
{{ else if and $cardView $cardViewScreenWidth }}
{{ if $groupByYear }}
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
{{ .Key }}
</h2>
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
</section>
</div>
{{ end }}
{{ else }}
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
{{ range .Pages }}
{{ partial "article-link/card.html" . }}
{{ end }}
{{ end }}
</section>
</div>
{{ end }}
{{ end }}
{{ else }}
<section class="mt-10 prose dark:prose-invert">
<p class="py-8 border-t">
<em>{{ i18n "term.no_articles" | emojify }}</em>
</p>
</section>
{{ end }}
{{ partial "pagination.html" . }}
{{ end }}

View File

@@ -0,0 +1,45 @@
{{ define "main" }}
{{ .Scratch.Set "scope" "list" }}
{{ $showHero := .Params.showHero | default site.Params.taxonomy.showHero | default false }}
{{ if $showHero }}
{{ $heroStyle := print "hero/" site.Params.taxonomy.heroStyle ".html" }}
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
{{ partial $heroStyle . }}
{{ else }}
{{ partial "hero/basic.html" . }}
{{ end }}
{{- end -}}
<header class="mt-5">
{{ if .Params.showBreadcrumbs | default (site.Params.taxonomy.showBreadcrumbs | default false) }}
{{ partial "breadcrumbs.html" . }}
{{ end }}
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title }}</h1>
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
{{ partial "article-meta/taxonomy.html" (dict "context" . "scope" "single") }}
</div>
</header>
{{ if .Content }}
<section class="flex flex-col max-w-full mt-0 prose dark:prose-invert lg:flex-row">
<div class="min-w-0 min-h-0 max-w-prose">
{{ .Content }}
</div>
</section>
{{ end }}
{{ if site.Params.taxonomy.cardView }}
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{{ range .Data.Terms }}
{{ partial "term-link/card.html" . }}
{{ end }}
</section>
{{ else }}
<section class="flex flex-wrap max-w-prose -mx-2 overflow-hidden">
{{ range .Data.Terms }}
{{ partial "term-link/text.html" . }}
{{ end }}
</section>
{{ end }}
{{ end }}

8
layouts/index.html Normal file
View File

@@ -0,0 +1,8 @@
{{ define "main" }}
{{ $partial := print "home/" .Site.Params.homepage.layout ".html" }}
{{ if templates.Exists ( printf "partials/%s" $partial ) }}
{{ partial $partial . }}
{{ else }}
{{ partial "home/profile.html" . }}
{{ end }}
{{ end }}

View File

@@ -0,0 +1,11 @@
{{ if isset site.Params.fathomAnalytics "domain" }}
<script
defer
src="https://{{ site.Params.fathomAnalytics.domain }}/script.js"
data-site="{{ site.Params.fathomAnalytics.site }}"></script>
{{ else }}
<script
defer
src="https://cdn.usefathom.com/script.js"
data-site="{{ site.Params.fathomAnalytics.site }}"></script>
{{ end }}

View File

@@ -0,0 +1,10 @@
<script
async
src="https://www.googletagmanager.com/gtag/js?id={{ site.Config.Services.GoogleAnalytics.ID }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ site.Config.Services.GoogleAnalytics.ID }}');
</script>

View File

@@ -0,0 +1,12 @@
{{ if site.Params.fathomAnalytics.site }}
{{ partial "analytics/fathom.html" . }}
{{ end }}
{{ if site.Config.Services.GoogleAnalytics.ID }}
{{ partial "analytics/ga.html" . }}
{{ end }}
{{ if site.Params.umamiAnalytics.websiteid }}
{{ partial "analytics/umami.html" . }}
{{ end }}
{{ if site.Params.selineAnalytics.token }}
{{ partial "analytics/seline.html" . }}
{{ end }}

View File

@@ -0,0 +1,21 @@
<script
async
src="https://cdn.seline.so/seline.js"
data-token="{{ site.Params.selineAnalytics.token }}"
data-id="seline-script"></script>
{{ if .Site.Params.selineAnalytics.enableTrackEvent | default true }}
<script type="text/javascript">
document.querySelector('script[data-id="seline-script"]').addEventListener("load", function () {
const type = document.head.querySelector('meta[property = "og:type"]').getAttribute("content");
let title = document.head.querySelector('meta[property = "og:title"]').getAttribute("content");
let url = document.head.querySelector('meta[property = "og:url"]').getAttribute("content");
seline.track("user:" + type + ":" + title, {
type: type,
title: title,
url: url,
});
});
</script>
{{ end }}

View File

@@ -0,0 +1,27 @@
{{/* prettier-ignore-start */}}
{{ if isset site.Params.umamiAnalytics "domain" }}
<script
data-id="umami-script"
async
src="https://{{ site.Params.umamiAnalytics.domain }}/{{ with site.Params.umamiAnalytics.scriptName }}{{ . }}{{ else }}script.js{{ end }}"
data-website-id="{{ site.Params.umamiAnalytics.websiteid }}"
{{ with site.Params.umamiAnalytics.dataDomains }}data-domains="{{ . }}"{{ end }}></script>
{{ else }}
<script
data-id="umami-script"
async
src="https://analytics.umami.is/script.js"
data-website-id="{{ site.Params.umamiAnalytics.websiteid }}"></script>
{{ end }}
{{/* prettier-ignore-end */}}
{{ if .Site.Params.umamiAnalytics.enableTrackEvent | default true }}
<script type="text/javascript">
document.querySelector('script[data-id="umami-script"]').addEventListener("load", function () {
const type = document.head.querySelector('meta[property = "og:type"]').getAttribute("content");
let title = document.head.querySelector('meta[property = "og:title"]').getAttribute("content");
let url = document.head.querySelector('meta[property = "og:url"]').getAttribute("content");
umami.track(type + ":" + title, { url: url });
});
</script>
{{ end }}

View File

@@ -0,0 +1,135 @@
{{/* Used by
1. article shortcode
The four variants of article-link are very similar. Despite appearances, as of df859f:
- _shortcode.html uses $page := $target, while the others use $page := .Page
- card-related.html does not have the hideFeatureImage option, while the others do
$page is added intentionally to prevent small differences inside
*/}}
{{ $target := .target }}
{{ $shortcodeShowSummary := .showSummary }}
{{ $shortcodeCompactSummary := .compactSummary }}
{{ $constrainItemsWidth := site.Params.list.constrainItemsWidth | default false }}
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{ $cardClasses := "flex flex-col md:flex-row relative" }}
{{ $imgWrapperClasses := "" }}
{{ $cardContentClasses := "" }}
{{ if site.Params.list.showCards }}
{{ $cardClasses = printf "%s overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600" $cardClasses }}
{{ $imgWrapperClasses = "" }}
{{ $cardContentClasses = printf "%s p-4 pt-2" $cardContentClasses }}
{{ else }}
{{ $cardClasses = printf "%s" $cardClasses }}
{{ $imgWrapperClasses = printf "%s thumbnail-shadow md:mr-7" $imgWrapperClasses }}
{{ $cardContentClasses = printf "%s mt-3 md:mt-0" $cardContentClasses }}
{{ end }}
{{ if $constrainItemsWidth }}
{{ $cardClasses = printf "%s max-w-prose" $cardClasses }}
{{ end }}
{{ $page := $target }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ if not $target.Params.hideFeatureImage }}
{{/* frontmatter */}}
{{ with $page }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{/* page resources */}}
{{ if not (or $featured $featuredURL) }}
{{ $images := .Resources.ByType "image" }}
{{ range slice "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{/* fallback to default */}}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultFeaturedImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $featuredURL = (.Resize "600x").RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
<article class="article-link--shortcode {{ $cardClasses }}">
{{ with $featuredURL }}
<div class="flex-none relative overflow-hidden {{ $imgWrapperClasses }} thumbnail">
<img
src="{{ . }}"
role="presentation"
loading="lazy"
decoding="async"
class="not-prose absolute inset-0 w-full h-full object-cover">
</div>
{{ end }}
<div class="{{ $cardContentClasses }}">
<header class="items-center text-start text-xl font-semibold">
<a
{{ with $page.Params.externalUrl }}
href="{{ . }}" target="_blank" rel="external"
{{ else }}
href="{{ $page.RelPermalink }}"
{{ end }}
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
<h2>
{{ $target.Title | emojify }}
{{ if $target.Params.externalUrl }}
<span class="cursor-default align-top text-xs text-neutral-400 dark:text-neutral-500">
<span class="rtl:hidden">&#8599;</span>
<span class="ltr:hidden">&#8598;</span>
</span>
{{ end }}
</h2>
</a>
{{ if and $target.Draft site.Params.article.showDraftLabel }}
<div class="ms-2">
{{ partial "badge.html" (i18n "article.draft" | emojify) }}
</div>
{{ end }}
{{ if templates.Exists "partials/extend-article-link.html" }}
{{ partial "extend-article-link.html" $target }}
{{ end }}
</header>
<div class="text-sm text-neutral-500 dark:text-neutral-400">
{{ partial "article-meta/basic.html" $target }}
</div>
{{ if $shortcodeShowSummary | default $target.Params.showSummary | default site.Params.list.showSummary | default false }}
{{ $compactSummary := $shortcodeCompactSummary | default $target.Params.compactSummary | default false }}
<div
class="article-link__summary prose dark:prose-invert max-w-fit mt-1 {{ if $compactSummary -}}
line-clamp-3
{{- end }}">
{{ $target.Summary | plainify }}
</div>
{{ end }}
</div>
</article>

View File

@@ -0,0 +1,101 @@
{{/* Used by
1. layouts/partials/related.html (related articles in single page)
*/}}
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{ $page := .Page }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{/* frontmatter */}}
{{ with $page }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{/* page resources */}}
{{ if not (or $featured $featuredURL) }}
{{ $images := .Resources.ByType "image" }}
{{ range slice "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{/* fallback to default */}}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultFeaturedImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $featuredURL = (.Resize "600x").RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
<article
class="article-link--related relative min-h-full min-w-full overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600">
{{ with $featuredURL }}
<div class="flex-none relative overflow-hidden thumbnail_card_related">
<img
src="{{ . }}"
role="presentation"
loading="lazy"
decoding="async"
fetchpriority="low"
class="not-prose absolute inset-0 w-full h-full object-cover">
</div>
{{ end }}
{{ if and .Draft .Site.Params.article.showDraftLabel }}
<span class="absolute top-0 right-0 m-2">
{{ partial "badge.html" (i18n "article.draft" | emojify) }}
</span>
{{ end }}
<div class="p-4">
<header>
<a
{{ with $page.Params.externalUrl }}
href="{{ . }}" target="_blank" rel="external"
{{ else }}
href="{{ $page.RelPermalink }}"
{{ end }}
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
<h2>
{{ .Title | emojify }}
{{ if .Params.externalUrl }}
<span class="cursor-default align-top text-xs text-neutral-400 dark:text-neutral-500">
<span class="rtl:hidden">&#8599;</span>
<span class="ltr:hidden">&#8598;</span>
</span>
{{ end }}
</h2>
</a>
</header>
<div class="text-sm text-neutral-500 dark:text-neutral-400">
{{ partial "article-meta/basic.html" . }}
</div>
{{ if .Params.showSummary | default (.Site.Params.list.showSummary | default false) }}
<div class="article-link__summary prose dark:prose-invert mt-1 line-clamp-5">
{{ .Summary | plainify }}
</div>
{{ end }}
</div>
<div class="px-6 pt-4 pb-2"></div>
</article>

View File

@@ -0,0 +1,104 @@
{{/* Used by
1. list.html and term.html (when the cardView option is enabled)
2. Recent articles template (when the cardView option is enabled)
3. Shortcode list.html
*/}}
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{ $page := .Page }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ if not .Params.hideFeatureImage }}
{{/* frontmatter */}}
{{ with $page }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{/* page resources */}}
{{ if not (or $featured $featuredURL) }}
{{ $images := .Resources.ByType "image" }}
{{ range slice "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{/* fallback to default */}}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultFeaturedImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $featuredURL = (.Resize "600x").RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
<article
class="article-link--card relative min-h-full min-w-full overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600">
{{ with $featuredURL }}
<div class="flex-none relative overflow-hidden thumbnail_card">
<img
src="{{ . }}"
role="presentation"
loading="lazy"
decoding="async"
class="not-prose absolute inset-0 w-full h-full object-cover">
</div>
{{ end }}
{{ if and .Draft .Site.Params.article.showDraftLabel }}
<span class="absolute top-0 right-0 m-2">
{{ partial "badge.html" (i18n "article.draft" | emojify) }}
</span>
{{ end }}
<div class="p-4">
<header>
<a
{{ with $page.Params.externalUrl }}
href="{{ . }}" target="_blank" rel="external"
{{ else }}
href="{{ $page.RelPermalink }}"
{{ end }}
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
<h2>
{{ .Title | emojify }}
{{ if .Params.externalUrl }}
<span class="cursor-default align-top text-xs text-neutral-400 dark:text-neutral-500">
<span class="rtl:hidden">&#8599;</span>
<span class="ltr:hidden">&#8598;</span>
</span>
{{ end }}
</h2>
</a>
</header>
<div class="text-sm text-neutral-500 dark:text-neutral-400">
{{ partial "article-meta/basic.html" . }}
</div>
{{ if .Params.showSummary | default (.Site.Params.list.showSummary | default false) }}
<div class="article-link__summary prose dark:prose-invert mt-1 line-clamp-5">
{{ .Summary | plainify }}
</div>
{{ end }}
</div>
<div class="px-6 pt-4 pb-2"></div>
</article>

View File

@@ -0,0 +1,123 @@
{{/* Used by
1. list.html and term.html (when the cardView option is not enabled)
2. Recent articles template (when the cardView option is not enabled)
3. Shortcode list.html
*/}}
{{ $constrainItemsWidth := site.Params.list.constrainItemsWidth | default false }}
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{ $cardClasses := "flex flex-col md:flex-row relative" }}
{{ $imgWrapperClasses := "" }}
{{ $cardContentClasses := "" }}
{{ if .Params.showCards | default site.Params.list.showCards }}
{{ $cardClasses = printf "%s overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600" $cardClasses }}
{{ $imgWrapperClasses = "" }}
{{ $cardContentClasses = printf "%s p-4" $cardContentClasses }}
{{ else }}
{{ $cardClasses = $cardClasses }}
{{ $imgWrapperClasses = printf "%s thumbnail-shadow md:mr-7" $imgWrapperClasses }}
{{ $cardContentClasses = printf "%s mt-3 md:mt-0" $cardContentClasses }}
{{ end }}
{{ if $constrainItemsWidth }}
{{ $cardClasses = printf "%s max-w-prose" $cardClasses }}
{{ end }}
{{ $page := .Page }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ if not .Params.hideFeatureImage }}
{{/* frontmatter */}}
{{ with $page }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{/* page resources */}}
{{ if not (or $featured $featuredURL) }}
{{ $images := .Resources.ByType "image" }}
{{ range slice "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{/* fallback to default */}}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultFeaturedImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $featuredURL = (.Resize "600x").RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
<article class="article-link--simple {{ $cardClasses }}">
{{ with $featuredURL }}
<div class="flex-none relative overflow-hidden {{ $imgWrapperClasses }} thumbnail">
<img
src="{{ . }}"
role="presentation"
loading="lazy"
decoding="async"
class="not-prose absolute inset-0 w-full h-full object-cover">
</div>
{{ end }}
<div class="{{ $cardContentClasses }}">
<header class="items-center text-start text-xl font-semibold">
<a
{{ with $page.Params.externalUrl }}
href="{{ . }}" target="_blank" rel="external"
{{ else }}
href="{{ $page.RelPermalink }}"
{{ end }}
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
<h2>
{{ .Title | emojify }}
{{ if .Params.externalUrl }}
<span class="cursor-default align-top text-xs text-neutral-400 dark:text-neutral-500">
<span class="rtl:hidden">&#8599;</span>
<span class="ltr:hidden">&#8598;</span>
</span>
{{ end }}
</h2>
</a>
{{ if and .Draft .Site.Params.article.showDraftLabel }}
<div class="ms-2">{{ partial "badge.html" (i18n "article.draft" | emojify) }}</div>
{{ end }}
{{ if templates.Exists "partials/extend-article-link.html" }}
{{ partial "extend-article-link.html" . }}
{{ end }}
</header>
<div class="text-sm text-neutral-500 dark:text-neutral-400">
{{ partial "article-meta/basic.html" . }}
</div>
{{ if .Params.showSummary | default (site.Params.list.showSummary | default false) }}
<div class="article-link__summary prose dark:prose-invert max-w-fit mt-1 line-clamp-3">
{{ .Summary | plainify }}
</div>
{{ end }}
</div>
<div class="px-6 pt-4 pb-2"></div>
</article>

View File

@@ -0,0 +1,125 @@
{{/* Determine the correct context and scope */}}
{{/* This allows for different logic depending on where the partial is called */}}
{{ $context := . }}
{{ $scope := default nil }}
{{ if (reflect.IsMap . ) }}
{{ $context = .context }}
{{ $scope = cond (not .scope) nil .scope }}
{{ end }}
{{ with $context }}
{{ $meta := newScratch }}
{{/* Gather partials for this context */}}
{{ $shouldShowDate := false }}
{{ if .Params.showDate | default (.Site.Params.article.showDate | default true) }}
{{ $shouldShowDate = true }}
{{ else }}
{{ $shouldShowDate = false }}
{{ end }}
{{ if (.Params.showDateOnlyInArticle | default (.Site.Params.article.showDateOnlyInArticle | default false)) }}
{{ if (ne $scope "single") }}
{{/* only takes effect when not called directly in the header of single.html */}}
{{ $shouldShowDate = false }}
{{ end }}
{{ end }}
{{ if $shouldShowDate }}
{{ $meta.Add "partials" (slice (partial "meta/date.html" .Date)) }}
{{ end }}
{{ if and (.Params.showDateUpdated | default (.Site.Params.article.showDateUpdated | default false)) (ne (partial
"functions/date.html" .Date) (partial "functions/date.html" .Lastmod)) (gt (.Lastmod | time.Format "2006") 1)
}}
{{ $meta.Add "partials" (slice (partial "meta/date-updated.html" .Lastmod)) }}
{{ end }}
{{ if and (.Params.showWordCount | default (.Site.Params.article.showWordCount | default false)) (ne .WordCount 0) }}
{{ $meta.Add "partials" (slice (partial "meta/word-count.html" .)) }}
{{ end }}
{{ if and (.Params.showReadingTime | default (.Site.Params.article.showReadingTime | default true)) (ne .ReadingTime 0) }}
{{ $meta.Add "partials" (slice (partial "meta/reading-time.html" .)) }}
{{ end }}
{{ if and (not .Params.externalURL) (.Params.showViews | default (.Site.Params.article.showViews | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/views.html" .)) }}
{{ end }}
{{ if and (not .Params.externalURL) (.Params.showLikes | default (.Site.Params.article.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes.html" .)) }}
{{ end }}
{{ if and (eq $scope "single") (not .Params.externalURL) (.Params.showLikes | default (.Site.Params.article.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes_button.html" .)) }}
{{ end }}
{{ if and (eq $scope "single") (.Params.showEdit | default (.Site.Params.article.showEdit | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/edit.html" .)) }}
{{ end }}
{{ if and (eq $scope "single") (.Params.showZenMode | default (.Site.Params.article.showZenMode | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/zen-mode.html" .)) }}
{{ end }}
<div class="flex flex-row flex-wrap items-center">
{{/* Output partials */}}
{{ with ($meta.Get "partials") }}
{{ delimit . "<span class=\"px-2 text-primary-500\">&middot;</span>" | safeHTML }}
{{ end }}
{{/* Output draft label */}}
{{ if and (eq $scope "single") (and .Draft .Site.Params.article.showDraftLabel) }}
<span class="ps-2">{{ partial "badge.html" (i18n "article.draft" | emojify) }}</span>
{{ end }}
</div>
{{ if .Params.showAuthorsBadges | default (.Site.Params.article.showAuthorsBadges | default false) }}
<div class="flex flex-row flex-wrap items-center">
{{ range $taxonomy, $terms := .Site.Taxonomies }}
{{ if (eq $taxonomy "authors") }}
{{ if (gt (len ($context.GetTerms $taxonomy)) 0) }}
{{ range $i, $a := $context.GetTerms $taxonomy }}
{{ if not (eq $i 0) }}
,&nbsp;
{{ end }}
<a href="{{ $a.RelPermalink }}" class="relative">{{ $a.LinkTitle }}</a>
{{ end }}
{{ end }}
{{ end }}
{{ end }}
</div>
{{ end }}
{{/* Output taxonomies */}}
{{ if .Params.showTaxonomies | default (.Site.Params.article.showTaxonomies | default false) }}
<div class="flex flex-row flex-wrap items-center">
{{ range $taxonomy, $terms := .Site.Taxonomies }}
{{ if and (not (eq $taxonomy "authors")) (not (eq $taxonomy "series")) }}
{{ if (gt (len ($context.GetTerms $taxonomy)) 0) }}
{{ range $context.GetTerms $taxonomy }}
<a class="relative mt-[0.5rem] me-2" href="{{ .RelPermalink }}">
{{ partial "badge.html" .LinkTitle }}
</a>
{{ end }}
{{ end }}
{{ end }}
{{ end }}
</div>
{{ end }}
{{/* Output only category */}}
{{ if .Params.showCategoryOnly | default (.Site.Params.article.showCategoryOnly | default false) }}
<div class="flex flex-row flex-wrap items-center">
{{ range (.GetTerms "categories") }}
<a class="relative mt-[0.5rem] me-2" href="{{ .RelPermalink }}">
{{ partial "badge.html" .LinkTitle }}
</a>
{{ end }}
</div>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,35 @@
{{/* Determine the correct context and scope */}}
{{/* This allows for different logic depending on where the partial is called */}}
{{ $context := . }}
{{ $scope := default nil }}
{{ if (reflect.IsMap . ) }}
{{ $context = .context }}
{{ $scope = cond (not .scope) nil .scope }}
{{ end }}
{{ with $context }}
{{ $meta := newScratch }}
{{/* Gather partials for this context */}}
{{ if (.Params.showViews | default (.Site.Params.list.showViews | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/views.html" .)) }}
{{ end }}
{{ if (.Params.showLikes | default (.Site.Params.list.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes.html" .)) }}
{{ end }}
{{ if and (eq $scope "single") (.Params.showLikes | default (.Site.Params.list.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes_button.html" .)) }}
{{ end }}
<div class="flex flex-row flex-wrap items-center">
{{/* Output partials */}}
{{ with ($meta.Get "partials") }}
{{ delimit . "<span class=\"px-2 text-primary-500\">&middot;</span>" | safeHTML }}
{{ end }}
</div>
{{ end }}

View File

@@ -0,0 +1,35 @@
{{/* Determine the correct context and scope */}}
{{/* This allows for different logic depending on where the partial is called */}}
{{ $context := . }}
{{ $scope := default nil }}
{{ if (reflect.IsMap . ) }}
{{ $context = .context }}
{{ $scope = cond (not .scope) nil .scope }}
{{ end }}
{{ with $context }}
{{ $meta := newScratch }}
{{/* Gather partials for this context */}}
{{ if (.Params.showViews | default (.Site.Params.taxonomy.showViews | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/views.html" .)) }}
{{ end }}
{{ if (.Params.showLikes | default (.Site.Params.taxonomy.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes.html" .)) }}
{{ end }}
{{ if and (eq $scope "single") (.Params.showLikes | default (.Site.Params.taxonomy.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes_button.html" .)) }}
{{ end }}
<div class="flex flex-row flex-wrap items-center">
{{/* Output partials */}}
{{ with ($meta.Get "partials") }}
{{ delimit . "<span class=\"px-2 text-primary-500\">&middot;</span>" | safeHTML }}
{{ end }}
</div>
{{ end }}

View File

@@ -0,0 +1,35 @@
{{/* Determine the correct context and scope */}}
{{/* This allows for different logic depending on where the partial is called */}}
{{ $context := . }}
{{ $scope := default nil }}
{{ if (reflect.IsMap . ) }}
{{ $context = .context }}
{{ $scope = cond (not .scope) nil .scope }}
{{ end }}
{{ with $context }}
{{ $meta := newScratch }}
{{/* Gather partials for this context */}}
{{ if (.Params.showViews | default (.Site.Params.term.showViews | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/views.html" .)) }}
{{ end }}
{{ if (.Params.showLikes | default (.Site.Params.term.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes.html" .)) }}
{{ end }}
{{ if and (eq $scope "single") (.Params.showLikes | default (.Site.Params.term.showLikes | default false)) }}
{{ $meta.Add "partials" (slice (partial "meta/likes_button.html" .)) }}
{{ end }}
<div class="flex flex-row flex-wrap items-center">
{{/* Output partials */}}
{{ with ($meta.Get "partials") }}
{{ delimit . "<span class=\"px-2 text-primary-500\">&middot;</span>" | safeHTML }}
{{ end }}
</div>
{{ end }}

View File

@@ -0,0 +1,47 @@
{{ if .Params.showPagination | default (.Site.Params.article.showPagination | default true) }}
{{ if or .NextInSection .PrevInSection }}
{{ $next := .NextInSection }}
{{ $prev := .PrevInSection }}
{{ if .Params.invertPagination | default (.Site.Params.article.invertPagination | default false) }}
{{ $next = .PrevInSection }}
{{ $prev = .NextInSection }}
{{ end }}
<div class="pt-8">
<hr class="border-dotted border-neutral-300 dark:border-neutral-600">
<div class="flex justify-between pt-3">
<span class="flex flex-col">
{{ if $prev }}
<a
class="flex text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
href="{{ $prev.RelPermalink }}">
<span class="leading-6">
<span class="inline-block rtl:rotate-180">&larr;</span>&ensp;{{ $prev.Title | emojify }}
</span>
</a>
{{ if .Params.showDate | default (.Site.Params.article.showDate | default true) }}
<span class="ms-6 mt-1 text-xs text-neutral-500 dark:text-neutral-400">
{{ partial "meta/date.html" $prev.Date }}
</span>
{{ end }}
{{ end }}
</span>
<span class="flex flex-col items-end">
{{ if $next }}
<a
class="flex text-right text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
href="{{ $next.RelPermalink }}">
<span class="leading-6">
{{ $next.Title | emojify }}&ensp;<span class="inline-block rtl:rotate-180">&rarr;</span>
</span>
</a>
{{ if .Params.showDate | default (.Site.Params.article.showDate | default true) }}
<span class="me-6 mt-1 text-xs text-neutral-500 dark:text-neutral-400">
{{ partial "meta/date.html" $next.Date }}
</span>
{{ end }}
{{ end }}
</span>
</div>
</div>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,62 @@
{{ $disableImageOptimization := .Site.Params.disableImageOptimization | default false }}
<div class="flex author author-extra mt-4">
{{ with .data.image }}
{{ $authorImage := "" }}
{{ if or (hasPrefix . "http://") (hasPrefix . "https://") }}
{{ $authorImage = resources.GetRemote . }}
{{ else }}
{{ $authorImage = resources.Get . }}
{{ end }}
{{ if $authorImage }}
{{ $final := $authorImage }}
{{ $squareImage := $authorImage }}
{{ if not (or $disableImageOptimization (eq $authorImage.MediaType.SubType "svg")) }}
{{ $final = $authorImage.Fill "192x192" }}
{{ $shortSide := int (math.Min $authorImage.Width $authorImage.Height) }}
{{ $squareImage = $authorImage.Crop (printf "%dx%d" $shortSide $shortSide ) }}
{{ end }}
<img
class="!mt-0 !mb-0 h-24 w-24 rounded-full me-4"
width="96"
height="96"
alt="{{ $.Site.Params.Author.name | default " Author" }}"
src="{{ $final.RelPermalink }}"
data-zoom-src="{{ $squareImage.RelPermalink }}">
{{ end }}
{{ end }}
<div class="place-self-center">
{{ $link := .link }}
{{ with .data.name | markdownify | emojify }}
<div class="text-[0.6rem] uppercase leading-3 text-neutral-500 dark:text-neutral-400">
{{ i18n "author.byline_title" | markdownify }}
</div>
<a
{{ if $link }}href="{{ $link }}"{{ end }}
class="font-semibold leading-6 text-neutral-800 dark:text-neutral-300">
{{ . }}
</a>
{{ end }}
{{ with .data.bio | markdownify }}
<div class="text-sm text-neutral-700 dark:text-neutral-400">{{ . }}</div>
{{ end }}
<div class="text-2xl sm:text-lg">
<div class="flex flex-wrap text-neutral-400 dark:text-neutral-500">
{{ range .data.social }}
{{ range $name, $link := . }}
<a
class="px-1 hover:text-primary-700 dark:hover:text-primary-400"
href="{{ $link }}"
target="_blank"
aria-label="{{ $name | title }}"
title="{{ $name | title }}"
rel="me noopener noreferrer">
<span class="inline-block align-text-bottom">
{{ partial "icon.html" $name }}
</span>
</a>
{{ end }}
{{ end }}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,17 @@
{{ with .Site.Params.Author.links }}
<div class="flex flex-wrap text-neutral-400 dark:text-neutral-500">
{{ range $links := . }}
{{ range $name, $url := $links }}
<a
class="px-1 hover:text-primary-700 dark:hover:text-primary-400"
href="{{ $url | safeURL }}"
target="_blank"
aria-label="{{ $name | title }}"
title="{{ $name | title }}"
rel="me noopener noreferrer"
><span class="inline-block align-text-bottom">{{ partial "icon.html" $name }}</span></a
>
{{ end }}
{{ end }}
</div>
{{ end }}

View File

@@ -0,0 +1,57 @@
{{ $disableImageOptimization := .Site.Params.disableImageOptimization | default false }}
<div class="flex author">
{{ with .Site.Params.Author.image }}
{{ $authorImage := "" }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $authorImage = resources.GetRemote . }}
{{ else }}
{{ $authorImage = resources.Get . }}
{{ end }}
{{ if $authorImage }}
{{ $final := $authorImage }}
{{ $squareImage := $authorImage }}
{{ if not (or $disableImageOptimization (eq $authorImage.MediaType.SubType "svg")) }}
{{ $final = $authorImage.Fill "192x192" }}
{{ $shortSide := int (math.Min $authorImage.Width $authorImage.Height) }}
{{ $squareImage = $authorImage.Crop (printf "%dx%d" $shortSide $shortSide ) }}
{{ end }}
<img
class="!mt-0 !mb-0 h-24 w-24 rounded-full me-4"
width="96"
height="96"
alt="{{ $.Site.Params.Author.name | default " Author" }}"
src="{{ $final.RelPermalink }}"
data-zoom-src="{{ $squareImage.RelPermalink }}">
{{ else }}
{{ $authorImage := resources.GetRemote . }}
{{ $final := $authorImage }}
{{ $squareImage := $authorImage }}
{{ if not $disableImageOptimization }}
{{ $final = $authorImage.Fill "192x192" }}
{{ $shortSide := int (math.Min $authorImage.Width $authorImage.Height) }}
{{ $squareImage = $authorImage.Crop (printf "%dx%d" $shortSide $shortSide ) }}
{{ end }}
<img
class="!mt-0 !mb-0 h-24 w-24 rounded-full me-4"
width="96"
height="96"
alt="{{ $.Site.Params.Author.name | default " Author" }}"
src="{{ $final.RelPermalink }}"
data-zoom-src="{{ $squareImage.RelPermalink }}">
{{ end }}
{{ end }}
<div class="place-self-center">
{{ with .Site.Params.Author.name | markdownify }}
<div class="text-[0.6rem] uppercase leading-3 text-neutral-500 dark:text-neutral-400">
{{ i18n "author.byline_title" | markdownify }}
</div>
<div class="font-semibold leading-6 text-neutral-800 dark:text-neutral-300">
{{ . }}
</div>
{{ end }}
{{ with .Site.Params.Author.bio | markdownify }}
<div class="text-sm text-neutral-700 dark:text-neutral-400">{{ . }}</div>
{{ end }}
<div class="text-2xl sm:text-lg">{{ partialCached "author-links.html" . }}</div>
</div>
</div>

View File

@@ -0,0 +1,6 @@
<span class="flex cursor-pointer">
<span
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
{{ . }}
</span>
</span>

View File

@@ -0,0 +1,19 @@
<ol class="text-sm text-neutral-500 dark:text-neutral-400 print:hidden">
{{ template "crumb" (dict "p1" . "p2" .) }}
</ol>
{{ define "crumb" }}
{{ if .p1.Parent }}
{{ template "crumb" (dict "p1" .p1.Parent "p2" .p2 ) }}
{{ else if not .p1.IsHome }}
{{ template "crumb" (dict "p1" .p1.Site.Home "p2" .p2 ) }}
{{ end }}
<li class="{{ if or (eq .p1 .p2) (.p1.IsHome) }}hidden{{ else }}inline{{ end }}">
<a class="hover:underline decoration-neutral-300 dark:underline-neutral-600" href="{{ .p1.RelPermalink }}"
>{{ if .p1.Title }}
{{- .p1.Title -}}
{{ else }}
{{- .p1.Section -}}
{{ end }}</a
><span class="px-1 text-primary-500">/</span>
</li>
{{ end }}

View File

@@ -0,0 +1,47 @@
{{ with site.Data.contributors }}
<section class="contributors-section mt-12 mb-16">
<div class="flex flex-col items-center mb-6">
<div class="flex items-center gap-2 mb-2">
<span class="text-2xl text-neutral-800 dark:text-neutral-200">
{{ partial "icon.html" "github" }}
</span>
<h2 class="text-2xl font-extrabold text-neutral-800 dark:text-neutral-200">
{{ i18n "contributors.title" | default "Contributors" }}
</h2>
</div>
<p class="text-neutral-600 dark:text-neutral-400 text-center">
{{ i18n "contributors.description" | default (printf "%d amazing people have contributed to Blowfish" (len .)) }}
</p>
</div>
<div class="flex flex-wrap justify-center gap-3">
{{ range . }}
<a
href="{{ .profile_url }}"
target="_blank"
rel="noopener noreferrer"
class="group"
title="{{ .username }} ({{ .contributions }} contributions)"
aria-label="Contributor: {{ .username }}">
<img
class="nozoom h-10 w-10 rounded-full transition-all group-hover:scale-110 group-hover:ring-2 group-hover:ring-primary-500"
src="{{ .avatar_url }}"
alt="{{ .username }}"
width="40"
height="40"
loading="lazy">
</a>
{{ end }}
</div>
<div class="mt-6 flex justify-center">
<a
href="https://github.com/nunocoracao/blowfish/graphs/contributors"
target="_blank"
rel="noopener noreferrer"
class="text-sm text-neutral-600 dark:text-neutral-400 hover:text-primary-500 transition-colors">
{{ i18n "contributors.viewAll" | default "View all contributors on GitHub" }} &rarr;
</a>
</div>
</section>
{{ end }}

View File

@@ -0,0 +1,8 @@
{{/* example of extend-footer */}}
{{/*
<p class="text-sm text-neutral-500 dark:text-neutral-400">
<a class="hover:underline hover:decoration-primary-400 hover:text-primary-500" rel="me" href="https://masto.ai/@blowfish">
Follow me on Mastodon
</a>
</p>
*/}}

View File

@@ -0,0 +1 @@
<script defer src="https://umami.novicelab.io/script.js" data-website-id="6dc76b26-f911-4b0e-9827-6c2bfd49df04"></script>

View File

@@ -0,0 +1,76 @@
<footer id="site-footer" class="py-10 print:hidden">
{{/* Footer menu */}}
{{ if .Site.Params.footer.showMenu | default true }}
{{ if .Site.Menus.footer }}
{{ $onlyIcon := true }}
{{ range .Site.Menus.footer }}
{{ if .Name }}
{{ $onlyIcon = false }}
{{ break }}
{{ end }}
{{ end }}
{{ $navClass := printf "flex flex-row pb-4 text-base font-medium text-neutral-500 dark:text-neutral-400 %s" (cond $onlyIcon "overflow-x-auto py-2" "") }}
{{ $ulClass := printf "flex list-none %s" (cond $onlyIcon "flex-row" "flex-col sm:flex-row") }}
{{ $liClass := printf "flex mb-1 text-end sm:mb-0 sm:me-7 sm:last:me-0 %s" (cond $onlyIcon "me-4" "") }}
<nav class="{{ $navClass }}">
<ul class="{{ $ulClass }}">
{{ range .Site.Menus.footer }}
<li class=" {{ $liClass }}">
<a
class="decoration-primary-500 hover:underline hover:decoration-2 hover:underline-offset-2 flex items-center"
href="{{ .URL }}"
title="{{ .Title }}">
{{ if .Pre }}
<span {{ if and .Pre .Name }}class="mr-1"{{ end }}>
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
{{ .Name | markdownify }}
</a>
</li>
{{ end }}
</ul>
</nav>
{{ end }}
{{ end }}
<div class="flex items-center justify-between">
{{/* Copyright */}}
{{ if .Site.Params.footer.showCopyright | default true }}
<p class="text-sm text-neutral-500 dark:text-neutral-400">
{{- with replace .Site.Params.copyright "{ year }" now.Year }}
{{ . | markdownify }}
{{- else }}
&copy;
{{ now.Format "2006" }}
{{ .Site.Params.Author.name | markdownify }}
{{- end }}
</p>
{{ end }}
{{/* Theme attribution */}}
{{ if .Site.Params.footer.showThemeAttribution | default true }}
<p class="text-xs text-neutral-500 dark:text-neutral-400">
{{ $hugo := printf `<a class="hover:underline hover:decoration-primary-400 hover:text-primary-500"
href="https://gohugo.io/" target="_blank" rel="noopener noreferrer">Hugo</a>`
}}
{{ $blowfish := printf `<a class="hover:underline hover:decoration-primary-400 hover:text-primary-500"
href="https://blowfish.page/" target="_blank" rel="noopener noreferrer">Blowfish</a>`
}}
{{ i18n "footer.powered_by" (dict "Hugo" $hugo "Theme" $blowfish) | safeHTML }}
</p>
{{ end }}
</div>
{{ if not .Site.Params.disableImageZoom | default true }}
<script>
mediumZoom(document.querySelectorAll("img:not(.nozoom)"), {
margin: 24,
background: "rgba(0,0,0,0.5)",
scrollOffset: 0,
});
</script>
{{ end }}
{{/* Extend footer - eg. for extra scripts, etc. */}}
{{ if templates.Exists "partials/extend-footer.html" }}
{{ partialCached "extend-footer.html" . }}
{{ end }}
</footer>

View File

@@ -0,0 +1 @@
{{ return time.Format (site.Language.Params.dateFormat | default ":date_long") . }}

View File

@@ -0,0 +1,13 @@
{{ $uid := .Page.RelPermalink }}
{{ $ctx := . }}
{{ range seq 16 }}
{{ with $ctx }}
{{ $uid = printf "%s-%d" $uid .Ordinal }}
{{ $ctx = .Parent }}
{{ else }}
{{ break }}
{{ end }}
{{ end }}
{{ return md5 $uid }}

210
layouts/partials/head.html Normal file
View File

@@ -0,0 +1,210 @@
<head>
<meta charset="utf-8">
{{ with .Site.Language.Params.htmlCode | default .Site.LanguageCode }}
<meta http-equiv="content-language" content="{{ . }}">
{{ end }}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="theme-color">
{{/* Title */}}
{{ if .IsHome }}
<title>{{ .Site.Title | emojify }}</title>
<meta name="title" content="{{ .Site.Title | emojify }}">
{{ else }}
<title>{{ .Title | emojify }} &middot; {{ .Site.Title | emojify }}</title>
<meta name="title" content="{{ .Title | emojify }} &middot; {{ .Site.Title | emojify }}">
{{ end }}
{{/* Metadata */}}
{{ with (.Params.Summary | default .Params.Description) | default .Site.Params.description }}
<meta name="description" content="{{ . }}">
{{ end }}
{{ with .Params.Tags | default .Site.Params.keywords }}
<meta name="keywords" content="{{ range . }}{{ . }},{{ end }}">
{{ end }}
{{ with .Site.Params.robots }}
<meta name="robots" content="{{ . }}">
{{ end }}
{{ with .Params.robots }}
<meta name="robots" content="{{ . }}">
{{ end }}
<link rel="canonical" href="{{ .Permalink }}">
{{ range .AlternativeOutputFormats }}
{{ printf `
<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .RelPermalink ($.Site.Title | emojify) |
safeHTML
}}
{{ end }}
{{/* Me */}}
{{ with .Site.Params.Author.name }}
<meta name="author" content="{{ . }}">
{{ end }}
{{ with .Site.Params.Author.links }}
{{ range $links := . }}
{{ range $name, $url := $links }}
{{ if not (strings.HasPrefix $url "mailto:") }}
<link href="{{ $url }}" rel="me">
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{/* Social */}}
{{ template "_internal/opengraph.html" . }}
{{ template "_internal/twitter_cards.html" . }}
{{/* Use defaultSocialImage if feature image does not exist */}}
{{ $featureImage := "" }}
{{ $pageImages := .Resources.ByType "image" }}
{{ range slice "*featured*" "*cover*" "*thumbnail*" }}
{{ if not $featureImage }}
{{ $featureImage = $pageImages.GetMatch . }}
{{ end }}
{{ end }}
{{ if not $featureImage }}
{{ with .Site.Params.defaultSocialImage }}
{{ $socialImage := "" }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $socialImage = resources.GetRemote . }}
{{ else }}
{{ $socialImage = resources.Get . }}
{{ end }}
{{ with $socialImage }}
<meta name="twitter:image" content="{{ .RelPermalink | absURL }}">
<meta property="og:image" content="{{ .RelPermalink | absURL }}">
{{ end }}
{{ end }}
{{ end }}
{{/* Site Verification */}}
{{ with .Site.Params.verification.google }}
<meta name="google-site-verification" content="{{ . }}">
{{ end }}
{{ with .Site.Params.verification.bing }}
<meta name="msvalidate.01" content="{{ . }}">
{{ end }}
{{ with .Site.Params.verification.pinterest }}
<meta name="p:domain_verify" content="{{ . }}">
{{ end }}
{{ with .Site.Params.verification.yandex }}
<meta name="yandex-verification" content="{{ . }}">
{{ end }}
{{ with .Site.Params.verification.fediverse }}
<meta name="fediverse:creator" content="{{ . }}">
{{ end }}
{{ $alg := .Site.Params.fingerprintAlgorithm | default "sha512" }}
{{/* CSS */}}
{{ $cssResources := slice }}
{{ $schemeName := .Site.Params.colorScheme | default "blowfish" }}
{{ $cssScheme := resources.Get (printf "css/schemes/%s.css" (lower $schemeName)) | default (resources.Get "css/schemes/blowfish.css") }}
{{ $cssResources = $cssResources | append $cssScheme }}
{{ $cssResources = $cssResources | append (resources.Get "css/compiled/main.css") }}
{{ with resources.Get "css/custom.css" }}
{{ $cssResources = $cssResources | append . }}
{{ end }}
{{ if not .Site.Params.disableImageZoom | default true }}
{{ $cssResources = $cssResources | append (resources.Get "lib/zoom/style.css") }}
{{ end }}
{{ $bundleCSS := $cssResources | resources.Concat "css/main.bundle.css" | resources.Minify | resources.Fingerprint $alg }}
<link
type="text/css"
rel="stylesheet"
href="{{ $bundleCSS.RelPermalink }}"
integrity="{{ $bundleCSS.Data.Integrity }}">
{{/* JS loaded immediately */}}
{{ $jsAppearance := resources.Get "js/appearance.js" | resources.ExecuteAsTemplate "js/appearance.js" . | resources.Minify | resources.Fingerprint $alg }}
<script
type="text/javascript"
src="{{ $jsAppearance.RelPermalink }}"
integrity="{{ $jsAppearance.Data.Integrity }}"></script>
{{ $enableA11y := .Site.Params.enableA11y | default false }}
{{ if $enableA11y }}
{{ $jsA11y := resources.Get "js/a11y.js" | resources.Minify | resources.Fingerprint $alg }}
<script src="{{ $jsA11y.RelPermalink }}" integrity="{{ $jsA11y.Data.Integrity }}"></script>
{{ end }}
{{ $showZenMode := .Params.showZenMode | default (.Site.Params.article.showZenMode | default false) }}
{{ $shouldIncludeZenMode := or $enableA11y $showZenMode }}
{{ if and .IsPage $shouldIncludeZenMode }}
{{ $jsZenMode := resources.Get "js/zen-mode.js" | resources.Minify | resources.Fingerprint $alg }}
<script
type="text/javascript"
src="{{ $jsZenMode.RelPermalink }}"
integrity="{{ $jsZenMode.Data.Integrity }}"></script>
{{ end }}
{{ if not .Site.Params.disableImageZoom | default true }}
{{ $zoomJS := resources.Get "lib/zoom/zoom.min.umd.js" | resources.Fingerprint $alg }}
<script src="{{ $zoomJS.RelPermalink }}" integrity="{{ $zoomJS.Data.Integrity }}"></script>
{{ end }}
{{/* JS deferred */}}
{{ $jsResources := slice }}
{{ if site.Params.footer.showScrollToTop | default true }}
{{ $jsResources = $jsResources | append (resources.Get "js/scroll-to-top.js") }}
{{ end }}
{{ if .Site.Params.enableSearch | default false }}
{{ $jsResources = $jsResources | append (resources.Get "lib/fuse/fuse.min.js") | append (resources.Get "js/search.js") }}
{{ end }}
{{ if .Site.Params.enableCodeCopy | default false }}
{{ $jsResources = $jsResources | append (resources.Get "js/code.js") }}
{{ end }}
{{ if .Site.Params.rtl | default false }}
{{ $jsResources = $jsResources | append (resources.Get "js/rtl.js") }}
{{ end }}
{{ $jsResources = $jsResources | append (resources.Get "js/katex-render.js") }}
{{ $jsResources = $jsResources | append (resources.Get "js/print-support.js") }}
{{ if $jsResources }}
{{ $bundleJS := $jsResources | resources.Concat "js/main.bundle.js" | resources.Minify | resources.Fingerprint $alg }}
<script
defer
type="text/javascript"
id="script-bundle"
src="{{ $bundleJS.RelPermalink }}"
integrity="{{ $bundleJS.Data.Integrity }}"
data-copy="{{ i18n "code.copy" }}"
data-copied="{{ i18n "code.copied" }}"></script>
{{ end }}
{{/* Conditional loaded resources */}}
{{ partial "vendor.html" . }}
{{/* Icons */}}
{{ if templates.Exists "partials/favicons.html" }}
{{ partialCached "favicons.html" .Site }}
{{ else }}
<link rel="apple-touch-icon" sizes="180x180" href="{{ "apple-touch-icon.png" | relURL }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ "favicon-32x32.png" | relURL }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ "favicon-16x16.png" | relURL }}">
<link rel="manifest" href="{{ "site.webmanifest" | relURL }}">
{{ end }}
{{/* Schema */}}
{{ partial "schema.html" . }}
{{/* Analytics */}}
{{ if hugo.IsProduction }}
{{ partial "analytics/main.html" . }}
{{ end }}
{{/* Extend head - eg. for custom analytics scripts, etc. */}}
{{ if templates.Exists "partials/extend-head.html" }}
{{ partialCached "extend-head.html" .Site }}
{{ end }}
{{/* Uncached extend head - Example: https://gohugo.io/methods/page/hasshortcode/ */}}
{{ if templates.Exists "partials/extend-head-uncached.html" }}
{{ partial "extend-head-uncached.html" . }}
{{ end }}
{{/* Advertisement */}}
{{ with .Site.Params.advertisement.adsense }}
<meta name="google-adsense-account" content="{{ . }}">
<script
async
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client={{ . }}"
crossorigin="anonymous"></script>
{{ end }}
</head>

View File

@@ -0,0 +1,83 @@
<div class="main-menu flex items-center w-full gap-2 p-1 pl-0">
{{ if .Site.Params.Logo }}
{{ $logo := resources.Get .Site.Params.Logo }}
{{ if $logo }}
<div>
<a href="{{ "" | relLangURL }}" class="flex">
<span class="sr-only">{{ .Site.Title | markdownify }}</span>
{{ if eq $logo.MediaType.SubType "svg" }}
<span class="logo object-scale-down object-left nozoom">
{{ $logo.Content | safeHTML }}
</span>
{{ else }}
<img
src="{{ $logo.RelPermalink }}"
width="{{ div $logo.Width 2 }}"
height="{{ div $logo.Height 2 }}"
class="logo max-h-20 max-w-20 object-scale-down object-left nozoom"
alt="">
{{ end }}
</a>
</div>
{{ end }}
{{ end }}
{{ if not .Site.Params.disableTextInHeader | default true }}
<a href="{{ "" | relLangURL }}" class="text-base font-medium truncate min-w-0 shrink">
{{ .Site.Title | markdownify }}
</a>
{{ end }}
<div class="flex items-center ms-auto">
<div class="hidden md:flex">
{{ partial "header/components/desktop-menu.html" . }}
</div>
<div class="flex md:hidden">
{{ partial "header/components/mobile-menu.html" . }}
</div>
</div>
</div>
{{ if .Site.Menus.subnavigation }}
<div
class="main-menu flex pb-3 flex-col items-end justify-between md:justify-start space-x-3 {{ if .Site.Params.Logo }}
-mt-[15px]
{{ end }}">
<div class="hidden md:flex items-center space-x-5">
{{ range .Site.Menus.subnavigation }}
<a
href="{{ .URL }}"
{{ if or (strings.HasPrefix .URL "http:" ) (strings.HasPrefix .URL "https:" ) }}
target="_blank"
{{ end }}
class="flex items-center bf-icon-color-hover">
{{ if .Pre }}
<span {{ if and .Pre .Name }}class="mr-1"{{ end }}>
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
<span class="text-xs font-light" title="{{ .Title }}">
{{ .Name | markdownify }}
</span>
</a>
{{ end }}
</div>
</div>
{{ end }}
{{ if .Site.Params.highlightCurrentMenuArea }}
<script>
(function () {
var mainmenu = document.querySelector(".main-menu");
if (!mainmenu) return;
var path = window.location.pathname;
var links = mainmenu.querySelectorAll('a[href="' + path + '"]');
links.forEach(function (link) {
var targets = link.querySelectorAll("span");
targets.forEach(function (el) {
el.classList.add("active");
});
});
})();
</script>
{{ end }}

View File

@@ -0,0 +1,88 @@
{{- $prefix := .prefix | default "" -}}
<div class="flex items-center">
<button
id="{{ $prefix }}a11y-toggle"
aria-label="Open accessibility panel"
aria-expanded="false"
type="button"
class="bf-icon-color-hover"
role="button"
aria-pressed="false">
{{ partial "icon.html" "a11y" }}
</button>
<div id="{{ $prefix }}a11y-overlay" class="fixed inset-0 hidden" style="z-index:9999;"></div>
<div
id="{{ $prefix }}a11y-panel"
role="dialog"
aria-labelledby="{{ $prefix }}a11y-panel-title"
class="a11y-panel-enter fixed hidden p-6 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 rounded-lg shadow-xl bg-neutral-50 dark:bg-neutral-800 border border-neutral-300 dark:border-neutral-700"
style="min-width: 20rem; z-index:9999;">
<div class="flex items-center justify-between mb-6">
<h3
id="{{ $prefix }}a11y-panel-title"
class="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
{{ i18n "a11y.title" }}
</h3>
<button
id="{{ $prefix }}a11y-close"
class="text-neutral-500 hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200"
aria-label="Close a11y panel">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="space-y-5">
{{ $toggles := slice }}
{{ $shouldDisableBlur := or site.Params.homepage.layoutBackgroundBlur site.Params.article.layoutBackgroundBlur site.Params.list.layoutBackgroundBlur }}
{{ $shouldDisableBackgroundImage := or
(eq site.Params.homepage.layout "background")
(eq site.Params.article.heroStyle "background")
(eq site.Params.list.heroStyle "background")
(eq site.Params.taxonomy.heroStyle "background")
(eq site.Params.term.heroStyle "background")
}}
{{ if $shouldDisableBlur }}
{{ $toggles = $toggles | append (dict "id" (print $prefix "disable-blur") "label" (i18n "a11y.disable_blur")) }}
{{ end }}
{{ if $shouldDisableBackgroundImage }}
{{ $toggles = $toggles | append (dict "id" (print $prefix "disable-images") "label" (i18n "a11y.disable_images")) }}
{{ end }}
{{- $toggles = $toggles | append
(dict "id" (print $prefix "underline-links") "label" (i18n "a11y.show_link_underline"))
(dict "id" (print $prefix "zen-mode") "label" (i18n "article.zen_mode_title.enable"))
-}}
{{- range $toggles }}
<div class="flex items-center justify-between">
<label for="{{ .id }}" class="text-sm font-medium text-neutral-700 dark:text-neutral-300">
{{ .label }}
</label>
<div class="ios-toggle">
<input type="checkbox" id="{{ .id }}">
</div>
</div>
{{- end }}
<div class="flex items-center justify-between">
<label
for="{{ $prefix }}font-size-select"
class="text-sm font-medium text-neutral-700 dark:text-neutral-300">
{{ i18n "a11y.font_size" }}
</label>
<select
id="{{ $prefix }}font-size-select"
class="border rounded-lg px-3 py-1.5 pr-8 text-neutral-900 text-sm dark:bg-neutral-700 dark:text-neutral-200 focus:ring-primary-500 focus:border-primary-500">
{{ $fontSizes := slice "default" "12px" "14px" "16px" "18px" "20px" "22px" "24px" }}
{{ range $fontSizes }}
<option value="{{ . }}">{{ . }}</option>
{{ end }}
</select>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,116 @@
<nav class="flex items-center gap-x-5 h-12">
{{ if .Site.Menus.main }}
{{ range .Site.Menus.main }}
{{ template "DesktopMenu" . }}
{{ end }}
{{ end }}
{{ partial "header/components/translations.html" . }}
{{ if .Site.Params.enableA11y | default false }}
{{ partial "header/components/a11y.html" (dict "prefix" "desktop-") }}
{{ end }}
{{ if .Site.Params.enableSearch | default false }}
<button
id="search-button"
aria-label="Search"
class="text-base bf-icon-color-hover"
title="{{ i18n "search.open_button_title" }}">
{{ partial "icon.html" "search" }}
</button>
{{ end }}
{{ if .Site.Params.footer.showAppearanceSwitcher | default false }}
<div class="flex items-center">
<button
id="appearance-switcher"
aria-label="Dark mode switcher"
type="button"
class="text-base bf-icon-color-hover">
<div class="flex items-center justify-center dark:hidden">
{{ partial "icon.html" "moon" }}
</div>
<div class="items-center justify-center hidden dark:flex">
{{ partial "icon.html" "sun" }}
</div>
</button>
</div>
{{ end }}
</nav>
{{ define "DesktopMenu" }}
{{ if .HasChildren }}
<div class="nested-menu">
<div class="cursor-pointer flex items-center">
<a
{{ if .URL }}
href="{{ .URL }}"
{{ if or (strings.HasPrefix .URL "http:" ) (strings.HasPrefix .URL "https:" ) }}
target="_blank"
{{ end }}
{{ else }}
tabindex="0"
{{ end }}
{{ with or .Name .Pre }}aria-label="{{ . }}"{{ end }}
class="flex items-center text-base font-medium bf-icon-color-hover"
title="{{ .Title }}">
{{ if .Pre }}
<span {{ if and .Pre .Name }}class="mr-1"{{ end }}>
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
<span class="text-bg font-bg break-normal" title="{{ .Title }}">
{{ .Name | markdownify }}
</span>
<span>
{{ partial "icon.html" "chevron-down" }}
</span>
</a>
</div>
<div class="menuhide">
<div class="pt-2 p-5 mt-2 rounded-xl backdrop-blur shadow-2xl bg-neutral/25 dark:bg-neutral-800/25">
<div class="flex flex-col space-y-3">
{{ range .Children }}
<a
href="{{ .URL }}"
{{ if or (strings.HasPrefix .URL "http:" ) (strings.HasPrefix .URL "https:" ) }}
target="_blank"
{{ end }}
{{ with or .Name .Pre }}aria-label="{{ . }}"{{ end }}
class="flex items-center bf-icon-color-hover">
{{ if .Pre }}
<span {{ if and .Pre .Name }}class="mr-1"{{ end }}>
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
<span class="text-sm font-sm" title="{{ .Title }}">
{{ .Name | markdownify }}
</span>
</a>
{{ end }}
</div>
</div>
</div>
</div>
{{ else }}
<a
href="{{ .URL }}"
{{ if or (strings.HasPrefix .URL "http:" ) (strings.HasPrefix .URL "https:" ) }}
target="_blank"
{{ end }}
class="flex items-center bf-icon-color-hover"
{{ with or .Name .Pre }}aria-label="{{ . }}"{{ end }}
title="{{ .Title }}">
{{ if .Pre }}
<span {{ if and .Pre .Name }}class="mr-1"{{ end }}>
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
{{ if .Name }}
<span class="text-base font-medium break-normal">
{{ .Name | markdownify }}
</span>
{{ end }}
</a>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,152 @@
<div class="flex items-center h-14 gap-4">
{{ if .Site.Params.enableSearch | default false }}
<button
id="search-button-mobile"
aria-label="Search"
class="flex items-center justify-center bf-icon-color-hover"
title="{{ i18n "search.open_button_title" }}">
{{ partial "icon.html" "search" }}
</button>
{{ end }}
{{ if .Site.Params.footer.showAppearanceSwitcher | default false }}
<button
id="appearance-switcher-mobile"
type="button"
aria-label="Dark mode switcher"
class="flex items-center justify-center text-neutral-900 hover:text-primary-600 dark:text-neutral-200 dark:hover:text-primary-400">
<div class="dark:hidden">
{{ partial "icon.html" "moon" }}
</div>
<div class="hidden dark:block">
{{ partial "icon.html" "sun" }}
</div>
</button>
{{ end }}
{{ if or
.Site.Menus.main
.Site.Menus.subnavigation
.Site.Params.enableA11y
}}
<input type="checkbox" id="mobile-menu-toggle" autocomplete="off" class="hidden peer">
<label for="mobile-menu-toggle" class="flex items-center justify-center cursor-pointer bf-icon-color-hover">
{{ partial "icon.html" "bars" }}
</label>
<div
role="dialog"
aria-modal="true"
style="scrollbar-gutter: stable;"
class="fixed inset-0 z-50 invisible overflow-y-auto px-6 py-20 opacity-0 transition-[opacity,visibility] duration-300 peer-checked:visible peer-checked:opacity-100 bg-neutral-50/97 dark:bg-neutral-900/99
{{ if site.Params.enableStyledScrollbar | default true }}bf-scrollbar{{ end }}">
<label
for="mobile-menu-toggle"
class="fixed end-8 top-5 flex items-center justify-center z-50 h-12 w-12 cursor-pointer select-none rounded-full bf-icon-color-hover border bf-border-color bf-border-color-hover bg-neutral-50 dark:bg-neutral-900">
{{ partial "icon.html" "xmark" }}
</label>
<nav class="mx-auto max-w-md space-y-6">
{{ template "mobile-main-menu" . }}
{{ template "mobile-subnavigation" . }}
{{ template "mobile-footer-components" . }}
</nav>
</div>
{{ end }}
</div>
{{ define "mobile-main-menu" }}
{{ range .Site.Menus.main }}
{{ $submenuId := printf "fullscreen-submenu-%s" (.Identifier | default .Name | anchorize) }}
<div class="px-2">
<a
href="{{ .URL }}"
{{ with or .Name .Pre }}aria-label="{{ . }}"{{ end }}
{{ if or (strings.HasPrefix .URL "http:" ) (strings.HasPrefix .URL "https:" ) }}
target="_blank"
{{ end }}
class="flex items-center gap-4 group bf-icon-color-hover text-neutral-700 dark:text-neutral-200">
{{ if .Pre }}
<span class="flex items-center justify-center h-8 w-8 text-2xl">
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
<span title="{{ .Title }}" class="text-2xl font-bold tracking-tight">
{{ .Name | markdownify }}
</span>
{{ if .HasChildren }}
<label
for="{{ $submenuId }}"
class="ms-auto flex items-center justify-center h-10 w-10 cursor-pointer rounded-lg bf-icon-color-hover border bf-border-color bf-border-color-hover">
{{ partial "icon.html" "chevron-down" }}
</label>
{{ end }}
</a>
{{ if .HasChildren }}
<input checked type="checkbox" id="{{ $submenuId }}" autocomplete="off" class="hidden peer/full">
<div
class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-300 peer-checked/full:grid-rows-[1fr]">
<div class="overflow-hidden">
<div class="ms-7 mt-4">
{{ range .Children }}
<a
href="{{ .URL }}"
{{ with or .Name .Pre }}aria-label="{{ . }}"{{ end }}
{{ if or (strings.HasPrefix .URL "http:" ) (strings.HasPrefix .URL "https:" ) }}
target="_blank"
{{ end }}
class="flex items-center gap-3 p-2 group bf-icon-color-hover text-neutral-700 dark:text-neutral-200">
{{ if .Pre }}
<span class="flex items-center justify-center h-5 w-5">
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
<span title="{{ .Title }}" class="text-lg">
{{ .Name | markdownify }}
</span>
</a>
{{ end }}
</div>
</div>
</div>
{{ end }}
</div>
{{ end }}
{{ end }}
{{ define "mobile-subnavigation" }}
{{ if .Site.Menus.subnavigation }}
<div class="flex flex-wrap gap-4 mt-8 pt-8 border-t bf-border-color">
{{ range .Site.Menus.subnavigation }}
<a
href="{{ .URL }}"
{{ with or .Name .Pre }}aria-label="{{ . }}"{{ end }}
class="inline-flex items-center gap-2 px-2 py-2 bf-icon-color-hover rounded-full text-sm">
{{ if .Pre }}
<span class="flex items-center justify-center h-4 w-4">
{{ partial "icon.html" .Pre }}
</span>
{{ end }}
<span title="{{ .Title }}">{{ .Name | markdownify }}</span>
</a>
{{ end }}
</div>
{{ end }}
{{ end }}
{{ define "mobile-footer-components" }}
{{ if or
hugo.IsMultilingual
.Site.Params.enableA11y
}}
<div
class="flex flex-wrap items-center [&_span]:text-2xl [&_.translation_button_.icon]:text-4xl! [&_.translation_button_span]:text-base! [&_.translation_.menuhide_span]:text-sm! gap-x-6 ps-2 mt-8 pt-8 border-t bf-border-color">
{{ partial "header/components/translations.html" . }}
{{ if .Site.Params.enableA11y | default false }}
{{ partial "header/components/a11y.html" (dict "prefix" "mobile-menu-") }}
{{ end }}
</div>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,23 @@
{{ if .IsTranslated }}
<div class="translation nested-menu">
<button class="cursor-pointer flex items-center">
<span class="me-1">
{{ partial "icon.html" "language" }}
</span>
<span class="text-sm font-medium bf-icon-color-hover" title="{{ .Title }}">
{{- i18n "global.language" | markdownify -}}
</span>
</button>
<ul class="menuhide">
<li class="rounded-xl backdrop-blur shadow-2xl p-2 flex flex-col gap-1">
{{ range .AllTranslations }}
<a href="{{ .RelPermalink }}" class="flex items-center bf-icon-color-hover px-3 py-1">
<span class="text-sm font-sm" title="{{ .Title }}">
{{ .Language.Params.displayName | emojify }}
</span>
</a>
{{ end }}
</li>
</ul>
</div>
{{ end }}

View File

@@ -0,0 +1,16 @@
<div class="min-h-[148px]"></div>
<div class="fixed inset-x-0 z-100">
<div
id="menu-blur"
class="absolute opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-2xl backdrop-saturate-220 backdrop-brightness-112 dark:backdrop-saturate-180 dark:backdrop-brightness-95 bg-primary-200/80 bg-linear-60 dark:bg-primary-800/30 shadow-xl"></div>
<div class="relative m-auto leading-7 max-w-7xl px-6 sm:px-14 md:px-24 lg:px-32">
{{ partial "header/basic.html" . }}
</div>
</div>
{{ $backgroundBlur := resources.Get "js/background-blur.js" }}
{{ $backgroundBlur = $backgroundBlur | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $backgroundBlur.RelPermalink }}"
integrity="{{ $backgroundBlur.Data.Integrity }}"
data-blur-id="menu-blur"></script>

View File

@@ -0,0 +1,6 @@
<div class="min-h-[148px]"></div>
<div class="fixed inset-x-0 bg-neutral dark:bg-neutral-800 z-100">
<div class="relative m-auto leading-7 max-w-7xl px-6 sm:px-14 md:px-24 lg:px-32">
{{ partial "header/basic.html" . }}
</div>
</div>

View File

@@ -0,0 +1,18 @@
<div class="min-h-[148px]"></div>
<div
class="fixed inset-x-0 min-h-[130px] opacity-65 bg-gradient-to-b from-neutral from-60% dark:from-neutral-800 to-transparent mix-blend-normal z-80"></div>
<div class="fixed inset-x-0 z-100">
<div
id="menu-blur"
class="absolute opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-2xl shadow-2xl"></div>
<div class="relative m-auto leading-7 max-w-7xl px-6 sm:px-14 md:px-24 lg:px-32">
{{ partial "header/basic.html" . }}
</div>
</div>
{{ $backgroundBlur := resources.Get "js/background-blur.js" }}
{{ $backgroundBlur = $backgroundBlur | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $backgroundBlur.RelPermalink }}"
integrity="{{ $backgroundBlur.Data.Integrity }}"
data-blur-id="menu-blur"></script>

View File

@@ -0,0 +1,16 @@
<div class="min-h-[148px]"></div>
<div class="fixed inset-x-0 z-100">
<div
id="menu-blur"
class="absolute opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-2xl shadow-2xl bg-neutral/25 dark:bg-neutral-800/25"></div>
<div class="relative m-auto leading-7 max-w-7xl px-6 sm:px-14 md:px-24 lg:px-32">
{{ partial "header/basic.html" . }}
</div>
</div>
{{ $backgroundBlur := resources.Get "js/background-blur.js" }}
{{ $backgroundBlur = $backgroundBlur | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $backgroundBlur.RelPermalink }}"
integrity="{{ $backgroundBlur.Data.Integrity }}"
data-blur-id="menu-blur"></script>

View File

@@ -0,0 +1,98 @@
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{ $useDefault := false }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $images := .Resources.ByType "image" }}
{{ range slice "*background*" "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultBackgroundImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ $useDefault = true }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ $useDefault = true }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $size := site.Store.Get "backgroundImageWidth" }}
{{ $featuredURL = (.Resize $size).RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ $isParentList := eq (.Scratch.Get "scope") "list" }}
{{ $shouldBlur := $.Params.layoutBackgroundBlur | default (or
(and ($.Site.Params.article.layoutBackgroundBlur | default true) (not $isParentList))
(and ($.Site.Params.list.layoutBackgroundBlur | default true) ($isParentList))
)
}}
{{ $shouldAddHeaderSpace := $.Params.layoutBackgroundHeaderSpace | default (or
(and ($.Site.Params.article.layoutBackgroundHeaderSpace | default true) (not $isParentList))
(and ($.Site.Params.list.layoutBackgroundHeaderSpace | default true) ($isParentList))
)
}}
{{- with $featuredURL -}}
{{ if $shouldAddHeaderSpace | default true }}
<div id="hero" class="h-[150px] md:h-[200px]"></div>
{{ end }}
<div class="fixed inset-x-0 top-0 h-[800px] single_hero_background nozoom">
{{ $style := "" }}
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
{{ with $.Params.imagePosition | default $defaultPosition }}
{{ $style = printf "object-position: %s;" . }}
{{ end }}
<img
id="background-image"
src="{{ . }}"
role="presentation"
loading="eager"
decoding="async"
fetchpriority="high"
class="absolute inset-0 w-full h-full object-cover"
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
<div
class="absolute inset-0 bg-gradient-to-t from-neutral dark:from-neutral-800 to-transparent mix-blend-normal"></div>
<div
class="absolute inset-0 opacity-60 bg-gradient-to-t from-neutral dark:from-neutral-800 to-neutral-100 dark:to-neutral-800 mix-blend-normal"></div>
</div>
{{ if $shouldBlur | default false }}
<div
id="background-blur"
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
{{ $backgroundBlur := resources.Get "js/background-blur.js" }}
{{ $backgroundBlur = $backgroundBlur | resources.Minify | resources.Fingerprint ($.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $backgroundBlur.RelPermalink }}"
integrity="{{ $backgroundBlur.Data.Integrity }}"
data-blur-id="background-blur"
data-image-id="background-image"
data-image-url="{{ . }}"></script>
{{ end }}
{{- end -}}

View File

@@ -0,0 +1,63 @@
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{ $useDefault := false }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $images := .Resources.ByType "image" }}
{{ range slice "*background*" "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultBackgroundImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ $useDefault = true }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ $useDefault = true }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $size := site.Store.Get "backgroundImageWidth" }}
{{ $featuredURL = (.Resize $size).RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ with $featuredURL }}
{{ $style := "" }}
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
{{ with $.Params.imagePosition | default $defaultPosition }}
{{ $style = printf "object-position: %s;" . }}
{{ end }}
<div class="overflow-hidden h-36 md:h-56 lg:h-72">
<img
src="{{ . }}"
role="presentation"
loading="eager"
decoding="async"
fetchpriority="high"
class="w-full h-full nozoom object-cover"
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
</div>
{{ end }}

View File

@@ -0,0 +1,76 @@
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{ $useDefault := false }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $images := .Resources.ByType "image" }}
{{ range slice "*background*" "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultBackgroundImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ $useDefault = true }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ $useDefault = true }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $size := site.Store.Get "backgroundImageWidth" }}
{{ $featuredURL = (.Resize $size).RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ $caption := "" }}
{{ if .Params.featureimagecaption }}
{{- $caption = .Params.featureimagecaption -}}
{{ end }}
{{- $alt := .Page.Title -}}
{{- with .Page.Params.alt }}{{ $alt = . }}{{ end -}}
{{ with $featuredURL }}
{{ $style := "" }}
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
{{ with $.Params.imagePosition | default $defaultPosition }}
{{ $style = printf "object-position: %s;" . }}
{{ end }}
<figure>
<img
src="{{ . }}"
alt="{{ $alt }}"
loading="eager"
decoding="async"
fetchpriority="high"
class="w-full rounded-lg single_hero_round nozoom"
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
{{ if $caption }}
<figcaption class="text-sm text-neutral-700 dark:text-neutral-400 hover:underline text-center">
{{ $caption | markdownify }}
</figcaption>
{{ end }}
</figure>
{{ end }}

View File

@@ -0,0 +1,136 @@
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
{{/* === Background === */}}
{{ $useDefault := false }}
{{ $images := .Resources.ByType "image" }}
{{ $background := $images.GetMatch "*background*" }}
{{ $backgroundURL := "" }}
{{ if not (or $background $backgroundURL) }}
{{ $default := site.Store.Get "defaultBackgroundImage" }}
{{ if $default.url }}
{{ $backgroundURL = $default.url }}
{{ $useDefault = true }}
{{ else if $default.obj }}
{{ $background = $default.obj }}
{{ $useDefault = true }}
{{ end }}
{{ end }}
{{ if not (or $background $backgroundURL) }}
{{ range slice "*cover*" "*thumbnail*" "*feature*" }}
{{ if not $background }}{{ $background = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $backgroundURL }}
{{ with $background }}
{{ $backgroundURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $size := site.Store.Get "backgroundImageWidth" }}
{{ $backgroundURL = (.Resize $size).RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{/* === Featured === */}}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ with .Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $featuredURL = . }}
{{ else }}
{{ $featured = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $images := $.Resources.ByType "image" }}
{{ range slice "*feature*" "*cover*" "*thumbnail*" "*background*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{ if not (or $featured $featuredURL) }}
{{ $default := site.Store.Get "defaultFeaturedImage" }}
{{ if $default.url }}
{{ $featuredURL = $default.url }}
{{ else if $default.obj }}
{{ $featured = $default.obj }}
{{ end }}
{{ end }}
{{/* generate image URL if not hotlink */}}
{{ if not $featuredURL }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $size := site.Store.Get "backgroundImageWidth" }}
{{ $featuredURL = (.Resize $size).RelPermalink }}
{{ end }}
{{ end }}
{{ end }}
{{ $isParentList := eq (.Scratch.Get "scope") "list" }}
{{ $shouldBlur := $.Params.layoutBackgroundBlur | default (or
(and ($.Site.Params.article.layoutBackgroundBlur | default true) (not $isParentList))
(and ($.Site.Params.list.layoutBackgroundBlur | default true) ($isParentList))
)
}}
{{ with $featuredURL }}
<div class="overflow-hidden rounded-md h-36 md:h-56 lg:h-72 nozoom">
<img
src="{{ . }}"
role="presentation"
loading="eager"
decoding="async"
fetchpriority="high"
class="w-full h-full nozoom object-cover"
{{ with $.Params.imagePositionFeature }}style="object-position: {{ . }};"{{ end }}>
</div>
{{ end }}
<div class="single_hero_background nozoom fixed inset-x-0 top-0 h-[800px]">
{{ with $backgroundURL }}
{{ $style := "" }}
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
{{ with $.Params.imagePosition | default $defaultPosition }}
{{ $style = printf "object-position: %s;" . }}
{{ end }}
<img
id="background-image-main"
src="{{ . }}"
role="presentation"
loading="eager"
decoding="async"
fetchpriority="high"
class="absolute inset-0 h-full w-full object-cover"
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
{{ end }}
<div
class="from-neutral absolute inset-0 bg-gradient-to-t to-transparent mix-blend-normal dark:from-neutral-800"></div>
<div
class="from-neutral to-neutral absolute inset-0 bg-gradient-to-t opacity-30 mix-blend-normal dark:from-neutral-800 dark:to-neutral-800 dark:opacity-60"></div>
</div>
{{ if $shouldBlur | default false }}
<div
id="background-blur"
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
{{ $backgroundBlur := resources.Get "js/background-blur.js" }}
{{ $backgroundBlur = $backgroundBlur | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $backgroundBlur.RelPermalink }}"
integrity="{{ $backgroundBlur.Data.Integrity }}"
data-blur-id="background-blur"
data-image-id="background-image-main"
{{ with $background }}data-image-url="{{ .RelPermalink }}"{{ end }}></script>
{{ end }}

View File

@@ -0,0 +1,118 @@
{{ $disableImageOptimization := .Site.Params.disableImageOptimization | default false }}
<article class="prose dark:prose-invert max-w-full">
<div class="relative">
<div class="absolute inset-x-0 bottom-0 h-1/2"></div>
<div class="mx-auto max-w-7xl p-0">
<div class="relative sm:overflow-hidden">
<div class="fixed inset-x-0 top-0 -z-10">
{{ $useDefault := true }}
{{ $homepageImage := "" }}
{{ with .Site.Params.defaultBackgroundImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $homepageImage = resources.GetRemote . }}
{{ else }}
{{ $homepageImage = resources.Get . }}
{{ end }}
{{ end }}
{{ with .Site.Params.homepage.homepageImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $homepageImage = resources.GetRemote . }}
{{ $useDefault = false }}
{{ else }}
{{ $homepageImage = resources.Get . }}
{{ $useDefault = false }}
{{ end }}
{{ end }}
{{ if $homepageImage }}
{{ $style := "" }}
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
{{ with $.Params.imagePosition | default $defaultPosition }}
{{ $style = printf "object-position: %s;" . }}
{{ end }}
<img
id="background-image"
class="nozoom mt-0 mr-0 mb-0 ml-0 h-[1000px] w-full object-cover"
src="{{ $homepageImage.RelPermalink }}"
role="presentation"
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
<div
class="from-neutral absolute inset-0 h-[1000px] bg-gradient-to-t to-transparent mix-blend-normal dark:from-neutral-800"></div>
<div
class="from-neutral absolute inset-0 h-[1000px] bg-gradient-to-t to-neutral-100 opacity-60 mix-blend-normal dark:from-neutral-800 dark:to-neutral-800"></div>
{{ end }}
</div>
<div class="relative flex flex-col items-center justify-center px-1 py-1 text-center">
{{ with .Site.Params.Author.image }}
{{ $authorImage := "" }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $authorImage = resources.GetRemote . }}
{{ else }}
{{ $authorImage = resources.Get . }}
{{ end }}
{{ if $authorImage }}
{{ $final := $authorImage }}
{{ $squareImage := $authorImage }}
{{ if not (or $disableImageOptimization (eq $authorImage.MediaType.SubType "svg")) }}
{{ $final = $authorImage.Fill (print "288x288 q" ( $.Site.Params.Author.imagequality | default "96" )) }}
{{ $shortSide := int (math.Min $authorImage.Width $authorImage.Height) }}
{{ $squareImage = $authorImage.Crop (printf "%dx%d" $shortSide $shortSide ) }}
{{ end }}
<img
class="mb-2 h-36 w-36 rounded-full"
width="144"
height="144"
alt="{{ $.Site.Params.Author.name | default `Author` }}"
src="{{ $final.RelPermalink }}"
data-zoom-src="{{ $squareImage.RelPermalink }}">
{{ end }}
{{ end }}
<h1 class="mb-2 text-4xl font-extrabold text-neutral-800 dark:text-neutral-200">
{{ .Site.Params.Author.name | default .Site.Title }}
</h1>
{{ with .Site.Params.Author.headline }}
<h2 class="mt-0 mb-0 text-xl text-neutral-800 dark:text-neutral-300">
{{ . | markdownify }}
</h2>
{{ end }}
<div class="mt-3 mb-10 text-2xl">
{{ with .Site.Params.Author.links }}
<div class="flex flex-wrap">
{{ range $links := . }}
{{ range $name, $url := $links }}
<a
class="hover:text-primary-400 text-primary-800 dark:text-primary-200 px-1"
href="{{ $url }}"
target="_blank"
aria-label="{{ $name | title }}"
title="{{ $name | title }}"
rel="me noopener noreferrer">
{{ partial "icon.html" $name }}
</a>
{{ end }}
{{ end }}
</div>
{{ end }}
</div>
<section class="prose dark:prose-invert w-full">{{ .Content }}</section>
</div>
</div>
</div>
</div>
</article>
<section>
{{ partial "recent-articles/main.html" . }}
</section>
{{ if .Site.Params.homepage.layoutBackgroundBlur | default false }}
<div
id="background-blur"
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
{{ $backgroundBlur := resources.Get "js/background-blur.js" }}
{{ $backgroundBlur = $backgroundBlur | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $backgroundBlur.RelPermalink }}"
integrity="{{ $backgroundBlur.Data.Integrity }}"
data-blur-id="background-blur"
data-image-id="background-image"
{{ if $homepageImage }}data-image-url="{{ $homepageImage.RelPermalink }}"{{ end }}></script>
{{ end }}

View File

@@ -0,0 +1,53 @@
<div class="relative pt-16 pb-32">
<div aria-hidden="true" class="absolute inset-x-0 top-0 h-48 bg-gradient-to-b"></div>
<div class="relative">
<div class="lg:mx-auto lg:grid lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-2 lg:gap-24 lg:px-8">
<div class="mx-auto max-w-xl px-4 sm:px-6 lg:mx-0 lg:max-w-none lg:py-16 lg:px-0">
<article class="max-w-full prose dark:prose-invert">
{{ with .Title }}
<header>
<h1>{{ . | emojify }}</h1>
</header>
{{ end }}
<section class="w-full">{{ .Content }}</section>
</article>
</div>
<div class="mt-6 sm:mt-16 lg:mt-0 mx-auto max-w-xl px-4 sm:px-6 lg:mx-0 lg:max-w-none lg:py-16 lg:px-0">
<div class="-me-48 md:-me-16 lg:relative lg:m-0 lg:h-full lg:px-0 w-full">
{{ $useDefault := true }}
{{ $homepageImage := "" }}
{{ with .Site.Params.defaultBackgroundImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $homepageImage = resources.GetRemote . }}
{{ else }}
{{ $homepageImage = resources.Get . }}
{{ end }}
{{ end }}
{{ with .Site.Params.homepage.homepageImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $homepageImage = resources.GetRemote . }}
{{ $useDefault = false }}
{{ else }}
{{ $homepageImage = resources.Get . }}
{{ $useDefault = false }}
{{ end }}
{{ end }}
{{ if $homepageImage }}
{{ $style := "" }}
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
{{ with $.Params.imagePosition | default $defaultPosition }}
{{ $style = printf "object-position: %s;" . }}
{{ end }}
<img
class="w-full rounded-xl shadow-xl lg:absolute lg:h-full lg:w-auto lg:max-w-none"
src="{{ $homepageImage.RelPermalink }}"
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
{{ end }}
</div>
</div>
</div>
</div>
</div>
<section>
{{ partial "recent-articles/main.html" . }}
</section>

View File

@@ -0,0 +1,134 @@
{{ $disableImageOptimization := .Site.Params.disableImageOptimization | default false }}
{{ $disableHeroImageFilter := .Site.Params.homepage.disableHeroImageFilter | default false }}
<article class="max-w-full prose dark:prose-invert">
<div class="relative">
<div class="absolute inset-x-0 bottom-0 h-1/2"></div>
<div class="mx-auto max-w-7xl p-0">
<div class="relative shadow-xl sm:overflow-hidden rounded-2xl">
<div class="absolute inset-0">
{{ $useDefault := true }}
{{ $homepageImage := "" }}
{{ with .Site.Params.defaultBackgroundImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $homepageImage = resources.GetRemote . }}
{{ else }}
{{ $homepageImage = resources.Get . }}
{{ end }}
{{ end }}
{{ with .Site.Params.homepage.homepageImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $homepageImage = resources.GetRemote . }}
{{ $useDefault = false }}
{{ else }}
{{ $homepageImage = resources.Get . }}
{{ $useDefault = false }}
{{ end }}
{{ end }}
{{ if $homepageImage }}
{{ $style := "" }}
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
{{ with $.Params.imagePosition | default $defaultPosition }}
{{ $style = printf "object-position: %s;" . }}
{{ end }}
<img
class="h-full w-full object-cover nozoom mt-0 mr-0 mb-0 ml-0"
src="{{ $homepageImage.RelPermalink }}"
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
{{ if not $disableHeroImageFilter }}
<div
class="absolute inset-0 bg-gradient-to-r from-primary-500 to-secondary-600 dark:from-primary-600 dark:to-secondary-800 mix-blend-multiply"></div>
{{ else }}
<div
class="absolute inset-0 from-primary-500 to-secondary-600 dark:from-primary-600 dark:to-secondary-800 mix-blend-multiply"></div>
{{ end }}
{{ end }}
</div>
<div
class="relative px-4 py-16 sm:px-6 sm:py-24 lg:py-32 lg:px-8 flex flex-col items-center justify-center text-center">
{{ with .Site.Params.Author.image }}
{{ $authorImage := "" }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $authorImage = resources.GetRemote . }}
{{ else }}
{{ $authorImage = resources.Get . }}
{{ end }}
{{ if $authorImage }}
{{ $final := $authorImage }}
{{ $squareImage := $authorImage }}
{{ if not (or $disableImageOptimization (eq $authorImage.MediaType.SubType "svg")) }}
{{ $final = $authorImage.Fill (print "288x288 q" ( $.Site.Params.Author.imagequality | default "96" )) }}
{{ $shortSide := int (math.Min $authorImage.Width $authorImage.Height) }}
{{ $squareImage = $authorImage.Crop (printf "%dx%d" $shortSide $shortSide ) }}
{{ end }}
<img
class="mb-2 h-36 w-36 rounded-full"
width="144"
height="144"
alt="{{ $.Site.Params.Author.name | default `Author` }}"
src="{{ $final.RelPermalink }}"
data-zoom-src="{{ $squareImage.RelPermalink }}">
{{ end }}
{{ end }}
{{ if not $disableHeroImageFilter }}
<h1 class="mb-2 text-4xl font-extrabold text-neutral-200">
{{ .Site.Params.Author.name | default .Site.Title }}
</h1>
{{ with .Site.Params.Author.headline }}
<h2 class="mt-0 mb-0 text-xl text-neutral-300">
{{ . | markdownify }}
</h2>
{{ end }}
{{ else }}
<h1 class="mb-2 text-4xl font-extrabold text-neutral-800 dark:text-neutral-200">
{{ .Site.Params.Author.name | default .Site.Title }}
</h1>
{{ with .Site.Params.Author.headline }}
<h2 class="mt-0 mb-0 text-xl text-neutral-800 dark:text-neutral-300">
{{ . | markdownify }}
</h2>
{{ end }}
{{ end }}
<div class="mt-3 mb-10 text-2xl">
{{ with .Site.Params.Author.links }}
<div class="flex flex-wrap">
{{ range $links := . }}
{{ range $name, $url := $links }}
{{ if not $disableHeroImageFilter }}
<a
class="px-1 hover:text-primary-400 text-primary-300"
href="{{ $url }}"
target="_blank"
aria-label="{{ $name | title }}"
title="{{ $name | title }}"
rel="me noopener noreferrer">
{{ partial "icon.html" $name }}
</a>
{{ else }}
<a
class="px-1 hover:text-primary-400 text-primary-800 dark:text-primary-200"
href="{{ $url }}"
target="_blank"
aria-label="{{ $name | title }}"
title="{{ $name | title }}"
rel="me noopener noreferrer">
{{ partial "icon.html" $name }}
</a>
{{ end }}
{{ end }}
{{ end }}
</div>
{{ end }}
</div>
{{ if not $disableHeroImageFilter }}
<section class="prose prose-invert w-full">{{ .Content }}</section>
{{ else }}
<section class="prose dark:prose-invert w-full">{{ .Content }}</section>
{{ end }}
</div>
</div>
</div>
</div>
</article>
<section>
{{ partial "recent-articles/main.html" . }}
</section>

View File

@@ -0,0 +1,11 @@
<article class="max-w-full prose dark:prose-invert">
{{ with .Title }}
<header>
<h1>{{ . | emojify }}</h1>
</header>
{{ end }}
<section class="w-full">{{ .Content }}</section>
</article>
<section>
{{ partial "recent-articles/main.html" . }}
</section>

View File

@@ -0,0 +1,47 @@
{{ $disableImageOptimization := .Site.Params.disableImageOptimization | default false }}
<article
class="{{ if not .Site.Params.homepage.showRecent }}
h-full
{{ end }} flex flex-col items-center justify-center text-center">
<header class="relative px-1 py-1 flex flex-col items-center mb-3">
{{ with .Site.Params.Author.image }}
{{ $authorImage := "" }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $authorImage = resources.GetRemote . }}
{{ else }}
{{ $authorImage = resources.Get . }}
{{ end }}
{{ if $authorImage }}
{{ $final := $authorImage }}
{{ $squareImage := $authorImage }}
{{ if not (or $disableImageOptimization (eq $authorImage.MediaType.SubType "svg")) }}
{{ $final = $authorImage.Fill (print "288x288 q" ( $.Site.Params.Author.imagequality | default "96" )) }}
{{ $shortSide := int (math.Min $authorImage.Width $authorImage.Height) }}
{{ $squareImage = $authorImage.Crop (printf "%dx%d" $shortSide $shortSide ) }}
{{ end }}
<img
class="mb-2 h-36 w-36 rounded-full"
width="144"
height="144"
alt="{{ $.Site.Params.Author.name | default `Author` }}"
src="{{ $final.RelPermalink }}"
data-zoom-src="{{ $squareImage.RelPermalink }}">
{{ end }}
{{ end }}
<h1 class="text-4xl font-extrabold">
{{ .Site.Params.Author.name | default .Site.Title }}
</h1>
{{ with .Site.Params.Author.headline }}
<h2 class="text-xl text-neutral-500 dark:text-neutral-400">
{{ . | markdownify }}
</h2>
{{ end }}
<div class="mt-1 text-2xl">
{{ partialCached "author-links.html" . }}
</div>
</header>
<section class="prose dark:prose-invert w-full">{{ .Content }}</section>
</article>
<section>
{{ partial "recent-articles/main.html" . }}
</section>

View File

@@ -0,0 +1,43 @@
{{/*
Copied from Hugo v0.146
Source: https://github.com/gohugoio/hugo/blob/83cfdd78ca6469e6d7265323d9fad1448880e559/tpl/tplimpl/embedded/templates/_shortcodes/figure.html
*/}}
<figure{{ with .Get "class" }} class="{{ . }}"{{ end }}>
{{- if .Get "link" -}}
<a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }} class="inline-block">
{{- end -}}
{{- $u := urls.Parse (.Get "src") -}}
{{- $src := $u.String -}}
{{- if not $u.IsAbs -}}
{{- with or (.Page.Resources.Get $u.Path) (resources.Get $u.Path) -}}
{{- $src = .RelPermalink -}}
{{- end -}}
{{- end -}}
<img src="{{ $src }}"
{{- if or (.Get "alt") (.Get "caption") }}
alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" | markdownify| plainify }}{{ end }}"
{{- end -}}
{{- with .Get "width" }} width="{{ . }}"{{ end -}}
{{- with .Get "height" }} height="{{ . }}"{{ end -}}
{{- with .Get "loading" }} loading="{{ . }}"{{ end -}}
><!-- Closing img tag -->
{{- if .Get "link" }}</a>{{ end -}}
{{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}}
<figcaption>
{{ with (.Get "title") -}}
<h4>{{ . }}</h4>
{{- end -}}
{{- if or (.Get "caption") (.Get "attr") -}}<p>
{{- .Get "caption" | markdownify -}}
{{- with .Get "attrlink" }}
<a href="{{ . }}">
{{- end -}}
{{- .Get "attr" | markdownify -}}
{{- if .Get "attrlink" }}</a>{{ end }}</p>
{{- end }}
</figcaption>
{{- end }}
</figure>

View File

@@ -0,0 +1,6 @@
{{- $icon := resources.Get (print "icons/" . ".svg") -}}
{{- if $icon -}}
<span class="relative block icon">
{{- $icon.Content | safeHTML -}}
</span>
{{- end -}}

View File

@@ -0,0 +1,34 @@
{{- /* Override this file in your site to customize admonition type aliases and icon mappings */ -}}
{{- return dict
"typeMap" (dict
"attention" "warning"
"check" "success"
"cite" "quote"
"done" "success"
"error" "danger"
"fail" "failure"
"faq" "question"
"hint" "tip"
"help" "question"
"missing" "failure"
"summary" "abstract"
"tldr" "abstract"
)
"iconMap" (dict
"abstract" "file-lines"
"bug" "bug"
"caution" "fire"
"danger" "fire"
"example" "list-ol"
"failure" "xmark"
"important" "star"
"info" "circle-info"
"note" "circle-info"
"success" "check"
"todo" "list-check"
"tip" "lightbulb"
"question" "circle-question"
"quote" "quote-left"
"warning" "triangle-exclamation"
)
-}}

View File

@@ -0,0 +1,37 @@
{{/* disableImageOptimization */}}
{{ site.Store.Set "disableImageOptimization" (site.Params.disableImageOptimization | default false) }}
{{/* defaultFeaturedImage */}}
{{ $defaultFeaturedImage := "" }}
{{ $defaultFeaturedImageURL := "" }}
{{ with site.Params.defaultFeaturedImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $defaultFeaturedImageURL = . }}
{{ else }}
{{ $defaultFeaturedImage = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $defaultFeaturedImage = resources.Get . }}
{{ end }}
{{ end }}
{{ site.Store.Set "defaultFeaturedImage" (dict "url" $defaultFeaturedImageURL "obj" $defaultFeaturedImage) }}
{{/* defaultBackgroundImage */}}
{{ $defaultBackgroundImage := "" }}
{{ $defaultBackgroundImageURL := "" }}
{{ with site.Params.defaultBackgroundImage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ if site.Params.hotlinkFeatureImage }}
{{ $defaultBackgroundImageURL = . }}
{{ else }}
{{ $defaultBackgroundImage = resources.GetRemote . }}
{{ end }}
{{ else }}
{{ $defaultBackgroundImage = resources.Get . }}
{{ end }}
{{ end }}
{{ site.Store.Set "defaultBackgroundImage" (dict "url" $defaultBackgroundImageURL "obj" $defaultBackgroundImage) }}
{{/* backgroundImageWidth */}}
{{ site.Store.Set "backgroundImageWidth" (print (site.Params.backgroundImageWidth | default "1200") "x") }}

View File

@@ -0,0 +1,4 @@
<time datetime="{{ . | time.Format "2006-01-02T15:04:05-07:00" }}">
{{- i18n "article.date_updated" (dict "Date" (partial "functions/date.html" .)) -}}
</time>
{{- /* Trim EOF */ -}}

View File

@@ -0,0 +1,4 @@
<time datetime="{{ . | time.Format "2006-01-02T15:04:05-07:00" }}">
{{- i18n "article.date" (dict "Date" (partial "functions/date.html" .)) -}}
</time>
{{- /* Trim EOF */ -}}

View File

@@ -0,0 +1,19 @@
{{ $url := .Params.editURL | default (.Site.Params.article.editURL | default "#") }}
{{ $slash := "" }}
{{ if .Params.editAppendPath | default ( .Site.Params.article.editAppendPath | default false ) }}
{{ if ne (substr $url -1 1) "/" }}
{{ $slash = "/" }}
{{ end }}
{{ $url = printf "%s%s%s" $url $slash (path.Join .File.Path) }}
{{ end }}
<span class="mb-[2px]">
<a
href="{{ $url }}"
class="text-lg hover:text-primary-500"
rel="noopener noreferrer"
target="_blank"
title="{{ i18n "article.edit_title" }}"
><span class="inline-block align-text-bottom">{{ partial "icon.html" "edit" }}</span></a
>
</span>
{{- /* Trim EOF */ -}}

View File

@@ -0,0 +1,25 @@
<span>
{{ $id := "null" }}
{{ if eq .Kind "taxonomy" }}
{{ $id = delimit (slice "likes_taxonomy_" .Page.Data.Plural) "" }}
{{ else if eq .Kind "term" }}
{{ $id = delimit (slice "likes_term_" .Page.Data.Term) "" }}
{{ else }}
{{ $translations := .AllTranslations }}
{{ with .File }}
{{ $path := .Path }}
{{ range $translations }}
{{ $lang := print "." .Lang ".md" }}
{{ $path = replace $path $lang ".md" }}
{{ end }}
{{ $id = delimit (slice "likes_" $path) "" }}
{{ end }}
{{ end }}
<span
id="{{ $id }}"
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
title="likes"
>loading</span
>
<span class="inline-block align-text-bottom">{{ partial "icon.html" "heart" }}</span>
</span>

View File

@@ -0,0 +1,14 @@
<span>
<button
id="button_likes"
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
<span id="button_likes_heart" class="inline-block align-text-bottom hidden"
>{{ partial "icon.html" "heart" }}
</span>
<span id="button_likes_emtpty_heart" class="inline-block align-text-bottom"
>{{ partial "icon.html" "heart-empty" }}</span
>
<span id="button_likes_text">&nbsp;Like</span>
</button>
</span>
{{- /* Trim EOF */ -}}

View File

@@ -0,0 +1,4 @@
<span title="{{ i18n "article.reading_time_title" }}">
{{- i18n "article.reading_time" .ReadingTime | markdownify | emojify -}}
</span>
{{- /* Trim EOF */ -}}

View File

@@ -0,0 +1,25 @@
<span>
{{ $id := "null" }}
{{ if eq .Kind "taxonomy" }}
{{ $id = delimit (slice "views_taxonomy_" .Page.Data.Plural) "" }}
{{ else if eq .Kind "term" }}
{{ $id = delimit (slice "views_term_" .Page.Data.Term) "" }}
{{ else }}
{{ $translations := .AllTranslations }}
{{ with .File }}
{{ $path := .Path }}
{{ range $translations }}
{{ $lang := print "." .Lang ".md" }}
{{ $path = replace $path $lang ".md" }}
{{ end }}
{{ $id = delimit (slice "views_" $path) "" }}
{{ end }}
{{ end }}
<span
id="{{ $id }}"
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
title="views"
>loading</span
>
<span class="inline-block align-text-bottom">{{ partial "icon.html" "eye" }}</span>
</span>

View File

@@ -0,0 +1,4 @@
<span>
{{- i18n "article.word_count" .WordCount | markdownify -}}
</span>
{{- /* Trim EOF */ -}}

View File

@@ -0,0 +1,10 @@
<span class="mb-[2px]">
<span
id="zen-mode-button"
class="text-lg hover:text-primary-500"
title="{{ i18n "article.zen_mode_title.enable" }}"
data-title-i18n-disable="{{ i18n "article.zen_mode_title.enable" }}"
data-title-i18n-enable="{{ i18n "article.zen_mode_title.disable" }}">
<span class="inline-block align-text-bottom">{{ partial "icon.html" "expand" }}</span>
</span>
</span>

View File

@@ -0,0 +1,52 @@
{{- if gt .Paginator.TotalPages 1 -}}
<ul class="flex flex-row mt-8 justify-center">
{{- .Scratch.Set "paginator.ellipsed" false -}}
{{ if $.Paginator.HasPrev }}
<li>
<a
href="{{ $.Paginator.Prev.URL }}"
class="mx-1 block min-w-[1.8rem] rounded text-center hover:bg-primary-600 hover:text-neutral"
rel="prev"
>&larr;</a
>
</li>
{{ end }}
{{- range $.Paginator.Pagers -}}
{{- $right := sub .TotalPages .PageNumber -}}
{{- $showNumber := or (le .PageNumber 1) (eq $right 0) -}}
{{- $showNumber := or $showNumber (and (gt .PageNumber (sub $.Paginator.PageNumber 3)) (lt .PageNumber (add $.Paginator.PageNumber 3))) -}}
{{- if $showNumber -}}
{{- $.Scratch.Set "paginator.ellipsed" false -}}
{{- $.Scratch.Set "paginator.shouldEllipse" false -}}
{{- else -}}
{{- $.Scratch.Set "paginator.shouldEllipse" (not ($.Scratch.Get "paginator.ellipsed") ) -}}
{{- $.Scratch.Set "paginator.ellipsed" true -}}
{{- end -}}
{{- if $showNumber -}}
<li>
<a
href="{{ .URL }}"
class="{{ if eq . $.Paginator }}
bg-primary-200 dark:bg-primary-400 dark:text-neutral-800
{{ end }} mx-1 block min-w-[1.8rem] rounded text-center hover:bg-primary-600 hover:text-neutral"
>{{ .PageNumber }}</a
>
</li>
{{- else if ($.Scratch.Get "paginator.shouldEllipse") -}}
<li class="page-item ">
<span class="page-link" aria-hidden="true">&hellip;</span>
</li>
{{- end -}}
{{- end -}}
{{ if $.Paginator.HasNext }}
<li>
<a
href="{{ $.Paginator.Next.URL }}"
class="mx-1 block min-w-[1.8rem] rounded text-center hover:bg-primary-600 hover:text-neutral"
rel="next"
>&rarr;</a
>
</li>
{{ end }}
</ul>
{{- end -}}

View File

@@ -0,0 +1,47 @@
{{ $recentArticles := 5 }}
{{ $showMoreLinkDest := "/posts/" }}
{{ if index .Site.Params.homepage "showRecentItems" }}
{{ $recentArticles = .Site.Params.homepage.showRecentItems }}
{{ end }}
<h2 class="mt-8 text-2xl font-extrabold mb-10">{{ i18n "shortcode.recent_articles" | emojify }}</h2>
<div class="flex mb-6 px-4 py-2 mb-8 text-base rounded-md bg-primary-100 dark:bg-primary-900">
<span class="flex items-center justify-between grow dark:text-neutral-300">
<span class="prose dark:prose-invert"
>This is a demo of theme's list configurations: <code id="config">card view</code></span
>
<button
id="switch-config-button"
class="px-4 !text-neutral !no-underline rounded-md bg-primary-600 hover:!bg-primary-500 dark:bg-primary-800 dark:hover:!bg-primary-700">
Switch config &orarr;
</button>
</span>
</div>
<div id="CardViewProse" class="h-full">
{{ partial "recent-articles/cardview.html" . }}
</div>
<div id="CardViewScreenWidth" class="hidden h-full">
{{ partial "recent-articles/cardview-fullwidth.html" . }}
</div>
<div id="NormalView" class="hidden h-full">
{{ partial "recent-articles/list.html" . }}
</div>
{{ if .Site.Params.homepage.showMoreLink | default false }}
{{ if index .Site.Params.homepage "showRecentItems" }}
{{ $showMoreLinkDest = .Site.Params.homepage.showMoreLinkDest }}
{{ end }}
<div class="mt-10 flex justify-center">
<a href="{{ $showMoreLinkDest }}">
<button
class="bg-transparent hover:text-primary-500 prose dark:prose-invert font-semibold py-2 px-4 border border-primary-500 hover:border-transparent rounded">
{{ i18n "recent.show_more" | markdownify }}
</button>
</a>
</div>
{{ end }}

View File

@@ -0,0 +1,13 @@
{{ $recentArticles := 5 }}
{{ $recentArticles = .Site.Params.homepage.showRecentItems }}
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
{{ range first $recentArticles (.Paginate (where .Site.RegularPages "Type" "in"
.Site.Params.mainSections)).Pages
}}
{{ partial "article-link/card.html" . }}
{{ end }}
</section>
</div>

View File

@@ -0,0 +1,11 @@
{{ $recentArticles := 5 }}
{{ $recentArticles = .Site.Params.homepage.showRecentItems }}
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{{ range first $recentArticles (.Paginate (where .Site.RegularPages "Type" "in"
.Site.Params.mainSections)).Pages
}}
{{ partial "article-link/card.html" . }}
{{ end }}
</section>

View File

@@ -0,0 +1,9 @@
{{ $recentArticles := 5 }}
{{ $recentArticles = .Site.Params.homepage.showRecentItems }}
<section class="space-y-10 w-full">
{{ range first $recentArticles (.Paginate (where .Site.RegularPages "Type" "in" .Site.Params.mainSections)).Pages }}
{{ partial "article-link/simple.html" . }}
{{ end }}
</section>

View File

@@ -0,0 +1,30 @@
{{ $recentArticles := 5 }}
{{ $showMoreLinkDest := "/posts/" }}
{{ if .Site.Params.homepage.showRecent | default false }}
{{ if index .Site.Params.homepage "showRecentItems" }}
{{ $recentArticles = .Site.Params.homepage.showRecentItems }}
{{ end }}
<h2 class="mt-8 text-2xl font-extrabold mb-10">{{ i18n "shortcode.recent_articles" | emojify }}</h2>
{{ if and .Site.Params.homepage.cardView (not .Site.Params.homepage.cardViewScreenWidth) | default false }}
{{ partial "recent-articles/cardview.html" . }}
{{ else if and .Site.Params.homepage.cardView .Site.Params.homepage.cardViewScreenWidth | default false }}
{{ partial "recent-articles/cardview-fullwidth.html" . }}
{{ else }}
{{ partial "recent-articles/list.html" . }}
{{ end }}
{{ if .Site.Params.homepage.showMoreLink | default false }}
{{ if index .Site.Params.homepage "showRecentItems" }}
{{ $showMoreLinkDest = .Site.Params.homepage.showMoreLinkDest }}
{{ end }}
<div class="mt-10 flex justify-center">
<a href="{{ $showMoreLinkDest | relLangURL }}">
<button
class="bg-transparent hover:text-primary-500 prose dark:prose-invert font-semibold py-2 px-4 border border-primary-500 hover:border-transparent rounded">
{{ i18n "recent.show_more" | markdownify }}
</button>
</a>
</div>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,11 @@
{{ if .Params.showRelatedContent | default (.Site.Params.article.showRelatedContent | default false) }}
{{ $related := .Site.RegularPages.Related . | first .Site.Params.article.relatedContentLimit }}
{{ with $related }}
<h2 class="mt-8 text-2xl font-extrabold mb-10">{{ i18n "article.related_articles" | emojify }}</h2>
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{{ range . }}
{{ partial "article-link/card-related.html" . }}
{{ end }}
</section>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,73 @@
{{ if .IsHome -}}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"@id": {{ site.Home.Permalink | safeURL }},
"name": "{{ .Site.Title | safeJS }}",
{{ with .Site.Params.description }}"description": "{{ . | safeJS }}",{{ end }}
{{ with .Site.LanguageCode }}"inLanguage": "{{ . }}",{{ end }}
"url": {{ site.Home.Permalink | safeURL }},
{{ with .Site.Params.keywords }}"keywords": {{ . }},{{ end }}
"publisher" : {
"@type": "Person",
"name": "{{ .Site.Params.Author.name | safeJS }}"
}
}
</script>
{{ else }}
{{- $iso8601 := "2006-01-02T15:04:05-07:00" -}}
<script type="application/ld+json">
[{
"@context": "https://schema.org",
"@type": "Article",
"articleSection": "{{ (site.GetPage .Section).Title | safeJS }}",
"name": "{{ .Title | safeJS }}",
"headline": "{{ .Title | safeJS }}",
{{ with .Description }}"description": "{{ . | safeJS }}",{{ end }}
{{ with .Site.LanguageCode }}"inLanguage": "{{ . }}",{{ end }}
"url" : {{ .Permalink }},
"author" : {
"@type": "Person",
"name": "{{ .Site.Params.Author.name | safeJS }}"
},
{{ with .PublishDate }}"copyrightYear": "{{ .Format "2006" }}",{{ end }}
{{ with .Date }}"dateCreated": "{{ .Format $iso8601 }}",{{ end }}
{{ with .PublishDate }}"datePublished": "{{ .Format $iso8601 }}",{{ end }}
{{ with .ExpiryDate }}"expires": "{{ .Format $iso8601 }}",{{ end }}
{{ with .Lastmod }}"dateModified": "{{ .Format $iso8601 }}",{{ end }}
{{ if .Keywords }}
{{ with .Keywords }}"keywords": {{ . }},{{ end }}
{{ else }}
{{ with .Params.tags }}"keywords": {{ . }},{{ end }}
{{ end }}
"mainEntityOfPage": "true",
"wordCount": "{{ .WordCount }}"
}{{ if site.Params.enableStructuredBreadcrumbs | default false }},
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{{- $position := 0 -}}
{{- range .Ancestors -}}
{{- if not .IsHome -}}
{{- $position = add $position 1 -}}
{
"@type": "ListItem",
"position": {{ $position }},
"name": "{{ .Title }}",
"item": {{ .Permalink }}
},
{{- end -}}
{{- end -}}
{{- $position = add $position 1 -}}
{
"@type": "ListItem",
"position": {{ $position }},
"name": "{{ .Title }}",
"item": {{ .Permalink }}
}
]
}{{ end }}]
</script>
{{ end }}

View File

@@ -0,0 +1,21 @@
{{ $isRTL := .Site.Params.rtl | default false }}
{{ $coffeeIsRight := and (eq (lower site.Params.buymeacoffee.globalWidgetPosition) "right") .Site.Params.buymeacoffee.globalWidget }}
{{ if not $coffeeIsRight }}
{{ $coffeeIsRight = false }}
{{ end }}
{{ $needAvoidCoffee := ne $coffeeIsRight $isRTL }}
{{ $toTopYOffset := cond $needAvoidCoffee "bottom-24" "bottom-6" }}
<div
id="scroll-to-top"
class="fixed {{ $toTopYOffset }} end-6 z-50 transform translate-y-4 opacity-0 duration-200">
<a
href="#the-top"
class="pointer-events-auto flex h-12 w-12 items-center justify-center rounded-full bg-neutral/50 text-xl text-neutral-700 hover:text-primary-600 dark:bg-neutral-800/50 dark:text-neutral dark:hover:text-primary-400"
aria-label="{{ i18n "nav.scroll_to_top_title" }}"
title="{{ i18n "nav.scroll_to_top_title" }}">
&uarr;
</a>
</div>

View File

@@ -0,0 +1,43 @@
<div
id="search-wrapper"
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
data-url="{{ "" | absLangURL }}">
<div
id="search-modal"
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
<header class="relative z-10 flex items-center justify-between flex-none px-2">
<form class="flex items-center flex-auto min-w-0">
<div class="flex items-center justify-center w-8 h-8 text-neutral-400">
{{ partial "icon.html" "search" }}
</div>
<input
type="search"
id="search-query"
class="flex flex-auto h-12 mx-1 bg-transparent appearance-none focus:outline-dotted focus:outline-2 focus:outline-transparent"
placeholder="{{ i18n "search.input_placeholder" }}"
tabindex="0">
</form>
<button
id="close-search-button"
class="flex items-center justify-center w-8 h-8 text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
title="{{ i18n "search.close_button_title" }}">
{{ partial "icon.html" "xmark" }}
</button>
</header>
<section class="flex-auto px-2 overflow-auto">
<ul id="search-results">
<!-- <li class="mb-2">
<a class="flex items-center px-3 py-2 rounded-md appearance-none bg-neutral-100 dark:bg-neutral-700 focus:bg-primary-100 hover:bg-primary-100 dark:hover:bg-primary-900 dark:focus:bg-primary-900 focus:outline-dotted focus:outline-transparent focus:outline-2" href="${value.item.permalink}" tabindex="0">
<div class="grow">
<div class="-mb-1 text-lg font-bold">${value.item.title}</div>
<div class="text-sm text-neutral-500 dark:text-neutral-400">${value.item.section}<span class="px-2 text-primary-500">&middot;</span>${value.item.date}</span></div>
<div class="text-sm italic">${value.item.summary}</div>
</div>
<div class="ml-2 ltr:block rtl:hidden text-neutral-500">&rarr;</div>
<div class="mr-2 ltr:hidden rtl:block text-neutral-500">&larr;</div>
</a>
</li> -->
</ul>
</section>
</div>
</div>

View File

@@ -0,0 +1,5 @@
{{ if .Params.series }}
<details class="mt-2 mb-5 overflow-hidden rounded-lg ms-0 ps-5">
{{ partial "series/series_base.html" . }}
</details>
{{ end }}

View File

@@ -0,0 +1,7 @@
{{ if .Params.series }}
<details
class="mt-2 mb-5 overflow-hidden rounded-lg ms-0 ps-5"
{{ if .Params.seriesOpened | default (.Site.Params.article.seriesOpened | default false) }}open{{ end }}>
{{ partial "series/series_base.html" . }}
</details>
{{ end }}

View File

@@ -0,0 +1,25 @@
{{ if .Params.series }}
<summary
class="py-1 text-lg font-semibold cursor-pointer bg-primary-200 text-neutral-800 -ms-5 ps-5 dark:bg-primary-800 dark:text-neutral-100">
{{ index .Params.series 0 }} -
{{ i18n "article.part_of_series" }}
</summary>
{{ $seriesName := strings.ToLower (index .Params.series 0) }}
{{ range $post := sort (index .Site.Taxonomies.series $seriesName) "Params.series_order" }}
{{ if eq $post.Permalink $.Page.Permalink }}
<div
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
{{ i18n "article.part" }} {{ $post.Params.series_order }}:
{{ i18n "article.this_article" }}
</div>
{{ else }}
<div
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
<a href="{{ $post.RelPermalink }}">
{{ i18n "article.part" }} {{ $post.Params.series_order }}:
{{ $post.Params.title }}
</a>
</div>
{{ end }}
{{ end }}
{{ end }}

View File

@@ -0,0 +1,17 @@
{{ with .Params.sharingLinks | default (.Site.Params.article.sharingLinks | default false) }}
{{ $links := site.Data.sharing }}
<section class="flex flex-row flex-wrap justify-center pt-4 text-xl">
{{ range . }}
{{ with index $links . }}
<a
target="_blank"
class="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="{{ printf .url $.Permalink $.Title }}"
title="{{ i18n .title }}"
aria-label="{{ i18n .title }}">
{{ partial "icon.html" .icon }}
</a>
{{ end }}
{{ end }}
</section>
{{ end }}

View File

@@ -0,0 +1,55 @@
{{ with site.Data.sponsors }}
<section class="sponsors-section mt-16 mb-8">
<div class="flex flex-col items-center mb-6">
<div class="flex items-center gap-2 mb-2">
<span class="text-2xl text-primary-500">
{{ partial "icon.html" "heart" }}
</span>
<h2 class="text-2xl font-extrabold text-neutral-800 dark:text-neutral-200">
{{ i18n "sponsors.title" | default "Sponsors" }}
</h2>
</div>
<p class="text-neutral-600 dark:text-neutral-400 text-center max-w-md">
{{ i18n "sponsors.description" | default "Special thanks to our sponsors who help make Blowfish possible." }}
</p>
</div>
<div class="flex flex-wrap justify-center gap-4">
{{ range . }}
<a
href="{{ .profile_url }}"
target="_blank"
rel="noopener noreferrer"
class="group relative flex w-24 flex-col items-center"
title="{{ .username }}"
aria-label="Sponsor: {{ .username }}">
<img
class="nozoom h-16 w-16 rounded-full ring-2 ring-primary-500 transition-transform group-hover:scale-110"
src="{{ .avatar_url }}"
alt="{{ .username }}"
width="64"
height="64"
loading="lazy">
<span class="mt-2 block w-full truncate text-center text-sm text-neutral-600 transition-colors group-hover:text-primary-500 dark:text-neutral-400">
@{{ .username }}
</span>
<span
class="pointer-events-none absolute left-1/2 top-full z-10 mt-1 w-max max-w-48 -translate-x-1/2 rounded-md bg-neutral-900 px-2 py-1 text-xs text-white opacity-0 shadow-md transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100 group-hover:visible group-focus-within:visible dark:bg-neutral-100 dark:text-neutral-900 invisible">
@{{ .username }}
</span>
</a>
{{ end }}
</div>
<div class="mt-8 flex justify-center">
<a
href="https://github.com/sponsors/nunocoracao"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-500 transition-colors">
<span>{{ partial "icon.html" "heart" }}</span>
<span>{{ i18n "sponsors.cta" | default "Become a Sponsor" }}</span>
</a>
</div>
</section>
{{ end }}

View File

@@ -0,0 +1,67 @@
{{ $disableImageOptimization := .Page.Site.Params.disableImageOptimization | default false }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ with .Page.Params.featureimage }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $featured = resources.GetRemote . }}
{{ else }}
{{ $featured = resources.Get . }}
{{ end }}
{{ end }}
{{ if not $featured }}
{{ $images := .Page.Resources.ByType "image" }}
{{ range slice "*feature*" "*cover*" "*thumbnail*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
{{ end }}
{{ end }}
{{ with $featured }}
{{ $featuredURL = .RelPermalink }}
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
{{ $featuredURL = (.Resize "600x").RelPermalink }}
{{ end }}
{{ end }}
<div class="min-w-full">
<div class="border-neutral-200 dark:border-neutral-700 border-2 rounded overflow-hidden shadow-2xl relative">
{{ with $featuredURL }}
<figure class="not-prose flex-none relative overflow-hidden thumbnail_card">
<img
src="{{ . }}"
alt="{{ $.Page.Title }}"
loading="lazy"
decoding="async"
fetchpriority="low"
class="not-prose absolute inset-0 w-full h-full object-cover">
</figure>
{{ end }}
{{ if site.Params.taxonomy.showTermCount | default true }}
<span class="absolute bottom-0 right-0 m-2">
<span class="flex">
<span
class="rounded-md border border-primary-400 px-1 py-[1px] text-xl font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
{{ .Count }}
</span>
</span>
</span>
{{ end }}
<div class="px-6 py-4">
<a
{{ with .Page.Params.externalUrl }}
href="{{ . }}" target="_blank" rel="external"
{{ else }}
href="{{ .Page.RelPermalink }}"
{{ end }}
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
<div
class="font-bold text-xl text-neutral-800 decoration-primary-500 hover:underline hover:underline-offset-2 dark:text-neutral">
{{ .Page.Title | emojify }}
</div>
</a>
</div>
<div class="px-6 pt-4 pb-2"></div>
</div>
</div>

View File

@@ -0,0 +1,15 @@
<article class="w-full px-2 my-3 overflow-hidden sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/4">
<h2 class="flex items-center">
<a
class="text-xl font-medium decoration-primary-500 hover:underline hover:underline-offset-2"
href="{{ .Page.RelPermalink }}"
>{{ .Page.Title }}</a
>
{{ if site.Params.taxonomy.showTermCount | default true }}
<span class="px-2 text-base text-primary-500">&middot;</span>
<span class="text-base text-neutral-400">
{{ .Count }}
</span>
{{ end }}
</h2>
</article>

125
layouts/partials/toc.html Normal file
View File

@@ -0,0 +1,125 @@
<details
open
id="TOCView"
class="toc-right mt-0 overflow-y-auto overscroll-contain bf-scrollbar rounded-lg -ms-5 ps-5 pe-2 hidden lg:block">
<summary
class="block py-1 text-lg font-semibold cursor-pointer bg-neutral-100 text-neutral-800 -ms-5 ps-5 dark:bg-neutral-700 dark:text-neutral-100 lg:hidden">
{{ i18n "article.table_of_contents" }}
</summary>
<div
class="min-w-[220px] py-2 border-dotted border-s-1 -ms-5 ps-5 dark:border-neutral-600">
{{ .TableOfContents | emojify }}
</div>
</details>
<details class="toc-inside mt-0 overflow-hidden rounded-lg -ms-5 ps-5 lg:hidden">
<summary
class="py-1 text-lg font-semibold cursor-pointer bg-neutral-100 text-neutral-800 -ms-5 ps-5 dark:bg-neutral-700 dark:text-neutral-100 lg:hidden">
{{ i18n "article.table_of_contents" }}
</summary>
<div
class="py-2 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
{{ .TableOfContents | emojify }}
</div>
</details>
{{ if .Site.Params.smartTOC }}
<script>
(function () {
'use strict'
const SCROLL_OFFSET_RATIO = 0.33
const TOC_SELECTOR = '#TableOfContents'
const ANCHOR_SELECTOR = '.anchor'
const TOC_LINK_SELECTOR = 'a[href^="#"]'
const NESTED_LIST_SELECTOR = 'li ul'
const ACTIVE_CLASS = 'active'
let isJumpingToAnchor = false
function getActiveAnchorId(anchors, offsetRatio) {
const threshold = window.scrollY + window.innerHeight * offsetRatio
const tocLinks = [...document.querySelectorAll('#TableOfContents a[href^="#"]')]
const tocIds = new Set(tocLinks.map(link => link.getAttribute('href').substring(1)))
if (isJumpingToAnchor) {
for (let i = 0; i < anchors.length; i++) {
const anchor = anchors[i]
if (!tocIds.has(anchor.id)) continue
const top = anchor.getBoundingClientRect().top + window.scrollY
if (Math.abs(window.scrollY - top) < 100) {
return anchor.id
}
}
}
for (let i = anchors.length - 1; i >= 0; i--) {
const top = anchors[i].getBoundingClientRect().top + window.scrollY
if (top <= threshold && tocIds.has(anchors[i].id)) {
return anchors[i].id
}
}
return anchors.find(anchor => tocIds.has(anchor.id))?.id || ''
}
function updateTOC({ toc, anchors, links, scrollOffset, collapseInactive }) {
const activeId = getActiveAnchorId(anchors, scrollOffset)
if (!activeId) return
links.forEach(link => {
const isActive = link.getAttribute('href') === `#${activeId}`
link.classList.toggle(ACTIVE_CLASS, isActive)
if (collapseInactive) {
const ul = link.closest('li')?.querySelector('ul')
if (ul) ul.style.display = isActive ? '' : 'none'
}
})
if (collapseInactive) {
const activeLink = toc.querySelector(`a[href="#${CSS.escape(activeId)}"]`)
let el = activeLink
while (el && el !== toc) {
if (el.tagName === 'UL') el.style.display = ''
if (el.tagName === 'LI') el.querySelector('ul')?.style.setProperty('display', '')
el = el.parentElement
}
}
}
function initTOC() {
const toc = document.querySelector(TOC_SELECTOR)
if (!toc) return
const collapseInactive = {{ if site.Params.smartTOCHideUnfocusedChildren }}true{{ else }}false{{ end }}
const anchors = [...document.querySelectorAll(ANCHOR_SELECTOR)]
const links = [...toc.querySelectorAll(TOC_LINK_SELECTOR)]
if (collapseInactive) {
toc.querySelectorAll(NESTED_LIST_SELECTOR).forEach(ul => ul.style.display = 'none')
}
links.forEach(link => {
link.addEventListener('click', () => {
isJumpingToAnchor = true
})
})
const config = {
toc,
anchors,
links,
scrollOffset: SCROLL_OFFSET_RATIO,
collapseInactive
}
window.addEventListener('scroll', () => updateTOC(config), { passive: true })
window.addEventListener('hashchange', () => updateTOC(config), { passive: true })
updateTOC(config)
}
document.readyState === 'loading'
? document.addEventListener('DOMContentLoaded', initTOC)
: initTOC()
})()
</script>
{{ end }}

View File

@@ -0,0 +1,195 @@
{{/* Mermaid */}}
{{ if .Page.HasShortcode "mermaid" }}
{{ $mermaidLib := resources.Get "lib/mermaid/mermaid.min.js" }}
{{ $mermaidConfig := resources.Get "js/mermaid.js" }}
{{ $mermaidConfig := $mermaidConfig | resources.Minify }}
{{ $mermaidJS := slice $mermaidLib $mermaidConfig | resources.Concat "js/mermaid.bundle.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
defer
type="text/javascript"
src="{{ $mermaidJS.RelPermalink }}"
integrity="{{ $mermaidJS.Data.Integrity }}"></script>
{{ end }}
{{/* Chart */}}
{{ if .Page.HasShortcode "chart" }}
{{ $chartLib := resources.Get "lib/chart/chart.min.js" }}
{{ $chartConfig := resources.Get "js/chart.js" }}
{{ $chartConfig := $chartConfig | resources.Minify }}
{{ $chartJS := slice $chartLib $chartConfig | resources.Concat "js/chart.bundle.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
defer
type="text/javascript"
src="{{ $chartJS.RelPermalink }}"
integrity="{{ $chartJS.Data.Integrity }}"></script>
{{ end }}
{{/* Katex */}}
{{ if .Page.HasShortcode "katex" }}
{{ $katexCSS := resources.Get "lib/katex/katex.min.css" }}
{{ $katexCSS := $katexCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<link
type="text/css"
rel="stylesheet"
href="{{ $katexCSS.RelPermalink }}"
integrity="{{ $katexCSS.Data.Integrity }}">
{{ $katexLib := resources.Get "lib/katex/katex.min.js" }}
{{ $katexRenderLib := resources.Get "lib/katex/auto-render.min.js" }}
{{ $katexJS := slice $katexLib $katexRenderLib | resources.Concat "js/katex.bundle.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
defer
type="text/javascript"
src="{{ $katexJS.RelPermalink }}"
integrity="{{ $katexJS.Data.Integrity }}"
id="katex-render"></script>
{{ $katexFonts := resources.Match "lib/katex/fonts/*" }}
{{ range $katexFonts }}
<!-- {{ .RelPermalink }} -->
{{ end }}
{{ end }}
{{/* TypeIt */}}
{{ if .Page.HasShortcode "typeit" }}
{{ $typeitLib := resources.Get "lib/typeit/typeit.umd.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script defer src="{{ $typeitLib.RelPermalink }}" integrity="{{ $typeitLib.Data.Integrity }}"></script>
{{ end }}
{{/* Packery */}}
{{ if .Page.HasShortcode "gallery" }}
{{ $galleryCSS := resources.Get "css/components/gallery.css" }}
{{ $galleryCSS = $galleryCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<link
type="text/css"
rel="stylesheet"
href="{{ $galleryCSS.RelPermalink }}"
integrity="{{ $galleryCSS.Data.Integrity }}">
{{ $packeryLib := resources.Get "lib/packery/packery.pkgd.min.js" }}
{{ $packeryLib = $packeryLib | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
defer
type="text/javascript"
src="{{ $packeryLib.RelPermalink }}"
integrity="{{ $packeryLib.Data.Integrity }}"></script>
{{ $jsShortcodeGallery := resources.Get "js/shortcodes/gallery.js" }}
{{ $jsShortcodeGallery = $jsShortcodeGallery | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $jsShortcodeGallery.RelPermalink }}"
integrity="{{ $jsShortcodeGallery.Data.Integrity }}"></script>
{{ end }}
{{/* tw-elements */}}
{{ if or (.Page.HasShortcode "carousel") (.Page.HasShortcode "timeline") }}
{{ $carouselCSS := resources.Get "css/components/carousel.css" }}
{{ $carouselCSS = $carouselCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<link
type="text/css"
rel="stylesheet"
href="{{ $carouselCSS.RelPermalink }}"
integrity="{{ $carouselCSS.Data.Integrity }}">
{{ $twelementsLib := resources.Get "lib/tw-elements/index.min.js" }}
{{ $twelementsLib = $twelementsLib | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
defer
type="text/javascript"
src="{{ $twelementsLib.RelPermalink }}"
integrity="{{ $twelementsLib.Data.Integrity }}"></script>
{{ end }}
{{/* youtubeLite */}}
{{ if .Page.HasShortcode "youtubeLite" }}
{{ $youtubeLiteCSS := resources.Get "lib/lite-youtube-embed/lite-yt-embed.css" }}
{{ $youtubeLiteCSS = $youtubeLiteCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<link
type="text/css"
rel="stylesheet"
href="{{ $youtubeLiteCSS.RelPermalink }}"
integrity="{{ $youtubeLiteCSS.Data.Integrity }}">
{{ $youtubeLiteLib := resources.Get "lib/lite-youtube-embed/lite-yt-embed.js" }}
{{ $youtubeLiteLib = $youtubeLiteLib | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script
type="text/javascript"
src="{{ $youtubeLiteLib.RelPermalink }}"
integrity="{{ $youtubeLiteLib.Data.Integrity }}"></script>
{{ end }}
{{/* Repo cards */}}
{{ $repoCards := slice "codeberg" "forgejo" "gitea" "github" "hugging-face" }}
{{ $hasRepoCards := false }}
{{ range $repoCards }}
{{ if $.Page.HasShortcode . }}
{{ $hasRepoCards = true }}
{{ end }}
{{ end }}
{{ if $hasRepoCards }}
{{ $repoColors := site.Data.repoColors }}
{{ $cssRules := slice }}
{{/* default color */}}
{{ $cssRules = $cssRules | append ".language-dot[data-language=\"default\"] { background-color: #0077b6; }" }}
{{/* Hugging Face model color */}}
{{ $cssRules = $cssRules | append ".language-dot[data-language=\"model\"] { background-color: #ff6b35; }" }}
{{ range $lang, $color := $repoColors }}
{{ $cssRules = $cssRules | append (printf ".language-dot[data-language=\"%s\"] { background-color: %s; }" $lang $color) }}
{{ end }}
{{ $repoCardCSS := resources.FromString "css/repo-cards.css" (delimit $cssRules "\n")
| minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512")
}}
<link rel="stylesheet" href="{{ $repoCardCSS.RelPermalink }}" integrity="{{ $repoCardCSS.Data.Integrity }}">
{{ end }}
{{/* tabs */}}
{{ if .Page.HasShortcode "tabs" }}
{{ $tabJS := resources.Get "js/shortcodes/tabs.js" }}
{{ with $tabJS | minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
{{ end }}
{{ end }}
{{/* Firebase */}}
{{ if site.Params.firebase.apiKey }}
{{ $firebase := resources.Get "js/firebase.js" }}
{{ $firebase = $firebase | resources.Minify | resources.Fingerprint (site.Params.fingerprintAlgorithm | default "sha512") }}
<script type="module" src="{{ $firebase.RelPermalink }}" integrity="{{ $firebase.Data.Integrity }}"></script>
{{ if in (slice "page" "section") .Kind }}
{{ $translations := .AllTranslations }}
{{ with .File }}
{{ $path := .Path }}
{{ range $translations }}
{{ $path = replace $path (print "." .Lang ".md") ".md" }}
{{ end }}
{{ partial "inline/firebase-config.html" (dict "views" (printf "views_%s" $path) "likes" (printf "likes_%s" $path)) }}
{{ end }}
{{ else if eq .Kind "term" }}
{{ partial "inline/firebase-config.html" (dict "views" (printf "views_term_%s" .Data.Term) "likes" (printf "likes_term_%s" .Data.Term)) }}
{{ else if eq .Kind "taxonomy" }}
{{ partial "inline/firebase-config.html" (dict "views" (printf "views_taxonomy_%s" .Data.Plural) "likes" (printf "likes_taxonomy_%s" .Data.Plural)) }}
{{ else if eq .Kind "home" }}
{{ partial "inline/firebase-config.html" (dict "views" "views_home" "likes" "likes_home") }}
{{ end }}
{{ end }}
{{ define "_partials/inline/firebase-config.html" }}
<script id="firebase-config"
type="application/json"
data-views="{{ .views }}"
data-likes="{{ .likes }}">
{
"config": {
"apiKey": "{{ site.Params.firebase.apiKey }}",
"authDomain": "{{ site.Params.firebase.authDomain }}",
"projectId": "{{ site.Params.firebase.projectId }}",
"storageBucket": "{{ site.Params.firebase.storageBucket }}",
"messagingSenderId": "{{ site.Params.firebase.messagingSenderId }}",
"appId": "{{ site.Params.firebase.appId }}",
"measurementId": "{{ site.Params.firebase.measurementId }}"
}
}
</script>
{{ end }}

7
layouts/robots.txt Normal file
View File

@@ -0,0 +1,7 @@
User-agent: *
{{- if hugo.IsProduction | or (eq .Site.Params.env "production") }}
Allow: /
{{- else }}
Disallow: /
{{- end }}
Sitemap: {{ "sitemap.xml" | absURL }}

View File

@@ -0,0 +1,46 @@
{{ $id := delimit (slice "accordion" (partial "functions/uid.html" .)) "-" }}
{{ $mode := .Get "mode" | default "collapse" }}
{{ $separated := .Get "separated" | default false }}
{{ $isSeparated := or (eq $separated true) (eq $separated "true") }}
<div
id="{{ $id }}"
class="{{ if $isSeparated }}space-y-2{{ else }}border border-neutral-200 dark:border-neutral-700 rounded-lg overflow-hidden{{ end }}"
data-accordion="{{ $mode }}"
data-accordion-separated="{{ $isSeparated }}"
>
{{- .Inner -}}
</div>
{{ if $isSeparated }}
<style>
#{{ $id }} > details + details {
margin-top: 0.5rem;
}
</style>
{{ else }}
<style>
#{{ $id }} > details + details {
border-top: 1px solid rgb(var(--color-neutral-200));
}
.dark #{{ $id }} > details + details {
border-top-color: rgb(var(--color-neutral-700));
}
</style>
{{ end }}
{{ if eq $mode "collapse" }}
<script>
(() => {
const root = document.getElementById("{{ $id }}");
if (!root) return;
const items = root.querySelectorAll("details[data-accordion-item]");
items.forEach((item) => {
item.addEventListener("toggle", () => {
if (!item.open) return;
items.forEach((other) => {
if (other !== item) other.removeAttribute("open");
});
});
});
})();
</script>
{{ end }}

View File

@@ -0,0 +1,37 @@
{{ $parent := .Parent }}
{{ $separated := false }}
{{ if $parent }}
{{ $separated = $parent.Get "separated" | default false }}
{{ end }}
{{ $isSeparated := or (eq $separated true) (eq $separated "true") }}
{{ $title := .Get "title" | default (.Get "header") }}
{{ $icon := .Get "icon" }}
{{ $md := .Get "md" | default true }}
{{ $open := .Get "open" | default false }}
{{ $isOpen := or (eq $open true) (eq $open "true") }}
<details
class="{{ if $isSeparated }}rounded-lg border border-neutral-200 dark:border-neutral-700 overflow-hidden{{ else }}border-none{{ end }}"
data-accordion-item
{{ if $isOpen }}open{{ end }}
>
<summary class="flex w-full cursor-pointer items-center justify-between gap-4 px-4 py-3 text-left text-lg font-semibold text-neutral-900 dark:text-neutral-100">
<span class="flex items-center gap-2">
{{ with $icon }}
{{ partial "icon.html" . }}
{{ end }}
<span>{{ $title }}</span>
</span>
<span>
{{ partial "icon" "chevron-down" }}
</span>
</summary>
<div class="px-4 pb-4 text-neutral-700 dark:text-neutral-300">
{{ if $md }}
{{- .Inner | markdownify -}}
{{ else }}
{{- .Inner -}}
{{ end }}
</div>
</details>

View File

@@ -0,0 +1,37 @@
{{ if .IsNamedParams }}
{{ $.Scratch.Set "icon" (default "triangle-exclamation" (.Get "icon") ) }}
{{ $.Scratch.Set "cardColor" (.Get "cardColor") }}
{{ $.Scratch.Set "iconColor" (.Get "iconColor") }}
{{ $.Scratch.Set "textColor" (.Get "textColor") }}
{{ else }}
{{ $.Scratch.Set "icon" (default "triangle-exclamation" (.Get 0) ) }}
{{ end }}
<div
{{ if not ($.Scratch.Get "cardColor") }}
class="flex px-4 py-3 rounded-md shadow bg-primary-100 dark:bg-primary-900"
{{ else }}
class="flex px-4 py-3 rounded-md shadow" style="background-color: {{ $.Scratch.Get "cardColor" }}"
{{ end }}
>
<span
{{ if not ($.Scratch.Get "iconColor") }}
class="text-primary-400 pe-3 flex items-center"
{{ else }}
class="pe-3 flex items-center" style="color: {{ $.Scratch.Get "iconColor" }}"
{{ end }}
>
{{ partial "icon.html" ($.Scratch.Get "icon") }}
</span>
<span
{{ if not ($.Scratch.Get "textColor") }}
class="dark:text-neutral-300"
{{ else }}
style="color: {{ $.Scratch.Get "textColor" }}"
{{ end }}
>
{{- .Inner | markdownify -}}
</span>
</div>

View File

@@ -0,0 +1,13 @@
{{ $link := .Get "link" }}
{{ $showSummary := .Get "showSummary" }}
{{ $compactSummary := .Get "compactSummary" | default false }}
{{ $target := .Page }}
{{ if ne $link .Page.RelPermalink }}
{{ $target = index (first 1 (where .Site.AllPages "RelPermalink" $link)) 0 }}
{{ end }}
{{ if $target }}
<section class="space-y-10 w-full">
{{ $context := dict "target" $target "showSummary" $showSummary "compactSummary" $compactSummary }}
{{ partial "article-link/_shortcode.html" $context }}
</section>
{{ end }}

View File

@@ -0,0 +1 @@
{{ partial "badge.html" .Inner }}

View File

@@ -0,0 +1,8 @@
<a
class="!rounded-md bg-primary-600 px-4 py-2 !text-neutral !no-underline hover:!bg-primary-500 dark:bg-primary-800 dark:hover:!bg-primary-700"
{{ with .Get "href" }}href="{{ . }}"{{ end }}
{{ with .Get "target" }}target="{{ . }}"{{ end }}
{{ with .Get "rel" }}rel="{{ . }}"{{ end }}
role="button">
{{ .Inner }}
</a>

View File

@@ -0,0 +1,145 @@
{{ $id := delimit (slice "carousel" (partial "functions/uid.html" .) (now.UnixNano)) "-" }}
{{ $aspect := (split (.Get "aspectRatio") "-") }}
{{ $aspectx := default "16" (index $aspect 0) }}
{{ $aspecty := default "9" (index $aspect 1) }}
{{ $interval := default "2000" (.Get "interval") }}
{{ $page := .Page.Resources }}
{{ $imagesTemp := .Get "images" }}
{{ $imagesTemp = strings.TrimPrefix "{" $imagesTemp }}
{{ $imagesTemp = strings.TrimSuffix "}" $imagesTemp }}
{{ $imagesTemp := strings.Split $imagesTemp "," }}
{{ $images := slice }}
{{ range $imagesTemp }}
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
{{ $images = $images | append (dict "resource" (resources.GetRemote .) "key" .) }}
{{ else }}
{{ range ($page.Match .) }}
{{ $images = $images | append (dict "resource" . "key" .Name) }}
{{ end }}
{{ end }}
{{ end }}
{{ $captionsParam := .Get "captions" }}
{{ $captions := dict }}
{{ if $captionsParam }}
{{ $captionsTemp := strings.TrimPrefix "{" $captionsParam }}
{{ $captionsTemp = strings.TrimSuffix "}" $captionsTemp }}
{{ range (strings.Split $captionsTemp ",") }}
{{ $pair := strings.Split . ":" }}
{{ if ge (len $pair) 2 }}
{{ $key := strings.TrimSpace (index $pair 0) }}
{{ $value := strings.TrimSpace (delimit (after 1 $pair) ":") }}
{{ $captions = merge $captions (dict $key $value) }}
{{ end }}
{{ end }}
{{ end }}
{{ if not .Parent }}<div class="width-patch"></div>{{ end }}
<div
id="{{ $id }}"
class="relative"
data-twe-carousel-init
data-twe-ride="carousel"
data-twe-interval="{{ $interval }}">
<div
class="absolute right-0 bottom-0 left-0 z-2 mx-[15%] mb-10 flex list-none justify-center p-0"
data-twe-carousel-indicators>
{{ $num := 0 }}
{{ range $images }}
<button
type="button"
data-twe-target="#{{ $id }}"
data-twe-slide-to="{{ $num }}"
{{ if eq $num 0 }}data-twe-carousel-active aria-current="true"{{ end }}
class="mx-[3px] box-content h-[3px] w-[30px] flex-initial cursor-pointer border-0 border-y-[10px] border-solid border-transparent bg-neutral bg-clip-padding p-0 -indent-[999px] opacity-50 transition-opacity duration-[600ms] ease-[cubic-bezier(0.25,0.1,0.25,1.0)] motion-reduce:transition-none"
aria-label="Slide {{ $num }}"></button>
{{ $num = add $num 1 }}
{{ end }}
</div>
<div
class="relative w-full after:clear-both after:block after:content-['']"
style="overflow-x: clip; overflow-y: visible;">
{{ range $index, $image := $images }}
{{ $hiddenClass := cond (eq $index 0) "" "hidden" }}
{{ $resource := index $image "resource" }}
{{ $key := index $image "key" }}
{{ $caption := "" }}
{{ $candidates := slice }}
{{ if $resource }}
{{ $candidates = $candidates | append $resource.Name (path.Base $resource.Name) $resource.RelPermalink (path.Base $resource.RelPermalink) }}
{{ end }}
{{ if $key }}
{{ $candidates = $candidates | append $key (path.Base $key) }}
{{ end }}
{{ range $candidates }}
{{ if and (not $caption) . }}
{{ with (index $captions .) }}{{ $caption = . }}{{ end }}
{{ end }}
{{ end }}
<div
class="relative float-left -mr-[100%] {{ $hiddenClass }} w-full transition-transform ease-in-out motion-reduce:transition-none"
data-twe-carousel-item
style="transition-duration: {{ $interval }}ms;"
{{ if eq $index 0 }}data-twe-carousel-active{{ end }}>
<div class="single_hero_background relative overflow-hidden" style="aspect-ratio: {{ $aspectx }} / {{ $aspecty }};">
<img
src="{{ $resource.RelPermalink }}"
class="block absolute top-0 object-cover w-full h-full not-prose nozoom"
alt="carousel image {{ add $index 1 }}">
</div>
{{ if $caption }}
<figcaption
class="absolute left-0 right-0"
style="top: calc(100%);"
>{{ $caption | markdownify }}</figcaption>
{{ end }}
</div>
{{ end }}
</div>
<button
class="absolute top-0 bottom-0 left-0 z-2 flex w-[15%] items-center justify-center border-0 bg-none p-0 text-center opacity-50 transition-opacity duration-150 ease-[cubic-bezier(0.25,0.1,0.25,1.0)] hover:no-underline hover:opacity-90 hover:outline-none focus:no-underline focus:opacity-90 focus:outline-none motion-reduce:transition-none"
type="button"
data-twe-target="#{{ $id }}"
data-twe-slide="prev">
<span class="inline-block h-8 w-8">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="4.5"
stroke="currentColor"
class="h-6 w-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
</svg>
</span>
<span
class="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]"
>Previous</span
>
</button>
<button
class="absolute top-0 bottom-0 right-0 z-[1] flex w-[15%] items-center justify-center border-0 bg-none p-0 text-center opacity-50 transition-opacity duration-150 ease-[cubic-bezier(0.25,0.1,0.25,1.0)] hover:no-underline hover:opacity-90 hover:outline-none focus:no-underline focus:opacity-90 focus:outline-none motion-reduce:transition-none"
type="button"
data-twe-target="#{{ $id }}"
data-twe-slide="next">
<span class="inline-block h-8 w-8">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="4.5"
stroke="currentColor"
class="h-6 w-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
</svg>
</span>
<span
class="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]"
>Next</span
>
</button>
</div>

Some files were not shown because too many files have changed in this diff Show More