Add Lotusdocs theme

This commit is contained in:
Abner Coimbre
2026-01-11 16:46:05 -08:00
parent d051d46d1f
commit 8a4d04db58
577 changed files with 40404 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
{{ define "main"}}
<div class="d-flex flex-column justify-content-center align-items-center four-oh-four">
<div class="px-1">
<h1 class="text-center">Page not found :(</h1>
<h4 class="text-center">The page you are looking for doesn't exist or has been moved.</h4>
</div>
<div class="px-1">
<a href="{{ "" | relURL }}"><i class="material-icons size-48 me-0">home</i></a>
<!-- <a href="{{ "" | relURL }}"><i class="material-icons size-48 me-0">menu_book</i></a> -->
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,65 @@
<!DOCTYPE html>
{{ $.Scratch.Delete "social_list" }}
<!-- social_list -->
{{ $social_params := slice "github" "twitter" "bluesky" "instagram" "rss" }}
{{ range $social_params }}
{{ if isset site.Params.social . }}
{{ $.Scratch.Add "social_list" (slice .) }}
{{ end }}
{{ end }}
<!--
Is an image_compare landing page template enabled?
If so, collect and consolidate the items config from each enabled instance
-->
{{ $ic := newScratch }}
{{ range $value := .Site.Data.landing }}
{{ $template := string (replaceRE `( |-{1,})` "_" $value.template) }}
{{ if eq $template "image_compare" }}
{{ if $value.enable }}
{{ $ic.Add "enabled" (slice $value.enable) }}
{{ range $value.items }}
{{ $ic.Add "config" (slice (.config | jsonify | safeJS)) }}
{{ end }}
{{ end }}
{{- end }}
{{ end }}
{{ $.Scratch.Set "image_compare_enabled" (in ($ic.Get "enabled") true) }}
<html lang="{{ site.LanguageCode }}" dir="ltr">
{{- partial "head.html" . -}}
<body>
<div>
{{- partial "header.html" . -}}
</div>
<div class="mb-4" id="content">
{{- block "main" . }}{{- end }}
</div>
{{- partial "footer.html" . -}}
<!-- Lotus Docs JS -->
{{ $app := resources.Get "/js/app.js" }}
{{- if not hugo.IsServer }}
{{- $js := (slice $app) | resources.Concat "/js/bundle.js" | minify | fingerprint "sha384" }}
<script type="text/javascript" src="{{ $js.Permalink }}" integrity="{{ $js.Data.Integrity }}"></script>
{{- else }}
{{- $js := (slice $app) | resources.Concat "/js/bundle.js" }}
<script type="text/javascript" src="{{ $js.Permalink }}" {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end }}></script>
{{- end }}
<!-- Image Compare Viewer Config -->
{{ if ($.Scratch.Get "image_compare_enabled") }}
<script>
const viewers = document.querySelectorAll(".image-compare");
let configs = [
{{ (delimit ($ic.Get `config`) `,`) | safeJS }}
];
viewers.forEach((element, i) => {
let view = new ImageCompare(element, configs[i]).mount();
});
</script>
{{ end }}
</body>
</html>

View File

@@ -0,0 +1,4 @@
<pre class="mermaid">
{{- .Inner }}
</pre>
{{ .Page.Store.Set "hasMermaid" true }}

View File

@@ -0,0 +1,85 @@
<!-- Prism Render Hook -->
{{ if eq .Page.Site.Params.docs.prism true }}
{{- $attributes := .Attributes }}
{{- $ordinal := .Ordinal }}
<!-- Create SHA1 Hash of Code Block-->
{{ $innerRemoveLB := replaceRE "\r\n?|\n" "" .Inner | string }}
{{ $innerHash := substr (sha1 (printf "%s%s" $innerRemoveLB $ordinal)) 0 7 }}
{{- $classes := slice (printf "language-%s" .Type) .Attributes.class }}
<!-- Options List -->
{{- $options := .Options }}
{{ $optionslist := newScratch }}
{{ range $k, $v := $options }}
{{ $optionslist.Add "options" (printf " %s" $k) }}
{{ end }}
{{ $optionsclasslist := $optionslist.Get "options" }}
<!-- Line Number Highlight -->
{{- if isset .Options "hl_lines" }}
{{ $lines := .Options.hl_lines }}
{{ $offset := 0 }}
{{- if isset .Options "linenostart" }}
{{ $offset = .Options.linenostart }}
{{ else }}
{{ $offset = 1 }}
{{ end }}
{{ $data := newScratch }}
{{ range $value := $lines }}
{{ $value = uniq $value }}
{{ if lt (len $value) 2 }}
{{ $value = slice (add $offset (index $value 0)) }}
{{ else }}
{{ $value = slice (delimit (slice (add $offset (index $value 0)) (add $offset (index $value 1))) "-") }}
{{ end }}
{{ $data.Add "lines" $value }}
{{ end }}
{{ $lines = delimit ($data.Get "lines") "," }}
{{- $attributes = merge $attributes (dict "data-line" $lines) }}
<!-- https://github.com/PrismJS/prism/issues/2714 -->
{{- if and (isset .Options "linenos") (ne .Options.linenos false) }}
{{- $attributes = merge $attributes (dict "data-line-offset" (string $offset)) }}
{{ else }}
{{- $attributes = merge $attributes (dict "data-line-offset" (string (sub $offset 1))) }}
{{ end }}
{{ end -}}
<!-- Line Numbers -->
{{- if and (isset .Options "linenos") (ne .Options.linenos false) }}
{{- $classes = $classes | append "line-numbers" }} <!-- https://discourse.gohugo.io/t/scope-issue-inside-conditional-block-i-think/29273/4 -->
{{ end -}}
<!-- Line Number Start -->
{{- if isset .Options "linenostart" }}
{{- $attributes = merge $attributes (dict "data-start" (string .Options.linenostart)) }}
{{ end -}}
<!-- Linkable Line Numbers -->
{{- if and (isset .Options "anchorlinenos") (ne .Options.anchorlinenos false) }}
{{- $classes = $classes | append "linkable-line-numbers" }}
{{ end -}}
{{- $attributes = merge $attributes (dict "class" (delimit $classes " ")) }}
<div class="prism-codeblock {{ $optionsclasslist }}">
<pre id="{{ $innerHash }}"
{{- range $k, $v := $attributes }}
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
{{- end -}}
>
<code>
{{- .Inner -}}
</code>
</pre>
</div>
{{ else }}
{{ $result := transform.HighlightCodeBlock . }}
{{ $result.Wrapped }}
{{ end }}

View File

@@ -0,0 +1 @@
<h{{ .Level }} id="{{ .Anchor | safeURL }}">{{ .Text | safeHTML }} <a href="#{{ .Anchor | safeURL }}" class="anchor" aria-hidden="true"><i class="material-icons align-middle">link</i></a></h{{ .Level }}>

View File

@@ -0,0 +1,33 @@
{{ $dest := .Destination }}
{{ $text := .PlainText }}
{{ $url := urls.Parse $dest }}
{{ $image := newScratch }}
{{ with $url.Scheme }}
{{ $image.Set "resource" (resources.GetRemote $dest) }}
{{ else }}
{{ $image.Set "resource" (resources.Get $dest) }}
{{ end }}
{{ if .Title }}
<figure>
{{ with ($image.Get "resource") }}
{{ if eq .MediaType.SubType "svg" }}
{{ .Content | safeHTML }}
{{ else }}
<img src="{{ .RelPermalink | safeURL }}" alt="{{ $text }}" width="{{ .Width }}" height="{{ .Height }}" loading="lazy">
{{ end }}
{{ else }}
<img src="{{ .Destination | safeURL }}" alt="{{ $text }}" width="{{ .Width }}" height="{{ .Height }}" loading="lazy">
{{ end }}
<figcaption>{{ .Title | markdownify }}</figcaption>
</figure>
{{ else }}
{{ with ($image.Get "resource") }}
{{ if eq .MediaType.SubType "svg" }}
{{ .Content | safeHTML }}
{{ else }}
<img src="{{ .RelPermalink | safeURL }}" alt="{{ $text }}" width="{{ .Width }}" height="{{ .Height }}" loading="lazy">
{{ end }}
{{ end }}
{{ end }}

View File

@@ -0,0 +1,262 @@
{{- /* Last modified: 2023-09-04T09:23:04-07:00 */}}
{{- /*
Copyright 2023 Veriphor LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
*/}}
{{- /*
This render hook resolves internal destinations by looking for a matching:
1. Content page
2. Page resource (a file in the current page bundle)
3. Section resource (a file in the current section)
4. Global resource (a file in the assets directory)
It skips the section resource lookup if the current page is a leaf bundle.
External destinations are not modified.
You must place global resources in the assets directory. If you have placed
your resources in the static directory, and you are unable or unwilling to move
them, you must mount the static directory to the assets directory by including
both of these entries in your site configuration:
[[module.mounts]]
source = 'assets'
target = 'assets'
[[module.mounts]]
source = 'static'
target = 'assets'
By default, if this render hook is unable to resolve a destination, including a
fragment if present, it passes the destination through without modification. To
emit a warning or error, set the error level in your site configuration:
[params.render_hooks.link]
errorLevel = 'warning' # ignore (default), warning, or error (fails the build)
When you set the error level to warning, and you are in a development
environment, you can visually highlight broken internal links:
[params.render_hooks.link]
errorLevel = 'warning' # ignore (default), warning, or error (fails the build)
highlightBroken = true # true or false (default)
This will add a "broken" class to anchor elements with invalid src attributes.
Add a rule to your CSS targeting the broken links:
a.broken {
background: #ff0;
border: 2px solid #f00;
padding: 0.1em 0.2em;
}
This render hook may be unable to resolve destinations created with the ref and
relref shortcodes. Unless you set the error level to ignore you should not use
either of these shortcodes in conjunction with this render hook.
@context {string} Destination The link destination.
@context {page} Page A reference to the page containing the link.
@context {string} PlainText The link description as plain text.
@context {string} Text The link description.
@context {string} Title The link title.
@returns {template.html}
*/}}
{{- /* Initialize. */}}
{{- $renderHookName := "link" }}
{{- /* Verify minimum required version. */}}
{{- $minHugoVersion := "0.114.0" }}
{{- if lt hugo.Version $minHugoVersion }}
{{- errorf "The %q render hook requires Hugo v%s or later." $renderHookName $minHugoVersion }}
{{- end }}
{{- /* Error level when unable to resolve destination: ignore, warning, or error. */}}
{{- $errorLevel := or site.Params.render_hooks.link.errorLevel "ignore" | lower }}
{{- /* If true, adds "broken" class to broken links. Applicable in development environment when errorLevel is warning. */}}
{{- $highlightBrokenLinks := or site.Params.render_hooks.link.highlightBroken false }}
{{- /* Validate error level. */}}
{{- if not (in (slice "ignore" "warning" "error") $errorLevel) }}
{{- errorf "The %q render hook is misconfigured. The errorLevel %q is invalid. Please check your site configuration." $renderHookName $errorLevel }}
{{- end }}
{{- /* Determine content path for warning and error messages. */}}
{{- $contentPath := "" }}
{{- with .Page.File }}
{{- $contentPath = .Path }}
{{- else }}
{{- $contentPath = .Path }}
{{- end }}
{{- /* Parse destination. */}}
{{- $u := urls.Parse .Destination }}
{{- /* Set common message. */}}
{{- $msg := printf "The %q render hook was unable to resolve the destination %q in %s" $renderHookName $u.String $contentPath }}
{{- /* Set attributes for anchor element. */}}
{{- $attrs := dict "href" $u.String }}
{{- if $u.IsAbs }}
{{- /* Destination is a remote resource. */}}
{{- with .Page.Site.Params.docs.extLinkNewTab | default true }}
{{- $attrs = merge $attrs (dict "rel" "external" "target" "_blank") }}
{{- else }}
{{- $attrs = merge $attrs (dict "rel" "external") }}
{{- end -}}
{{- else }}
{{- with $u.Path }}
{{- with $p := or ($.Page.GetPage .) ($.Page.GetPage (strings.TrimRight "/" .)) }}
{{- /* Destination is a page. */}}
{{- $href := .RelPermalink }}
{{- $tooltipDelay := dict "show" 550 "hide" 300 | jsonify | htmlEscape }}
{{- $pathDesc := .Description }}
{{- $pathTitle := .Title }}
{{- $pathParentSection := .Parent.Title | upper }}
{{- $pathHTML := printf "<a href='%s'><p>%s</p><strong>%s</strong><br>%s</a>" $href $pathParentSection $pathTitle $pathDesc }}
{{- with $u.RawQuery }}
{{- $href = printf "%s?%s" $href . }}
{{- end }}
{{- with $u.Fragment }}
{{- $ctx := dict
"contentPath" $contentPath
"errorLevel" $errorLevel
"page" $p
"parsedURL" $u
"renderHookName" $renderHookName
}}
{{- partial "inline/h-rh-l/validate-fragment.html" $ctx }}
{{- $href = printf "%s#%s" $href . }}
{{- end }}
{{- if eq .Page.Site.Params.docs.intLinkTooltip true }}
{{- $attrs = dict "href" $href "data-bs-toggle" "tooltip" "data-bs-delay" $tooltipDelay "data-bs-html" "true" "data-bs-title" $pathHTML }}
{{- else }}
{{- $attrs = dict "href" $href }}
{{- end -}}
{{- else }}
{{- with $.Page.Resources.Get $u.Path }}
{{- /* Destination is a page resource; drop query and fragment. */}}
{{- $attrs = dict "href" .RelPermalink }}
{{- else }}
{{- with (and (ne $.Page.BundleType "leaf") ($.Page.CurrentSection.Resources.Get $u.Path)) }}
{{- /* Destination is a section resource, and current page is not a leaf bundle. */}}
{{- $attrs = dict "href" .RelPermalink }}
{{- else }}
{{- with resources.Get $u.Path }}
{{- /* Destination is a global resource; drop query and fragment. */}}
{{- $attrs = dict "href" .RelPermalink }}
{{- else }}
{{- if eq $errorLevel "warning" }}
{{- warnf $msg }}
{{- if and $highlightBrokenLinks (eq hugo.Environment "development") }}
{{- $attrs = merge $attrs (dict "class" "broken") }}
{{- end }}
{{- else if eq $errorLevel "error" }}
{{- errorf $msg }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- else }}
{{- with $u.Fragment }}
{{- /* Destination is on the same page; prepend relative permalink. */}}
{{- $ctx := dict
"contentPath" $contentPath
"errorLevel" $errorLevel
"page" $.Page
"parsedURL" $u
"renderHookName" $renderHookName
}}
{{- partial "inline/h-rh-l/validate-fragment.html" $ctx }}
{{- $attrs = dict "href" (printf "%s#%s" $.Page.RelPermalink .) }}
{{- else }}
{{- if eq $errorLevel "warning" }}
{{- warnf $msg }}
{{- if and $highlightBrokenLinks (eq hugo.Environment "development") }}
{{- $attrs = merge $attrs (dict "class" "broken") }}
{{- end }}
{{- else if eq $errorLevel "error" }}
{{- errorf $msg }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- with .Title }}
{{- $attrs = merge $attrs (dict "title" .) }}
{{- end -}}
{{- /* Render anchor element. */ -}}
<a
{{- range $k, $v := $attrs }}
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
{{- end -}}
>{{ .Text | safeHTML }}{{- if $u.IsAbs }}{{ with .Page.Site.Params.docs.extLinkNewTab | default true }}<svg width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M14 5c-.552 0-1-.448-1-1s.448-1 1-1h6c.552 0 1 .448 1 1v6c0 .552-.448 1-1 1s-1-.448-1-1v-3.586l-7.293 7.293c-.391.39-1.024.39-1.414 0-.391-.391-.391-1.024 0-1.414l7.293-7.293h-3.586zm-9 2c-.552 0-1 .448-1 1v11c0 .552.448 1 1 1h11c.552 0 1-.448 1-1v-4.563c0-.552.448-1 1-1s1 .448 1 1v4.563c0 1.657-1.343 3-3 3h-11c-1.657 0-3-1.343-3-3v-11c0-1.657 1.343-3 3-3h4.563c.552 0 1 .448 1 1s-.448 1-1 1h-4.563z"/></svg>{{ end }}{{ end }}</a>
{{- define "partials/inline/h-rh-l/validate-fragment.html" }}
{{- /*
Validates the fragment portion of a link destination.
@context {string} contentPath The page containing the link.
@context {srting} errorLevel The error level when unable to resolve destination; ignore (default), warning, or error.
@context {page} page The page corresponding to the link destination
@context {struct} parsedURL The link destination parsed by urls.Parse.
@context {string} renderHookName The name of the render hook.
*/}}
{{- /* Initialize. */}}
{{- $contentPath := .contentPath }}
{{- $errorLevel := .errorLevel }}
{{- $p := .page }}
{{- $u := .parsedURL }}
{{- $renderHookName := .renderHookName }}
{{- /* Validate. */}}
{{- with $u.Fragment }}
{{- if $p.Fragments.Identifiers.Contains . }}
{{- if gt ($p.Fragments.Identifiers.Count .) 1 }}
{{- $msg := printf "The %q render hook detected duplicate heading IDs %q in %s" $renderHookName . $contentPath }}
{{- if eq $errorLevel "warning" }}
{{- warnf $msg }}
{{- else if eq $errorLevel "error" }}
{{- errorf $msg }}
{{- end }}
{{- end }}
{{- else }}
{{- /* Determine target path for warning and error message. */}}
{{- $targetPath := "" }}
{{- with $p.File }}
{{- $targetPath = .Path }}
{{- else }}
{{- $targetPath = .Path }}
{{- end }}
{{- /* Set common message. */}}
{{- $msg := printf "The %q render hook was unable to find heading ID %q in %s. See %s" $renderHookName . $targetPath $contentPath }}
{{- if eq $targetPath $contentPath }}
{{- $msg = printf "The %q render hook was unable to find heading ID %q in %s" $renderHookName . $targetPath }}
{{- end }}
{{- /* Throw warning or error. */}}
{{- if eq $errorLevel "warning" }}
{{- warnf $msg }}
{{- else if eq $errorLevel "error" }}
{{- errorf $msg }}
{{- end }}
{{- end }}
{{- end }}
{{- end -}}

