First commit
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:
9
layouts/404.html
Normal file
9
layouts/404.html
Normal file
@@ -0,0 +1,9 @@
|
||||
{{ define "main" }}
|
||||
<h1 class="mb-3 text-4xl font-extrabold">{{ i18n "error.404_title" | emojify }}</h1>
|
||||
<p class="mt-8 mb-12 text-neutral-400 dark:text-neutral-500">
|
||||
{{ i18n "error.404_error" | emojify }}
|
||||
</p>
|
||||
<div class="prose dark:prose-invert">
|
||||
<p>{{ i18n "error.404_description" | emojify }}</p>
|
||||
</div>
|
||||
{{ end }}
|
||||
93
layouts/_default/_markup/render-blockquote.html
Normal file
93
layouts/_default/_markup/render-blockquote.html
Normal file
@@ -0,0 +1,93 @@
|
||||
{{- /* To customize your own admonitions, you can do the following:
|
||||
|
||||
1. Change colors
|
||||
|
||||
Define your colors in `assets/css/custom.css` using the variables from:
|
||||
https://github.com/nunocoracao/blowfish/blob/main/assets/css/components/admonition.css
|
||||
|
||||
Example:
|
||||
```
|
||||
:root {
|
||||
--adm-note-bg: red;
|
||||
}
|
||||
html.dark {
|
||||
--adm-note-bg: green;
|
||||
}
|
||||
```
|
||||
|
||||
This will update the background color for light and dark mode.
|
||||
|
||||
2. Change icons and type settings
|
||||
|
||||
You can override the default type and icon mappings by creating your own
|
||||
`impls/hooks/admonition-maps.html` in your `layouts` folder.
|
||||
This allows you to assign different icons or styles for each admonition type.
|
||||
*/ -}}
|
||||
|
||||
{{- if eq .Type "alert" -}}
|
||||
{{- $maps := partialCached "impls/hooks/admonition-maps.html" . -}}
|
||||
{{- $typeMap := $maps.typeMap -}}
|
||||
{{- $iconMap := $maps.iconMap -}}
|
||||
|
||||
{{- $rawType := .AlertType | lower -}}
|
||||
{{- $normalizedType := index $typeMap $rawType | default $rawType -}}
|
||||
{{- $iconName := .Attributes.icon | default (index $iconMap $normalizedType) | default "circle-info" -}}
|
||||
|
||||
{{- $admonitionTitle := .AlertTitle | default ((i18n (printf "admonition.%s" $normalizedType) | default $normalizedType) | title) -}}
|
||||
|
||||
{{- $containerClass := "admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" -}}
|
||||
{{- $headerClass := "flex items-center gap-2 font-semibold text-inherit" -}}
|
||||
{{- $contentClass := "admonition-content mt-3 text-base leading-relaxed text-inherit" -}}
|
||||
|
||||
{{- $isCollapsible := in (slice "+" "-") .AlertSign -}}
|
||||
{{- if $isCollapsible -}}
|
||||
<details
|
||||
class="{{ $containerClass }} group"
|
||||
data-type="{{ $normalizedType }}"
|
||||
{{ if eq .AlertSign "+" }}open{{ end }}>
|
||||
<summary class="{{ $headerClass }} cursor-pointer">
|
||||
<div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg">
|
||||
{{- partial "icon.html" $iconName -}}
|
||||
</div>
|
||||
<div class="grow">
|
||||
{{ $admonitionTitle }}
|
||||
</div>
|
||||
<div
|
||||
class="ms-auto flex h-5 w-5 items-center justify-center transition-transform ease-in-out -rotate-90 group-open:rotate-0 print:hidden">
|
||||
{{- partial "icon.html" "chevron-down" -}}
|
||||
</div>
|
||||
</summary>
|
||||
{{- if .Text -}}
|
||||
<div class="{{ $contentClass }}">
|
||||
{{- .Text | safeHTML -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</details>
|
||||
{{- else -}}
|
||||
<div class="{{ $containerClass }}" data-type="{{ $normalizedType }}">
|
||||
<div class="{{ $headerClass }}">
|
||||
<div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg">
|
||||
{{- partial "icon.html" $iconName -}}
|
||||
</div>
|
||||
<div class="grow">
|
||||
{{ $admonitionTitle }}
|
||||
</div>
|
||||
</div>
|
||||
{{- if .Text -}}
|
||||
<div class="{{ $contentClass }}">
|
||||
{{- .Text | safeHTML -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
|
||||
{{- else -}}
|
||||
<blockquote
|
||||
{{- range $k, $v := .Attributes -}}
|
||||
{{- if $v -}}
|
||||
{{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr -}}
|
||||
{{- end -}}
|
||||
{{- end -}}>
|
||||
{{- .Text | safeHTML -}}
|
||||
</blockquote>
|
||||
{{- end -}}
|
||||
10
layouts/_default/_markup/render-codeblock.html
Normal file
10
layouts/_default/_markup/render-codeblock.html
Normal file
@@ -0,0 +1,10 @@
|
||||
{{- $title := or .Attributes.title "" -}}
|
||||
{{- $lang := or .Type "text" -}}
|
||||
<div class="highlight-wrapper">
|
||||
{{- with $title -}}
|
||||
<div class="codeblock-title">
|
||||
{{- $title -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- transform.Highlight .Inner $lang .Options -}}
|
||||
</div>
|
||||
10
layouts/_default/_markup/render-heading.html
Normal file
10
layouts/_default/_markup/render-heading.html
Normal file
@@ -0,0 +1,10 @@
|
||||
{{ $anchor := anchorize .Anchor }}
|
||||
<h{{ .Level }} class="relative group">{{ .Text | safeHTML }}
|
||||
<div id="{{ $anchor }}" class="anchor"></div>
|
||||
{{ if .Page.Params.showHeadingAnchors | default (.Page.Site.Params.article.showHeadingAnchors | default true) }}
|
||||
<span
|
||||
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
|
||||
<a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#{{ $anchor }}" aria-label="{{ i18n "article.anchor_label" }}">#</a>
|
||||
</span>
|
||||
{{ end }}
|
||||
</h{{ .Level }}>
|
||||
114
layouts/_default/_markup/render-image.html
Normal file
114
layouts/_default/_markup/render-image.html
Normal file
@@ -0,0 +1,114 @@
|
||||
{{- define "RenderImageSimple" -}}
|
||||
{{- $imgObj := .imgObj -}}
|
||||
{{- $src := .src -}}
|
||||
{{- $alt := .alt -}}
|
||||
<img
|
||||
class="my-0 rounded-md"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
fetchpriority="low"
|
||||
alt="{{ $alt }}"
|
||||
src="{{ $src }}"
|
||||
{{ with $imgObj -}}
|
||||
{{ with $imgObj.Width }}width="{{ . }}"{{ end }}
|
||||
{{ with $imgObj.Height }}height="{{ . }}"{{ end }}
|
||||
{{- end }}>
|
||||
{{- end -}}
|
||||
|
||||
{{- define "RenderImageResponsive" -}}
|
||||
{{/* Responsive Image
|
||||
The current setting sizes="(min-width: 768px) 50vw, 65vw" makes the iPhone 16 and 16 Pro
|
||||
select a smaller image, while the iPhone 16 Pro Max selects a larger image.
|
||||
|
||||
Steps:
|
||||
1. Check the media queries in the `sizes` property.
|
||||
2. Find the first matching value. For example, on a mobile device with a CSS pixel width
|
||||
of 390px and DPR = 3 (iPhone 13), given setting sizes="(min-width: 768px) 50vw, 100vw",
|
||||
it matches the `100vw` option.
|
||||
3. Calculate the optimal image size: 390 × 3 × 100% (100vw) = 1170.
|
||||
4. Find the corresponding match in the `srcset`.
|
||||
|
||||
To make the browser select a smaller image on mobile devices
|
||||
override the template and change the `sizes` property to "(min-width: 768px) 50vw, 30vw"
|
||||
|
||||
The sizes="auto" is valid only when loading="lazy".
|
||||
*/}}
|
||||
{{- $imgObj := .imgObj -}}
|
||||
{{- $alt := .alt -}}
|
||||
{{- $originalWidth := $imgObj.Width -}}
|
||||
|
||||
{{- $img800 := $imgObj -}}
|
||||
{{- $img1280 := $imgObj -}}
|
||||
{{- if gt $originalWidth 800 -}}
|
||||
{{- $img800 = $imgObj.Resize "800x" -}}
|
||||
{{- end -}}
|
||||
{{- if gt $originalWidth 1280 -}}
|
||||
{{- $img1280 = $imgObj.Resize "1280x" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- $srcset := printf "%s 800w, %s 1280w" $img800.RelPermalink $img1280.RelPermalink -}}
|
||||
|
||||
|
||||
<img
|
||||
class="my-0 rounded-md"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
fetchpriority="auto"
|
||||
alt="{{ $alt }}"
|
||||
{{ with $imgObj.Width }}width="{{ . }}"{{ end }}
|
||||
{{ with $imgObj.Height }}height="{{ . }}"{{ end }}
|
||||
src="{{ $img800.RelPermalink }}"
|
||||
srcset="{{ $srcset }}"
|
||||
sizes="(min-width: 768px) 50vw, 65vw"
|
||||
data-zoom-src="{{ $imgObj.RelPermalink }}">
|
||||
{{- end -}}
|
||||
|
||||
{{- define "RenderImageCaption" -}}
|
||||
{{- with .caption -}}
|
||||
<figcaption>{{ . | markdownify }}</figcaption>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- $disableImageOptimizationMD := .Page.Site.Params.disableImageOptimizationMD | default false -}}
|
||||
{{- $urlStr := .Destination | safeURL -}}
|
||||
{{- $url := urls.Parse $urlStr -}}
|
||||
{{- $altText := .Text -}}
|
||||
{{- $caption := .Title -}}
|
||||
{{- $isRemote := findRE "^(https?|data)" $url.Scheme -}}
|
||||
{{- $resource := "" -}}
|
||||
|
||||
{{- if not $isRemote -}}
|
||||
{{- $resource = or ($.Page.Resources.GetMatch $urlStr) (resources.Get $urlStr) -}}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
<figure
|
||||
{{- range $k, $v := .Attributes -}}
|
||||
{{- if $v -}}
|
||||
{{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr -}}
|
||||
{{- end -}}
|
||||
{{- end -}}>
|
||||
{{- if $isRemote -}}
|
||||
{{- template "RenderImageSimple" (dict "imgObj" "" "src" $urlStr "alt" $altText) -}}
|
||||
{{- else if $resource -}}
|
||||
{{- $isSVG := eq $resource.MediaType.SubType "svg" -}}
|
||||
{{- $shouldOptimize := and (not $disableImageOptimizationMD) (not $isSVG) -}}
|
||||
{{- if $shouldOptimize -}}
|
||||
{{- template "RenderImageResponsive" (dict "imgObj" $resource "alt" $altText) -}}
|
||||
{{- else -}}
|
||||
{{/* Not optimize image
|
||||
If it is an SVG file, pass the permalink
|
||||
Otherwise, pass the resource to allow width and height attributes
|
||||
*/}}
|
||||
{{- if $isSVG -}}
|
||||
{{- template "RenderImageSimple" (dict "imgObj" "" "src" $resource.RelPermalink "alt" $altText) -}}
|
||||
{{- else -}}
|
||||
{{- template "RenderImageSimple" (dict "imgObj" $resource "src" $resource.RelPermalink "alt" $altText) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- template "RenderImageSimple" (dict "imgObj" "" "src" $urlStr "alt" $altText) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- template "RenderImageCaption" (dict "caption" $caption) -}}
|
||||
</figure>
|
||||
38
layouts/_default/_markup/render-link.html
Normal file
38
layouts/_default/_markup/render-link.html
Normal file
@@ -0,0 +1,38 @@
|
||||
{{/* From Hugo, Apache v2 license
|
||||
https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_markup/render-link.html
|
||||
*/}}
|
||||
{{- $u := urls.Parse .Destination -}}
|
||||
{{- $href := $u.String -}}
|
||||
{{- if strings.HasPrefix $u.String "#" -}}
|
||||
{{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment -}}
|
||||
{{- else if and $href (not $u.IsAbs) -}}
|
||||
{{- $path := strings.TrimPrefix "./" $u.Path -}}
|
||||
{{- with or
|
||||
($.PageInner.GetPage $path)
|
||||
($.PageInner.Resources.Get $path)
|
||||
(resources.Get $path)
|
||||
-}}
|
||||
{{- $href = .RelPermalink -}}
|
||||
{{- with $u.RawQuery -}}
|
||||
{{- $href = printf "%s?%s" $href . -}}
|
||||
{{- end -}}
|
||||
{{- with $u.Fragment -}}
|
||||
{{- $href = printf "%s#%s" $href . -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Open external links in a new tab if
|
||||
configured to do so by the page param externalLinkForceNewTab
|
||||
or site param article.externalLinkForceNewTab
|
||||
and the link starts with http: or https:
|
||||
*/}}
|
||||
{{- $forceNewTab := .Page.Params.externalLinkForceNewTab | default (.Page.Site.Params.article.externalLinkForceNewTab | default true) -}}
|
||||
{{- $isExternal := or (strings.HasPrefix .Destination "http:") (strings.HasPrefix .Destination "https:") -}}
|
||||
{{- $newTab := and $forceNewTab $isExternal -}}
|
||||
|
||||
<a href="{{ $href }}" {{ with .Title }}title="{{ . }}"{{ end }}
|
||||
{{- if $newTab }} target="_blank" rel="noreferrer"{{ end }}>
|
||||
{{- .Text | safeHTML -}}
|
||||
</a>
|
||||
{{- /* Trim EOF */ -}}
|
||||
54
layouts/_default/baseof.html
Normal file
54
layouts/_default/baseof.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="{{ with site.Params.isoCode | default (site.LanguageCode | default `en`) }}{{- . -}}{{ end }}"
|
||||
dir="{{ cond (site.Params.rtl | default false) `rtl` `ltr` }}"
|
||||
class="scroll-smooth"
|
||||
data-default-appearance="{{ site.Params.defaultAppearance | default `light` }}"
|
||||
data-auto-appearance="{{ site.Params.autoSwitchAppearance | default `true` }}">
|
||||
{{- partial "head.html" . -}}
|
||||
{{- partialCached "init.html" . -}}
|
||||
|
||||
{{ $bodyLayout := "flex flex-col h-screen m-auto leading-7 max-w-7xl px-6 sm:px-14 md:px-24 lg:px-32" }}
|
||||
{{ $bodyColor := "text-lg bg-neutral text-neutral-900 dark:bg-neutral-800 dark:text-neutral" }}
|
||||
<body class="{{ $bodyLayout }} {{ $bodyColor }} {{ if site.Params.enableStyledScrollbar | default true }}bf-scrollbar{{ end }}">
|
||||
<div id="the-top" class="absolute flex self-center">
|
||||
<a
|
||||
class="px-3 py-1 text-sm -translate-y-8 rounded-b-lg bg-primary-200 focus:translate-y-0 dark:bg-neutral-600"
|
||||
href="#main-content">
|
||||
<span class="font-bold text-primary-600 pe-2 dark:text-primary-400">↓</span>
|
||||
{{ i18n "nav.skip_to_main" }}
|
||||
</a>
|
||||
</div>
|
||||
{{ $header := print "header/" site.Params.header.layout ".html" }}
|
||||
{{ if templates.Exists ( printf "partials/%s" $header ) }}
|
||||
{{ partial $header . }}
|
||||
{{ else }}
|
||||
{{ partial "header/basic.html" . }}
|
||||
{{ end }}
|
||||
<div class="relative flex flex-col grow">
|
||||
<main id="main-content" class="grow">
|
||||
{{ block "main" . }}{{ end }}
|
||||
{{ if and (site.Params.footer.showScrollToTop | default true) }}
|
||||
{{- partial "scroll-to-top.html" . -}}
|
||||
{{ end }}
|
||||
</main>
|
||||
{{- partial "footer.html" . -}}
|
||||
{{ if site.Params.enableSearch | default false }}
|
||||
{{- partial "search.html" . -}}
|
||||
{{ end }}
|
||||
</div>
|
||||
</body>
|
||||
{{ if site.Params.buymeacoffee.globalWidget | default false }}
|
||||
<script
|
||||
data-name="BMC-Widget"
|
||||
data-cfasync="false"
|
||||
src="https://cdnjs.buymeacoffee.com/1.0.0/widget.prod.min.js"
|
||||
data-id="{{ site.Params.buymeacoffee.identifier }}"
|
||||
data-description="Support me on Buy me a coffee!"
|
||||
{{ with site.Params.buymeacoffee.globalWidgetMessage }}data-message="{{ . }}"{{ end }}
|
||||
{{ with site.Params.buymeacoffee.globalWidgetColor | default `#FFDD00` }}data-color="{{ . }}"{{ end }}
|
||||
{{ with site.Params.buymeacoffee.globalWidgetPosition }}data-position="{{ . }}"{{ end }}
|
||||
data-x_margin="18"
|
||||
data-y_margin="18"></script>
|
||||
{{ end }}
|
||||
</html>
|
||||
33
layouts/_default/index.json
Normal file
33
layouts/_default/index.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{{- $index := slice -}}
|
||||
{{ $pages := .Site.Pages }}
|
||||
{{ range .Site.Home.Translations }}
|
||||
{{ $pages = $pages | lang.Merge .Site.Pages }}
|
||||
{{ end }}
|
||||
{{- range $pages -}}
|
||||
{{- if not .Params.excludeFromSearch -}}
|
||||
{{- $section := .Site.GetPage "section" .Section -}}
|
||||
{{- if .Date -}}
|
||||
{{- $index = $index | append (dict
|
||||
"date" (.Date | time.Format (.Site.Language.Params.dateFormat | default ":date_long"))
|
||||
"title" (.Title | emojify | safeJS)
|
||||
"section" ($section.Title | emojify | safeJS)
|
||||
"summary" (.Summary | plainify | htmlUnescape | safeJS)
|
||||
"content" (.Plain | safeJS)
|
||||
"permalink" .RelPermalink
|
||||
"externalUrl" .Params.externalUrl
|
||||
"type" .Type
|
||||
) -}}
|
||||
{{- else -}}
|
||||
{{- $index = $index | append (dict
|
||||
"title" (.Title | emojify | safeJS)
|
||||
"section" ($section.Title | emojify | safeJS)
|
||||
"summary" (.Summary | plainify | htmlUnescape | safeJS)
|
||||
"content" (.Plain | safeJS)
|
||||
"permalink" .RelPermalink
|
||||
"externalUrl" .Params.externalUrl
|
||||
"type" .Type
|
||||
) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $index | jsonify -}}
|
||||
141
layouts/_default/list.html
Normal file
141
layouts/_default/list.html
Normal file
@@ -0,0 +1,141 @@
|
||||
{{ define "main" }}
|
||||
{{ .Scratch.Set "scope" "list" }}
|
||||
{{ $enableToc := .Params.showTableOfContents | default (site.Params.list.showTableOfContents | default false) }}
|
||||
{{ $showToc := and $enableToc (in .TableOfContents "<ul") }}
|
||||
|
||||
{{/* Hero */}}
|
||||
{{ if site.Params.list.showHero | default false }}
|
||||
{{ $heroStyle := print "hero/" site.Params.list.heroStyle ".html" }}
|
||||
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
|
||||
{{ partial $heroStyle . }}
|
||||
{{ else }}
|
||||
{{ partial "hero/basic.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Header */}}
|
||||
<header>
|
||||
{{ if .Params.showBreadcrumbs | default (site.Params.list.showBreadcrumbs | default false) }}
|
||||
{{ partial "breadcrumbs.html" . }}
|
||||
{{ end }}
|
||||
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title }}</h1>
|
||||
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||
{{ partial "article-meta/list.html" (dict "context" . "scope" "single") }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{/* Description (markdown content) */}}
|
||||
{{ $tocMargin := cond $showToc "mt-12" "mt-0" }}
|
||||
{{ $topClass := cond (hasPrefix site.Params.header.layout "fixed") "lg:top-[140px]" "lg:top-10" }}
|
||||
<section class="{{ $tocMargin }} prose flex max-w-full flex-col dark:prose-invert lg:flex-row mb-10">
|
||||
{{ if $showToc }}
|
||||
<div class="order-first lg:ms-auto px-0 lg:order-last lg:ps-8 lg:max-w-2xs">
|
||||
<div class="toc ps-5 print:hidden lg:sticky {{ $topClass }}">
|
||||
{{ partial "toc.html" . }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="min-w-0 min-h-0 max-w-prose w-full">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{/* Article Grid */}}
|
||||
{{ if gt .Pages 0 }}
|
||||
{{ $cardView := .Params.cardView | default (site.Params.list.cardView | default false) }}
|
||||
{{ $cardViewScreenWidth := .Params.cardViewScreenWidth | default (site.Params.list.cardViewScreenWidth | default false) }}
|
||||
{{ $groupByYear := .Params.groupByYear | default (site.Params.list.groupByYear | default false) }}
|
||||
{{ $orderByWeight := .Params.orderByWeight | default (site.Params.list.orderByWeight | default false) }}
|
||||
{{ $groupByYear := and (not $orderByWeight) $groupByYear }}
|
||||
|
||||
{{ if not $cardView }}
|
||||
<section class="space-y-10 w-full">
|
||||
{{ if not $orderByWeight }}
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
{{ if $groupByYear }}
|
||||
<h2 class="mt-12 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||
{{ .Key }}
|
||||
</h2>
|
||||
{{ end }}
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/simple.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ range (.Paginate (.Pages.ByWeight)).Pages }}
|
||||
{{ partial "article-link/simple.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{/* else: is cardView */}}
|
||||
{{ else }}
|
||||
{{ if $groupByYear }}
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
{{ if $cardViewScreenWidth }}
|
||||
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
|
||||
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||
{{ .Key }}
|
||||
</h2>
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
</div>
|
||||
{{ else }}
|
||||
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||
{{ .Key }}
|
||||
</h2>
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* else: not groupByYear */}}
|
||||
{{ else }}
|
||||
{{ if $cardViewScreenWidth }}
|
||||
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
||||
{{ if not $orderByWeight }}
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ range (.Paginate (.Pages.ByWeight)).Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
</div>
|
||||
{{ else }}
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
{{ if not $orderByWeight }}
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ range (.Paginate (.Pages.ByWeight)).Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}{{/* End of cardViewScreenWidth */}}
|
||||
{{ end }}{{/* End of groupByYear */}}
|
||||
{{ end }}{{/* End of cardView */}}
|
||||
{{/* else: .Pages not greater to zero */}}
|
||||
{{ else }}
|
||||
<section class="mt-10 prose dark:prose-invert">
|
||||
<p class="py-8 border-t">
|
||||
<em>{{ i18n "list.no_articles" | emojify }}</em>
|
||||
</p>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ partial "pagination.html" . }}
|
||||
{{ end }}
|
||||
55
layouts/_default/rss.xml
Normal file
55
layouts/_default/rss.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
{{- $pctx := . -}}
|
||||
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
|
||||
{{- $pages := slice -}}
|
||||
{{- if or $.IsHome $.IsSection -}}
|
||||
{{- $pages = $pctx.RegularPages -}}
|
||||
{{- else -}}
|
||||
{{- $pages = $pctx.Pages -}}
|
||||
{{- end -}}
|
||||
{{- $limit := .Site.Config.Services.RSS.Limit -}}
|
||||
{{- if ge $limit 1 -}}
|
||||
{{- $pages = $pages | first $limit -}}
|
||||
{{- end -}}
|
||||
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
|
||||
<generator>Hugo -- gohugo.io</generator>
|
||||
<language>{{ site.LanguageCode }}</language>{{ with .Site.Params.Author.email }}
|
||||
<managingEditor>{{.}}{{ with $.Site.Params.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Params.Author.email }}
|
||||
<webMaster>{{.}}{{ with $.Site.Params.Author.name }} ({{.}}){{end}}</webMaster>{{end}}
|
||||
{{ if .Site.Params.footer.showCopyright | default true -}}
|
||||
<copyright>{{ with replace .Site.Params.copyright "{ year }" now.Year }}{{.}}{{ else }}© {{ now.Format "2006" }} {{ .Site.Params.Author.name }}{{- end }}</copyright>
|
||||
{{- end }}
|
||||
{{ if not .Date.IsZero }}<lastBuildDate>{{ (index $pages.ByLastmod.Reverse 0).Lastmod.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||
{{- with .OutputFormats.Get "RSS" -}}
|
||||
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
|
||||
{{- end -}}
|
||||
{{ if and (.Site.Params.rssnext.feedId) (.Site.Params.rssnext.userId) }}
|
||||
<follow_challenge>
|
||||
<feedId>{{ .Site.Params.rssnext.feedId }}</feedId>
|
||||
<userId>{{ .Site.Params.rssnext.userId }}</userId>
|
||||
</follow_challenge>
|
||||
{{ end }}
|
||||
{{ range $pages }}
|
||||
<item>
|
||||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||
{{ with .Site.Params.Author.email }}<author>{{.}}{{ with $.Site.Params.Author.name }} ({{.}}){{end}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>
|
||||
{{ range ( where .Site.RegularPages ".RelPermalink" .RelPermalink | first 1 ) }}
|
||||
{{- $images := .Resources.ByType "image" -}}
|
||||
{{- $featured := $images.GetMatch "*feature*" -}}
|
||||
{{- if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end -}}
|
||||
{{- with $featured -}}
|
||||
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="{{ $featured.Permalink }}" />
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
</item>
|
||||
{{ end }}
|
||||
</channel>
|
||||
</rss>
|
||||
18
layouts/_default/simple.html
Normal file
18
layouts/_default/simple.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{{ define "main" }}
|
||||
<article class="max-w-full">
|
||||
<header>
|
||||
{{ if .Params.showBreadcrumbs | default (.Site.Params.article.showBreadcrumbs | default false) }}
|
||||
{{ partial "breadcrumbs.html" . }}
|
||||
{{ end }}
|
||||
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
||||
{{ .Title | emojify }}
|
||||
</h1>
|
||||
</header>
|
||||
<section class="max-w-full mt-6 prose dark:prose-invert">
|
||||
{{ .Content }}
|
||||
</section>
|
||||
<footer class="pt-8">
|
||||
{{ partial "sharing-links.html" . }}
|
||||
</footer>
|
||||
</article>
|
||||
{{ end }}
|
||||
132
layouts/_default/single.html
Normal file
132
layouts/_default/single.html
Normal file
@@ -0,0 +1,132 @@
|
||||
{{ define "main" }}
|
||||
{{ .Scratch.Set "scope" "single" }}
|
||||
<article>
|
||||
{{/* Hero */}}
|
||||
{{ if .Params.showHero | default (site.Params.article.showHero | default false) }}
|
||||
{{ $heroStyle := .Params.heroStyle }}
|
||||
{{ if not $heroStyle }}{{ $heroStyle = site.Params.article.heroStyle }}{{ end }}
|
||||
{{ $heroStyle := print "hero/" $heroStyle ".html" }}
|
||||
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
|
||||
{{ partial $heroStyle . }}
|
||||
{{ else }}
|
||||
{{ partial "hero/basic.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Header */}}
|
||||
<header id="single_header" class="mt-5 max-w-prose">
|
||||
{{ if .Params.showBreadcrumbs | default (site.Params.article.showBreadcrumbs | default false) }}
|
||||
{{ partial "breadcrumbs.html" . }}
|
||||
{{ end }}
|
||||
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
||||
{{ .Title | emojify }}
|
||||
</h1>
|
||||
<div class="mt-1 mb-6 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||
{{ partial "article-meta/basic.html" (dict "context" . "scope" "single") }}
|
||||
</div>
|
||||
{{ if not (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
|
||||
{{ template "SingleAuthor" . }}
|
||||
{{ end }}
|
||||
</header>
|
||||
|
||||
{{/* Body */}}
|
||||
<section class="flex flex-col max-w-full mt-0 prose dark:prose-invert lg:flex-row">
|
||||
{{ $enableToc := site.Params.article.showTableOfContents | default false }}
|
||||
{{ $enableToc = .Params.showTableOfContents | default $enableToc }}
|
||||
{{ $showToc := and $enableToc (in .TableOfContents "<ul") }}
|
||||
{{ $topClass := cond (hasPrefix site.Params.header.layout "fixed") "lg:top-[140px]" "lg:top-10" }}
|
||||
{{ if $showToc }}
|
||||
<div class="order-first lg:ms-auto px-0 lg:order-last lg:ps-8 lg:max-w-2xs">
|
||||
<div class="toc ps-5 print:hidden lg:sticky {{ $topClass }}">
|
||||
{{ partial "toc.html" . }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
|
||||
<div class="min-w-0 min-h-0 max-w-fit">
|
||||
{{ partial "series/series.html" . }}
|
||||
<div class="article-content max-w-prose mb-20">
|
||||
{{ .Content }}
|
||||
{{ $defaultReplyByEmail := site.Params.replyByEmail }}
|
||||
{{ $replyByEmail := default $defaultReplyByEmail .Params.replyByEmail }}
|
||||
{{ if $replyByEmail }}
|
||||
<strong class="block mt-8">
|
||||
<a
|
||||
target="_blank"
|
||||
class="m-1 rounded bg-neutral-300 p-1.5 text-neutral-700 hover:bg-primary-500 hover:text-neutral dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-primary-400 dark:hover:text-neutral-800"
|
||||
href="mailto:{{ site.Params.Author.email }}?subject={{ replace (printf "Reply to %s" .Title) "\"" "'" }}">
|
||||
{{ i18n "article.reply_by_email" | default "Reply by Email" }}
|
||||
</a>
|
||||
</strong>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ if (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
|
||||
{{ template "SingleAuthor" . }}
|
||||
{{ end }}
|
||||
{{ partial "series/series-closed.html" . }}
|
||||
{{ partial "sharing-links.html" . }}
|
||||
{{ partial "related.html" . }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{/* Footer */}}
|
||||
<footer class="pt-8 max-w-prose print:hidden">
|
||||
{{ partial "article-pagination.html" . }}
|
||||
{{ if .Params.showComments | default (site.Params.article.showComments | default false) }}
|
||||
{{ if templates.Exists "partials/comments.html" }}
|
||||
<div class="pt-3">
|
||||
<hr class="border-dotted border-neutral-300 dark:border-neutral-600">
|
||||
<div class="pt-3">
|
||||
{{ partial "comments.html" . }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{ warnf "[BLOWFISH] Comments are enabled for %s but no comments partial exists." .File.Path }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</footer>
|
||||
</article>
|
||||
{{ end }}
|
||||
|
||||
{{ define "SingleAuthor" }}
|
||||
{{ $authorsData := site.Data.authors }}
|
||||
{{ $taxonomies := site.Taxonomies.authors }}
|
||||
{{ $baseURL := site.BaseURL }}
|
||||
{{ $taxonomyLink := 0 }}
|
||||
{{ $showAuthor := 0 }}
|
||||
{{ $isAuthorBottom := (.Params.showAuthorBottom | default ( site.Params.article.showAuthorBottom | default false)) }}
|
||||
|
||||
{{ if not (strings.HasSuffix $baseURL "/") }}
|
||||
{{ $baseURL = delimit (slice $baseURL "/") "" }}
|
||||
{{ end }}
|
||||
|
||||
{{ if .Params.showAuthor | default (site.Params.article.showAuthor | default true) }}
|
||||
{{ $showAuthor = 1 }}
|
||||
{{ partial "author.html" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $author := .Page.Params.authors }}
|
||||
{{ $authorData := index $authorsData $author }}
|
||||
{{- if $authorData -}}
|
||||
{{ range $taxonomyname, $taxonomy := $taxonomies }}
|
||||
{{ if (eq $taxonomyname $author) }}
|
||||
{{ $taxonomyLink = delimit (slice $baseURL "authors/" $author "/") "" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $finalLink := $taxonomyLink }}
|
||||
{{ $currentLang := site.Language.Lang }}
|
||||
{{ if eq site.LanguagePrefix "" }}
|
||||
{{ $finalLink = printf "%sauthors/%s/" $baseURL $author }}
|
||||
{{ else }}
|
||||
{{ $finalLink = printf "%s%s/authors/%s/" $baseURL $currentLang $author }}
|
||||
{{ end }}
|
||||
{{ partial "author-extra.html" (dict "context" . "data" $authorData "link" $finalLink) }}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{ if or $taxonomyLink $showAuthor }}
|
||||
<div class="{{ cond $isAuthorBottom "mb-10" "mb-5" }}"></div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
23
layouts/_default/sitemap.xml
Normal file
23
layouts/_default/sitemap.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
{{ range .Data.Pages }}{{ if and (not .Sitemap.Disable) (not (in .Site.Params.sitemap.excludedKinds .Kind)) }}{{ if and (.Param "xml" | default true) (or (not .Params.externalUrl) (and (.Params.externalUrl) (and (not (hasPrefix .Params.externalUrl "http://")) (not (hasPrefix .Params.externalUrl "https://"))))) (not .Params.excludeFromSearch) }}
|
||||
{{- if .Permalink -}}{{ $link := .Permalink }}{{ if .Params.externalUrl }}{{ $link = printf "%s%s" .Site.BaseURL (substr .Params.externalUrl 1) }}{{ end }}
|
||||
<url>
|
||||
<loc>{{ $link }}</loc>{{ if not .Lastmod.IsZero }}
|
||||
<lastmod>{{ safeHTML ( .Lastmod.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
|
||||
<changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
|
||||
<priority>{{ .Sitemap.Priority }}</priority>{{ end }}{{ if .IsTranslated }}{{ range .Translations }}
|
||||
<xhtml:link
|
||||
rel="alternate"
|
||||
hreflang="{{ .Language.LanguageCode }}"
|
||||
href="{{ .Permalink }}"
|
||||
/>{{ end }}
|
||||
<xhtml:link
|
||||
rel="alternate"
|
||||
hreflang="{{ .Language.LanguageCode }}"
|
||||
href="{{ $link }}"
|
||||
/>{{ end }}
|
||||
</url>
|
||||
{{- end -}}{{ end }}{{ end }}{{ end }}
|
||||
</urlset>
|
||||
118
layouts/_default/term.html
Normal file
118
layouts/_default/term.html
Normal file
@@ -0,0 +1,118 @@
|
||||
{{ define "main" }}
|
||||
{{ .Scratch.Set "scope" "term" }}
|
||||
{{ $showHero := .Params.showHero | default site.Params.term.showHero | default false }}
|
||||
{{ if $showHero }}
|
||||
{{ $heroStyle := print "hero/" site.Params.term.heroStyle ".html" }}
|
||||
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
|
||||
{{ partial $heroStyle . }}
|
||||
{{ else }}
|
||||
{{ partial "hero/basic.html" . }}
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
<header class="mt-5">
|
||||
{{ if .Params.showBreadcrumbs | default (site.Params.term.showBreadcrumbs | default false) }}
|
||||
{{ partial "breadcrumbs.html" . }}
|
||||
{{ end }}
|
||||
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title }}</h1>
|
||||
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||
{{ partial "article-meta/term.html" (dict "context" . "scope" "single") }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="flex flex-col max-w-full mt-0 mb-5 prose dark:prose-invert lg:flex-row">
|
||||
{{ if .Content }}
|
||||
<div class="min-w-0 min-h-0 max-w-prose">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
|
||||
{{ if gt .Pages 0 }}
|
||||
{{ $cardView := .Params.cardView | default (site.Params.term.cardView | default false) }}
|
||||
{{ $cardViewScreenWidth := site.Params.term.cardViewScreenWidth | default false }}
|
||||
{{ $groupByYear := .Params.groupByYear | default (site.Params.term.groupByYear | default false) }}
|
||||
|
||||
{{ if not $cardView }}
|
||||
|
||||
<section class="space-y-10 w-full">
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
{{ if $groupByYear }}
|
||||
<h2 class="mt-12 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||
{{ .Key }}
|
||||
</h2>
|
||||
{{ end }}
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/simple.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ else if and $cardView (not $cardViewScreenWidth) }}
|
||||
|
||||
{{ if $groupByYear }}
|
||||
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||
{{ .Key }}
|
||||
</h2>
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ else }}
|
||||
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ else if and $cardView $cardViewScreenWidth }}
|
||||
|
||||
{{ if $groupByYear }}
|
||||
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
|
||||
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||
{{ .Key }}
|
||||
</h2>
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ else }}
|
||||
|
||||
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
||||
{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups }}
|
||||
{{ range .Pages }}
|
||||
{{ partial "article-link/card.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ else }}
|
||||
<section class="mt-10 prose dark:prose-invert">
|
||||
<p class="py-8 border-t">
|
||||
<em>{{ i18n "term.no_articles" | emojify }}</em>
|
||||
</p>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ partial "pagination.html" . }}
|
||||
{{ end }}
|
||||
45
layouts/_default/terms.html
Normal file
45
layouts/_default/terms.html
Normal file
@@ -0,0 +1,45 @@
|
||||
{{ define "main" }}
|
||||
{{ .Scratch.Set "scope" "list" }}
|
||||
{{ $showHero := .Params.showHero | default site.Params.taxonomy.showHero | default false }}
|
||||
{{ if $showHero }}
|
||||
{{ $heroStyle := print "hero/" site.Params.taxonomy.heroStyle ".html" }}
|
||||
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
|
||||
{{ partial $heroStyle . }}
|
||||
{{ else }}
|
||||
{{ partial "hero/basic.html" . }}
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
<header class="mt-5">
|
||||
{{ if .Params.showBreadcrumbs | default (site.Params.taxonomy.showBreadcrumbs | default false) }}
|
||||
{{ partial "breadcrumbs.html" . }}
|
||||
{{ end }}
|
||||
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title }}</h1>
|
||||
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||
{{ partial "article-meta/taxonomy.html" (dict "context" . "scope" "single") }}
|
||||
</div>
|
||||
</header>
|
||||
{{ if .Content }}
|
||||
<section class="flex flex-col max-w-full mt-0 prose dark:prose-invert lg:flex-row">
|
||||
<div class="min-w-0 min-h-0 max-w-prose">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ if site.Params.taxonomy.cardView }}
|
||||
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
{{ range .Data.Terms }}
|
||||
{{ partial "term-link/card.html" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ else }}
|
||||
|
||||
<section class="flex flex-wrap max-w-prose -mx-2 overflow-hidden">
|
||||
{{ range .Data.Terms }}
|
||||
{{ partial "term-link/text.html" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
8
layouts/index.html
Normal file
8
layouts/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{{ define "main" }}
|
||||
{{ $partial := print "home/" .Site.Params.homepage.layout ".html" }}
|
||||
{{ if templates.Exists ( printf "partials/%s" $partial ) }}
|
||||
{{ partial $partial . }}
|
||||
{{ else }}
|
||||
{{ partial "home/profile.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
11
layouts/partials/analytics/fathom.html
Normal file
11
layouts/partials/analytics/fathom.html
Normal 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 }}
|
||||
10
layouts/partials/analytics/ga.html
Normal file
10
layouts/partials/analytics/ga.html
Normal 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>
|
||||
12
layouts/partials/analytics/main.html
Normal file
12
layouts/partials/analytics/main.html
Normal 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 }}
|
||||
21
layouts/partials/analytics/seline.html
Normal file
21
layouts/partials/analytics/seline.html
Normal 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 }}
|
||||
27
layouts/partials/analytics/umami.html
Normal file
27
layouts/partials/analytics/umami.html
Normal 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 }}
|
||||
135
layouts/partials/article-link/_shortcode.html
Normal file
135
layouts/partials/article-link/_shortcode.html
Normal 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">↗</span>
|
||||
<span class="ltr:hidden">↖</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>
|
||||
101
layouts/partials/article-link/card-related.html
Normal file
101
layouts/partials/article-link/card-related.html
Normal 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">↗</span>
|
||||
<span class="ltr:hidden">↖</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>
|
||||
104
layouts/partials/article-link/card.html
Normal file
104
layouts/partials/article-link/card.html
Normal 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">↗</span>
|
||||
<span class="ltr:hidden">↖</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>
|
||||
123
layouts/partials/article-link/simple.html
Normal file
123
layouts/partials/article-link/simple.html
Normal 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">↗</span>
|
||||
<span class="ltr:hidden">↖</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>
|
||||
125
layouts/partials/article-meta/basic.html
Normal file
125
layouts/partials/article-meta/basic.html
Normal 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\">·</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) }}
|
||||
,
|
||||
{{ 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 }}
|
||||
35
layouts/partials/article-meta/list.html
Normal file
35
layouts/partials/article-meta/list.html
Normal 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\">·</span>" | safeHTML }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
35
layouts/partials/article-meta/taxonomy.html
Normal file
35
layouts/partials/article-meta/taxonomy.html
Normal 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\">·</span>" | safeHTML }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
35
layouts/partials/article-meta/term.html
Normal file
35
layouts/partials/article-meta/term.html
Normal 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\">·</span>" | safeHTML }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
47
layouts/partials/article-pagination.html
Normal file
47
layouts/partials/article-pagination.html
Normal 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">←</span> {{ $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 }} <span class="inline-block rtl:rotate-180">→</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 }}
|
||||
62
layouts/partials/author-extra.html
Normal file
62
layouts/partials/author-extra.html
Normal 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>
|
||||
17
layouts/partials/author-links.html
Normal file
17
layouts/partials/author-links.html
Normal 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 }}
|
||||
57
layouts/partials/author.html
Normal file
57
layouts/partials/author.html
Normal 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>
|
||||
6
layouts/partials/badge.html
Normal file
6
layouts/partials/badge.html
Normal 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>
|
||||
19
layouts/partials/breadcrumbs.html
Normal file
19
layouts/partials/breadcrumbs.html
Normal 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 }}
|
||||
47
layouts/partials/contributors.html
Normal file
47
layouts/partials/contributors.html
Normal 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" }} →
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
8
layouts/partials/extend-footer.html
Normal file
8
layouts/partials/extend-footer.html
Normal 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>
|
||||
*/}}
|
||||
1
layouts/partials/extend-head.html
Normal file
1
layouts/partials/extend-head.html
Normal file
@@ -0,0 +1 @@
|
||||
<script defer src="https://umami.novicelab.io/script.js" data-website-id="6dc76b26-f911-4b0e-9827-6c2bfd49df04"></script>
|
||||
76
layouts/partials/footer.html
Normal file
76
layouts/partials/footer.html
Normal 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 }}
|
||||
©
|
||||
{{ 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>
|
||||
1
layouts/partials/functions/date.html
Normal file
1
layouts/partials/functions/date.html
Normal file
@@ -0,0 +1 @@
|
||||
{{ return time.Format (site.Language.Params.dateFormat | default ":date_long") . }}
|
||||
13
layouts/partials/functions/uid.html
Normal file
13
layouts/partials/functions/uid.html
Normal 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
210
layouts/partials/head.html
Normal 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 }} · {{ .Site.Title | emojify }}</title>
|
||||
<meta name="title" content="{{ .Title | emojify }} · {{ .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>
|
||||
83
layouts/partials/header/basic.html
Normal file
83
layouts/partials/header/basic.html
Normal 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 }}
|
||||
88
layouts/partials/header/components/a11y.html
Normal file
88
layouts/partials/header/components/a11y.html
Normal 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>
|
||||
116
layouts/partials/header/components/desktop-menu.html
Normal file
116
layouts/partials/header/components/desktop-menu.html
Normal 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 }}
|
||||
152
layouts/partials/header/components/mobile-menu.html
Normal file
152
layouts/partials/header/components/mobile-menu.html
Normal 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 }}
|
||||
23
layouts/partials/header/components/translations.html
Normal file
23
layouts/partials/header/components/translations.html
Normal 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 }}
|
||||
16
layouts/partials/header/fixed-fill-blur.html
Normal file
16
layouts/partials/header/fixed-fill-blur.html
Normal 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>
|
||||
6
layouts/partials/header/fixed-fill.html
Normal file
6
layouts/partials/header/fixed-fill.html
Normal 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>
|
||||
18
layouts/partials/header/fixed-gradient.html
Normal file
18
layouts/partials/header/fixed-gradient.html
Normal 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>
|
||||
16
layouts/partials/header/fixed.html
Normal file
16
layouts/partials/header/fixed.html
Normal 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>
|
||||
98
layouts/partials/hero/background.html
Normal file
98
layouts/partials/hero/background.html
Normal 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 -}}
|
||||
63
layouts/partials/hero/basic.html
Normal file
63
layouts/partials/hero/basic.html
Normal 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 }}
|
||||
76
layouts/partials/hero/big.html
Normal file
76
layouts/partials/hero/big.html
Normal 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 }}
|
||||
136
layouts/partials/hero/thumbAndBackground.html
Normal file
136
layouts/partials/hero/thumbAndBackground.html
Normal 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 }}
|
||||
118
layouts/partials/home/background.html
Normal file
118
layouts/partials/home/background.html
Normal 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 }}
|
||||
53
layouts/partials/home/card.html
Normal file
53
layouts/partials/home/card.html
Normal 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>
|
||||
134
layouts/partials/home/hero.html
Normal file
134
layouts/partials/home/hero.html
Normal 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>
|
||||
11
layouts/partials/home/page.html
Normal file
11
layouts/partials/home/page.html
Normal 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>
|
||||
47
layouts/partials/home/profile.html
Normal file
47
layouts/partials/home/profile.html
Normal 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>
|
||||
@@ -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>
|
||||
6
layouts/partials/icon.html
Normal file
6
layouts/partials/icon.html
Normal file
@@ -0,0 +1,6 @@
|
||||
{{- $icon := resources.Get (print "icons/" . ".svg") -}}
|
||||
{{- if $icon -}}
|
||||
<span class="relative block icon">
|
||||
{{- $icon.Content | safeHTML -}}
|
||||
</span>
|
||||
{{- end -}}
|
||||
34
layouts/partials/impls/hooks/admonition-maps.html
Normal file
34
layouts/partials/impls/hooks/admonition-maps.html
Normal 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"
|
||||
)
|
||||
-}}
|
||||
37
layouts/partials/init.html
Normal file
37
layouts/partials/init.html
Normal 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") }}
|
||||
4
layouts/partials/meta/date-updated.html
Normal file
4
layouts/partials/meta/date-updated.html
Normal 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 */ -}}
|
||||
4
layouts/partials/meta/date.html
Normal file
4
layouts/partials/meta/date.html
Normal 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 */ -}}
|
||||
19
layouts/partials/meta/edit.html
Normal file
19
layouts/partials/meta/edit.html
Normal 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 */ -}}
|
||||
25
layouts/partials/meta/likes.html
Normal file
25
layouts/partials/meta/likes.html
Normal 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>
|
||||
14
layouts/partials/meta/likes_button.html
Normal file
14
layouts/partials/meta/likes_button.html
Normal 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"> Like</span>
|
||||
</button>
|
||||
</span>
|
||||
{{- /* Trim EOF */ -}}
|
||||
4
layouts/partials/meta/reading-time.html
Normal file
4
layouts/partials/meta/reading-time.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<span title="{{ i18n "article.reading_time_title" }}">
|
||||
{{- i18n "article.reading_time" .ReadingTime | markdownify | emojify -}}
|
||||
</span>
|
||||
{{- /* Trim EOF */ -}}
|
||||
25
layouts/partials/meta/views.html
Normal file
25
layouts/partials/meta/views.html
Normal 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>
|
||||
4
layouts/partials/meta/word-count.html
Normal file
4
layouts/partials/meta/word-count.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<span>
|
||||
{{- i18n "article.word_count" .WordCount | markdownify -}}
|
||||
</span>
|
||||
{{- /* Trim EOF */ -}}
|
||||
10
layouts/partials/meta/zen-mode.html
Normal file
10
layouts/partials/meta/zen-mode.html
Normal 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>
|
||||
52
layouts/partials/pagination.html
Normal file
52
layouts/partials/pagination.html
Normal 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"
|
||||
>←</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">…</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"
|
||||
>→</a
|
||||
>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{- end -}}
|
||||
47
layouts/partials/recent-articles-demo.html
Normal file
47
layouts/partials/recent-articles-demo.html
Normal 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 ↻
|
||||
</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 }}
|
||||
13
layouts/partials/recent-articles/cardview-fullwidth.html
Normal file
13
layouts/partials/recent-articles/cardview-fullwidth.html
Normal 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>
|
||||
11
layouts/partials/recent-articles/cardview.html
Normal file
11
layouts/partials/recent-articles/cardview.html
Normal 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>
|
||||
9
layouts/partials/recent-articles/list.html
Normal file
9
layouts/partials/recent-articles/list.html
Normal 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>
|
||||
30
layouts/partials/recent-articles/main.html
Normal file
30
layouts/partials/recent-articles/main.html
Normal 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 }}
|
||||
11
layouts/partials/related.html
Normal file
11
layouts/partials/related.html
Normal 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 }}
|
||||
73
layouts/partials/schema.html
Normal file
73
layouts/partials/schema.html
Normal 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 }}
|
||||
21
layouts/partials/scroll-to-top.html
Normal file
21
layouts/partials/scroll-to-top.html
Normal 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" }}">
|
||||
↑
|
||||
</a>
|
||||
</div>
|
||||
43
layouts/partials/search.html
Normal file
43
layouts/partials/search.html
Normal 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">·</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">→</div>
|
||||
<div class="mr-2 ltr:hidden rtl:block text-neutral-500">←</div>
|
||||
</a>
|
||||
</li> -->
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
5
layouts/partials/series/series-closed.html
Normal file
5
layouts/partials/series/series-closed.html
Normal 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 }}
|
||||
7
layouts/partials/series/series.html
Normal file
7
layouts/partials/series/series.html
Normal 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 }}
|
||||
25
layouts/partials/series/series_base.html
Normal file
25
layouts/partials/series/series_base.html
Normal 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 }}
|
||||
17
layouts/partials/sharing-links.html
Normal file
17
layouts/partials/sharing-links.html
Normal 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 }}
|
||||
55
layouts/partials/sponsors.html
Normal file
55
layouts/partials/sponsors.html
Normal 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 }}
|
||||
67
layouts/partials/term-link/card.html
Normal file
67
layouts/partials/term-link/card.html
Normal 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>
|
||||
15
layouts/partials/term-link/text.html
Normal file
15
layouts/partials/term-link/text.html
Normal 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">·</span>
|
||||
<span class="text-base text-neutral-400">
|
||||
{{ .Count }}
|
||||
</span>
|
||||
{{ end }}
|
||||
</h2>
|
||||
</article>
|
||||
125
layouts/partials/toc.html
Normal file
125
layouts/partials/toc.html
Normal 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 }}
|
||||
195
layouts/partials/vendor.html
Normal file
195
layouts/partials/vendor.html
Normal file
@@ -0,0 +1,195 @@
|
||||
{{/* Mermaid */}}
|
||||
{{ if .Page.HasShortcode "mermaid" }}
|
||||
{{ $mermaidLib := resources.Get "lib/mermaid/mermaid.min.js" }}
|
||||
{{ $mermaidConfig := resources.Get "js/mermaid.js" }}
|
||||
{{ $mermaidConfig := $mermaidConfig | resources.Minify }}
|
||||
{{ $mermaidJS := slice $mermaidLib $mermaidConfig | resources.Concat "js/mermaid.bundle.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script
|
||||
defer
|
||||
type="text/javascript"
|
||||
src="{{ $mermaidJS.RelPermalink }}"
|
||||
integrity="{{ $mermaidJS.Data.Integrity }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{/* Chart */}}
|
||||
{{ if .Page.HasShortcode "chart" }}
|
||||
{{ $chartLib := resources.Get "lib/chart/chart.min.js" }}
|
||||
{{ $chartConfig := resources.Get "js/chart.js" }}
|
||||
{{ $chartConfig := $chartConfig | resources.Minify }}
|
||||
{{ $chartJS := slice $chartLib $chartConfig | resources.Concat "js/chart.bundle.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script
|
||||
defer
|
||||
type="text/javascript"
|
||||
src="{{ $chartJS.RelPermalink }}"
|
||||
integrity="{{ $chartJS.Data.Integrity }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{/* Katex */}}
|
||||
{{ if .Page.HasShortcode "katex" }}
|
||||
{{ $katexCSS := resources.Get "lib/katex/katex.min.css" }}
|
||||
{{ $katexCSS := $katexCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<link
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
href="{{ $katexCSS.RelPermalink }}"
|
||||
integrity="{{ $katexCSS.Data.Integrity }}">
|
||||
{{ $katexLib := resources.Get "lib/katex/katex.min.js" }}
|
||||
{{ $katexRenderLib := resources.Get "lib/katex/auto-render.min.js" }}
|
||||
{{ $katexJS := slice $katexLib $katexRenderLib | resources.Concat "js/katex.bundle.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script
|
||||
defer
|
||||
type="text/javascript"
|
||||
src="{{ $katexJS.RelPermalink }}"
|
||||
integrity="{{ $katexJS.Data.Integrity }}"
|
||||
id="katex-render"></script>
|
||||
{{ $katexFonts := resources.Match "lib/katex/fonts/*" }}
|
||||
{{ range $katexFonts }}
|
||||
<!-- {{ .RelPermalink }} -->
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* TypeIt */}}
|
||||
{{ if .Page.HasShortcode "typeit" }}
|
||||
{{ $typeitLib := resources.Get "lib/typeit/typeit.umd.js" | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script defer src="{{ $typeitLib.RelPermalink }}" integrity="{{ $typeitLib.Data.Integrity }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{/* Packery */}}
|
||||
{{ if .Page.HasShortcode "gallery" }}
|
||||
{{ $galleryCSS := resources.Get "css/components/gallery.css" }}
|
||||
{{ $galleryCSS = $galleryCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<link
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
href="{{ $galleryCSS.RelPermalink }}"
|
||||
integrity="{{ $galleryCSS.Data.Integrity }}">
|
||||
{{ $packeryLib := resources.Get "lib/packery/packery.pkgd.min.js" }}
|
||||
{{ $packeryLib = $packeryLib | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script
|
||||
defer
|
||||
type="text/javascript"
|
||||
src="{{ $packeryLib.RelPermalink }}"
|
||||
integrity="{{ $packeryLib.Data.Integrity }}"></script>
|
||||
|
||||
{{ $jsShortcodeGallery := resources.Get "js/shortcodes/gallery.js" }}
|
||||
{{ $jsShortcodeGallery = $jsShortcodeGallery | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{ $jsShortcodeGallery.RelPermalink }}"
|
||||
integrity="{{ $jsShortcodeGallery.Data.Integrity }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{/* tw-elements */}}
|
||||
{{ if or (.Page.HasShortcode "carousel") (.Page.HasShortcode "timeline") }}
|
||||
{{ $carouselCSS := resources.Get "css/components/carousel.css" }}
|
||||
{{ $carouselCSS = $carouselCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<link
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
href="{{ $carouselCSS.RelPermalink }}"
|
||||
integrity="{{ $carouselCSS.Data.Integrity }}">
|
||||
{{ $twelementsLib := resources.Get "lib/tw-elements/index.min.js" }}
|
||||
{{ $twelementsLib = $twelementsLib | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script
|
||||
defer
|
||||
type="text/javascript"
|
||||
src="{{ $twelementsLib.RelPermalink }}"
|
||||
integrity="{{ $twelementsLib.Data.Integrity }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{/* youtubeLite */}}
|
||||
{{ if .Page.HasShortcode "youtubeLite" }}
|
||||
{{ $youtubeLiteCSS := resources.Get "lib/lite-youtube-embed/lite-yt-embed.css" }}
|
||||
{{ $youtubeLiteCSS = $youtubeLiteCSS | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<link
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
href="{{ $youtubeLiteCSS.RelPermalink }}"
|
||||
integrity="{{ $youtubeLiteCSS.Data.Integrity }}">
|
||||
{{ $youtubeLiteLib := resources.Get "lib/lite-youtube-embed/lite-yt-embed.js" }}
|
||||
{{ $youtubeLiteLib = $youtubeLiteLib | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{ $youtubeLiteLib.RelPermalink }}"
|
||||
integrity="{{ $youtubeLiteLib.Data.Integrity }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{/* Repo cards */}}
|
||||
{{ $repoCards := slice "codeberg" "forgejo" "gitea" "github" "hugging-face" }}
|
||||
{{ $hasRepoCards := false }}
|
||||
{{ range $repoCards }}
|
||||
{{ if $.Page.HasShortcode . }}
|
||||
{{ $hasRepoCards = true }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $hasRepoCards }}
|
||||
{{ $repoColors := site.Data.repoColors }}
|
||||
{{ $cssRules := slice }}
|
||||
|
||||
{{/* default color */}}
|
||||
{{ $cssRules = $cssRules | append ".language-dot[data-language=\"default\"] { background-color: #0077b6; }" }}
|
||||
|
||||
{{/* Hugging Face model color */}}
|
||||
{{ $cssRules = $cssRules | append ".language-dot[data-language=\"model\"] { background-color: #ff6b35; }" }}
|
||||
|
||||
{{ range $lang, $color := $repoColors }}
|
||||
{{ $cssRules = $cssRules | append (printf ".language-dot[data-language=\"%s\"] { background-color: %s; }" $lang $color) }}
|
||||
{{ end }}
|
||||
{{ $repoCardCSS := resources.FromString "css/repo-cards.css" (delimit $cssRules "\n")
|
||||
| minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512")
|
||||
}}
|
||||
<link rel="stylesheet" href="{{ $repoCardCSS.RelPermalink }}" integrity="{{ $repoCardCSS.Data.Integrity }}">
|
||||
{{ end }}
|
||||
|
||||
{{/* tabs */}}
|
||||
{{ if .Page.HasShortcode "tabs" }}
|
||||
{{ $tabJS := resources.Get "js/shortcodes/tabs.js" }}
|
||||
{{ with $tabJS | minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Firebase */}}
|
||||
{{ if site.Params.firebase.apiKey }}
|
||||
{{ $firebase := resources.Get "js/firebase.js" }}
|
||||
{{ $firebase = $firebase | resources.Minify | resources.Fingerprint (site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||
<script type="module" src="{{ $firebase.RelPermalink }}" integrity="{{ $firebase.Data.Integrity }}"></script>
|
||||
|
||||
{{ if in (slice "page" "section") .Kind }}
|
||||
{{ $translations := .AllTranslations }}
|
||||
{{ with .File }}
|
||||
{{ $path := .Path }}
|
||||
{{ range $translations }}
|
||||
{{ $path = replace $path (print "." .Lang ".md") ".md" }}
|
||||
{{ end }}
|
||||
{{ partial "inline/firebase-config.html" (dict "views" (printf "views_%s" $path) "likes" (printf "likes_%s" $path)) }}
|
||||
{{ end }}
|
||||
{{ else if eq .Kind "term" }}
|
||||
{{ partial "inline/firebase-config.html" (dict "views" (printf "views_term_%s" .Data.Term) "likes" (printf "likes_term_%s" .Data.Term)) }}
|
||||
{{ else if eq .Kind "taxonomy" }}
|
||||
{{ partial "inline/firebase-config.html" (dict "views" (printf "views_taxonomy_%s" .Data.Plural) "likes" (printf "likes_taxonomy_%s" .Data.Plural)) }}
|
||||
{{ else if eq .Kind "home" }}
|
||||
{{ partial "inline/firebase-config.html" (dict "views" "views_home" "likes" "likes_home") }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ define "_partials/inline/firebase-config.html" }}
|
||||
<script id="firebase-config"
|
||||
type="application/json"
|
||||
data-views="{{ .views }}"
|
||||
data-likes="{{ .likes }}">
|
||||
{
|
||||
"config": {
|
||||
"apiKey": "{{ site.Params.firebase.apiKey }}",
|
||||
"authDomain": "{{ site.Params.firebase.authDomain }}",
|
||||
"projectId": "{{ site.Params.firebase.projectId }}",
|
||||
"storageBucket": "{{ site.Params.firebase.storageBucket }}",
|
||||
"messagingSenderId": "{{ site.Params.firebase.messagingSenderId }}",
|
||||
"appId": "{{ site.Params.firebase.appId }}",
|
||||
"measurementId": "{{ site.Params.firebase.measurementId }}"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{{ end }}
|
||||
7
layouts/robots.txt
Normal file
7
layouts/robots.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
User-agent: *
|
||||
{{- if hugo.IsProduction | or (eq .Site.Params.env "production") }}
|
||||
Allow: /
|
||||
{{- else }}
|
||||
Disallow: /
|
||||
{{- end }}
|
||||
Sitemap: {{ "sitemap.xml" | absURL }}
|
||||
46
layouts/shortcodes/accordion.html
Normal file
46
layouts/shortcodes/accordion.html
Normal file
@@ -0,0 +1,46 @@
|
||||
{{ $id := delimit (slice "accordion" (partial "functions/uid.html" .)) "-" }}
|
||||
{{ $mode := .Get "mode" | default "collapse" }}
|
||||
{{ $separated := .Get "separated" | default false }}
|
||||
{{ $isSeparated := or (eq $separated true) (eq $separated "true") }}
|
||||
|
||||
<div
|
||||
id="{{ $id }}"
|
||||
class="{{ if $isSeparated }}space-y-2{{ else }}border border-neutral-200 dark:border-neutral-700 rounded-lg overflow-hidden{{ end }}"
|
||||
data-accordion="{{ $mode }}"
|
||||
data-accordion-separated="{{ $isSeparated }}"
|
||||
>
|
||||
{{- .Inner -}}
|
||||
</div>
|
||||
{{ if $isSeparated }}
|
||||
<style>
|
||||
#{{ $id }} > details + details {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
{{ else }}
|
||||
<style>
|
||||
#{{ $id }} > details + details {
|
||||
border-top: 1px solid rgb(var(--color-neutral-200));
|
||||
}
|
||||
.dark #{{ $id }} > details + details {
|
||||
border-top-color: rgb(var(--color-neutral-700));
|
||||
}
|
||||
</style>
|
||||
{{ end }}
|
||||
{{ if eq $mode "collapse" }}
|
||||
<script>
|
||||
(() => {
|
||||
const root = document.getElementById("{{ $id }}");
|
||||
if (!root) return;
|
||||
const items = root.querySelectorAll("details[data-accordion-item]");
|
||||
items.forEach((item) => {
|
||||
item.addEventListener("toggle", () => {
|
||||
if (!item.open) return;
|
||||
items.forEach((other) => {
|
||||
if (other !== item) other.removeAttribute("open");
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{{ end }}
|
||||
37
layouts/shortcodes/accordionItem.html
Normal file
37
layouts/shortcodes/accordionItem.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{{ $parent := .Parent }}
|
||||
{{ $separated := false }}
|
||||
{{ if $parent }}
|
||||
{{ $separated = $parent.Get "separated" | default false }}
|
||||
{{ end }}
|
||||
{{ $isSeparated := or (eq $separated true) (eq $separated "true") }}
|
||||
|
||||
{{ $title := .Get "title" | default (.Get "header") }}
|
||||
{{ $icon := .Get "icon" }}
|
||||
{{ $md := .Get "md" | default true }}
|
||||
{{ $open := .Get "open" | default false }}
|
||||
{{ $isOpen := or (eq $open true) (eq $open "true") }}
|
||||
|
||||
<details
|
||||
class="{{ if $isSeparated }}rounded-lg border border-neutral-200 dark:border-neutral-700 overflow-hidden{{ else }}border-none{{ end }}"
|
||||
data-accordion-item
|
||||
{{ if $isOpen }}open{{ end }}
|
||||
>
|
||||
<summary class="flex w-full cursor-pointer items-center justify-between gap-4 px-4 py-3 text-left text-lg font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
<span class="flex items-center gap-2">
|
||||
{{ with $icon }}
|
||||
{{ partial "icon.html" . }}
|
||||
{{ end }}
|
||||
<span>{{ $title }}</span>
|
||||
</span>
|
||||
<span>
|
||||
{{ partial "icon" "chevron-down" }}
|
||||
</span>
|
||||
</summary>
|
||||
<div class="px-4 pb-4 text-neutral-700 dark:text-neutral-300">
|
||||
{{ if $md }}
|
||||
{{- .Inner | markdownify -}}
|
||||
{{ else }}
|
||||
{{- .Inner -}}
|
||||
{{ end }}
|
||||
</div>
|
||||
</details>
|
||||
37
layouts/shortcodes/alert.html
Normal file
37
layouts/shortcodes/alert.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{{ if .IsNamedParams }}
|
||||
{{ $.Scratch.Set "icon" (default "triangle-exclamation" (.Get "icon") ) }}
|
||||
{{ $.Scratch.Set "cardColor" (.Get "cardColor") }}
|
||||
{{ $.Scratch.Set "iconColor" (.Get "iconColor") }}
|
||||
{{ $.Scratch.Set "textColor" (.Get "textColor") }}
|
||||
{{ else }}
|
||||
{{ $.Scratch.Set "icon" (default "triangle-exclamation" (.Get 0) ) }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
<div
|
||||
{{ if not ($.Scratch.Get "cardColor") }}
|
||||
class="flex px-4 py-3 rounded-md shadow bg-primary-100 dark:bg-primary-900"
|
||||
{{ else }}
|
||||
class="flex px-4 py-3 rounded-md shadow" style="background-color: {{ $.Scratch.Get "cardColor" }}"
|
||||
{{ end }}
|
||||
>
|
||||
<span
|
||||
{{ if not ($.Scratch.Get "iconColor") }}
|
||||
class="text-primary-400 pe-3 flex items-center"
|
||||
{{ else }}
|
||||
class="pe-3 flex items-center" style="color: {{ $.Scratch.Get "iconColor" }}"
|
||||
{{ end }}
|
||||
>
|
||||
{{ partial "icon.html" ($.Scratch.Get "icon") }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
{{ if not ($.Scratch.Get "textColor") }}
|
||||
class="dark:text-neutral-300"
|
||||
{{ else }}
|
||||
style="color: {{ $.Scratch.Get "textColor" }}"
|
||||
{{ end }}
|
||||
>
|
||||
{{- .Inner | markdownify -}}
|
||||
</span>
|
||||
</div>
|
||||
13
layouts/shortcodes/article.html
Normal file
13
layouts/shortcodes/article.html
Normal file
@@ -0,0 +1,13 @@
|
||||
{{ $link := .Get "link" }}
|
||||
{{ $showSummary := .Get "showSummary" }}
|
||||
{{ $compactSummary := .Get "compactSummary" | default false }}
|
||||
{{ $target := .Page }}
|
||||
{{ if ne $link .Page.RelPermalink }}
|
||||
{{ $target = index (first 1 (where .Site.AllPages "RelPermalink" $link)) 0 }}
|
||||
{{ end }}
|
||||
{{ if $target }}
|
||||
<section class="space-y-10 w-full">
|
||||
{{ $context := dict "target" $target "showSummary" $showSummary "compactSummary" $compactSummary }}
|
||||
{{ partial "article-link/_shortcode.html" $context }}
|
||||
</section>
|
||||
{{ end }}
|
||||
1
layouts/shortcodes/badge.html
Normal file
1
layouts/shortcodes/badge.html
Normal file
@@ -0,0 +1 @@
|
||||
{{ partial "badge.html" .Inner }}
|
||||
8
layouts/shortcodes/button.html
Normal file
8
layouts/shortcodes/button.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<a
|
||||
class="!rounded-md bg-primary-600 px-4 py-2 !text-neutral !no-underline hover:!bg-primary-500 dark:bg-primary-800 dark:hover:!bg-primary-700"
|
||||
{{ with .Get "href" }}href="{{ . }}"{{ end }}
|
||||
{{ with .Get "target" }}target="{{ . }}"{{ end }}
|
||||
{{ with .Get "rel" }}rel="{{ . }}"{{ end }}
|
||||
role="button">
|
||||
{{ .Inner }}
|
||||
</a>
|
||||
145
layouts/shortcodes/carousel.html
Normal file
145
layouts/shortcodes/carousel.html
Normal file
@@ -0,0 +1,145 @@
|
||||
{{ $id := delimit (slice "carousel" (partial "functions/uid.html" .) (now.UnixNano)) "-" }}
|
||||
{{ $aspect := (split (.Get "aspectRatio") "-") }}
|
||||
{{ $aspectx := default "16" (index $aspect 0) }}
|
||||
{{ $aspecty := default "9" (index $aspect 1) }}
|
||||
{{ $interval := default "2000" (.Get "interval") }}
|
||||
|
||||
{{ $page := .Page.Resources }}
|
||||
{{ $imagesTemp := .Get "images" }}
|
||||
{{ $imagesTemp = strings.TrimPrefix "{" $imagesTemp }}
|
||||
{{ $imagesTemp = strings.TrimSuffix "}" $imagesTemp }}
|
||||
{{ $imagesTemp := strings.Split $imagesTemp "," }}
|
||||
{{ $images := slice }}
|
||||
{{ range $imagesTemp }}
|
||||
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
|
||||
{{ $images = $images | append (dict "resource" (resources.GetRemote .) "key" .) }}
|
||||
{{ else }}
|
||||
{{ range ($page.Match .) }}
|
||||
{{ $images = $images | append (dict "resource" . "key" .Name) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $captionsParam := .Get "captions" }}
|
||||
{{ $captions := dict }}
|
||||
{{ if $captionsParam }}
|
||||
{{ $captionsTemp := strings.TrimPrefix "{" $captionsParam }}
|
||||
{{ $captionsTemp = strings.TrimSuffix "}" $captionsTemp }}
|
||||
{{ range (strings.Split $captionsTemp ",") }}
|
||||
{{ $pair := strings.Split . ":" }}
|
||||
{{ if ge (len $pair) 2 }}
|
||||
{{ $key := strings.TrimSpace (index $pair 0) }}
|
||||
{{ $value := strings.TrimSpace (delimit (after 1 $pair) ":") }}
|
||||
{{ $captions = merge $captions (dict $key $value) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if not .Parent }}<div class="width-patch"></div>{{ end }}
|
||||
<div
|
||||
id="{{ $id }}"
|
||||
class="relative"
|
||||
data-twe-carousel-init
|
||||
data-twe-ride="carousel"
|
||||
data-twe-interval="{{ $interval }}">
|
||||
<div
|
||||
class="absolute right-0 bottom-0 left-0 z-2 mx-[15%] mb-10 flex list-none justify-center p-0"
|
||||
data-twe-carousel-indicators>
|
||||
{{ $num := 0 }}
|
||||
{{ range $images }}
|
||||
<button
|
||||
type="button"
|
||||
data-twe-target="#{{ $id }}"
|
||||
data-twe-slide-to="{{ $num }}"
|
||||
{{ if eq $num 0 }}data-twe-carousel-active aria-current="true"{{ end }}
|
||||
class="mx-[3px] box-content h-[3px] w-[30px] flex-initial cursor-pointer border-0 border-y-[10px] border-solid border-transparent bg-neutral bg-clip-padding p-0 -indent-[999px] opacity-50 transition-opacity duration-[600ms] ease-[cubic-bezier(0.25,0.1,0.25,1.0)] motion-reduce:transition-none"
|
||||
aria-label="Slide {{ $num }}"></button>
|
||||
{{ $num = add $num 1 }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="relative w-full after:clear-both after:block after:content-['']"
|
||||
style="overflow-x: clip; overflow-y: visible;">
|
||||
{{ range $index, $image := $images }}
|
||||
{{ $hiddenClass := cond (eq $index 0) "" "hidden" }}
|
||||
{{ $resource := index $image "resource" }}
|
||||
{{ $key := index $image "key" }}
|
||||
{{ $caption := "" }}
|
||||
{{ $candidates := slice }}
|
||||
{{ if $resource }}
|
||||
{{ $candidates = $candidates | append $resource.Name (path.Base $resource.Name) $resource.RelPermalink (path.Base $resource.RelPermalink) }}
|
||||
{{ end }}
|
||||
{{ if $key }}
|
||||
{{ $candidates = $candidates | append $key (path.Base $key) }}
|
||||
{{ end }}
|
||||
{{ range $candidates }}
|
||||
{{ if and (not $caption) . }}
|
||||
{{ with (index $captions .) }}{{ $caption = . }}{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<div
|
||||
class="relative float-left -mr-[100%] {{ $hiddenClass }} w-full transition-transform ease-in-out motion-reduce:transition-none"
|
||||
data-twe-carousel-item
|
||||
style="transition-duration: {{ $interval }}ms;"
|
||||
{{ if eq $index 0 }}data-twe-carousel-active{{ end }}>
|
||||
<div class="single_hero_background relative overflow-hidden" style="aspect-ratio: {{ $aspectx }} / {{ $aspecty }};">
|
||||
<img
|
||||
src="{{ $resource.RelPermalink }}"
|
||||
class="block absolute top-0 object-cover w-full h-full not-prose nozoom"
|
||||
alt="carousel image {{ add $index 1 }}">
|
||||
</div>
|
||||
{{ if $caption }}
|
||||
<figcaption
|
||||
class="absolute left-0 right-0"
|
||||
style="top: calc(100%);"
|
||||
>{{ $caption | markdownify }}</figcaption>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="absolute top-0 bottom-0 left-0 z-2 flex w-[15%] items-center justify-center border-0 bg-none p-0 text-center opacity-50 transition-opacity duration-150 ease-[cubic-bezier(0.25,0.1,0.25,1.0)] hover:no-underline hover:opacity-90 hover:outline-none focus:no-underline focus:opacity-90 focus:outline-none motion-reduce:transition-none"
|
||||
type="button"
|
||||
data-twe-target="#{{ $id }}"
|
||||
data-twe-slide="prev">
|
||||
<span class="inline-block h-8 w-8">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="4.5"
|
||||
stroke="currentColor"
|
||||
class="h-6 w-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]"
|
||||
>Previous</span
|
||||
>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="absolute top-0 bottom-0 right-0 z-[1] flex w-[15%] items-center justify-center border-0 bg-none p-0 text-center opacity-50 transition-opacity duration-150 ease-[cubic-bezier(0.25,0.1,0.25,1.0)] hover:no-underline hover:opacity-90 hover:outline-none focus:no-underline focus:opacity-90 focus:outline-none motion-reduce:transition-none"
|
||||
type="button"
|
||||
data-twe-target="#{{ $id }}"
|
||||
data-twe-slide="next">
|
||||
<span class="inline-block h-8 w-8">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="4.5"
|
||||
stroke="currentColor"
|
||||
class="h-6 w-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]"
|
||||
>Next</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user