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

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 }}