View File

@@ -0,0 +1,104 @@
<!DOCTYPE html>
{{ $.Scratch.Delete "social_list" }}
{{ $.Scratch.Set "pathName" (printf "%s" (.Site.Params.docs.pathName | default "docs")) }}
<!-- social_list -->
{{ $social_params := slice "github" "twitter" "bluesky" "instagram" "rss" }}
{{ range $social_params }}
{{ if isset site.Params.social . }}
{{ $.Scratch.Add "social_list" (slice .) }}
{{ end }}
{{ end }}
<html lang="{{ site.LanguageCode }}">
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "head.html") . -}}
<body>
<div class="content">
<div class="page-wrapper toggled">
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "sidebar.html") . -}}
<!-- Start Page Content -->
<main class="page-content bg-transparent">
{{ if hugo.IsMultilingual }}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "top-header.html") . -}}
{{ else }}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "top-header.html") . -}}
{{ end }}
<div class="container-fluid">
<div class="layout-spacing">
{{ $currentPage := . -}}
{{ if site.Params.docs.breadcrumbs | default true }}
<div class="d-md-flex justify-content-between align-items-center">
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "breadcrumbs.html") . -}}
</div>
{{ end }}
<div class="row flex-xl-nowrap">
{{ if site.Params.docs.toc | default true }}
<div class="docs-toc col-xl-3 {{ if .IsNode }}visually-hidden{{ else }}{{end}} {{ if and (ne .Params.toc false) (ne .TableOfContents "<nav id=\"TableOfContents\"></nav>") }}{{ else }}visually-hidden{{ end }} {{ if site.Params.docs.toc | default true }}{{ else }}visually-hidden{{ end }} d-xl-block">
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "toc.html") . -}}
</div>
{{ end }}
{{ if site.Params.docs.tocMobile | default true }}
<div class="docs-toc-mobile {{ if .IsNode }}visually-hidden{{ else }}{{end}} {{ if and (ne .Params.toc false) (ne .TableOfContents "<nav id=\"TableOfContents\"></nav>") }}{{ else }}visually-hidden{{ end }} {{ if site.Params.docs.tocMobile | default true }}{{ else }}visually-hidden{{ end }} d-print-none d-xl-none">
<button id="toc-dropdown-btn" class="btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-offset="0,0" aria-expanded="false">
Table of Contents
</button>
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "toc-mobile.html") . -}}
</div>
{{ end -}}
<div class="docs-content col-12 {{ if .IsNode }}{{ else }}{{ if site.Params.docs.toc | default true }}{{ if and (ne .Params.toc false) (ne .TableOfContents "<nav id=\"TableOfContents\"></nav>") }}col-xl-9{{else}}{{end}}{{ else }}{{ end }}{{ end }} mt-0">
<div class="mb-0 d-flex">
{{ if site.Params.docs.titleIcon | default false }}
<i class="material-icons title-icon me-2">{{- .Params.icon | default "article" }}</i>
{{ end }}
<h1 class="content-title mb-0">
{{ $currentPage.Title }}
{{ if .Draft }}
<span class="badge bg-default fs-6 mb-1 align-middle">DRAFT</span>
{{ end }}
</h1>
</div>
{{ if site.Params.docs.descriptions | default false }}
<p class="lead mb-3">{{ $currentPage.Description | markdownify }}</p>
{{ end }}
<div id="content" class="main-content" {{ if eq .Site.Params.docs.toc true -}}data-bs-spy="scroll" data-bs-root-margin="0px 0px -65%" data-bs-target="#toc-mobile"{{ end }}>
{{ block "main" . }}{{ end }}
</div>
<div>
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "doc-nav.html") . -}}
</div>
</div>
</div>
</div>
</div>
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer.html") . -}}
</main>
</div>
</div>
{{ if site.Params.docs.backToTop | default true }}
<!-- Back to top -->
<button onclick="topFunction()" id="back-to-top" aria-label="Back to Top Button" class="back-to-top fs-5"><svg width="24" height="24"><path d="M12,10.224l-6.3,6.3L4.32,15.152,12,7.472l7.68,7.68L18.3,16.528Z" style="fill:#fff"/></svg></button>
<!-- Back to top -->
{{ end }}
<!-- Dark Mode Switch JS -->
{{ if eq .Site.Params.docs.darkMode true -}}
{{ $darkModeSwitch := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/darkmode-switch.js") | js.Build | minify }}
<script>{{ $darkModeSwitch.Content | safeJS }}</script>
{{ end -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer/footer-scripts.html") . -}}
<!-- DocSearch Config -->
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer/docsearch.html") . -}}
{{ end }}
<!-- FlexSearch Config -->
{{ if or (not (isset .Site.Params.flexsearch "enabled")) (eq .Site.Params.flexsearch.enabled true) -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ else }}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer/flexsearch.html") . -}}
{{ end }}
{{ end }}
</body>
</html>

View File

@@ -0,0 +1,24 @@
{{ define "main" }}
<div class="row flex-xl-wrap">
{{ range .Pages.ByWeight }}
<div id="list-item" class="col-md-4 col-12 mt-4 pt-2">
<a class="text-decoration-none text-reset" href="{{ .Permalink }}">
<div class="card h-100 features feature-full-bg rounded p-4 position-relative overflow-hidden border-1">
<span class="icon-color d-flex my-3">
<i class="material-icons align-middle">{{- .Params.icon | default "article" }}</i>
{{ if .Draft }}<span class="badge bg-default ms-2 fs-6 align-middle">DRAFT</span>{{ end }}
{{ if .IsSection }}<i class="material-icons icon-faint align-middle ms-auto" title="Directory">folder</i>{{ end }}
</span>
<div class="card-body p-0 content">
<p class="fs-5 fw-semibold card-title mb-1">{{ .Title }}</p>
<p class="para card-text mb-0">{{ .Description | truncate (.Site.Params.docs.listDescTrunc | default 100) | markdownify }}</p>
</div>
<!-- <div class="position-absolute top-0 end-0">
<i class="material-icons opacity-05">{{- .Params.icon }}</i>
</div> -->
</div>
</a>
</div><!--end col-->
{{ end }}
</div>
{{ end }}

View File

@@ -0,0 +1,37 @@
{{ define "main" }}
<!-- override Prism's default copy messages to 'empty'. Required by CSS styling.
see: https://prismjs.com/plugins/copy-to-clipboard/#styling -->
<div data-prismjs-copy="" data-prismjs-copy-success="" data-prismjs-copy-error="">
{{ .Content }}
</div>
{{ if and .GitInfo .Site.Params.docs.repoURL -}}
{{ partial (printf "%s/%s" ($.Scratch.Get "pathName") "gitinfo") . }}
{{ end -}}
{{ if .Page.Store.Get "hasMermaid" }}
{{ $mermaid := resources.Get (printf "%s/%s" ($.Scratch.Get "pathName") "js/mermaid.min.js") }}
{{ if hugo.IsProduction }}
{{ $mermaid = $mermaid | fingerprint "sha384" }}
{{ end }}
<script src="{{ $mermaid.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $mermaid.Data.Integrity }}"{{ end }}></script>
<script>
const config = {
startOnLoad:true,
logLevel: "error",
align: "center",
theme:"null"
};
mermaid.initialize(config);
</script>
{{ end }}
{{ if .Site.Params.feedback.enabled | default false -}}
{{ if or (.Site.Params.plausible.dataDomain) (.Site.Config.Services.GoogleAnalytics.ID) }}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "footer/feedback.html") . -}}
{{ else }}
{{ errorf "Either Google Analytics or Plausible Analytics must be configured before enabling the Feedback Widget." }}
{{ end }}
{{ end -}}
{{ end }}

View File

@@ -0,0 +1,24 @@
{{ define "main" }}
{{ $landingData := .Site.Data.landing }}
{{ $map := newScratch }}
{{ range $key, $value := $landingData }}
{{ $sectionTitle := (index $key) }}
{{ $weight := string ($value.weight) }}
{{ $template := string (replaceRE `( |-{1,})` "_" $value.template) }}
{{ with and $template $weight }}
{{ $map.SetInMap "wgtTpl" $sectionTitle (dict
"weight" $weight
"template" $template
"sectionTitle" $sectionTitle
)}}
{{ end }}
{{ end }}
{{ range sort ($map.Get "wgtTpl") ".weight" }}
{{ $.Scratch.Set "sectionTitle" .sectionTitle }}
{{ $path := printf "landing/%s.html" .template }}
{{ partial $path $.Page }}
{{ end }}
{{/* printf "%s" (sort ($map.Get "wgtTpl") ".weight" ) */}}
{{ end }}

View File

@@ -0,0 +1,39 @@
<nav aria-label="breadcrumb" class="d-inline-block pb-2 mt-1 mt-sm-0">
<ul id="breadcrumbs" class="breadcrumb bg-transparent mb-0" itemscope itemtype="https://schema.org/BreadcrumbList">
{{- $data := newScratch }}
{{ $docRoot := .FirstSection.Title }}
{{- range $index, $value := (after 1 .Ancestors.Reverse) }}
{{ if eq $docRoot .Title }}
<li class="breadcrumb-item text-capitalize active" aria-current="page" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
{{- $data.Set "counter" $index }}
{{- $data.Add "counter" 1 }}
<a itemprop="item" href="{{ .RelPermalink }}">
<i class="material-icons size-20 align-text-bottom" itemprop="name">Home</i>
</a>
<meta itemprop="position" content='{{ $data.Get "counter"}}' />
</li>
{{ else }}
<li class="breadcrumb-item text-capitalize" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
{{- $data.Add "counter" 1 }}
<a itemprop="item" href="{{ .RelPermalink }}">
<span itemprop="name">{{ .Title }}</span>
</a>
<meta itemprop="position" content='{{ $data.Get "counter"}}' />
</li>
{{ end }}
{{- end }}
{{ if eq .Title $docRoot }}
<li class="breadcrumb-item text-capitalize active" aria-current="page" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
{{- $data.Add "counter" 1 }}
<i class="material-icons size-20 align-text-bottom" itemprop="name">Home</i>
<meta itemprop="position" content='{{ $data.Get "counter"}}' />
</li>
{{ else }}
<li class="breadcrumb-item text-capitalize active" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
{{- $data.Add "counter" 1 }}
<span itemprop="name">{{ .Title }}</span>
<meta itemprop="position" content='{{ $data.Get "counter"}}' />
</li>
{{ end }}
</ul>
</nav>

View File

@@ -0,0 +1,37 @@
<hr class="doc-hr">
<div id="doc-nav" class="d-print-none">
{{ if or .Prev .Next -}}
<!-- https://www.feliciano.tech/blog/custom-sort-hugo-single-pages/ -->
{{ $pages := where .Site.RegularPages "Section" .Section -}}
<div class="row flex-xl-nowrap {{ if .Next }}{{ else }}flex-row-reverse{{ end }}">
{{ with $pages.Next . -}}
<div class="col-sm-6 pt-2 doc-next">
<a href="{{ .RelPermalink }}">
<div class="card h-100 my-1">
<div class="card-body py-2">
<p class="card-title fs-5 fw-semibold lh-base mb-0"><i class="material-icons align-middle">navigate_before</i> {{ .Title }}</p>
{{ if eq .Site.Params.docs.navDesc true -}}
<p class="card-text ms-2">{{ .Description | truncate (.Site.Params.docs.navDescTrunc | default 40) }}</p>
{{ end }}
</div>
</div>
</a>
</div>
{{ end -}}
{{ with $pages.Prev . -}}
<div class="col-sm-6 pt-2 doc-prev">
<a class="ms-auto" href="{{ .RelPermalink }}">
<div class="card h-100 my-1 text-end">
<div class="card-body py-2">
<p class="card-title fs-5 fw-semibold lh-base mb-0">{{ .Title }} <i class="material-icons align-middle">navigate_next</i></p>
{{ if eq .Site.Params.docs.navDesc true -}}
<p class="card-text me-2">{{ .Description | truncate (.Site.Params.docs.navDescTrunc | default 40) }}</p>
{{ end }}
</div>
</div>
</a>
</div>
{{ end -}}
</div>
{{ end -}}
</div>

View File

@@ -0,0 +1,16 @@
<!-- Footer Start -->
<footer class="shadow py-3 d-print-none">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col">
<div class="text-sm-start text-center mx-md-2">
<p class="mb-0">
{{ $yearToken := (cond (isset .Site.Params (lower "copyrightYearToken")) $.Site.Params.copyrightYearToken ":YEAR:") }}
{{ replace $.Site.Params.footer.copyright $yearToken (string (now.Format "2006")) | markdownify }}
</p>
</div>
</div><!--end col-->
</div><!--end row-->
</div><!--end container-->
</footer><!--end footer-->
<!-- End -->

View File

@@ -0,0 +1,18 @@
<script>
window.addEventListener('DOMContentLoaded', function() {
// DocSearch Config
docsearch({
container: '#docsearch',
appId: '{{ .Site.Params.docsearch.appID }}',
apiKey: '{{ .Site.Params.docsearch.apiKey }}',
indexName: '{{ .Site.Params.docsearch.indexName }}',
placeholder: 'Search Documentation',
translations: {
button: {
buttonText: '{{ i18n "search_title" }}',
buttonAriaLabel: 'Search',
}
}
});
});
</script>

View File

@@ -0,0 +1,319 @@
{{ if eq .Site.Params.feedback.emoticonTpl true -}}
{{ $emoticons := slice "very_satisfied" "satisfied" "dissatisfied" "very_dissatisfied" }}
<div class="container d-flex align-items-center justify-content-center">
<div id="feedback-widget-emoticon" class="mt-4">
<div id="feedback-cta" class="d-flex justify-content-center py-1">
<span class="fs-6 align-self-center align-middle ms-2 me-0">
{{ i18n "feedback_helpful" | default "Was this page helpful?" }}
</span>
<div class="d-flex">
<div class="form-emoji d-flex ps-2">
{{ range $index, $value := $emoticons }}
<div class="radio-emoji">
<input type="radio" name="feedback-emoticon" autocomplete="off" value="{{ $value | humanize | title }}" id="radio_{{ $index }}" required>
<label class="d-flex justify-content-center align-items-center" for="radio_{{ $index }}">
{{ $emoticon := resources.Get ( printf "%s%s.svg" "images/icons/feedback/" $value ) }}
{{ $emoticon.Content | safeHTML }}
<!-- <i class="feedback-emoji material-icons">
sentiment_{{ $value }}
</i> -->
</label>
</div>
{{ end }}
</div>
</div>
</div>
<div id="feedback-emoji-end" class="d-flex justify-content-md-start justify-content-sm-center">
</div>
<div id="feedback-send">
<div id="text-wrapper">
<textarea id="textarea-emoji" cols="55" maxlength="500" rows="3" placeholder="(Optional) Try to be as specific and detailed as possible!" type="text" class="feedback-textarea-emoji p-2 mb-2"></textarea>
</div>
<div id="feedback-emoji-submit-container" class="d-flex justify-content-end">
<button type="button" id="feedback-emoji-submit-button" class="feedback-submit-emoji-btn btn btn-sm btn-primary mt-1 mb-2" role="button" type="submit" disabled>
{{ i18n "feedback_submit" | default "Submit" }}
</button>
</div>
</div>
</div>
</div>
<script>
const emojiTextWrapper = document.getElementById("text-wrapper");
const emojiRadios = document.querySelectorAll('input[name="feedback-emoticon"]');
const emojiContainer = document.getElementById("feedback-widget-emoticon");
const feedbackEmojiCta = document.getElementById("feedback-cta");
const feedbackEmojiSubmitContainer = document.getElementById("feedback-emoji-submit-container");
const feedbackEmojiSubmitBtn = document.getElementById("feedback-emoji-submit-button");
// Show emoji text area
emojiRadios.forEach(emojiRadio => {
emojiRadio.addEventListener('change', function(event) {
const emojiRadioID = event.target.id;
emojiContainer.style.borderRadius = '8px'
feedbackEmojiCta.classList.add("px-4");
emojiTextWrapper.classList.add("is-visible");
feedbackEmojiSubmitBtn.removeAttribute("disabled");
feedbackEmojiSubmitContainer.classList.add("is-visible");
// emojiTextWrapper.addEventListener('transitionend', function () {
// // do something!!!
// }
// );
})
});
// Submit emoticon feedback to Google or Plausible analytics
feedbackEmojiSubmitBtn.addEventListener('click', event => {
const result = document.getElementById("feedback-emoji-end");
const sendArea = document.getElementById("feedback-send");
const rating = document.querySelector('input[name="feedback-emoticon"]:checked').value;
const message = document.getElementById('textarea-emoji').value;
function success(e) {
feedbackEmojiCta.remove();
sendArea.remove();
result.innerHTML = '{{ .Site.Params.feedback.successMsg | default "Thank you for helping to improve our documentation!" }}';
result.classList.add("is-visible");
}
try {
{{ if .Site.Config.Services.GoogleAnalytics.ID -}}
{{ if not .Site.Params.feedback.eventDest | or (in .Site.Params.feedback.eventDest "google") -}}
gtag('event', '{{ replaceRE `( {1,})` "_" (.Site.Params.feedback.emoticonEventName | default "feedback" | lower) }}',
{
'rating': rating.replace(/\s+/g, '_').toLowerCase(),
'message': message,
'event_callback': success
}
),
{{ end -}}
{{ end -}}
{{ if .Site.Params.plausible.dataDomain -}}
{{ if not .Site.Params.feedback.eventDest | or (in .Site.Params.feedback.eventDest "plausible") -}}
plausible('{{ .Site.Params.feedback.emoticonEventName | default "Feedback" }}',
{
callback: success,
props:
{
rating: rating,
message: message
}
}
)
{{ end -}}
{{ end -}}
} catch (err) {
console.log(err);
feedbackEmojiCta.remove();
sendArea.remove();
result.innerHTML = '{{ .Site.Params.feedback.errorMsg | default "Sorry! There was an error while attempting to submit your feedback!" }}';
result.classList.add("is-visible");
}
// console.log("feedback submitted")
});
</script>
{{ else }}
<div id="feedback-widget" class="feedback-container pt-4">
<div id="feedback-init" class="d-flex justify-content-md-start justify-content-sm-center">
<span class="fs-5 fw-bold me-2 align-self-center">{{ i18n "feedback_helpful" | default "Was this page helpful?" }}</span>
<div class="d-flex">
<button id="posBtn" class="feedback-btn btn btn-primary btn-sm me-2" type="submit">
<span class="fs-6">
<i class="material-icons me-1 align-middle">thumb_up</i>
{{ i18n "feedback_yes" | default "Yes" }}
</span>
</button>
<button id="negBtn" class="feedback-btn btn btn-primary btn-sm me-2" type="submit">
<span class="fs-6">
<i class="material-icons me-1 align-middle">thumb_down</i>
{{ i18n "feedback_no" | default "No" }}
</span>
</button>
</div>
</div>
<div id="feedback-end" class="d-flex justify-content-md-start justify-content-sm-center fs-4 fw-bold me-2">
</div>
<div id="feedback-form-pos" data-type="positive" class="feedback-form justify-content-md-start justify-content-sm-center">
<p class="fw-bold fs-5">{{ .Site.Params.feedback.positiveFormTitle | default "What did you like?" }}</p>
{{ range $index, $value := .Site.Params.feedback.positiveForm }}
{{ $rating := (index . 0) }}
{{ $description := (index . 1) }}
<div class="form-check pb-1">
<input class="form-check-input" type="radio" name="feedback" autocomplete="off" value="{{ $rating | lower }}" id="radio{{ $index }}Pos" required>
<label class="form-check-label" for="radio{{ $index }}Pos">
{{ $rating }}
{{ if $description }}
<p class="feedback-radio-desc fw-normal mb-0">{{ $description }}</p>
{{ end }}
</label>
</div>
<div id="radio{{ $index }}PosTextContainer" class="feedback-textarea-container"></div>
{{ end }}
</div>
<div id="feedback-form-neg" data-type="negative" class="feedback-form justify-content-sm-center">
<p class="fw-bold fs-5">{{ .Site.Params.feedback.negativeFormTitle | default "What went wrong?" }}</p>
{{ range $index, $value := .Site.Params.feedback.negativeForm }}
{{ $rating := (index . 0) }}
{{ $description := (index . 1) }}
<div class="form-check pb-1">
<input class="form-check-input" type="radio" name="feedback" autocomplete="off" value="{{ $rating | lower }}" id="radio{{ $index }}Neg" required>
<label class="form-check-label" for="radio{{ $index }}Neg">
{{ $rating }}
{{ if $description }}
<p class="feedback-radio-desc fw-normal mb-0">{{ $description }}</p>
{{ end }}
</label>
</div>
<div id="radio{{ $index }}NegTextContainer" class="feedback-textarea-container"></div>
{{ end }}
</div>
<div id="feedback-submit-container">
<button id="feedback-submit-button" class="feedback-submit-btn btn btn-primary mt-3" role="button" type="submit" disabled>
<span class="fs-6">
{{ i18n "feedback_submit" | default "Submit" }}
</span>
</button>
</div>
</div>
<script>
// https://stackoverflow.com/a/29017547
const feedbackInit = document.getElementById("feedback-init");
const posBtn = document.getElementById("posBtn");
const negBtn = document.getElementById("negBtn");
const feedbackBtn = document.querySelectorAll('.feedback-btn');
const feedbackFormPos = document.getElementById("feedback-form-pos");
const feedbackFormNeg = document.getElementById("feedback-form-neg");
const feedbackSubmitBtn = document.getElementById("feedback-submit-button");
const feedbackSubmitContainer = document.getElementById("feedback-submit-container");
// Show feedback form
feedbackBtn.forEach(btn => {
btn.addEventListener('click', event => {
const btnID = btn.id
feedbackInit.style.opacity = '0'
feedbackInit.addEventListener('transitionend', function () {
feedbackInit.remove();
if (btnID == 'posBtn') {
feedbackFormPos.classList.add("is-visible");
feedbackFormNeg.remove();
feedbackSubmitContainer.classList.add("is-visible");
} else if (btnID == 'negBtn') {
feedbackFormNeg.classList.add("is-visible");
feedbackFormPos.remove();
feedbackSubmitContainer.classList.add("is-visible");
}
}
);
})
});
// Add text area box to the selected feedback radio
const Radios = document.querySelectorAll('input[name="feedback"]');
const textareas = document.querySelectorAll('.feedback-textarea-container');
Radios.forEach(radio => {
radio.addEventListener('change', function(event) {
const radioID = event.target.id;
const addOn = document.getElementById(radioID+"TextContainer");
// First, remove 'activeBox' class and <textarea> from all textarea containers
for (const textarea of textareas) {
while (textarea.firstChild) {
textarea.removeChild(textarea.firstChild);
}
}
addOn.innerHTML +='<textarea id="textarea" cols="55" maxlength="500" rows="3" placeholder="(Optional) Try to be as specific and detailed as possible!" type="text" class="feedback-textarea p-2 mb-2"></textarea>';
feedbackSubmitBtn.removeAttribute("disabled");
// console.log(radioID);
});
});
// Submit feedback to Google or Plausible analytics
feedbackSubmitBtn.addEventListener('click', event => {
const form = document.querySelector('.feedback-form');
const formType = form.getAttribute("data-type");
const result = document.getElementById("feedback-end");
const rating = document.querySelector('input[name="feedback"]:checked').value;
const message = document.getElementById('textarea').value;
function success(e) {
form.remove();
feedbackSubmitContainer.remove();
result.innerHTML = '{{ .Site.Params.feedback.successMsg | default "Thank you for helping to improve our documentation!" }}';
result.classList.add("is-visible");
}
try {
if (formType == "positive") {
{{ if .Site.Config.Services.GoogleAnalytics.ID -}}
{{ if not .Site.Params.feedback.eventDest | or (in .Site.Params.feedback.eventDest "google") -}}
gtag('event', '{{ replaceRE `( {1,})` "_" (.Site.Params.feedback.positiveEventName | default "Positive Feedback" | lower) }}',
{
'rating': rating,
'message': message,
'event_callback': success
}
),
{{ end -}}
{{ end -}}
{{ if .Site.Params.plausible.dataDomain -}}
{{ if not .Site.Params.feedback.eventDest | or (in .Site.Params.feedback.eventDest "plausible") -}}
plausible('{{ .Site.Params.feedback.positiveEventName | default "Positive Feedback" }}',
{
callback: success,
props:
{
rating: rating,
message: message
}
}
)
{{ end -}}
{{ end -}}
} else if (formType == "negative") {
{{ if .Site.Config.Services.GoogleAnalytics.ID -}}
{{ if not .Site.Params.feedback.eventDest | or (in .Site.Params.feedback.eventDest "google") -}}
gtag('event', '{{ replaceRE `( {1,})` "_" (.Site.Params.feedback.negativeEventName | default "Positive Feedback" | lower) }}',
{
'rating': rating,
'message': message,
'event_callback': success
}
),
{{ end -}}
{{ end -}}
{{ if .Site.Params.plausible.dataDomain -}}
{{ if not .Site.Params.feedback.eventDest | or (in .Site.Params.feedback.eventDest "plausible") -}}
plausible('{{ .Site.Params.feedback.negativeEventName | default "Negative Feedback" }}',
{
callback: success,
props:
{
rating: rating,
message: message
}
}
)
{{ end -}}
{{ end -}}
}
} catch (err) {
console.log(err);
form.remove();
feedbackSubmitContainer.remove();
result.innerHTML = '{{ .Site.Params.feedback.errorMsg | default "Sorry! There was an error while attempting to submit your feedback!" }}';
result.classList.add("is-visible");
}
// console.log("feedback submitted")
});
</script>
{{ end }}

View File

@@ -0,0 +1,215 @@
<script type="module">
var suggestions = document.getElementById('suggestions');
var search = document.getElementById('flexsearch');
const flexsearchContainer = document.getElementById('FlexSearchCollapse');
const hideFlexsearchBtn = document.getElementById('hideFlexsearch');
const configObject = { toggle: false }
const flexsearchContainerCollapse = new Collapse(flexsearchContainer, configObject) // initialized with no keyboard
if (search !== null) {
document.addEventListener('keydown', inputFocus);
flexsearchContainer.addEventListener('shown.bs.collapse', function () {
search.focus();
});
// hide search collapse containder by clicking outside (except top header)
var topHeader = document.getElementById("top-header");
document.addEventListener('click', function(elem) {
if (!flexsearchContainer.contains(elem.target) && !topHeader.contains(elem.target))
flexsearchContainerCollapse.hide();
});
}
hideFlexsearchBtn.addEventListener('click', () =>{
flexsearchContainerCollapse.hide()
})
function inputFocus(e) {
if (e.ctrlKey && e.key === '/') {
e.preventDefault();
flexsearchContainerCollapse.toggle();
}
if (e.key === 'Escape' ) {
search.blur();
// suggestions.classList.add('d-none');
flexsearchContainerCollapse.hide();
}
};
document.addEventListener('click', function(event) {
var isClickInsideElement = suggestions.contains(event.target);
if (!isClickInsideElement) {
suggestions.classList.add('d-none');
}
});
/*
Source:
- https://dev.to/shubhamprakash/trap-focus-using-javascript-6a3
*/
document.addEventListener('keydown',suggestionFocus);
function suggestionFocus(e) {
const suggestionsHidden = suggestions.classList.contains('d-none');
if (suggestionsHidden) return;
const focusableSuggestions= [...suggestions.querySelectorAll('a')];
if (focusableSuggestions.length === 0) return;
const index = focusableSuggestions.indexOf(document.activeElement);
if (e.key === "ArrowUp") {
e.preventDefault();
const nextIndex = index > 0 ? index - 1 : 0;
focusableSuggestions[nextIndex].focus();
}
else if (e.key === "ArrowDown") {
e.preventDefault();
const nextIndex= index + 1 < focusableSuggestions.length ? index + 1 : index;
focusableSuggestions[nextIndex].focus();
}
}
/*
Source:
- https://github.com/nextapps-de/flexsearch#index-documents-field-search
- https://raw.githack.com/nextapps-de/flexsearch/master/demo/autocomplete.html
*/
(function(){
var index = new FlexSearch.Document({
// charset: "latin:default",
tokenize: {{ .Site.Params.flexsearch.tokenize | default "forward" }},
minlength: {{ .Site.Params.flexsearch.minQueryChar | default 0}},
cache: {{ .Site.Params.flexsearch.cache | default 100 }},
optimize: {{ .Site.Params.flexsearch.optimize | default true }},
document: {
id: 'id',
store: [
"href", "title", "description"
],
index: ["title", "description", "content"]
}
});
// Not yet supported: https://github.com/nextapps-de/flexsearch#complex-documents
// var docs = [
// {{ range $index, $page := (where .Site.Pages "Section" "docs") -}}
// {
// id: {{ $index }},
// href: {{ .Permalink }},
// title: {{ .Title }},
// description: {{ .Params.description }},
// content: {{ .Content }}
// },
// {{ end -}}
// ];
// https://discourse.gohugo.io/t/range-length-or-last-element/3803/2
{{ $list := slice }}
{{- if and (isset .Site.Params.flexsearch "searchsectionsindex") (not (eq (len .Site.Params.flexsearch.searchSectionsIndex) 0)) }}
{{- if eq .Site.Params.flexsearch.searchSectionsIndex "ALL" }}
{{- $list = .Site.Pages }}
{{- else }}
{{- $list = (where .Site.Pages "Type" "in" .Site.Params.flexsearch.searchSectionsIndex) }}
{{- if (in .Site.Params.flexsearch.searchSectionsIndex "HomePage") }}
{{ $list = $list | append .Site.Home }}
{{- end }}
{{- end }}
{{- else }}
{{- $list = (where .Site.Pages "Section" ($.Scratch.Get "pathName")) }}
{{- end }}
{{ $len := (len $list) -}}
{{ range $index, $element := $list -}}
index.add(
{
id: {{ $index }},
href: "{{ .RelPermalink }}",
title: {{ .Title }},
{{ with .Description -}}
description: {{ . }},
{{ else -}}
description: {{ .Summary | htmlUnescape | plainify }},
{{ end -}}
content: {{ .Content | htmlUnescape | plainify }}
}
);
{{ end -}}
search.addEventListener('input', show_results, true);
function show_results(){
const maxResult = {{ .Site.Params.flexsearch.maxResult | default 5}};
const minlength = {{ .Site.Params.flexsearch.minQueryChar | default 0}};
var searchQuery = sanitizeHTML(this.value);
var results = index.search(searchQuery, {limit: maxResult, enrich: true});
// flatten results since index.search() returns results for each indexed field
const flatResults = new Map(); // keyed by href to dedupe results
for (const result of results.flatMap(r => r.result)) {
if (flatResults.has(result.doc.href)) continue;
flatResults.set(result.doc.href, result.doc);
}
suggestions.innerHTML = "";
suggestions.classList.remove('d-none');
// inform user of search query minimum character requirement
if (searchQuery.length < minlength) {
const minCharMessage = document.createElement('div')
minCharMessage.innerHTML = `Please type at least <strong>${minlength}</strong> characters`
minCharMessage.classList.add("suggestion__no-results");
suggestions.appendChild(minCharMessage);
return;
} else {
// inform user that no results were found
if (flatResults.size === 0 && searchQuery) {
const noResultsMessage = document.createElement('div')
noResultsMessage.innerHTML = {{ i18n "search_no_results" | default "No results for" }} + ` "<strong>${searchQuery}</strong>"`
noResultsMessage.classList.add("suggestion__no-results");
suggestions.appendChild(noResultsMessage);
return;
}
}
// construct a list of suggestions
for(const [href, doc] of flatResults) {
const entry = document.createElement('div');
suggestions.appendChild(entry);
const a = document.createElement('a');
a.href = href;
entry.appendChild(a);
const title = document.createElement('span');
title.textContent = doc.title;
title.classList.add("suggestion__title");
a.appendChild(title);
const description = document.createElement('span');
description.textContent = doc.description;
description.classList.add("suggestion__description");
a.appendChild(description);
suggestions.appendChild(entry);
if(suggestions.childElementCount == maxResult) break;
}
}
}());
</script>

View File

@@ -0,0 +1,57 @@
{{ $dayjs := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/dayjs.min.js") }}
{{ $relativeTime := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/relativeTime.min.js") }}
{{ $app := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/app.js") -}}
{{ $slice := slice $dayjs $relativeTime $app -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ $docsearch := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/docsearch.min.js") }}
{{ $slice = $slice | append $docsearch -}}
{{ end }}
{{ if site.Params.docs.toc | default true }}
{{ if eq .Site.Params.docs.scrollSpy true -}}
{{ $simplescrollspy := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/simple-scrollspy.min.js") }}
{{ $slice = $slice | append $simplescrollspy -}}
{{ end -}}
{{ if eq .Site.Params.docs.scrollSpy true -}}
{{ $scrollspyScript := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/scrollspy-script.js") }}
{{ $scrollspyScript := $scrollspyScript | js.Build -}}
{{ $slice = $slice | append $scrollspyScript -}}
{{ end -}}
{{ end -}}
{{ if site.Params.docs.tocMobile | default true }}
{{ $tocmobilescrollspy := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/toc-mobile-scrollspy.js") }}
{{ $slice = $slice | append $tocmobilescrollspy -}}
{{ end -}}
{{ if eq .Site.Params.docs.prism true -}}
{{ $prism := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/prism.js") }}
{{- $opts := dict
"params" (dict "langPath" (urls.JoinPath .Site.BaseURL "docs/js/components/"))
-}}
{{ $prism := $prism | js.Build $opts -}}
{{ $slice = $slice | append $prism -}}
{{ end -}}
<!-- Bootstrap JS -->
{{ $js := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/bootstrap.js") }}
{{ $params := dict }}
{{ $sourceMap := cond hugo.IsProduction "" "inline" }}
{{ $opts := dict "sourceMap" $sourceMap "minify" hugo.IsProduction "target" "es2018" "params" $params }}
{{ $js = $js | js.Build $opts }}
{{ if hugo.IsProduction }}
{{ $js = $js | fingerprint "sha384" }}
{{ end }}
<script src="{{ $js.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end -}} defer></script>
{{ $js := $slice | resources.Concat (printf "/%s/%s" ($.Scratch.Get "pathName") "js/bundle.js") -}}
{{- if not hugo.IsServer }}
{{- $js := $js | minify | fingerprint "sha384" }}
<script type="text/javascript" src="{{ $js.Permalink }}" integrity="{{ $js.Data.Integrity }}" crossorigin="anonymous" defer></script>
{{- else }}
<script type="text/javascript" src="{{ $js.Permalink }}" defer></script>
{{- end }}

View File

@@ -0,0 +1,16 @@
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.getElementById("content"), {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
// • rendering keys, e.g.:
throwOnError : false
});
});
</script>

View File

@@ -0,0 +1,47 @@
{{ $repoURL := slice .Site.Params.docs.repoURL }}
{{ $repoHostname := (urls.Parse (.Site.Params.docs.repoURL)).Hostname }}
{{ $filePath := replace .File.Path "\\" "/" }}
{{ $iconPath := "" }}
{{ if strings.Contains ($repoHostname | lower) "github" }}
{{ $repoURL = $repoURL | append "blob" (.Site.Params.docs.repoBranch | default "main") }}
{{ $iconPath = "images/social/github_icon.svg" }}
{{ else if strings.Contains ($repoHostname | lower) "gitlab" }}
{{ $repoURL = $repoURL | append "-/blob" (.Site.Params.docs.repoBranch | default "main") }}
{{ $iconPath = "images/social/gitlab_icon.svg" }}
{{ else if strings.Contains ($repoHostname | lower) "bitbucket" }}
{{ $repoURL = $repoURL | append "src" (.Site.Params.docs.repoBranch | default "master") }}
{{ $iconPath = "images/social/bitbucket_icon.svg" }}
{{ end }}
{{ $repoURL = $repoURL | append "content" .Site.LanguagePrefix $filePath }}
{{ $repoURL = delimit $repoURL "/" }}
{{ $editPageURL := replaceRE "(https?://)|(/)+" "$1$2" $repoURL }}
<div class="gitinfo d-flex flex-wrap justify-content-between align-items-center opacity-85 {{ if or .Site.Params.docs.lastMod .Site.Params.docs.editPage -}}pt-3{{ else }}visually-hidden{{ end }}">
{{ if .Site.Params.docs.editPage | default false -}}
<div id="edit-this-page" class="mt-1">
<a href="{{ $editPageURL }}" alt="{{ .Title }}" rel="noopener noreferrer" target="_blank">
<!-- <span class="material-icons size-20 align-text-bottom text-primary">edit</span> -->
<span class="me-1 align-text-bottom">
{{ with resources.Get $iconPath }}
{{ .Content | safeHTML }}
{{ end }}
</span>
Edit this page
<!-- <span class="material-icons size-20 align-text-bottom text-primary">open_in_new</span> -->
</a>
</div>
{{ end }}
{{ if .Site.Params.docs.lastMod | default false -}}
<div id="last-modified" class="mt-1">
<p class="mb-0 fw-semibold">Last updated <span
{{ if .Site.Params.docs.lastModRelative | default true -}}id="relativetime"{{ else }}{{ end }}
data-authdate="{{ dateFormat "2006-01-02T15:04:05Z0700" .GitInfo.AuthorDate }}"
{{ if .Site.Params.docs.lastModRelative | default true -}}title="{{ dateFormat "02 Jan 2006, 15:04 MST" .GitInfo.AuthorDate }}"{{ else }}{{ end }}>
{{ dateFormat "02 Jan 2006, 15:04 MST" .GitInfo.AuthorDate }}
</span>. <span class="material-icons size-20 align-text-bottom opacity-75">history</span>
</p>
</div>
{{ end }}
</div>

View File

@@ -0,0 +1,111 @@
<head>
<meta charset="utf-8" />
<title>
{{- $url := replace .Permalink ( printf "%s" .Site.BaseURL) "" }}
{{- if eq $url "/" }}
{{- .Site.Title }}
{{- else }}
{{- if .Params.heading }}
{{ .Params.heading }}
{{ else }}
{{- if eq .Title .Site.Title }}
{{- .Title }}
{{- else }}
{{- .Title }} | {{ .Site.Params.docs.Title | default (.Site.Title) }}
{{- end }}
{{- end }}
{{- end -}}
</title>
{{- if not hugo.IsProduction }}
<meta name="robots" content="noindex">
{{- end }}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{- with .Description | default ($.Param "description") }}
<meta name="description" content="{{ . }}">
{{- end }}
<meta name="keywords" content="Documentation, Hugo, Hugo Theme, Bootstrap" />
<meta name="author" content="Colin Wilson - Lotus Labs" />
<meta name="email" content="support@aigis.uk" />
<meta name="website" content="https://lotusdocs.dev" />
<meta name="Version" content="v0.1.0" />
<!-- favicon -->
{{ block "favicon" . }}{{ partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "head/favicon.html") . }}{{ end }}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "head/opengraph") . }}
{{- partial (printf "%s/%s" ($.Scratch.Get "pathName") "head/twitter_cards") . }}
<!-- Atom feed autodiscovery -->
{{- with .Site.Title }}
<link rel="alternate" type="application/atom+xml" title="Atom feed for {{ . }}" href="/index.xml" />
{{- end }}
<!-- Dark Mode -->
{{ if eq .Site.Params.docs.darkMode true -}}
{{ $darkModeInit := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/darkmode-init.js") | js.Build | minify -}}
<script>{{ $darkModeInit.Content | safeJS }}</script>
{{ end -}}
<!-- FlexSearch -->
{{ if or (not (isset .Site.Params.flexsearch "enabled")) (eq .Site.Params.flexsearch.enabled true) -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ else }}
{{ $flexSearch := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/flexsearch.bundle.js") }}
{{- if not hugo.IsServer }}
{{ $flexSearch := $flexSearch | minify | fingerprint "sha384" }}
<script type="text/javascript" src="{{ $flexSearch.Permalink }}" integrity="{{ $flexSearch.Data.Integrity }}" crossorigin="anonymous"></script>
{{ else }}
<script type="text/javascript" src="{{ $flexSearch.Permalink }}"></script>
{{ end }}
{{ end }}
{{ end }}
<!-- Google Fonts -->
{{- partialCached "google-fonts" . }}
<!-- Custom CSS -->
{{- $options := dict "enableSourceMap" true }}
{{- if hugo.IsProduction}}
{{- $options := dict "enableSourceMap" false "outputStyle" "compressed" }}
{{- end }}
{{- $style := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "scss/style.scss") }}
{{- $style = $style | resources.ExecuteAsTemplate (printf "/%s/%s" ($.Scratch.Get "pathName") "scss/style.scss") . | css.Sass $options }}
{{- if hugo.IsProduction }}
{{- $style = $style | minify | fingerprint "sha384" }}
{{- end -}}
<link rel="stylesheet" href="{{ $style.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $style.Data.Integrity }}"{{ end -}} crossorigin="anonymous">
<!-- Katex CSS -->
{{- if .Params.katex -}}
{{- $options := dict "enableSourceMap" true }}
{{- if hugo.IsProduction}}
{{- $options := dict "enableSourceMap" false "outputStyle" "compressed" }}
{{- end -}}
{{- $katexCSS := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "scss/katex.scss") }}
{{- $katexCSS = $katexCSS | resources.ExecuteAsTemplate (printf "/%s/%s" ($.Scratch.Get "pathName") "scss/katex.scss") . | css.Sass $options }}
{{- if hugo.IsProduction }}
{{- $katexCSS = $katexCSS | minify | fingerprint "sha384" }}
{{- end -}}
<link rel="stylesheet" href="{{ $katexCSS.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $katexCSS.Data.Integrity }}"{{ end -}} crossorigin="anonymous">
{{- end -}}
<!-- Katex JS -->
{{- if .Params.katex -}}
{{ $katex := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/katex.js") }}
{{ $katexAutoRender := resources.Get (printf "/%s/%s" ($.Scratch.Get "pathName") "js/auto-render.js") }}
{{ if hugo.IsProduction }}
{{ $katex = $katex | minify | fingerprint "sha384" }}
{{ $katexAutoRender = $katexAutoRender | minify | fingerprint "sha384" }}
{{- end -}}
<script src="{{ $katex.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $katex.Data.Integrity }}"{{ end -}} defer></script>
<script src="{{ $katexAutoRender.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $katexAutoRender.Data.Integrity }}"{{ end -}} defer></script>
{{ end -}}
<!-- Katex Config -->
{{ if .Params.katex }}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "footer/katex.html") . -}}
{{ end }}
<!-- Plausible Analytics Config -->
{{- if not hugo.IsServer }}
{{ if and (.Site.Params.plausible.scriptURL | default "https://plausible.io") (.Site.Params.plausible.dataDomain) -}}
{{- partialCached (printf "%s/%s" ($.Scratch.Get "pathName") "head/plausible") . }}
{{- end -}}
{{- end -}}
<!-- Google Analytics v4 Config -->
{{- if not hugo.IsServer }}
{{- if .Site.Config.Services.GoogleAnalytics.ID }}
{{- template "_internal/google_analytics.html" . -}}
{{- end -}}
{{- end -}}
</head>

View File

@@ -0,0 +1,18 @@
{{ if os.FileExists "static/favicon.ico" -}}
<link rel="icon" href="{{ "favicon.ico" | absURL }}" sizes="any">
{{ end -}}
{{ if os.FileExists "static/favicon.svg" -}}
<link rel="icon" type="image/svg+xml" href="{{ "favicon.svg" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/apple-touch-icon.png" -}}
<link rel="apple-touch-icon" sizes="180x180" href="{{ "apple-touch-icon.png" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/favicon-32x32.png" -}}
<link rel="icon" type="image/png" sizes="32x32" href="{{ "favicon-32x32.png" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/favicon-16x16.png" -}}
<link rel="icon" type="image/png" sizes="16x16" href="{{ "favicon-16x16.png" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/site.webmanifest" -}}
<link rel="manifest" crossorigin="use-credentials" href="{{ "site.webmanifest" | absURL }}">
{{ end -}}

View File

@@ -0,0 +1,73 @@
{{ $images := $.Resources.ByType "image" }}
{{ $featured := $images.GetMatch "*feature*" }}
{{ if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end }}
{{ if not $featured }}
{{ $featured = resources.Get "/opengraph/card-base-2.png" }}
{{ $font := resources.Get "/opengraph/poppins-bold.ttf" }}
{{ $descPadding := 0 }}
{{ $title := $.LinkTitle }}
{{ $sizeTitle := 80 }}
{{ $sizeDesc := 50 }}
{{ if gt (len $title) 23 }}
{{ $sizeTitle = 60 }}
{{ end }}
{{ $textTitle := $title }}
{{ $textTitleOptions := dict
"color" "#2e2e2e"
"size" $sizeTitle
"lineSpacing" 10
"x" 65 "y" 180
"font" $font
}}
{{ $filters := slice (images.Text $textTitle $textTitleOptions) }}
{{ $logo := resources.Get "images/logos/logo.png" }}
{{ with $logo }}
{{ $logo = $logo.Resize "x60" }}
{{ $logoFilter := (images.Overlay $logo 65 110) }}
{{ $filters = $filters | append $logoFilter }}
{{ end }}
{{ if gt ($title | strings.RuneCount) 23 }}
{{ $descPadding = -30 }}
{{ if gt ($title | strings.RuneCount) 40 }}
{{ $descPadding = 40 }}
{{ end }}
{{ $sizeDesc = 45 }}
{{ end }}
{{ $description := $.Description }}
{{ with $description }}
{{ $textDesc := $description }}
{{ $textDescOptions := dict
"color" "#8e8e8e"
"size" $sizeDesc
"lineSpacing" 10
"x" 65 "y" (add 290 $descPadding)
"font" $font
}}
{{ $descFilter := (images.Text $textDesc $textDescOptions) }}
{{ $filters = $filters | append $descFilter }}
{{ end }}
{{ $textSiteTitle := $.Site.Title }}
{{ with $textSiteTitle }}
{{ $textSiteTitleOptions := dict
"color" "#5e5e5e"
"size" 35
"lineSpacing" 10
"x" 65 "y" 550
"font" $font
}}
{{ $siteTitleFilter := (images.Text $textSiteTitle $textSiteTitleOptions)}}
{{ $filters = $filters | append $siteTitleFilter }}
{{ end }}
{{ $featured = $featured | images.Filter $filters }}
{{ end }}
{{ return $featured }}

View File

@@ -0,0 +1,45 @@
<meta property="og:title" content="{{ .Title }}" />
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}" />
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
<meta property="og:url" content="{{ .Permalink }}" />
{{- with $.Params.images -}}
{{- range first 6 . }}<meta property="og:image" content="{{ . | absURL }}" />{{ end -}}
{{- else -}}
{{- $images := $.Resources.ByType "image" -}}
{{- $featured := partial (printf "%s/%s" ($.Scratch.Get "pathName") "head/get-featured-image.html") . }}
{{- with $featured -}}
<meta property="og:image" content="{{ $featured.Permalink }}"/>
{{- else -}}
{{- with $.Site.Params.images }}<meta property="og:image" content="{{ index . 0 | absURL }}"/>{{ end -}}
{{- end -}}
{{- end -}}
{{- if .IsPage }}
{{- $iso8601 := "2006-01-02T15:04:05-07:00" -}}
<meta property="article:section" content="{{ .Section }}" />
{{ with .PublishDate }}<meta property="article:published_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }} />{{ end }}
{{ with .Lastmod }}<meta property="article:modified_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }} />{{ end }}
{{- end -}}
{{- with .Params.audio }}<meta property="og:audio" content="{{ . }}" />{{ end }}
{{- with .Params.locale }}<meta property="og:locale" content="{{ . }}" />{{ end }}
{{- with .Site.Params.title }}<meta property="og:site_name" content="{{ . }}" />{{ end }}
{{- with .Params.videos }}{{- range . }}
<meta property="og:video" content="{{ . | absURL }}" />
{{ end }}{{ end }}
{{- /* If it is part of a series, link to related articles */}}
{{- $permalink := .Permalink }}
{{- $siteSeries := .Site.Taxonomies.series }}
{{- if $siteSeries }}
{{ with .Params.series }}{{- range $name := . }}
{{- $series := index $siteSeries ($name | urlize) }}
{{- range $page := first 6 $series.Pages }}
{{- if ne $page.Permalink $permalink }}<meta property="og:see_also" content="{{ $page.Permalink }}" />{{ end }}
{{- end }}
{{ end }}{{ end }}
{{- end }}
{{- /* Facebook Page Admin ID for Domain Insights */}}
{{- with .Site.Params.facebook_admin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}

View File

@@ -0,0 +1,2 @@
<script defer data-domain="{{ .Site.Params.plausible.dataDomain }}" {{ if .Site.Params.plausible.eventAPI }}data-api='{{ .Site.Params.plausible.eventAPI }}'{{ end }} src='{{ .Site.Params.plausible.scriptURL | default "https://plausible.io/js/script.js" }}'></script>
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>

View File

@@ -0,0 +1,23 @@
{{- with $.Params.images -}}
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="{{ index . 0 | absURL }}"/>
{{ else -}}
{{- $images := $.Resources.ByType "image" -}}
{{- $featured := partial (printf "%s/%s" ($.Scratch.Get "pathName") "head/get-featured-image.html") . }}
{{- with $featured -}}
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="{{ $featured.Permalink }}"/>
{{- else -}}
{{- with $.Site.Params.images -}}
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="{{ index . 0 | absURL }}"/>
{{ else -}}
<meta name="twitter:card" content="summary"/>
{{- end -}}
{{- end -}}
{{- end }}
<meta name="twitter:title" content="{{ .Title }}"/>
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>
{{ with .Site.Params.twitter -}}
<meta name="twitter:site" content="@{{ . }}"/>
{{ end -}}

View File

@@ -0,0 +1,21 @@
{{ $pageLang := .Page.Lang }}
{{ $translations := slice }}
{{ $docspath := $.Scratch.Get "pathName" }}
<!-- List available translations (excluding current page)-->
{{ range .Translations }}
<!-- Create 'available translations' slice -->
{{ $translations = $translations | append .Lang }}
<li><a class="dropdown-item" href="{{ .RelPermalink }}" role="button" rel="alternate" hreflang="{{ .Lang }}" lang="{{ .Lang }}">{{ .Language.LanguageName }}</a></li>
{{ end }}
<!-- List of configured languages without a translation for the current page (link to doc root) -->
{{ range .Site.Languages }}
{{ if ne $pageLang .Lang }}
<!-- If .Lang is in the 'available translations' slice, ignore it -->
{{ if in $translations .Lang }}
{{ else }}
<li><a class="dropdown-item" href="{{ .Lang | relURL }}/{{ $docspath }}" role="button" rel="alternate" hreflang="{{ .Lang }}" lang="{{ .Lang }}">{{ .LanguageName }}</a></li>
{{ end }}
{{ end }}
{{ end }}

View File

@@ -0,0 +1,8 @@
<link rel="preload" as="font" href="{{ "/docs/fonts/nunito-v25-latin-300.woff2" | absURL }}" type="font/woff2" crossorigin>
<link rel="preload" as="font" href="{{ "/docs/fonts/nunito-v25-latin-regular.woff2" | absURL }}" type="font/woff2" crossorigin>
<link rel="preload" as="font" href="{{ "/docs/fonts/nunito-v25-latin-500.woff2" | absURL }}" type="font/woff2" crossorigin>
<link rel="preload" as="font" href="{{ "/docs/fonts/nunito-v25-latin-600.woff2" | absURL }}" type="font/woff2" crossorigin>
<link rel="preload" as="font" href="{{ "/docs/fonts/nunito-v25-latin-700.woff2" | absURL }}" type="font/woff2" crossorigin>
<link rel="preload" as="font" href="{{ "/docs/fonts/source-code-pro-v22-latin-500.woff2" | absURL }}" type="font/woff2" crossorigin>
<link rel="preload" as="font" href="{{ "/docs/fonts/source-code-pro-v22-latin-700.woff2" | absURL }}" type="font/woff2" crossorigin>
<!-- <link rel="preload" as="font" href="{{ "/docs/fonts/material-symbols-outlined.woff2" | absURL }}" type="font/woff2" crossorigin> -->

View File

@@ -0,0 +1,116 @@
<!-- sidebar-wrapper -->
<nav id="sidebar" class="sidebar-wrapper">
<div class="sidebar-brand">
<a href='{{ with .Site.Params.docs.logoLinkURL }}{{ . }}{{ else }}{{ relLangURL "" }}{{ end }}' aria-label="HomePage" alt="HomePage">
{{ with resources.Get "images/logos/logo.svg" }}
{{ .Content | safeHTML }}
{{ end }}
</a>
</div>
<div class="sidebar-content" style="height: calc(100% - 131px);">
<ul class="sidebar-menu">
{{ $currentPage := . -}}
{{ $section := $currentPage.Section -}}
{{ range (where .Site.Sections.ByWeight "Section" "in" $section) }}
{{ $child_pages := union .Sections .Pages }}
{{ range $child_pages.ByWeight }}
{{ if or (.Sections) (.Pages) }}
{{ $active := in $currentPage.RelPermalink .RelPermalink }}
<li class="sidebar-dropdown {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }} {{ if $active }}current active{{ end }}">
<button class="btn">
{{ if eq .Site.Params.docs.sidebarIcons true -}}
<i class="material-icons me-2">{{- .Params.icon | default "notes" }}</i>
{{ end }}
{{- .Title }}
</button>
<div class="sidebar-submenu {{ if $active }}d-block{{ end }}">
<ul>
{{ range .Pages }}
{{ $active := in $currentPage.RelPermalink .RelPermalink }}
{{ if .IsSection }}
<li class="sidebar-dropdown nested {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }} {{ if $active }}current active{{ end }}">
<button class="btn">
{{ if eq .Site.Params.docs.sidebarIcons true -}}
<!-- <span class="material-icons me-2">{{- .Params.icon }}</span> -->
{{ end }}
{{- .Title }}
</button>
<div class="sidebar-submenu {{ if $active }}d-block{{ end }}">
<ul>
{{ range .Pages }}
{{ $active := in $currentPage.RelPermalink .RelPermalink }}
{{ if .IsSection }}
<li class="sidebar-dropdown nested {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }} {{ if $active }}current active{{ end }}">
<button class="btn">
{{ if eq .Site.Params.docs.sidebarIcons true -}}
<!-- <span class="material-icons me-2">{{- .Params.icon }}</span> -->
{{ end }}
{{- .Title }}
</button>
<div class="sidebar-submenu {{ if $active }}d-block{{ end }}">
<ul>
{{ range .Pages }}
{{ $active := in $currentPage.RelPermalink .RelPermalink }}
{{ if .IsSection }}
<li class="sidebar-dropdown nested {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }} {{ if $active }}current active{{ end }}">
<button class="btn">
{{ if eq .Site.Params.docs.sidebarIcons true -}}
<!-- <span class="material-icons me-2">{{- .Params.icon }}</span> -->
{{ end }}
{{- .Title }}
</button>
<div class="sidebar-submenu {{ if $active }}d-block{{ end }}">
<ul>
{{ range .Pages }}
{{ $active := in $currentPage.RelPermalink .RelPermalink }}
<li class="{{ if $active }}current{{ end }} {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }}"><a class="sidebar-nested-link" href="{{ .Permalink }}">{{ .Title }}</a></li>
{{ end }}
</ul>
</div>
</li>
{{ else }}
<li class="{{ if $active }}current{{ end }} {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }}"><a class="sidebar-nested-link" href="{{ .Permalink }}">{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ul>
</div>
</li>
{{ else }}
<li class="{{ if $active }}current{{ end }} {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }}"><a class="sidebar-nested-link" href="{{ .Permalink }}">{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ul>
</div>
</li>
{{ else }}
<li class="{{ if $active }}current{{ end }} {{ if eq .Site.Params.docs.sidebarIcons true -}}{{ else }}no-icon{{ end }}"><a class="sidebar-nested-link" href="{{ .Permalink }}">{{ .Title }}</a></li>
{{ end }}
{{ end }}
</ul>
</div>
</li>
{{ else }}
{{ $active := in $currentPage.RelPermalink .RelPermalink }}
<li class="{{ if $active }}current{{ end }}">
<a class="sidebar-root-link" href="{{ .Permalink }}">
{{ if eq .Site.Params.docs.sidebarIcons true -}}
<i class="material-icons me-2">{{ .Params.icon }}</i>
{{ end }}
{{ .Title }}
</a>
</li>
{{ end }}
{{ end }}
{{ end }}
</ul>
<!-- sidebar-menu -->
</div>
<!-- Sidebar Footer -->
<ul class="sidebar-footer list-unstyled mb-0">
<!-- <li class="list-inline-item mb-0">
<a href="javascript:void(0)" data-bs-toggle="modal" data-bs-target="#lang-selector-popup" class="btn btn-primary m-1">Language</a>
</li> -->
</ul>
<!-- Sidebar Footer -->
</nav>
<!-- sidebar-wrapper -->

View File

@@ -0,0 +1,2 @@
{{ $toc := .TableOfContents }}
{{ replaceRE "<ul>" "<ul class=\"dropdown-menu\">" (replaceRE "<nav id=\"TableOfContents\">" "<nav id=\"toc-mobile\">" $toc) 1 | safeHTML }}

View File

@@ -0,0 +1,8 @@
<toc>
<div class="fw-bold text-uppercase mb-2">On this page</div>
{{ if eq .Site.Params.docs.scrollSpy true -}}
{{ .TableOfContents | replaceRE "<nav id=\"TableOfContents\">" "<nav id=\"toc\">" | safeHTML }}
{{ else -}}
{{ .TableOfContents }}
{{ end -}}
</toc>

View File

@@ -0,0 +1,129 @@
<!-- Top Header -->
<div id="top-header" class="top-header d-print-none">
<div class="header-bar d-flex justify-content-between">
<div class="d-flex align-items-center">
<a href='{{ with .Site.Params.docs.logoLinkURL }}{{ . }}{{ else }}{{ relLangURL "" }}{{ end }}' class="logo-icon me-3" aria-label="HomePage" alt="HomePage">
<div class="small">
{{ with resources.Get "images/logos/mark.svg" }}
{{ .Content | safeHTML }}
{{ end }}
</div>
<div class="big">
{{ with resources.Get "images/logos/logo.svg" }}
{{ .Content | safeHTML }}
{{ end }}
</div>
</a>
<button id="close-sidebar" class="btn btn-icon btn-soft">
<span class="material-icons size-20 menu-icon align-middle">menu</span>
</button>
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
<span><div id="docsearch"></div></span>
{{ end }}
{{ if or (not (isset .Site.Params.flexsearch "enabled")) (eq .Site.Params.flexsearch.enabled true) -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ else }}
<!-- <form class="flexsearch position-relative flex-grow-1 ms-2 me-lg-2 d-none">
<input id="flexsearch" class="form-control is-search" type="search" placeholder="{{ i18n "search_title" }}" aria-label="{{ i18n "search_title" }}" autocomplete="off">
<div id="suggestions" class="shadow bg-white rounded d-none"></div>
</form> -->
<button id="flexsearch-button" class="ms-3 btn btn-soft" data-bs-toggle="collapse" data-bs-target="#FlexSearchCollapse" aria-expanded="false" aria-controls="FlexSearchCollapse">
<span class="material-icons size-20 menu-icon align-middle">search</span>
<span class="flexsearch-button-placeholder ms-1 me-2 d-none d-sm-block">{{ i18n "search_title" }}</span>
<div class="d-none d-sm-block">
<span class="flexsearch-button-keys">
<kbd class="flexsearch-button-cmd-key">
<svg width="44" height="15"><path d="M2.118,11.5A1.519,1.519,0,0,1,1,11.042,1.583,1.583,0,0,1,1,8.815a1.519,1.519,0,0,1,1.113-.458h.715V6.643H2.118A1.519,1.519,0,0,1,1,6.185,1.519,1.519,0,0,1,.547,5.071,1.519,1.519,0,0,1,1,3.958,1.519,1.519,0,0,1,2.118,3.5a1.519,1.519,0,0,1,1.114.458A1.519,1.519,0,0,1,3.69,5.071v.715H5.4V5.071A1.564,1.564,0,0,1,6.976,3.5,1.564,1.564,0,0,1,8.547,5.071,1.564,1.564,0,0,1,6.976,6.643H6.261V8.357h.715a1.575,1.575,0,0,1,1.113,2.685,1.583,1.583,0,0,1-2.227,0A1.519,1.519,0,0,1,5.4,9.929V9.214H3.69v.715a1.519,1.519,0,0,1-.458,1.113A1.519,1.519,0,0,1,2.118,11.5Zm0-.857a.714.714,0,0,0,.715-.714V9.214H2.118a.715.715,0,1,0,0,1.429Zm4.858,0a.715.715,0,1,0,0-1.429H6.261v.715a.714.714,0,0,0,.715.714ZM3.69,8.357H5.4V6.643H3.69ZM2.118,5.786h.715V5.071a.714.714,0,0,0-.715-.714.715.715,0,0,0-.5,1.22A.686.686,0,0,0,2.118,5.786Zm4.143,0h.715a.715.715,0,0,0,.5-1.22.715.715,0,0,0-1.22.5Z" fill="currentColor"></path><path d="M12.4,11.475H11.344l3.879-7.95h1.056Z" fill="currentColor"></path><path d="M25.073,5.384l-.864.576a2.121,2.121,0,0,0-1.786-.923,2.207,2.207,0,0,0-2.266,2.326,2.206,2.206,0,0,0,2.266,2.325,2.1,2.1,0,0,0,1.782-.918l.84.617a3.108,3.108,0,0,1-2.622,1.293,3.217,3.217,0,0,1-3.349-3.317,3.217,3.217,0,0,1,3.349-3.317A3.046,3.046,0,0,1,25.073,5.384Z" fill="currentColor"></path><path d="M30.993,5.142h-2.07v5.419H27.891V5.142h-2.07V4.164h5.172Z" fill="currentColor"></path><path d="M34.67,4.164c1.471,0,2.266.658,2.266,1.851,0,1.087-.832,1.809-2.134,1.855l2.107,2.691h-1.28L33.591,7.87H33.07v2.691H32.038v-6.4Zm-1.6.969v1.8h1.572c.832,0,1.22-.3,1.22-.918s-.411-.882-1.22-.882Z" fill="currentColor"></path><path d="M42.883,10.561H38.31v-6.4h1.033V9.583h3.54Z" fill="currentColor"></path></svg>
</kbd>
<kbd class="flexsearch-button-key">
<svg width="15" height="15"><path d="M5.926,12.279H4.41L9.073,2.721H10.59Z" fill="currentColor"/></svg>
</kbd>
</span>
</div>
</button>
{{ end }}
{{ end -}}
</div>
<div class="d-flex align-items-center">
<ul class="list-unstyled mb-0">
{{ with $.Scratch.Get "social_list" }}
{{ range . }}
{{ $path := printf "images/social/%s.%s" . "svg" }}
<li class="list-inline-item mb-0">
<a href="{{ if eq . `rss` }} {{ `index.xml` | absURL }} {{ else if eq . `bluesky` }} https://bsky.app/profile/{{ index site.Params.social . }} {{ else }} https://{{ . }}.com/{{ index site.Params.social . }} {{ end }}" alt="{{ . }}" rel="noopener noreferrer" target="_blank">
<div class="btn btn-icon btn-default border-0">
{{ with resources.Get $path }}
{{ .Content | safeHTML }}
{{ end }}
</div>
</a>
</li>
{{ end }}
{{ end }}
</ul>
{{ if eq .Site.Params.docs.darkMode true -}}
<button id="mode" class="btn btn-icon btn-default ms-2" type="button" aria-label="Toggle user interface mode">
<span class="toggle-dark">
<svg xmlns="http://www.w3.org/2000/svg" height="30" width="30" viewBox="0 0 48 48" fill="currentColor">
<title>{{ i18n "enable_dark_mode" | default "Enable dark mode" }}</title>
<path d="M24 42q-7.5 0-12.75-5.25T6 24q0-7.5 5.25-12.75T24 6q.4 0 .85.025.45.025 1.15.075-1.8 1.6-2.8 3.95-1 2.35-1 4.95 0 4.5 3.15 7.65Q28.5 25.8 33 25.8q2.6 0 4.95-.925T41.9 22.3q.05.6.075.975Q42 23.65 42 24q0 7.5-5.25 12.75T24 42Zm0-3q5.45 0 9.5-3.375t5.05-7.925q-1.25.55-2.675.825Q34.45 28.8 33 28.8q-5.75 0-9.775-4.025T19.2 15q0-1.2.25-2.575.25-1.375.9-3.125-4.9 1.35-8.125 5.475Q9 18.9 9 24q0 6.25 4.375 10.625T24 39Zm-.2-14.85Z"/>
</svg>
</span>
<span class="toggle-light">
<svg xmlns="http://www.w3.org/2000/svg" height="30" width="30" viewBox="0 0 48 48" fill="currentColor">
<title>{{ i18n "enable_light_mode" | default "Enable light mode" }}</title>
<path d="M24 31q2.9 0 4.95-2.05Q31 26.9 31 24q0-2.9-2.05-4.95Q26.9 17 24 17q-2.9 0-4.95 2.05Q17 21.1 17 24q0 2.9 2.05 4.95Q21.1 31 24 31Zm0 3q-4.15 0-7.075-2.925T14 24q0-4.15 2.925-7.075T24 14q4.15 0 7.075 2.925T34 24q0 4.15-2.925 7.075T24 34ZM3.5 25.5q-.65 0-1.075-.425Q2 24.65 2 24q0-.65.425-1.075Q2.85 22.5 3.5 22.5h5q.65 0 1.075.425Q10 23.35 10 24q0 .65-.425 1.075-.425.425-1.075.425Zm36 0q-.65 0-1.075-.425Q38 24.65 38 24q0-.65.425-1.075.425-.425 1.075-.425h5q.65 0 1.075.425Q46 23.35 46 24q0 .65-.425 1.075-.425.425-1.075.425ZM24 10q-.65 0-1.075-.425Q22.5 9.15 22.5 8.5v-5q0-.65.425-1.075Q23.35 2 24 2q.65 0 1.075.425.425.425.425 1.075v5q0 .65-.425 1.075Q24.65 10 24 10Zm0 36q-.65 0-1.075-.425-.425-.425-.425-1.075v-5q0-.65.425-1.075Q23.35 38 24 38q.65 0 1.075.425.425.425.425 1.075v5q0 .65-.425 1.075Q24.65 46 24 46ZM12 14.1l-2.85-2.8q-.45-.45-.425-1.075.025-.625.425-1.075.45-.45 1.075-.45t1.075.45L14.1 12q.4.45.4 1.05 0 .6-.4 1-.4.45-1.025.45-.625 0-1.075-.4Zm24.7 24.75L33.9 36q-.4-.45-.4-1.075t.45-1.025q.4-.45 1-.45t1.05.45l2.85 2.8q.45.45.425 1.075-.025.625-.425 1.075-.45.45-1.075.45t-1.075-.45ZM33.9 14.1q-.45-.45-.45-1.05 0-.6.45-1.05l2.8-2.85q.45-.45 1.075-.425.625.025 1.075.425.45.45.45 1.075t-.45 1.075L36 14.1q-.4.4-1.025.4-.625 0-1.075-.4ZM9.15 38.85q-.45-.45-.45-1.075t.45-1.075L12 33.9q.45-.45 1.05-.45.6 0 1.05.45.45.45.45 1.05 0 .6-.45 1.05l-2.8 2.85q-.45.45-1.075.425-.625-.025-1.075-.425ZM24 24Z"/>
</svg>
</span>
</button>
{{ end -}}
{{ if hugo.IsMultilingual }}
<div class="dropdown">
<button class="btn btn-link btn-default dropdown-toggle ps-2" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ site.Language.Lang | upper }}
</button>
<ul class="dropdown-menu text-end">
{{ partial (printf "%s/%s" ($.Scratch.Get "pathName") "i18nlist") . }}
</ul>
</div>
{{ end }}
</div>
</div>
<!-- FlexSearch Input Start -->
{{ if or (not (isset .Site.Params.flexsearch "enabled")) (eq .Site.Params.flexsearch.enabled true) -}}
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
{{ else }}
<div class="collapse" id="FlexSearchCollapse">
<div class="flexsearch-container">
<div class="flexsearch-keymap">
<li>
<kbd class="flexsearch-button-cmd-key"><svg width="15" height="15" aria-label="Arrow down" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3"></path></g></svg></kbd>
<kbd class="flexsearch-button-cmd-key"><svg width="15" height="15" aria-label="Arrow up" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 11.5v-8M10.5 6.5l-3-3-3 3"></path></g></svg></kbd>
<span class="flexsearch-key-label">{{ i18n "search_navigate" | default "to navigate" }}</span>
</li>
<li>
<kbd class="flexsearch-button-cmd-key"><svg width="15" height="15" aria-label="Enter key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3"></path></g></svg></kbd>
<span class="flexsearch-key-label">{{ i18n "search_select" | default "to select" }}</span>
</li>
<li>
<kbd class="flexsearch-button-cmd-key"><svg width="15" height="15" aria-label="Escape key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956"></path></g></svg></kbd>
<span class="flexsearch-key-label">{{ i18n "search_close" | default "to close" }}</span>
</li>
</div>
<form class="flexsearch position-relative flex-grow-1 ms-2 me-2">
<div class="d-flex flex-row">
<input id="flexsearch" class="form-control" type="search" placeholder="{{ i18n "search_title" }}" aria-label="{{ i18n "search_title" }}" autocomplete="off">
<button id="hideFlexsearch" type="button" class="ms-2 btn btn-soft">
{{ i18n "search_cancel" | default "cancel" }}
</button>
</div>
<div id="suggestions" class="shadow rounded-1 d-none"></div>
</form>
</div>
</div>
{{ end }}
{{ end }}
<!-- FlexSearch Input End -->
</div>
<!-- Top Header -->

View File

@@ -0,0 +1,22 @@
<!-- Footer Start -->
<footer class="footer footer-light footer-bar">
<div class="footer-py-30">
<div class="container text-center">
<div class="row align-items-center">
<div class="col-sm-6">
<div class="text-sm-start">
<p class="mb-0">
{{ $yearToken := (cond (isset .Site.Params (lower "copyrightYearToken")) $.Site.Params.copyrightYearToken ":YEAR:") }}
{{ replace $.Site.Params.footer.copyright $yearToken (string (now.Format "2006")) | markdownify }}
</p>
</div>
</div>
<div class="col-sm-6 mt-4 mt-sm-0 pt-2 pt-sm-0">
<!-- <p>Privacy</p> -->
</div><!--end col-->
</div><!--end row-->
</div><!--end container-->
</div>
</footer><!--end footer-->
<!-- Footer End -->

View File

@@ -0,0 +1,14 @@
{{ if .Site.Params.google_fonts }}
{{ $fonts := slice }}
{{ range .Site.Params.google_fonts }}
{{ $family := replace (index (.) 0) " " "+" }}
{{ $weights := replace (index (.) 1) " " "" }}
{{ $string := print $family ":" $weights }}
{{ $fonts = $fonts | append $string }}
{{ end }}
{{ $url_part := (delimit $fonts "|") | safeHTMLAttr }}
<link rel="preconnect" href="https://fonts.gstatic.com/" />
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />
<link {{ printf "href=\"https://fonts.googleapis.com/css?family=%s%s\"" $url_part "&display=block" | safeHTMLAttr }} rel="stylesheet">
{{ else}}
{{ end }}

View File

@@ -0,0 +1,62 @@
<head>
<meta charset="utf-8" />
<title>{{- .Site.Title }}</title>
{{- if not hugo.IsProduction }}
<meta name="robots" content="noindex">
{{- end }}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="A Lightweight, Modern Documentation Theme for Hugo" />
<meta name="keywords" content="Documentation, Hugo, Hugo Theme, Bootstrap" />
<meta name="author" content="Colin Wilson - Lotus Labs" />
<meta name="email" content="support@aigis.uk" />
<meta name="website" content="https://lotusdocs.dev" />
<meta name="Version" content="v0.1.0" />
<!-- favicon -->
{{ block "head/favicon" . }}{{ partialCached "head/favicon.html" . }}{{ end }}
<!-- Google Fonts -->
{{- partial "google-fonts" . }}
<!-- Custom CSS -->
{{- $options := dict "enableSourceMap" true }}
{{- if hugo.IsProduction}}
{{- $options := dict "enableSourceMap" false "outputStyle" "compressed" }}
{{- end }}
{{- $style := resources.Get "/scss/style.scss" }}
{{- $style = $style | resources.ExecuteAsTemplate "/scss/style.scss" . | css.Sass $options }}
{{- if hugo.IsProduction }}
{{- $style = $style | minify | fingerprint "sha384" }}
{{- end -}}
<link rel="stylesheet" href="{{ $style.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $style.Data.Integrity }}"{{ end -}}/>
<!-- Bootstrap JS -->
{{ $js := resources.Get "js/bootstrap.js" }}
{{ $params := dict }}
{{ $sourceMap := cond hugo.IsProduction "" "inline" }}
{{ $opts := dict "sourceMap" $sourceMap "minify" hugo.IsProduction "target" "es2018" "params" $params }}
{{ $js = $js | js.Build $opts }}
{{ if hugo.IsProduction }}
{{ $js = $js | fingerprint "sha384" }}
{{ end }}
<script src="{{ $js.RelPermalink }}" {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end -}} defer></script>
<!-- Image Compare Viewer -->
{{ if ($.Scratch.Get "image_compare_enabled") }}
{{ $imagecompare := resources.Get "js/image-compare-viewer.min.js" }}
{{- if not hugo.IsServer }}
{{- $js := (slice $imagecompare) | resources.Concat "/js/image-compare.js" | minify | fingerprint "sha384" }}
<script type="text/javascript" src="{{ $js.Permalink }}" integrity="{{ $js.Data.Integrity }}"></script>
{{- else }}
{{- $js := (slice $imagecompare) | resources.Concat "/js/image-compare.js" }}
<script type="text/javascript" src="{{ $js.Permalink }}" {{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end }}></script>
{{- end }}
{{- end }}
<!-- Plausible Analytics Config -->
{{- if not hugo.IsServer }}
{{ if and (.Site.Params.plausible.scriptURL) (.Site.Params.plausible.dataDomain) -}}
{{- partialCached "head/plausible" . }}
{{- end -}}
{{- end -}}
<!-- Google Analytics v4 Config -->
{{- if not hugo.IsServer }}
{{- if .Site.Config.Services.GoogleAnalytics.ID }}
{{- template "_internal/google_analytics.html" . -}}
{{- end -}}
{{- end -}}
</head>

View File

@@ -0,0 +1,18 @@
{{ if os.FileExists "static/favicon.ico" -}}
<link rel="icon" href="{{ "favicon.ico" | absURL }}" sizes="any">
{{ end -}}
{{ if os.FileExists "static/favicon.svg" -}}
<link rel="icon" type="image/svg+xml" href="{{ "favicon.svg" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/apple-touch-icon.png" -}}
<link rel="apple-touch-icon" sizes="180x180" href="{{ "apple-touch-icon.png" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/favicon-32x32.png" -}}
<link rel="icon" type="image/png" sizes="32x32" href="{{ "favicon-32x32.png" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/favicon-16x16.png" -}}
<link rel="icon" type="image/png" sizes="16x16" href="{{ "favicon-16x16.png" | absURL }}">
{{ end -}}
{{ if os.FileExists "static/site.webmanifest" -}}
<link rel="manifest" crossorigin="use-credentials" href="{{ "site.webmanifest" | absURL }}">
{{ end -}}

