Initial commit: Rapport Website (Hugo + Hextra)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 11:52:03 +02:00
commit e007bdd4e7
480 changed files with 41697 additions and 0 deletions
@@ -0,0 +1,31 @@
{{- /*
Extracts all headings from a page and adds them to the scratchpad.
The keys can be obtained from the scratchpad by using the "keys" key.
The titles can be obtained from the scratchpad by using the "titles" key.
The scratchpad must be initialized with empty slices before calling this function for the keys "keys" and "titles"
@param {any} target The element to extract headings from.
@param {any} scratch The scratchpad to add the keys and titles to.
@example {{ partial "utils/extract-headings.html" (dict "target" $h1 "scratch" $s) }}
*/ -}}
{{- range $heading := index .target.Headings -}}
{{- if and (eq $heading.Level 0) (not $heading.Title) -}}
{{- $.scratch.Add "keys" (slice $heading.Title) -}}
{{- else -}}
{{- $key := (printf "%s#%s" $heading.ID $heading.Title) -}}
{{- $.scratch.Add "keys" (slice $key) -}}
{{- end -}}
{{- $title := (printf "<h%d>%s" $heading.Level $heading.Title) | htmlUnescape -}}
{{- $.scratch.Add "titles" (slice $title) -}}
{{- partial "utils/extract-headings.html" (dict
"target" $heading
"scratch" $.scratch
)
}}
{{- end -}}
@@ -0,0 +1,21 @@
{{/* This utility is used to get the file path from absolute, relative path or URL. */}}
{{- $path := .path -}}
{{- $page := .page -}}
{{- $isLocal := not (urls.Parse $path).Scheme -}}
{{- $isPage := and (eq $page.Kind "page") (not $page.BundleType) -}}
{{- $startsWithSlash := hasPrefix $path "/" -}}
{{- $startsWithRelative := hasPrefix $path "../" -}}
{{- if and $path $isLocal -}}
{{- if $startsWithSlash -}}
{{/* File under static directory */}}
{{- $path = (relURL (strings.TrimPrefix "/" $path)) -}}
{{- else if and $isPage (not $startsWithRelative) -}}
{{/* File is a sibling to the individual page file */}}
{{ $path = (printf "../%s" $path) }}
{{- end -}}
{{- end -}}
{{- return $path -}}
@@ -0,0 +1,3 @@
{{- with . -}}
{{- . | time.Format (site.Params.dateFormat | default ":date_long") -}}
{{- end -}}
@@ -0,0 +1,93 @@
{{- /*
fragments.html - Split page content into searchable fragments
This partial processes a Hugo page and splits its content into fragments based on headings,
creating a data structure suitable for search indexing. It supports different fragment types
and handles hierarchical heading structures (h1, h2).
Parameters:
- .context (Page): The Hugo page to process
- .type (string): Fragment type - "content" (default), "heading", "title", or "summary"
Returns:
- dict: Map of heading keys to content fragments
Example:
Input page with content:
# Introduction
This is the intro text.
## Setup
Setup instructions here.
# Configuration
Config details here.
Output (type "content"):
{
"": "This is the intro text.",
"intro#Introduction": "This is the intro text. Setup instructions here.",
"setup#Setup": "Setup instructions here.",
"config#Configuration": "Config details here."
}
Fragment types:
- "content": Splits page content by headings (default)
- "heading": Returns heading keys with empty content
- "title": Returns empty content (title handled elsewhere)
- "summary": Returns page summary only
*/ -}}
{{- /* Extract page context and fragment type */ -}}
{{- $page := .context -}}
{{- $type := .type | default "content" -}}
{{- /* Process all headings */ -}}
{{- $s := newScratch -}}
{{- $s.Set "keys" slice -}}
{{- $s.Set "titles" slice -}}
{{- partial "utils/extract-headings.html" (dict "target" $page.Fragments "scratch" $s) -}}
{{- $headingKeys := $s.Get "keys" -}}
{{- $headingTitles := $s.Get "titles" -}}
{{- $content := $page.Content | htmlUnescape -}}
{{- $len := len $headingKeys -}}
{{- $data := dict -}}
{{ if eq $type "content" }}
{{/* Include full content of the page */}}
{{ if eq $len 0 }}
{{ $data = $data | merge (dict "" ($page.Plain | htmlUnescape | strings.TrimSpace)) }}
{{ else }}
{{/* Split the raw content from bottom to top */}}
{{ range seq $len }}
{{ $i := sub $len . }}
{{ $headingKey := index $headingKeys $i }}
{{ $headingTitle := index $headingTitles $i }}
{{ if eq $i 0 }}
{{ $data = $data | merge (dict $headingKey ($content | plainify | htmlUnescape | strings.TrimSpace)) }}
{{ else }}
{{ $parts := split $content (printf "%s" $headingTitle) }}
{{ $lastPart := index $parts (sub (len $parts) 1) }}
{{ $data = $data | merge (dict $headingKey ($lastPart | plainify | htmlUnescape | strings.TrimSpace)) }}
{{ $content = strings.TrimSuffix $lastPart $content }}
{{ $content = strings.TrimSuffix (printf "%s" $headingTitle) $content }}
{{ end }}
{{ end }}
{{ end }}
{{ else if (eq $type "heading" ) }}
{{/* Put heading keys with empty content to the data object */}}
{{ $data = dict "" "" }}
{{ range $headingKeys }}
{{ $data = $data | merge (dict . "") }}
{{ end }}
{{ else if (eq $type "title") }}
{{/* Use empty data object since title is included in search-data.json */}}
{{ $data = $data | merge (dict "" "") }}
{{ else if (eq $type "summary" ) }}
{{ $data = $data | merge (dict "" ($page.Summary | plainify | htmlUnescape | strings.TrimSpace)) }}
{{ end }}
{{ return $data }}
@@ -0,0 +1,15 @@
{{/*
Returns the language direction using the supported Hugo API for the running version.
Hugo v0.158.0 deprecated Language.LanguageDirection in favor of Language.Direction.
Keep the fallback so Hextra can continue supporting Hugo >= 0.146.0.
*/}}
{{- $language := . -}}
{{- $direction := "" -}}
{{- if ge (hugo.Version) "0.158.0" -}}
{{- $direction = $language.Direction -}}
{{- else -}}
{{- $direction = $language.LanguageDirection -}}
{{- end -}}
{{- return $direction -}}
@@ -0,0 +1,15 @@
{{/*
Returns the language label using the supported Hugo API for the running version.
Hugo v0.158.0 deprecated Language.LanguageName in favor of Language.Label.
Keep the fallback so Hextra can continue supporting Hugo >= 0.146.0.
*/}}
{{- $language := . -}}
{{- $label := "" -}}
{{- if ge (hugo.Version) "0.158.0" -}}
{{- $label = $language.Label -}}
{{- else -}}
{{- $label = $language.LanguageName -}}
{{- end -}}
{{- return $label -}}
@@ -0,0 +1,15 @@
{{/*
Returns the language locale using the supported Hugo API for the running version.
Hugo v0.158.0 deprecated Language.LanguageCode in favor of Language.Locale.
Keep the fallback so Hextra can continue supporting Hugo >= 0.146.0.
*/}}
{{- $language := . -}}
{{- $locale := "" -}}
{{- if ge (hugo.Version) "0.158.0" -}}
{{- $locale = $language.Locale -}}
{{- else -}}
{{- $locale = $language.LanguageCode -}}
{{- end -}}
{{- return $locale -}}
@@ -0,0 +1,14 @@
{{/*
Returns site data using the supported Hugo API for the running version.
Hugo v0.156.0 deprecated site.Data / .Site.Data in favor of hugo.Data.
Keep the fallback so Hextra can continue supporting Hugo >= 0.146.0.
*/}}
{{- $siteData := dict -}}
{{- if ge (hugo.Version) "0.156.0" -}}
{{- $siteData = hugo.Data -}}
{{- else -}}
{{- $siteData = site.Data -}}
{{- end -}}
{{- return $siteData -}}
@@ -0,0 +1,14 @@
{{/*
Returns all sites using the supported Hugo API for the running version.
Hugo v0.156.0 deprecated site.Sites / page.Sites in favor of hugo.Sites.
Keep the fallback so Hextra can continue supporting Hugo >= 0.146.0.
*/}}
{{- $sites := slice -}}
{{- if ge (hugo.Version) "0.156.0" -}}
{{- $sites = hugo.Sites -}}
{{- else -}}
{{- $sites = site.Sites -}}
{{- end -}}
{{- return $sites -}}
@@ -0,0 +1,79 @@
{{/* Render raw svg icon from site data */}}
{{- $siteData := partial "utils/hugo-compat/site-data.html" . -}}
{{- $name := .name -}}
{{- $icon := index $siteData.icons $name -}}
{{- $isRemoteIcon := false -}}
{{- if not $icon -}}
{{- $remoteProvider := "" -}}
{{- $remoteName := "" -}}
{{- if strings.Contains $name ":" -}}
{{- $parts := split $name ":" -}}
{{- if eq (len $parts) 2 -}}
{{- $remoteProvider = index $parts 0 -}}
{{- $remoteName = index $parts 1 -}}
{{- end -}}
{{- end -}}
{{- if and $remoteProvider $remoteName -}}
{{- $remoteEnabled := true -}}
{{- $remoteProviders := dict
"lucide" (dict "url" "https://unpkg.com/lucide-static@1/icons/%s.svg")
"tabler" (dict "url" "https://unpkg.com/@tabler/icons@3/icons/outline/%s.svg")
"simple" (dict "url" "https://cdn.jsdelivr.net/npm/simple-icons@16/icons/%s.svg")
-}}
{{- with site.Params.icons.remote -}}
{{- if isset . "enable" -}}
{{- $remoteEnabled = .enable -}}
{{- end -}}
{{- with .providers -}}
{{- $remoteProviders = merge $remoteProviders . -}}
{{- end -}}
{{- end -}}
{{- if $remoteEnabled -}}
{{- if not (findRE "^[A-Za-z0-9_-]+$" $remoteProvider) -}}
{{- errorf "invalid remote icon provider %q" $remoteProvider -}}
{{- end -}}
{{- if or (in $remoteName "..") (not (findRE "^[A-Za-z0-9._/-]+$" $remoteName)) -}}
{{- errorf "invalid remote icon name %q" $remoteName -}}
{{- end -}}
{{- with index $remoteProviders $remoteProvider -}}
{{- $remoteUrl := printf .url $remoteName -}}
{{- with try (resources.GetRemote $remoteUrl) -}}
{{- with .Err -}}
{{- errorf "Could not retrieve remote icon %q from %s. Reason: %s." $name $remoteUrl . -}}
{{- else with .Value -}}
{{- $icon = .Content -}}
{{- $isRemoteIcon = true -}}
{{- if and (not (strings.Contains $icon "fill=")) (not (strings.Contains $icon "stroke=")) -}}
{{- $icon = replaceRE "<svg" `<svg fill="currentColor"` $icon -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $icon -}}
{{ errorf "icon %q not found" $name }}
{{- end -}}
{{- $icon = $icon | safeHTML -}}
{{- if $isRemoteIcon -}}
{{- $icon = replaceRE `(<svg[^>]*?)\sclass=("[^"]*"|'[^']*'|[^\s>]+)` `$1` $icon -}}
{{- end -}}
{{- if .attributes -}}
{{- $attributes := .attributes -}}
{{- if $isRemoteIcon -}}
{{- $icon = replaceRE `(<svg[^>]*?)\swidth=("[^"]*"|'[^']*'|[^\s>]+)` `$1` $icon -}}
{{- $icon = replaceRE `(<svg[^>]*?)\sheight=("[^"]*"|'[^']*'|[^\s>]+)` `$1` $icon -}}
{{- end -}}
{{- $icon = replaceRE "<svg" (printf "<svg %s" $attributes) $icon -}}
{{- end -}}
{{- return ($icon | safeHTML) -}}
@@ -0,0 +1,25 @@
{{/* Get relative link of a page for given language */}}
{{/* If not found, return the homepage of the language page */}}
{{ $page := .context }}
{{ $lang := .lang }}
{{ $link := false }}
{{ range $page.AllTranslations }}
{{ if eq .Language.Lang $lang }}
{{ $link = .RelPermalink }}
{{ end }}
{{ end }}
{{ if not $link }}
{{ range where (partial "utils/hugo-compat/sites.html" .) ".Language.Lang" $lang }}
{{ $link = .Home.RelPermalink }}
{{ end }}
{{ end }}
{{ if not $link }}
{{ $link = site.Home.RelPermalink }}
{{ end }}
{{ return $link }}
@@ -0,0 +1,11 @@
{{ with .Description | plainify | htmlUnescape -}}
{{ . -}}
{{ else -}}
{{ if .IsHome -}}
{{ with .Site.Params.description | plainify | htmlUnescape -}}
{{ . -}}
{{ end -}}
{{ else -}}
{{ .Summary | plainify | htmlUnescape | chomp -}}
{{ end -}}
{{ end -}}
@@ -0,0 +1,10 @@
{{- with .Params.width -}}
{{- $pageWidthValues := dict "normal" "80rem" "wide" "90rem" "full" "100%" -}}
{{- $pageWidth := . -}}
{{- $maxPageWidth := (index $pageWidthValues $pageWidth) | default (index $pageWidthValues "normal") -}}
<style>
:root {
--hextra-max-page-width: {{ $maxPageWidth }};
}
</style>
{{- end -}}
@@ -0,0 +1,32 @@
{{- $page := .page -}}
{{- $by := .by | default "weight" -}}
{{- $order := .order | default "asc" -}}
{{- $pages := slice }}
{{- if eq $by "weight" }}
{{- $pages = $page.Pages.ByWeight }}
{{- else if eq $by "date" }}
{{- $pages = $page.Pages.ByDate }}
{{- else if eq $by "title" }}
{{- $pages = $page.Pages.ByTitle }}
{{- else if eq $by "expiryDate" }}
{{- $pages = $page.Pages.ByExpiryDate }}
{{- else if eq $by "publishDate" }}
{{- $pages = $page.Pages.ByPublishDate }}
{{- else if eq $by "lastmod" }}
{{- $pages = $page.Pages.ByLastmod }}
{{- else if eq $by "linkTitle" }}
{{- $pages = $page.Pages.ByLinkTitle }}
{{- else if eq $by "length" }}
{{- $pages = $page.Pages.ByLength }}
{{- else }}
{{- warnf "sort-pages: unknown sort field %q" $by -}}
{{- $pages = $page.Pages }}
{{ end -}}
{{- if eq $order "desc" }}
{{- $pages = $pages.Reverse }}
{{- end -}}
{{- return $pages -}}
@@ -0,0 +1,18 @@
{{/*
This utility replaces placeholders in a URL template string.
Usage:
{{ partial "utils/template-url.html" (dict "template" .url "values" (dict "url" $pageURL "title" $pageTitle "markdown_url" $markdownURL)) }}
Placeholders use the format {key} and values are URL-encoded automatically.
*/}}
{{- $template := .template -}}
{{- $values := .values | default dict -}}
{{- range $key, $value := $values -}}
{{- $placeholder := printf "{%s}" $key -}}
{{- $encoded := $value | urlquery -}}
{{- $template = replace $template $placeholder $encoded -}}
{{- end -}}
{{- return $template -}}
@@ -0,0 +1,19 @@
{{/*
This utility is used to retrieve the title of a page or section.
If no title is set, it falls back to using the directory or file name.
Based on https://github.com/thegeeklab/hugo-geekdoc/blob/v0.44.0/layouts/partials/utils/title.html
*/}}
{{- $title := "" }}
{{ if .LinkTitle }}
{{ $title = .LinkTitle }}
{{ else if .Title }}
{{ $title = .Title }}
{{ else if and .IsSection .File }}
{{ $title = path.Base .File.Dir | humanize | title }}
{{ else if and .IsPage .File }}
{{ $title = .File.BaseFileName | humanize | title }}
{{ end }}
{{ return $title -}}