View File

@@ -0,0 +1 @@
<script defer data-domain="{{ .Site.Params.plausible.dataDomain }}" {{ if .Site.Params.plausible.eventAPI }}data-api="{{ .Site.Params.plausible.eventAPI | default }}"{{ end }} src='{{ .Site.Params.plausible.scriptURL | default "plausible.io" }}'></script>

View File

@@ -0,0 +1,59 @@
<!-- Navbar Start -->
<header id="topnav">
<div class="container d-flex justify-content-between align-items-center">
<!-- Logo container-->
<a class="logo" aria-label="Home" href='{{ relLangURL "" }}'>
{{ with resources.Get "images/logos/mark.svg" }}
{{ .Content | safeHTML }}
{{ end }}
</a>
<!-- End Logo container-->
<div class="d-flex align-items-center">
<div id="navigation">
<!-- Navigation Menu -->
<ul class="navigation-menu nav-right">
{{- range .Site.Menus.primary }}
<li><a href="{{ relLangURL .URL }}">{{ .Name }}</a></li>
{{ end }}
</ul><!--end navigation menu-->
</div><!--end navigation-->
<!-- Social Links Start -->
{{ with $.Scratch.Get "social_list" }}
<ul class="social-link d-flex list-inline mb-0">
{{ range . }}
{{ $path := printf "images/social/%s.%s" . "svg" }}
<li class="list-inline-item mb-0">
<a href="{{ if eq . `rss` }} {{ `index.xml` | absURL }} {{ else if eq . `bluesky` }} https://bsky.app/profile/{{ index site.Params.social . }} {{ else }} https://{{ . }}.com/{{ index site.Params.social . }} {{ end }}" alt="{{ . }}" rel="noopener noreferrer" target="_blank">
<div class="btn btn-icon btn-landing border-0">
{{ with resources.Get $path }}
{{ .Content | safeHTML }}
{{ end }}
</div>
</a>
</li>
{{ end }}
</ul>
{{ end }}
<!-- Social Links End -->
<div class="menu-extras ms-3 me-2">
<div class="menu-item">
<!-- Mobile menu toggle-->
<button class="navbar-toggle btn btn-icon btn-soft-light" id="isToggle" aria-label="toggleMenu" onclick="toggleMenu()">
<div class="lines">
<span></span>
<span></span>
<span></span>
</div>
</button>
<!-- End mobile menu toggle-->
</div>
</div>
</div>
</div><!--end container-->
</header><!--end header-->
<!-- Navbar End -->

View File

@@ -0,0 +1,53 @@
{{ $sectionTitle := .Scratch.Get "sectionTitle" }}
{{ with (index .Site.Data.landing $sectionTitle) }}
{{ if eq .enable true }}
<!-- Features Start -->
<section id="{{ $sectionTitle }}">
<div class="container mt-100 mt-60">
<div class="row justify-content-center">
<div class="col-12 text-center">
<div class="section-title">
{{ with .title }}
<h4 class="title fw-bold mb-4">{{ . }}</h4>
{{ end }}
{{ with .subtitle }}
<p class="text-muted para-desc mb-4 pb-2 mx-auto">{{ . | markdownify }}</p>
{{ end }}
</div>
</div><!--end col-->
</div><!--end row-->
<div class="row">
<div class="col-lg-12">
<div class="row">
{{ range .items }}
<div class="col-md-4 col-12">
<div class="d-flex features feature-primary p-3">
<div class="{{ if .icon }}icon{{ else }}no-icon{{ end }} text-center rounded-3 text-primary me-3">
<i class="material-icons align-middle h4 mb-0">{{ .icon }}</i>
</div>
<div class="flex-1">
<h4 class="title">{{ .title }}</h4>
<p class="text-muted para mb-0">{{ .description | markdownify }}</p>
{{ with .ctaLink }}
<div class="mt-2">
<a href="{{ .url }}" class="mt-3 h6 text-primary">{{ .text | markdownify }} <i class="material-icons align-top">chevron_right</i></a>
</div>
{{ end }}
</div>
</div>
</div>
<!--end col-->
{{ end }}
</div>
<!--end row-->
</div>
<!--end col-->
</div>
<!--end row-->
</div>
<!--end container-->
</section>
<!-- features End -->
{{ end }}
{{ end }}

View File

@@ -0,0 +1,123 @@
{{ $sectionTitle := .Scratch.Get "sectionTitle" }}
{{ with (index .Site.Data.landing $sectionTitle) }}
{{ if eq .enable true }}
<!-- Hero Start -->
{{ with .backgroundImage }}
{{ $path := .path | default "images" }}
{{ $filename := .filename }}
{{ $pathDesktop := printf "%s/%s" $path $filename.desktop }}
{{ $pathMobile := printf "%s/%s" $path $filename.mobile }}
<style>
.hero {
{{ with $filename.desktop }}
{{ with resources.Get $pathDesktop }}
@media (min-width: 768px) {
background-image: url("{{ .RelPermalink }}");
}
{{ end }}
{{ end }}
{{ with $filename.mobile }}
{{ with resources.Get $pathMobile }}
background-image: url("{{ .RelPermalink }}");
{{ end }}
{{ end }}
}
</style>
{{ end }}
<section id="{{ $sectionTitle }}" class="d-table w-100 overflow-hidden hero">
<div class="container">
<div class="row mt-5 align-items-center">
<div class="col-lg-6 col-md-6">
<div class="title-heading">
{{ with .badge }}
<span class="badge {{ if eq .pill true }}rounded-pill{{ end }} bg-{{ if eq .soft true }}soft-{{ else }}hard-{{ end }}{{ .color }} mb-1">{{ .text }}</span>
{{ end }}
<h4 class="heading mt-0 mb-3">
{{ with .titleLogo }}
{{ $path := .path | default "images" }}
{{ $filename := .filename }}
{{ $height := .height | default "70px" }}
{{ $alt := .alt | default "logo image" }}
{{ $path := printf "%s/%s" $path $filename }}
{{ with $filename }}
<span>
{{ with $image := resources.Get $path }}
{{ if eq .MediaType.SubType "svg" }}
{{ .Content | safeHTML }}
{{ else }}
{{ with .Resize (printf "%dx%d webp q85 drawing Lanczos" .Width .Height) }}
<img src="{{ .RelPermalink }}" alt="{{ $alt }}" height="{{ $height }}">
{{ end }}
{{ end }}
{{ end }}
</span>
{{ end }}
{{ end }}
{{ with .title }}{{ . }}{{ end }}
</h4>
{{ with .subtitle }}
<p class="para-desc text-muted mb-0">{{ . | markdownify }}</p>
{{ end }}
<div class="mt-3">
{{ with .ctaButton }}
<a href="{{ relLangURL .url }}" class="btn btn-lg btn-primary me-2 mt-2">
{{ with .icon }}
<span class="material-icons align-middle">{{ . }}</span>
{{ end }}
{{ with .btnText }}
{{ . }}
{{ end }}
</a>
{{ end }}
{{ with .cta2Button }}
<a href="{{ relLangURL .url }}" class="btn btn-lg btn-outline-primary mt-2">
{{ with .icon }}
<span class="material-icons align-middle">{{ . }}</span>
{{ end }}
{{ with .btnText }}
{{ . }}
{{ end }}
</a>
{{ end }}
</div>
{{ with .info }}
<p class="text-muted mb-0 mt-3">{{ . | markdownify }}</p>
{{ end }}
</div>
</div>
<div class="col-lg-6 col-md-6 mt-4 pt-4 mt-sm-0 pt-sm-0">
{{ with .image }}
{{ $path := .path | default "images" }}
{{ $filename := .filename }}
{{ $alt := .alt | default "hero image" }}
{{ $boxShadow := .boxShadow | default false }}
{{ $rounded := .rounded | default false }}
{{ $path := printf "%s/%s" $path $filename }}
{{ with $filename }}
<div class="ms-lg-4">
<div class="classic-hero-image">
{{ with $image := resources.Get $path }}
{{ if eq .MediaType.SubType "svg" }}
{{ .Content | safeHTML }}
{{ else }}
{{ with .Resize (printf "%dx%d webp q85 drawing Lanczos" .Width .Height) }}
<img src="{{ .RelPermalink }}" class="{{ if $boxShadow }}shadow-lg{{ else }}{{ end }} mx-auto d-block {{ if $rounded }}rounded-top-4{{ else }}{{ end }}" width="{{ .Width }}" height="{{ .Height }}" alt="{{ $alt }}">
{{ end }}
{{ end }}
{{ end }}
</div>
</div>
{{ end }}
{{ end }}
</div><!--end col-->
</div><!--end row-->
</div> <!--end container-->
</section><!--end section-->
<!-- Hero End -->
{{ end }}
{{ end }}

View File

@@ -0,0 +1,68 @@
{{ $sectionTitle := .Scratch.Get "sectionTitle" }}
{{ with (index .Site.Data.landing $sectionTitle) }}
{{ if eq .enable true }}
<section id="{{ $sectionTitle }}">
<div class="container mt-100 mt-60">
<div class="row justify-content-center">
<div class="col-12 text-center">
<div class="section-title mb-4 pb-2">
{{ with .title }}
<h4 class="title fw-bold mb-4">{{ . }}</h4>
{{ end }}
{{ with .subtitle }}
<p class="text-muted para-desc mb-0 mx-auto">{{ . | markdownify }}</p>
{{ end }}
</div>
</div><!--end col-->
</div><!--end row-->
<div class="row justify-content-center">
<div class="col-lg-8 col-md-12 mt-3 pt-2 text-center">
<ul class="nav nav-pills nav-justified flex-column flex-sm-row rounded bg-primary" id="pills-tab-{{ $sectionTitle }}" role="tablist">
{{ range $index, $value := .items }}
<li class="nav-item">
<a class="nav-link rounded{{ if (eq $index 0) }} active{{ end }}" id="{{ anchorize .title }}-tab" data-bs-toggle="pill" href="#pills-{{ $sectionTitle }}-{{ anchorize .title }}" role="tab" aria-controls="pills-{{ anchorize .title }}" aria-selected="false">
<div class="text-center py-2">
<p class="mb-0 fs-6 fw-semibold">{{ .title }}</p>
</div>
</a><!--end nav link-->
</li><!--end nav item-->
{{ end }}
</ul><!--end nav pills-->
</div>
</div>
<div class="row">
<div class="col-12 mt-4 pt-2">
<div class="tab-content" id="pills-tab-{{ $sectionTitle }}Content">
{{ range $index, $value := .items }}
<div class="tab-pane fade show{{ if (eq $index 0) }} active{{ end }}" id="pills-{{ $sectionTitle }}-{{ anchorize .title }}" role="tabpanel" aria-labelledby="{{ anchorize .title }}-tab">
<div class="row align-items-center">
<div class="col-md-12">
{{ $title := .title }}
{{ $path := .imagePath | default "images" }}
{{ $imageBefore := .imageBefore }}
{{ $imageAfter := .imageAfter }}
{{ $pathBefore := printf "%s/%s" $path $imageBefore }}
{{ $pathAfter := printf "%s/%s" $path $imageAfter }}
{{ with and $imageBefore $imageAfter }}
<div id="{{ anchorize $title }}-compare" class="image-compare rounded-md border">
{{ with $imageBefore := resources.Get $pathBefore }}
<img src="{{ .RelPermalink }}" alt="{{ $imageBefore }}" />
{{ end }}
{{ with $imageAfter := resources.Get $pathAfter }}
<img src="{{ .RelPermalink }}" alt="{{ $imageAfter }}" />
{{ end }}
</div>
{{ end }}
</div><!--end col-->
</div><!--end row-->
</div><!--end teb pane-->
{{ end }}
</div><!--end tab content-->
</div><!--end col-->
</div><!--end row-->
</div><!--end container-->
</section>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,61 @@
{{ $sectionTitle := .Scratch.Get "sectionTitle" }}
{{ with (index .Site.Data.landing $sectionTitle) }}
{{ if eq .enable true }}
<!-- Single-Right Start -->
<section id="{{ $sectionTitle }}">
<div class="container mt-100 mt-60">
<div class="row align-items-center">
<div class="col-lg-6 col-md-5 {{ if eq .imgOrder.desktop 1 }}order-md-first{{ else }}order-md-last{{ end }} {{ if eq .imgOrder.mobile 1 }}order-first{{ else }}order-last{{ end }}">
<div class="position-relative">
{{ with .image }}
{{ $path := .path | default "images" }}
{{ $filename := .filename }}
{{ $alt := .alt | default "feature image (right)" }}
{{ $path := printf "%s/%s" $path $filename }}
{{ with $filename }}
<div class="text-center text-md-start">
{{ with $image := resources.Get $path }}
{{ if eq .MediaType.SubType "svg" }}
{{ .Content | safeHTML }}
{{ else }}
{{ with .Resize (printf "%dx%d webp q85 drawing Lanczos" .Width .Height) }}
<img src="{{ .RelPermalink }}" class="img-fluid" width="{{ .Width }}" height="{{ .Height }}" alt="{{ $alt }}">
{{ end }}
{{ end }}
{{ end }}
</div>
{{ end }}
{{ end }}
</div>
</div><!--end col-->
<div class="col-lg-6 col-md-7 mt-5 mt-sm-0 {{ if eq .imgOrder.desktop 1 }}order-md-last{{ else }}order-md-first{{ end }} {{ if eq .imgOrder.mobile 1 }}order-last{{ else }}order-first{{ end }}">
<div class="section-title">
{{ with .title }}
<h1 class="title mb-3">{{ . }}</h1>
{{ end }}
{{ with .subtitle }}
<p class="para-desc text-muted">{{ . | markdownify }}</p>
{{ end }}
{{ if .list }}
<ul class="list-unstyled text-muted">
{{ range .list }}
<li class="mb-1"><span class="text-primary h5 me-2"><i class="material-icons align-middle size-20">{{ .icon }}</i></span>{{ .text | markdownify }}</li>
{{ end }}
</ul>
{{ end }}
{{ with .ctaButton }}
<div class="mt-4">
<a href="{{ .url }}" class="mt-3 h6 text-primary">{{ .text | markdownify }} <i class="material-icons align-middle mb-1">chevron_right</i></a>
</div>
{{ end }}
</div>
</div>
</div>
</div>
</section>
<!-- Single-Right End -->
{{ end }}
{{ end }}

View File

@@ -0,0 +1,30 @@
{{ $icon_array := dict
"info" "info"
"warning" "warning"
"success" "check_circle"
"danger" "report"
"default" "notifications"
"primary" "info"
}}
<div class="alert alert-{{ with .Get "context" }}{{.}}{{ else }}default{{ end }} d-flex" role="alert">
<div class="flex-shrink-1 alert-icon">
{{ with .Get "icon" }}
{{ . }}
{{ else }}
<span class="material-icons size-20 me-2">
{{ with .Get "context" }}{{ index $icon_array . }}{{ else }}{{ $icon_array.default }}{{ end }}
</span>
{{- end -}}
</div>
{{ with .Get "text" }}
<div class="w-100">{{ . | $.Page.RenderString }}</div>
{{ else }}
{{ with .InnerDeindent }}
<div class="w-100">
{{ . }}
</div>
{{ else }}
{{ errorf "No valid text variable or Inner content given"}}
{{- end -}}
{{- end -}}
</div>

View File

@@ -0,0 +1,8 @@
{{- $inner := trim .Inner "\r\n" }}
{{- if hasPrefix $inner "$$" }}
<div class="equation leqno" >
{{- $inner }}
</div>
{{- else }}
{{- $inner }}
{{- end }}

View File

@@ -0,0 +1 @@
{{ .Inner | .Page.RenderString }}

View File

@@ -0,0 +1,37 @@
{{ $inner := replaceRE "^\r?\n" "" .Inner | string }}
{{ $innerRemoveLB := replaceRE "\r\n?|\n" "" .Inner | string }}
{{ $ordinal := .Ordinal }}
{{ $codeHash := sha1 (delimit (slice $innerRemoveLB $ordinal "prismshortcode") "") }}
{{ $codeHashShort := substr $codeHash 0 7 }}
{{ if len .Params | eq 0 }}
<pre id="{{ $codeHashShort }}"><code>{{ $inner }}</code></pre>
{{ else }}
{{ if .IsNamedParams }}
<div class="prism-shortcode {{- if .Get "line" }} data-line{{ end }}">
<pre {{- if .Get "id" }} id="{{ .Get "id" }}"{{ else }} id="{{ $codeHashShort }}"{{ end }} class="
{{- if .Get "lang" }}language-{{ .Get "lang" }}{{ end }}
{{- if .Get "line-numbers" }} line-numbers{{ end }}
{{- if .Get "linkable-line-numbers" }} linkable-line-numbers{{ end }}
{{- if .Get "command-line" }} command-line{{ end }}"
{{- /* file highlight plugin */ -}}
{{- if .Get "src" }} data-src="{{ .Get "src" }}"{{ end }}
{{- if .Get "src-range" }} data-range="{{ .Get "src-range" }}"{{ end }}
{{- /* line highlight plugin */ -}}
{{- if .Get "line" }} data-line="{{ .Get "line" }}"{{ end }}
{{- if .Get "line-offset" }} data-line-offset="{{ .Get "line-offset" }}"{{ end }}
{{- /* line number plugin */ -}}
{{- if .Get "start" }} data-start="{{ .Get "start" }}"{{ end }}
{{- /* command-line plugin */ -}}
{{- if .Get "user" }} data-user="{{ .Get "user" }}"{{ end }}
{{- if .Get "host" }} data-host="{{ .Get "host" }}"{{ end }}
{{- if .Get "prompt" }} data-prompt="{{ .Get "prompt" }}"{{ end }}
{{- if .Get "output" }} data-output="{{ .Get "output" }}"{{ end }}
>{{- if .Get "src" }}{{ $inner }}{{ else }}<code {{ if .Get "lang" }}class="language-{{ .Get "lang" }}"{{ end }}
>{{ $inner }}</code>{{ end }}</pre>
</div>
{{ else }}
<pre id="{{ $codeHashShort }}" class="language-{{ .Get 0 }}">
<code class="language-{{ .Get 0 }}">{{ $inner }}</code>
</pre>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,11 @@
<!-- https://github.com/rvanhorn/hugo-dynamic-tabs -->
{{ $tabName := .Get "tabName" }}
{{ $tabID := delimit (shuffle (slice "a" "b" "c" "d" "e" "f")) "" }}
{{ .Parent.Scratch.Add "tabName" (slice $tabName) }}
{{ .Parent.Scratch.Add "tabID" (slice $tabID) }}
<div class="tab-pane fade{{ if eq .Ordinal 0 }} show active{{ end }}" id="{{ $tabID }}" role="tabpanel" aria-labelledby="nav-1">
{{ $.Inner }}
</div>

View File

@@ -0,0 +1,8 @@
{{ $htmlTable := .Inner | .Page.RenderString }}
{{ $class := .Get 0 }}
{{ $old := "<table>" }}
{{ $new := printf "<table class=\"table %s\">" $class }}
{{ $htmlTable := replace $htmlTable $old $new }}
{{ $htmlTable := replaceRE "align=\"([a-z]+)\"" "style=\"text-align: $1\"" $htmlTable}}
{{ $htmlTable | safeHTML }}

View File

@@ -0,0 +1,25 @@
<!-- https://github.com/rvanhorn/hugo-dynamic-tabs -->
<nav>
<div class="nav nav-tabs" id="nav-tab" role="tablist">
{{ $tabTotal := .Get "tabTotal" }}
{{ $tabRightAlign := .Get "tabRightAlign" }}
{{ $tabName := .Scratch.Get "tabName" }}
{{ $tabID := .Scratch.Get "tabID" }}
{{ range $i, $sequence := (seq $tabTotal) }}
<a class="nav-link {{ if eq $i 0 }} active{{ end }}{{ if in $tabRightAlign (add 1 $i) }} ms-auto {{ end }}"
id="{{ (index $tabID $i) }}Tab" data-bs-toggle="tab" data-bs-target="#{{ (index $tabID $i) }}"
type="button" role="tab" aria-controls="{{ (index $tabID $i) }}" aria-selected="true">{{ (replaceRE "(\\s)" " " (index $tabName $i)) }}</a>
{{ end }}
</div>
</nav>
<div class="tab-content" id="nav-tab-content">
{{ .Inner }}
</div>