mirror of
https://github.com/imfing/hextra.git
synced 2025-07-01 08:47:11 -04:00
Compare commits
24 Commits
v0.5.0
...
122-improv
Author | SHA1 | Date | |
---|---|---|---|
9fc253dff5 | |||
0125822785 | |||
1e1d1c8716 | |||
1048ee47d7 | |||
5842f893a3 | |||
78ce7c2f2e | |||
0652772c15 | |||
13e4eb3414 | |||
529fcd8a62 | |||
21a13c49f9 | |||
98d0a3dc73 | |||
24f3178ea8 | |||
230cc438b7 | |||
214cb7994f | |||
792ad4b569 | |||
97e6945c04 | |||
93cb788e52 | |||
88b0f1b2ab | |||
a31b46f5e3 | |||
6641d36b98 | |||
e42d01898a | |||
6cd4c55613 | |||
cb09b7ce1e | |||
96c6ff073f |
@ -1,18 +1,22 @@
|
||||
const backToTop = document.querySelector("#backToTop");
|
||||
// Back to top button
|
||||
|
||||
document.addEventListener("scroll", (event) => {
|
||||
if (window.scrollY > 300) {
|
||||
backToTop.classList.remove("opacity-0");
|
||||
} else {
|
||||
backToTop.classList.add("opacity-0");
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const backToTop = document.querySelector("#backToTop");
|
||||
if (backToTop) {
|
||||
document.addEventListener("scroll", (e) => {
|
||||
if (window.scrollY > 300) {
|
||||
backToTop.classList.remove("opacity-0");
|
||||
} else {
|
||||
backToTop.classList.add("opacity-0");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function scrollUp() {
|
||||
window.scroll({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
|
@ -1,40 +1,51 @@
|
||||
// Dark theme toggle
|
||||
// Light / Dark theme toggle
|
||||
(function () {
|
||||
const defaultTheme = '{{ site.Params.theme.default | default `system`}}'
|
||||
|
||||
const themeToggleButtons = document.querySelectorAll(".theme-toggle");
|
||||
const themeToggleButtons = document.querySelectorAll(".theme-toggle");
|
||||
|
||||
// Change the icons inside the button based on previous settings
|
||||
if (
|
||||
localStorage.getItem("color-theme") === "dark" ||
|
||||
(!("color-theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
) {
|
||||
themeToggleButtons.forEach((el) => el.dataset.theme = "dark");
|
||||
} else {
|
||||
themeToggleButtons.forEach((el) => el.dataset.theme = "light");
|
||||
}
|
||||
// Change the icons of the buttons based on previous settings or system theme
|
||||
if (
|
||||
localStorage.getItem("color-theme") === "dark" ||
|
||||
(!("color-theme" in localStorage) &&
|
||||
((window.matchMedia("(prefers-color-scheme: dark)").matches && defaultTheme === "system") || defaultTheme === "dark"))
|
||||
) {
|
||||
themeToggleButtons.forEach((el) => el.dataset.theme = "dark");
|
||||
} else {
|
||||
themeToggleButtons.forEach((el) => el.dataset.theme = "light");
|
||||
}
|
||||
|
||||
themeToggleButtons.forEach((el) => {
|
||||
el.addEventListener("click", function () {
|
||||
if (localStorage.getItem("color-theme")) {
|
||||
if (localStorage.getItem("color-theme") === "light") {
|
||||
document.documentElement.classList.add("dark");
|
||||
document.documentElement.style.colorScheme = "dark";
|
||||
localStorage.setItem("color-theme", "dark");
|
||||
// Add click event handler to the buttons
|
||||
themeToggleButtons.forEach((el) => {
|
||||
el.addEventListener("click", function () {
|
||||
if (localStorage.getItem("color-theme")) {
|
||||
if (localStorage.getItem("color-theme") === "light") {
|
||||
setDarkTheme();
|
||||
localStorage.setItem("color-theme", "dark");
|
||||
} else {
|
||||
setLightTheme();
|
||||
localStorage.setItem("color-theme", "light");
|
||||
}
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
document.documentElement.style.colorScheme = "light";
|
||||
localStorage.setItem("color-theme", "light");
|
||||
if (document.documentElement.classList.contains("dark")) {
|
||||
setLightTheme();
|
||||
localStorage.setItem("color-theme", "light");
|
||||
} else {
|
||||
setDarkTheme();
|
||||
localStorage.setItem("color-theme", "dark");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (document.documentElement.classList.contains("dark")) {
|
||||
document.documentElement.classList.remove("dark");
|
||||
document.documentElement.style.colorScheme = "light";
|
||||
localStorage.setItem("color-theme", "light");
|
||||
} else {
|
||||
document.documentElement.classList.add("dark");
|
||||
document.documentElement.style.colorScheme = "dark";
|
||||
localStorage.setItem("color-theme", "dark");
|
||||
}
|
||||
}
|
||||
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
|
||||
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Listen for system theme changes
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
|
||||
if (defaultTheme === "system" && !("color-theme" in localStorage)) {
|
||||
e.matches ? setDarkTheme() : setLightTheme();
|
||||
themeToggleButtons.forEach((el) =>
|
||||
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light"
|
||||
);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
@ -1,3 +1,10 @@
|
||||
{{/* FlexSearch Index Data */}}
|
||||
{{- $indexType := site.Params.search.flexsearch.index | default "content" -}}
|
||||
|
||||
{{- if not (in (slice "content" "summary" "heading" "title" ) $indexType) -}}
|
||||
{{- errorf "unknown flexsearch index type: %s" $indexType -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- $pages := where .Site.Pages "Kind" "in" (slice "page" "section") -}}
|
||||
{{- $pages = where $pages "Params.excludeSearch" "!=" true -}}
|
||||
{{- $pages = where $pages "Content" "!=" "" -}}
|
||||
@ -7,7 +14,7 @@
|
||||
{{- range $index, $page := $pages -}}
|
||||
{{- $pageTitle := $page.LinkTitle | default $page.File.BaseFileName -}}
|
||||
{{- $pageLink := $page.RelPermalink -}}
|
||||
{{- $data := partial "utils/fragments" $page -}}
|
||||
{{- $data := partial "utils/fragments" (dict "context" $page "type" $indexType) -}}
|
||||
{{- $output = $output | merge (dict $pageLink (dict "title" $pageTitle "data" $data)) -}}
|
||||
{{- end -}}
|
||||
|
||||
|
@ -19,18 +19,29 @@ We have provided a [GitHub Actions workflow](https://docs.github.com/en/pages/ge
|
||||
|
||||
## Start as New Project
|
||||
|
||||
### Prerequisites
|
||||
There are two main ways to add the Hextra theme to your Hugo project.
|
||||
|
||||
Before we start, make sure we have [Hugo](https://gohugo.io/) installed.
|
||||
Please refer to Hugo's [official installation guide](https://gohugo.io/installation/) for more details.
|
||||
1. **Hugo Modules (Recommended)**: The simplest and recommended method. [Hugo modules](https://gohugo.io/hugo-modules/) let you pull in the theme directly from its online source. Theme is downloaded automatically and managed by Hugo.
|
||||
|
||||
[Hugo modules](https://gohugo.io/hugo-modules/) are the recommended way to manage Hugo themes. To use Hugo modules, we need to install [Git](https://git-scm.com/) and [Go](https://go.dev/).
|
||||
2. **Git Submodule**: Alternatively, add Hextra as a [Git Submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules). The theme will be downloaded by Git and stored in your project's `themes` folder.
|
||||
|
||||
### Setup Hextra as Hugo module
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
Before starting, you need to have the following softwares installed:
|
||||
|
||||
- [Hugo (extended version)](https://gohugo.io/installation/)
|
||||
- [Git](https://git-scm.com/)
|
||||
- [Go](https://go.dev/)
|
||||
|
||||
#### Steps
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Initialize a new Hugo site
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ hugo new site my-site --format=yaml
|
||||
```
|
||||
|
||||
@ -45,7 +56,7 @@ $ hugo mod init github.com/username/my-site
|
||||
$ hugo mod get github.com/imfing/hextra
|
||||
```
|
||||
|
||||
Edit `hugo.yaml` to enable Hextra theme:
|
||||
Configure `hugo.yaml` to use Hextra theme by adding the following:
|
||||
|
||||
```yaml
|
||||
module:
|
||||
@ -55,7 +66,7 @@ module:
|
||||
|
||||
### Create your first content pages
|
||||
|
||||
Let's create a new content page for the home page and the documentation page:
|
||||
Let's create new content page for the home page and the documentation page:
|
||||
|
||||
```shell
|
||||
$ hugo new content/_index.md
|
||||
@ -73,20 +84,101 @@ Voila! You can see your new site at `http://localhost:1313/`.
|
||||
{{% /steps %}}
|
||||
|
||||
|
||||
## Update Theme
|
||||
|
||||
{{% details title="How to update theme?" %}}
|
||||
|
||||
To update the theme to the [latest released version](https://github.com/imfing/hextra/releases), run the following command:
|
||||
To update all Hugo modules in your project to their latest versions, run the following command:
|
||||
|
||||
```shell
|
||||
$ hugo mod get -u
|
||||
```
|
||||
|
||||
To update only Hextra to the [latest released version](https://github.com/imfing/hextra/releases), run the following command:
|
||||
|
||||
```shell
|
||||
hugo mod get -u github.com/imfing/hextra
|
||||
```
|
||||
|
||||
See [Hugo Modules](https://gohugo.io/hugo-modules/use-modules/#update-all-modules) for more details.
|
||||
|
||||
{{% /details %}}
|
||||
|
||||
### Setup Hextra as Git submodule
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
Before starting, you need to have the following softwares installed:
|
||||
|
||||
- [Hugo (extended version)](https://gohugo.io/installation/)
|
||||
- [Git](https://git-scm.com/)
|
||||
|
||||
#### Steps
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Initialize a new Hugo site
|
||||
|
||||
```shell
|
||||
$ hugo new site my-site --format=yaml
|
||||
```
|
||||
|
||||
### Add Hextra theme as a Git submodule
|
||||
|
||||
```shell
|
||||
git submodule add https://github.com/imfing/hextra.git themes/hextra
|
||||
```
|
||||
|
||||
Configure `hugo.yaml` to use Hextra theme by adding the following:
|
||||
|
||||
```yaml
|
||||
theme: hextra
|
||||
```
|
||||
|
||||
### Create your first content pages
|
||||
|
||||
Let's create new content page for the home page and the documentation page:
|
||||
|
||||
```shell
|
||||
$ hugo new content/_index.md
|
||||
$ hugo new content/docs/_index.md
|
||||
```
|
||||
|
||||
### Preview the site locally
|
||||
|
||||
```shell
|
||||
$ hugo server --buildDrafts --disableFastRender
|
||||
```
|
||||
|
||||
Voila! You can see your new site at `http://localhost:1313/`.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
|
||||
When using [CI/CD](https://en.wikipedia.org/wiki/CI/CD) for Hugo website deployment, it's essential to ensure that the following command is executed before running the `hugo` command.
|
||||
|
||||
```shell
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Failure to run this command will result in the theme folder not being populated with Hextra theme files, leading to a build failure.
|
||||
|
||||
|
||||
{{% details title="How to update theme?" %}}
|
||||
|
||||
To update all submodules in your repository to their latest commits, run the following command:
|
||||
|
||||
```shell
|
||||
$ git submodule update --remote
|
||||
```
|
||||
|
||||
To update only Hextra to the latest commit, run the following command:
|
||||
|
||||
```shell
|
||||
git submodule update --remote themes/hextra
|
||||
```
|
||||
|
||||
See [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for more details.
|
||||
|
||||
{{% /details %}}
|
||||
|
||||
## Next
|
||||
|
||||
|
@ -5,7 +5,7 @@ weight: 2
|
||||
|
||||
Hugo reads its configuration from `hugo.yaml` in the root of your Hugo site.
|
||||
The config file is where you can configure all aspects of your site.
|
||||
You can find the config file for this site in `exampleSite/hugo.yaml` as a good starting point.
|
||||
Check out the config file for this site [`exampleSite/hugo.yaml`](https://github.com/imfing/hextra/blob/main/exampleSite/hugo.yaml) on GitHub to get a comprehensive idea of available settings and best practices.
|
||||
|
||||
<!--more-->
|
||||
|
||||
@ -181,6 +181,26 @@ Include both `favicon.ico` and `favicon.svg` files in your project to ensure you
|
||||
While `favicon.ico` is generally for older browsers, `favicon.svg` is supported by modern ones. The optional `favicon-dark.svg` can be included for a tailored experience in dark mode.
|
||||
Feel free to use tools like [favicon.io](https://favicon.io/) or [favycon](https://github.com/ruisaraiva19/favycon) to generate these icons.
|
||||
|
||||
### Theme Configuration
|
||||
|
||||
Use the `theme` setting to configure the default theme mode and toggle button, allowing visitors to switch between light or dark mode.
|
||||
|
||||
```yaml {filename="hugo.yaml"}
|
||||
params:
|
||||
theme:
|
||||
# light | dark | system
|
||||
default: system
|
||||
displayToggle: true
|
||||
```
|
||||
|
||||
Options for `theme.default`:
|
||||
|
||||
- `light` - always use light mode
|
||||
- `dark` - always use dark mode
|
||||
- `system` - sync with the operating system setting (default)
|
||||
|
||||
The `theme.displayToggle` parameter allows you to display a toggle button for changing themes.
|
||||
When set to `true`, visitors can switch between light or dark mode, overriding the default setting.
|
||||
|
||||
### Page Width
|
||||
|
||||
@ -193,11 +213,42 @@ params:
|
||||
width: wide
|
||||
```
|
||||
|
||||
There are three available options: `full`, `wide`, and `normal`.
|
||||
By default, the page width is set to `normal`.
|
||||
There are three available options: `full`, `wide`, and `normal`. By default, the page width is set to `normal`.
|
||||
|
||||
Similarly, the width of the navbar and footer can be customized by the `params.navbar.width` and `params.footer.width` parameters.
|
||||
|
||||
### Search Index
|
||||
|
||||
Full-text search powered by [FlexSearch](https://github.com/nextapps-de/flexsearch) is enabled by default.
|
||||
To customize the search index, set the `params.search.flexsearch.index` parameter in the config file:
|
||||
|
||||
```yaml {filename="hugo.yaml"}
|
||||
params:
|
||||
# Search
|
||||
search:
|
||||
enable: true
|
||||
type: flexsearch
|
||||
|
||||
flexsearch:
|
||||
# index page by: content | summary | heading | title
|
||||
index: content
|
||||
```
|
||||
|
||||
Options for `flexsearch.index`:
|
||||
|
||||
- `content` - full content of the page (default)
|
||||
- `summary` - summary of the page, see [Hugo Content Summaries](https://gohugo.io/content-management/summaries/) for more details
|
||||
- `heading` - level 1 and level 2 headings
|
||||
- `title` - only include the page title
|
||||
|
||||
To exclude a page from the search index, set the `excludeSearch: true` in the front matter of the page:
|
||||
|
||||
```yaml {filename="content/docs/guide/configuration.md"}
|
||||
---
|
||||
title: Configuration
|
||||
excludeSearch: true
|
||||
---
|
||||
```
|
||||
|
||||
### Google Analytics
|
||||
|
||||
|
@ -109,6 +109,11 @@ params:
|
||||
# full (100%), wide (90rem), normal (1280px)
|
||||
width: normal
|
||||
|
||||
theme:
|
||||
# light | dark | system
|
||||
default: system
|
||||
displayToggle: true
|
||||
|
||||
footer:
|
||||
displayCopyright: true
|
||||
displayPoweredBy: true
|
||||
@ -117,8 +122,15 @@ params:
|
||||
displayUpdatedDate: true
|
||||
dateFormat: "January 2, 2006"
|
||||
|
||||
# Search
|
||||
# flexsearch is enabled by default
|
||||
search:
|
||||
enable: true
|
||||
type: flexsearch
|
||||
|
||||
flexsearch:
|
||||
# index page by: content | summary | heading | title
|
||||
index: content
|
||||
|
||||
editURL:
|
||||
enable: true
|
||||
|
12
i18n/en.yaml
12
i18n/en.yaml
@ -1,7 +1,9 @@
|
||||
onThisPage: "On this page"
|
||||
backToTop: "Scroll to top"
|
||||
changeLanguage: "Change language"
|
||||
changeTheme: "Change theme"
|
||||
copyright: "© 2023 Hextra Project."
|
||||
editThisPage: "Edit this page on GitHub →"
|
||||
lastUpdated: "Last updated on"
|
||||
|
||||
backToTop: "Scroll to top"
|
||||
|
||||
copyright: "© 2023 Hextra Project."
|
||||
onThisPage: "On this page"
|
||||
readMore: "Read more →"
|
||||
searchPlaceholder: "Search..."
|
||||
|
9
i18n/ko.yaml
Normal file
9
i18n/ko.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
backToTop: "맨위로 스크롤"
|
||||
changeLanguage: "언어변경"
|
||||
changeTheme: "테마변경"
|
||||
copyright: "© 2023 Hextra Project."
|
||||
editThisPage: "Github에서 편집하기 →"
|
||||
lastUpdated: "마지막 수정일자"
|
||||
onThisPage: "페이지 목차"
|
||||
readMore: "더보기 →"
|
||||
searchPlaceholder: "검색..."
|
@ -12,4 +12,6 @@ onThisPage: "此页上"
|
||||
editThisPage: "在 GitHub 上编辑此页 →"
|
||||
lastUpdated: "最后更新于"
|
||||
|
||||
backToTop: "返回顶部"
|
||||
|
||||
copyright: "© 2023 Hextra Project."
|
||||
|
@ -1,12 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.Language.Lang }}">
|
||||
{{- partial "head.html" . -}}
|
||||
{{ partial "head.html" . -}}
|
||||
<body dir="ltr">
|
||||
{{- partial "navbar.html" . -}}
|
||||
{{ partial "navbar.html" . -}}
|
||||
{{- block "main" . }}{{ end -}}
|
||||
{{- if or (eq .Site.Params.footer.enable nil) (.Site.Params.footer.enable) }}
|
||||
{{ partial "footer.html" . }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
</body>
|
||||
{{ partial "scripts.html" . }}
|
||||
{{ partialCached "scripts.html" . }}
|
||||
{{ partial "third-party/scripts.html" . }}
|
||||
</html>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{{ define "main" }}
|
||||
{{- $readMore := (T "readMore") | default "Read more →" -}}
|
||||
<div class='mx-auto flex {{ partial "utils/page-width" . }}'>
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
<article class="w-full break-words flex min-h-[calc(100vh-var(--navbar-height))] min-w-0 justify-center pb-8 pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
@ -11,7 +12,7 @@
|
||||
<h3><a style="color: inherit; text-decoration: none;" class="block font-semibold mt-8 text-2xl " href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
|
||||
<p class="opacity-80 mt-6 leading-7">
|
||||
{{- partial "utils/page-description" . }}
|
||||
<span class="inline-block"> <a class="text-[color:hsl(var(--primary-hue),100%,50%)] underline underline-offset-2 decoration-from-font" href="{{ .RelPermalink }}">Read more →</a> </span>
|
||||
<span class="inline-block"> <a class="text-[color:hsl(var(--primary-hue),100%,50%)] underline underline-offset-2 decoration-from-font" href="{{ .RelPermalink }}">{{ $readMore }}</a> </span>
|
||||
</p>
|
||||
<p class="opacity-50 text-sm mt-6 leading-7">{{ partial "utils/format-date" .Date }}</p>
|
||||
</div>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{{- $enableFooterSwitches := .Scratch.Get "enableFooterSwitches" | default false -}}
|
||||
{{- $displayThemeToggle := site.Params.theme.displayToggle | default true -}}
|
||||
|
||||
{{- $copyright := (T "copyright") | default "© 2023 Hextra." -}}
|
||||
|
||||
@ -11,16 +12,19 @@
|
||||
{{ end -}}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
<footer class="hextra-footer bg-gray-100 pb-[env(safe-area-inset-bottom)] dark:bg-neutral-900 print:bg-transparent">
|
||||
{{- if $enableFooterSwitches }}
|
||||
<div class='mx-auto flex gap-2 py-2 px-4 {{ $footerWidth }}'>
|
||||
{{- if $enableFooterSwitches -}}
|
||||
<div class="mx-auto flex gap-2 py-2 px-4 {{ $footerWidth }}">
|
||||
{{- partial "language-switch.html" (dict "context" .) -}}
|
||||
{{- partial "theme-toggle.html" -}}
|
||||
{{- with $displayThemeToggle }}{{ partial "theme-toggle.html" }}{{ end -}}
|
||||
</div>
|
||||
{{ end -}}
|
||||
<hr class="dark:border-neutral-800" />
|
||||
{{- if or site.IsMultiLingual $displayThemeToggle -}}
|
||||
<hr class="dark:border-neutral-800" />
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
<div
|
||||
class='{{ $footerWidth }} mx-auto flex justify-center py-12 pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)] text-gray-600 dark:text-gray-400 md:justify-start'
|
||||
class="{{ $footerWidth }} mx-auto flex justify-center py-12 pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)] text-gray-600 dark:text-gray-400 md:justify-start"
|
||||
>
|
||||
<div class="flex w-full flex-col items-center sm:items-start">
|
||||
{{- if (.Site.Params.footer.displayPoweredBy | default true) }}<div class="font-semibold">{{ template "theme-credit" . }}</div>{{ end }}
|
||||
|
@ -33,13 +33,25 @@
|
||||
|
||||
<script>
|
||||
/* Initialize light/dark mode */
|
||||
if (localStorage.getItem("color-theme") === "dark" || (!("color-theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
||||
const defaultTheme = '{{ site.Params.theme.default | default `system`}}';
|
||||
|
||||
const setDarkTheme = () => {
|
||||
document.documentElement.classList.add("dark");
|
||||
document.documentElement.style.colorScheme = "dark";
|
||||
} else {
|
||||
}
|
||||
const setLightTheme = () => {
|
||||
document.documentElement.classList.remove("dark");
|
||||
document.documentElement.style.colorScheme = "light";
|
||||
}
|
||||
|
||||
if ("color-theme" in localStorage) {
|
||||
localStorage.getItem("color-theme") === "dark" ? setDarkTheme() : setLightTheme();
|
||||
} else {
|
||||
defaultTheme === "dark" ? setDarkTheme() : setLightTheme();
|
||||
if (defaultTheme === "system") {
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches ? setDarkTheme() : setLightTheme();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{{ partial "custom/head-end.html" . }}
|
||||
|
@ -30,7 +30,7 @@
|
||||
{{- $currentPage := . -}}
|
||||
{{- range .Site.Menus.main -}}
|
||||
{{- if eq .Params.type "search" -}}
|
||||
{{- partial "search.html" (dict "params" .Params) -}}
|
||||
{{- partialCached "search.html" $currentPage -}}
|
||||
{{- else -}}
|
||||
{{- $external := strings.HasPrefix .URL "http" -}}
|
||||
{{/* Display icon menu item */}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{- $jsTheme := resources.Get "js/theme.js" -}}
|
||||
{{- $jsTheme := resources.Get "js/theme.js" | resources.ExecuteAsTemplate "theme.js" . -}}
|
||||
{{- $jsMenu := resources.Get "js/menu.js" -}}
|
||||
{{- $jsTabs := resources.Get "js/tabs.js" -}}
|
||||
{{- $jsLang := resources.Get "js/lang.js" -}}
|
||||
@ -13,57 +13,20 @@
|
||||
{{- end -}}
|
||||
<script defer src="{{ $scripts.RelPermalink }}" integrity="{{ $scripts.Data.Integrity }}"></script>
|
||||
|
||||
{{/* FlexSearch */}}
|
||||
|
||||
{{/* Search */}}
|
||||
{{- if (site.Params.search.enable | default true) -}}
|
||||
{{- $jsSearchScript := printf "%s.search.js" .Language.Lang -}}
|
||||
{{- $jsSearch := resources.Get "js/flexsearch.js" | resources.ExecuteAsTemplate $jsSearchScript . -}}
|
||||
{{- if hugo.IsProduction -}}
|
||||
{{- $jsSearch = $jsSearch | minify | fingerprint -}}
|
||||
{{- $searchType := site.Params.search.type | default "flexsearch" -}}
|
||||
{{- if eq $searchType "flexsearch" -}}
|
||||
{{- $jsSearchScript := printf "%s.search.js" .Language.Lang -}}
|
||||
{{- $jsSearch := resources.Get "js/flexsearch.js" | resources.ExecuteAsTemplate $jsSearchScript . -}}
|
||||
{{- if hugo.IsProduction -}}
|
||||
{{- $jsSearch = $jsSearch | minify | fingerprint -}}
|
||||
{{- end -}}
|
||||
{{- $flexSearchJS := resources.Get "lib/flexsearch/flexsearch.bundle.min.js" | fingerprint -}}
|
||||
<script defer src="{{ $flexSearchJS.RelPermalink }}" integrity="{{ $flexSearchJS.Data.Integrity }}"></script>
|
||||
<script defer src="{{ $jsSearch.RelPermalink }}" integrity="{{ $jsSearch.Data.Integrity }}"></script>
|
||||
{{- else -}}
|
||||
{{- errorf `search type "%s" is not supported` $searchType -}}
|
||||
{{- end -}}
|
||||
{{- $flexSearchJS := resources.Get "lib/flexsearch/flexsearch.bundle.min.js" | fingerprint -}}
|
||||
<script defer src="{{ $flexSearchJS.RelPermalink }}" integrity="{{ $flexSearchJS.Data.Integrity }}"></script>
|
||||
<script defer src="{{ $jsSearch.RelPermalink }}" integrity="{{ $jsSearch.Data.Integrity }}"></script>
|
||||
{{- end -}}
|
||||
|
||||
{{/* Mermaid */}}
|
||||
{{/* FIXME: need to investigate .Page.Store hasMermaid is set for homepage */}}
|
||||
{{- if and (.Page.Store.Get "hasMermaid") (not .Page.IsHome) -}}
|
||||
{{- $mermaidJS := resources.Get "lib/mermaid/mermaid.min.js" | fingerprint -}}
|
||||
<script defer src="{{ $mermaidJS.RelPermalink }}" integrity="{{ $mermaidJS.Data.Integrity }}"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const theme = document.documentElement.classList.contains("dark") ? "dark" : "default";
|
||||
mermaid.initialize({ startOnLoad: true, theme: theme });
|
||||
});
|
||||
</script>
|
||||
{{- end -}}
|
||||
|
||||
{{/* KaTex */}}
|
||||
{{- if .Page.Params.math -}}
|
||||
{{- $katexCSS := resources.Get "lib/katex/katex.min.css" | fingerprint -}}
|
||||
{{- $katexJS := resources.Get "lib/katex/katex.min.js" | fingerprint -}}
|
||||
{{- $mhchemJS := resources.Get "lib/katex/mhchem.min.js" | fingerprint -}}
|
||||
{{- $katexAutoRenderJS := resources.Get "lib/katex/auto-render.min.js" | fingerprint -}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ $katexCSS.RelPermalink }}" integrity="{{ $katexCSS.Data.Integrity }}" />
|
||||
<script defer src="{{ $katexJS.RelPermalink }}" integrity="{{ $katexJS.Data.Integrity }}"></script>
|
||||
<script defer src="{{ $katexAutoRenderJS.RelPermalink }}" integrity="{{ $katexAutoRenderJS.Data.Integrity }}"></script>
|
||||
<script defer src="{{ $mhchemJS.RelPermalink }}" integrity="{{ $mhchemJS.Data.Integrity }}"></script>
|
||||
{{ $katexFonts := resources.Match "lib/katex/fonts/*" }}
|
||||
{{- range $katexFonts -}}
|
||||
{{ .Publish }}
|
||||
{{- end -}}
|
||||
<script>
|
||||
// TODO: make render options configurable
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{ left: "$$", right: "$$", display: true },
|
||||
{ left: "$", right: "$", display: false },
|
||||
{ left: "\\(", right: "\\)", display: false },
|
||||
{ left: "\\[", right: "\\]", display: true },
|
||||
],
|
||||
throwOnError: false,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{ end }}
|
||||
|
@ -6,134 +6,40 @@
|
||||
{{- $sidebarClass := cond $disableSidebar (cond $displayPlaceholder "md:hidden xl:block" "md:hidden") "md:sticky" -}}
|
||||
|
||||
{{- $navRoot := cond (eq site.Home.Type "docs") site.Home $context.FirstSection -}}
|
||||
{{- $navPages := union $navRoot.RegularPages $navRoot.Sections -}}
|
||||
{{- $pageURL := $context.RelPermalink -}}
|
||||
|
||||
|
||||
<aside class="sidebar-container flex flex-col print:hidden md:top-16 md:shrink-0 md:w-64 md:self-start max-md:[transform:translate3d(0,-100%,0)] {{ $sidebarClass }}">
|
||||
<!-- Search bar on small screen -->
|
||||
<div class="px-4 pt-4 md:hidden">
|
||||
{{ partial "search.html" }}
|
||||
</div>
|
||||
{{/* Search bar on small screen */}}
|
||||
{{- partialCached "sidebar/mobile-search" . -}}
|
||||
|
||||
<div class="hextra-scrollbar overflow-y-auto overflow-x-hidden p-4 grow md:h-[calc(100vh-var(--navbar-height)-var(--menu-height))]">
|
||||
<ul class="flex flex-col gap-1 md:hidden">
|
||||
<!-- Nav -->
|
||||
{{ template "sidebar-main" (dict "context" site.Home "pageURL" $pageURL "page" $context "toc" true) -}}
|
||||
{{ template "sidebar-footer" }}
|
||||
{{/* Mobile Navigation */}}
|
||||
{{ $treeMobile := partialCached "sidebar/section-tree" site.Home site.Home }}
|
||||
{{ partial "sidebar/render-tree" (dict "context" site.Home "page" $context "tree" ($treeMobile | unmarshal)) }}
|
||||
{{ partialCached "sidebar/extra" $context }}
|
||||
</ul>
|
||||
|
||||
<!-- Sidebar on large screen -->
|
||||
{{- if $disableSidebar -}}
|
||||
{{- if $displayPlaceholder }}<div class="max-xl:hidden h-0 w-64 shrink-0"></div>{{ end -}}
|
||||
{{ .context.Scratch.Set "enableFooterSwitches" true }}
|
||||
{{- else -}}
|
||||
<ul class="flex flex-col gap-1 max-md:hidden">
|
||||
{{ template "sidebar-main" (dict "context" $navRoot "page" $context "pageURL" $pageURL) }}
|
||||
{{ template "sidebar-footer" }}
|
||||
</ul>
|
||||
{{ end -}}
|
||||
</div>
|
||||
{{/* Hide theme switch when sidebar is disabled */}}
|
||||
{{ $switchesClass := cond $disableSidebar "md:hidden" "" }}
|
||||
<div class="{{ $switchesClass }} {{ with site.IsMultiLingual }}justify-end{{ end }} sticky bottom-0 bg-white dark:bg-dark mx-4 py-4 shadow-[0_-12px_16px_#fff] flex items-center gap-2 dark:border-neutral-800 dark:shadow-[0_-12px_16px_#111] contrast-more:border-neutral-400 contrast-more:shadow-none contrast-more:dark:shadow-none border-t" data-toggle-animation="show">
|
||||
{{- with site.IsMultiLingual }}
|
||||
{{ partial "language-switch" (dict "context" $context "grow" true) }}
|
||||
{{ partial "theme-toggle" (dict "hideLabel" true) }}
|
||||
{{ else }}
|
||||
<div class="flex grow flex-col">
|
||||
{{ partial "theme-toggle" }}
|
||||
</div>
|
||||
{{ end -}}
|
||||
{{/* Sidebar on large screen */}}
|
||||
<ul class="flex flex-col gap-1 max-md:hidden">
|
||||
{{ $tree := partialCached "sidebar/section-tree" $navRoot $navRoot }}
|
||||
{{ partial "sidebar/render-tree" (dict "context" $navRoot "page" $context "tree" ($tree | unmarshal)) }}
|
||||
{{ partialCached "sidebar/extra" $context }}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{ partial "sidebar/switches" (dict "context" $context "disableSidebar" $disableSidebar) }}
|
||||
</aside>
|
||||
|
||||
{{- define "sidebar-main" -}}
|
||||
{{ template "sidebar-tree" (dict "context" .context "level" 0 "page" .page "pageURL" .pageURL "toc" (.toc | default false)) }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "sidebar-tree" -}}
|
||||
{{- if ge .level 4 -}}
|
||||
{{- return -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- $context := .context -}}
|
||||
{{- $page := .page }}
|
||||
{{- $pageURL := .page.RelPermalink -}}
|
||||
{{- $level := .level -}}
|
||||
{{- $toc := .toc | default false -}}
|
||||
|
||||
{{- with $items := union .context.RegularPages .context.Sections -}}
|
||||
{{- if eq $level 0 -}}
|
||||
{{- range $items.ByWeight }}
|
||||
{{- $active := eq $pageURL .RelPermalink -}}
|
||||
{{- $shouldOpen := or (.Params.sidebar.open) (.IsAncestor $page) $active | default true }}
|
||||
<li class="{{ if $shouldOpen }}open{{ end }}">
|
||||
{{- template "sidebar-item-link" dict "context" . "active" $active "title" .LinkTitle "link" .RelPermalink -}}
|
||||
{{- if and $toc $active -}}
|
||||
{{- template "sidebar-toc" dict "page" . -}}
|
||||
{{- end -}}
|
||||
{{- template "sidebar-tree" dict "context" . "page" $page "pageURL" $pageURL "level" (add $level 1) "toc" $toc -}}
|
||||
</li>
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
<div class="pt-1 ltr:pr-0 overflow-hidden transition-all ease-in-out duration-200">
|
||||
<ul class='relative flex flex-col gap-1 before:absolute before:inset-y-1 before:w-px before:bg-gray-200 before:content-[""] ltr:ml-3 ltr:pl-3 ltr:before:left-0 rtl:mr-3 rtl:pr-3 rtl:before:right-0 dark:before:bg-neutral-800'>
|
||||
{{- range $items.ByWeight }}
|
||||
{{- $active := eq $pageURL .RelPermalink -}}
|
||||
{{- $shouldOpen := or (.Params.sidebar.open) (.IsAncestor $page) $active | default true }}
|
||||
{{- $title := .LinkTitle | default .File.BaseFileName -}}
|
||||
<li class="flex flex-col gap-1 {{ if $shouldOpen }}open{{ end }}">
|
||||
{{- template "sidebar-item-link" dict "context" . "active" $active "title" $title "link" .RelPermalink -}}
|
||||
{{- if and $toc $active -}}
|
||||
{{ template "sidebar-toc" dict "page" . }}
|
||||
{{- end }}
|
||||
{{ template "sidebar-tree" dict "context" . "page" $page "pageURL" $pageURL "level" (add $level 1) "toc" $toc }}
|
||||
</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "sidebar-toc" -}}
|
||||
{{ $page := .page }}
|
||||
{{ with $page.Fragments.Headings }}
|
||||
<ul class='flex flex-col gap-1 relative before:absolute before:inset-y-1 before:w-px before:bg-gray-200 before:content-[""] dark:before:bg-neutral-800 ltr:pl-3 ltr:before:left-0 rtl:pr-3 rtl:before:right-0 ltr:ml-3 rtl:mr-3'>
|
||||
{{- range . }}
|
||||
{{- with .Headings }}
|
||||
{{- range . -}}
|
||||
<li>
|
||||
<a
|
||||
href="#{{ anchorize .ID }}"
|
||||
class="flex rounded px-2 py-1.5 text-sm transition-colors [word-break:break-word] cursor-pointer [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none] contrast-more:border flex gap-2 before:opacity-25 before:content-['#'] text-gray-500 hover:bg-gray-100 hover:text-gray-900 dark:text-neutral-400 dark:hover:bg-primary-100/5 dark:hover:text-gray-50 contrast-more:text-gray-900 contrast-more:dark:text-gray-50 contrast-more:border-transparent contrast-more:hover:border-gray-900 contrast-more:dark:hover:border-gray-50"
|
||||
>
|
||||
{{- .Title -}}
|
||||
</a>
|
||||
</li>
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
</ul>
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "sidebar-footer" -}}
|
||||
{{- range site.Menus.sidebar -}}
|
||||
{{- $name := or (T .Identifier) .Name -}}
|
||||
{{ if eq .Params.type "separator" }}
|
||||
<li class="[word-break:break-word] mt-5 mb-2 px-2 py-1.5 text-sm font-semibold text-gray-900 first:mt-0 dark:text-gray-100">
|
||||
<span class="cursor-default">{{ $name }}</span>
|
||||
</li>
|
||||
{{ else }}
|
||||
<li>{{ template "sidebar-item-link" dict "active" false "title" $name "link" (.URL | relLangURL) }}</li>
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
{{- define "partials/sidebar/mobile-search" -}}
|
||||
<div class="px-4 pt-4 md:hidden">
|
||||
{{- partialCached "search.html" . -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
|
||||
{{- define "sidebar-item-link" -}}
|
||||
{{- $external := strings.HasPrefix .link "http" -}}
|
||||
{{- $open := .open | default true -}}
|
||||
<a
|
||||
class="flex items-center justify-between gap-2 cursor-pointer rounded px-2 py-1.5 text-sm transition-colors [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none] [word-break:break-word]
|
||||
{{- if .active }}
|
||||
@ -144,21 +50,30 @@
|
||||
href="{{ .link }}"
|
||||
{{ if $external }}target="_blank" rel="noreferer"{{ end }}
|
||||
>
|
||||
{{- .title -}}
|
||||
{{- .title | htmlUnescape | safeHTML -}}
|
||||
{{- with .context }}
|
||||
{{- if or .RegularPages .Sections }}
|
||||
<span class="hextra-sidebar-collapsible-button">
|
||||
{{- template "sidebear-collapsible-button" -}}
|
||||
</span>
|
||||
{{- end }}
|
||||
{{- if or .RegularPages .Sections .section }}{{ partialCached "sidebar/collapsible-button" . }}{{ end -}}
|
||||
{{ end -}}
|
||||
</a>
|
||||
{{- end -}}
|
||||
|
||||
{{- define "sidebar-separator" -}}
|
||||
<div class="mt-4 border-t py-4 dark:border-neutral-800 contrast-more:border-neutral-400 dark:contrast-more:border-neutral-400" />
|
||||
{{- end -}}
|
||||
{{- define "partials/sidebar/switches" -}}
|
||||
{{- $context := .context -}}
|
||||
{{- $disableSidebar := .disableSidebar -}}
|
||||
{{/* Hide theme switch when sidebar is disabled */}}
|
||||
{{ $switchesClass := cond $disableSidebar "md:hidden" "" -}}
|
||||
{{ $displayThemeToggle := (site.Params.theme.displayToggle | default true) -}}
|
||||
|
||||
{{- define "sidebear-collapsible-button" -}}
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-[18px] min-w-[18px] rounded-sm p-0.5 hover:bg-gray-800/5 dark:hover:bg-gray-100/5"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" class="origin-center transition-transform rtl:-rotate-180"></path></svg>
|
||||
{{ if or site.IsMultiLingual $displayThemeToggle }}
|
||||
<div class="{{ $switchesClass }} {{ with site.IsMultiLingual }}justify-end{{ end }} sticky bottom-0 bg-white dark:bg-dark mx-4 py-4 shadow-[0_-12px_16px_#fff] flex items-center gap-2 dark:border-neutral-800 dark:shadow-[0_-12px_16px_#111] contrast-more:border-neutral-400 contrast-more:shadow-none contrast-more:dark:shadow-none border-t" data-toggle-animation="show">
|
||||
{{- with site.IsMultiLingual -}}
|
||||
{{- partial "language-switch" (dict "context" $context "grow" true) -}}
|
||||
{{- with $displayThemeToggle }}{{ partial "theme-toggle" (dict "hideLabel" true) }}{{ end -}}
|
||||
{{- else -}}
|
||||
{{- with $displayThemeToggle -}}
|
||||
<div class="flex grow flex-col">{{ partial "theme-toggle" }}</div>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
3
layouts/partials/sidebar/collapsible-button.html
Normal file
3
layouts/partials/sidebar/collapsible-button.html
Normal file
@ -0,0 +1,3 @@
|
||||
<span class="hextra-sidebar-collapsible-button">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-[18px] min-w-[18px] rounded-sm p-0.5 hover:bg-gray-800/5 dark:hover:bg-gray-100/5"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" class="origin-center transition-transform rtl:-rotate-180"></path></svg>
|
||||
</span>
|
10
layouts/partials/sidebar/extra.html
Normal file
10
layouts/partials/sidebar/extra.html
Normal file
@ -0,0 +1,10 @@
|
||||
{{- range site.Menus.sidebar -}}
|
||||
{{- $name := or (T .Identifier) .Name -}}
|
||||
{{ if eq .Params.type "separator" }}
|
||||
<li class="[word-break:break-word] mt-5 mb-2 px-2 py-1.5 text-sm font-semibold text-gray-900 first:mt-0 dark:text-gray-100">
|
||||
<span class="cursor-default">{{ $name }}</span>
|
||||
</li>
|
||||
{{ else }}
|
||||
<li>{{ template "sidebar-item-link" (dict "active" false "title" $name "link" (.URL | relLangURL)) }}</li>
|
||||
{{ end }}
|
||||
{{- end -}}
|
@ -0,0 +1,15 @@
|
||||
{{- $entry := . -}}
|
||||
|
||||
<div class="pt-1 ltr:pr-0 overflow-hidden transition-all ease-in-out duration-200">
|
||||
<ul class='relative flex flex-col gap-1 before:absolute before:inset-y-1 before:w-px before:bg-gray-200 before:content-[""] ltr:ml-3 ltr:pl-3 ltr:before:left-0 rtl:mr-3 rtl:pr-3 rtl:before:right-0 dark:before:bg-neutral-800'>
|
||||
{{- range $entry }}
|
||||
{{- $shouldOpen := .open | default false }}
|
||||
<li class="flex flex-col gap-1 {{ if $shouldOpen }}open{{ end }}">
|
||||
{{- template "sidebar-item-link" (dict "context" . "active" false "title" .title "link" .link) -}}
|
||||
{{- if .section -}}
|
||||
{{- partial "sidebar/render-tree-branch-without-leaf" .section -}}
|
||||
{{- end -}}
|
||||
</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
</div>
|
19
layouts/partials/sidebar/render-tree-branch.html
Normal file
19
layouts/partials/sidebar/render-tree-branch.html
Normal file
@ -0,0 +1,19 @@
|
||||
{{- $context := .context -}}
|
||||
{{- $page := .page -}}
|
||||
{{- $entry := .entry -}}
|
||||
{{- $pageLink := .page.RelPermalink -}}
|
||||
|
||||
<div class="pt-1 ltr:pr-0 overflow-hidden transition-all ease-in-out duration-200">
|
||||
<ul class='relative flex flex-col gap-1 before:absolute before:inset-y-1 before:w-px before:bg-gray-200 before:content-[""] ltr:ml-3 ltr:pl-3 ltr:before:left-0 rtl:mr-3 rtl:pr-3 rtl:before:right-0 dark:before:bg-neutral-800'>
|
||||
{{- range $entry }}
|
||||
{{- $active := eq $pageLink .link -}}
|
||||
{{- $shouldOpen := or (.open) (hasPrefix $pageLink .link) $active | default true }}
|
||||
<li class="flex flex-col gap-1 {{ if $shouldOpen }}open{{ end }}">
|
||||
{{- template "sidebar-item-link" (dict "context" . "active" $active "title" .title "link" .link) -}}
|
||||
{{- if .section -}}
|
||||
{{- partial "sidebar/render-tree-branch" (dict "context" $context "entry" .section "page" $page) -}}
|
||||
{{- end -}}
|
||||
</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
</div>
|
18
layouts/partials/sidebar/render-tree.html
Normal file
18
layouts/partials/sidebar/render-tree.html
Normal file
@ -0,0 +1,18 @@
|
||||
{{- $context := .context -}}
|
||||
{{- $page := .page -}}
|
||||
{{- $pageLink := .page.RelPermalink -}}
|
||||
{{- range .tree -}}
|
||||
{{- $active := eq $pageLink .link -}}
|
||||
{{- $containsPage := hasPrefix $pageLink .link -}}
|
||||
{{- $shouldOpen := or (.open) $containsPage $active | default false }}
|
||||
<li class="{{ if $shouldOpen }}open{{ end }}">
|
||||
{{- template "sidebar-item-link" (dict "context" . "active" $active "title" .title "link" .link) -}}
|
||||
{{- if .section -}}
|
||||
{{- if not $containsPage -}}
|
||||
{{- partialCached "sidebar/render-tree-branch-without-leaf" .section .section -}}
|
||||
{{- else -}}
|
||||
{{- partial "sidebar/render-tree-branch" (dict "context" $context "entry" .section "page" $page) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
</li>
|
||||
{{ end }}
|
51
layouts/partials/sidebar/section-tree.html
Normal file
51
layouts/partials/sidebar/section-tree.html
Normal file
@ -0,0 +1,51 @@
|
||||
{{ $context := . -}}
|
||||
|
||||
{{- $pages := union .RegularPages .Sections -}}
|
||||
{{- $pages = where $pages "Params.sidebar.exclude" "!=" true -}}
|
||||
|
||||
{{- $data := slice -}}
|
||||
|
||||
{{- range $pages.ByWeight -}}
|
||||
{{ $structure := (partial "sidebar/section-walk" .) | unmarshal -}}
|
||||
{{ $data = $data | append $structure -}}
|
||||
{{ end -}}
|
||||
|
||||
{{- define "partials/sidebar/section-walk" -}}
|
||||
{{- with . -}}
|
||||
{
|
||||
"title": "{{ .LinkTitle | default .File.BaseFileName }}",
|
||||
"link": "{{ .RelPermalink }}",
|
||||
"toc": {{ partial "sidebar/section-page-toc" . }},
|
||||
"open": {{ .Params.sidebar.open | default false }}
|
||||
{{- if .IsSection }},
|
||||
"section": [
|
||||
{{ $pages := union .RegularPages .Sections -}}
|
||||
{{ $pages = where $pages "Params.sidebar.exclude" "!=" true -}}
|
||||
{{ range $index, $page := $pages.ByWeight -}}
|
||||
{{ partial "sidebar/section-walk" . }}{{ if not (ge $index (sub (len $pages) 1)) }},{{ end -}}
|
||||
{{ end -}}
|
||||
]
|
||||
{{ end -}}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "partials/sidebar/section-page-toc" -}}
|
||||
{{/* Get level 2 headings list used mainly for mobile navigation */}}
|
||||
[
|
||||
{{- with .Fragments.Headings -}}
|
||||
{{/* Loop over level 1 headings */}}
|
||||
{{- range . }}
|
||||
{{- with .Headings }}
|
||||
{{ $headings := . }}
|
||||
{{- range $index, $heading := $headings }}
|
||||
{{ $heading.Title | jsonify (dict "noHTMLEscape" true) }}
|
||||
{{- if not (ge $index (sub (len $headings) 1)) }},{{ end -}}
|
||||
{{ end -}}
|
||||
{{- end -}}
|
||||
{{ end -}}
|
||||
{{- end -}}
|
||||
]
|
||||
{{- end -}}
|
||||
|
||||
{{ return ($data | jsonify (dict "noHTMLEscape" true)) }}
|
21
layouts/partials/sidebar/toc.html
Normal file
21
layouts/partials/sidebar/toc.html
Normal file
@ -0,0 +1,21 @@
|
||||
{{ $page := . }}
|
||||
{{ with $page.Fragments.Headings }}
|
||||
<ul
|
||||
class='flex flex-col gap-1 relative before:absolute before:inset-y-1 before:w-px before:bg-gray-200 before:content-[""] dark:before:bg-neutral-800 ltr:pl-3 ltr:before:left-0 rtl:pr-3 rtl:before:right-0 ltr:ml-3 rtl:mr-3'
|
||||
>
|
||||
{{- range . }}
|
||||
{{- with .Headings }}
|
||||
{{- range . -}}
|
||||
<li>
|
||||
<a
|
||||
href="#{{ anchorize .ID }}"
|
||||
class="flex rounded px-2 py-1.5 text-sm transition-colors [word-break:break-word] cursor-pointer [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none] contrast-more:border gap-2 before:opacity-25 before:content-['#'] text-gray-500 hover:bg-gray-100 hover:text-gray-900 dark:text-neutral-400 dark:hover:bg-primary-100/5 dark:hover:text-gray-50 contrast-more:text-gray-900 contrast-more:dark:text-gray-50 contrast-more:border-transparent contrast-more:hover:border-gray-900 contrast-more:dark:hover:border-gray-50"
|
||||
>
|
||||
{{- .Title -}}
|
||||
</a>
|
||||
</li>
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
</ul>
|
||||
{{ end }}
|
42
layouts/partials/third-party/scripts.html
vendored
Normal file
42
layouts/partials/third-party/scripts.html
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
{{/* Mermaid */}}
|
||||
{{/* FIXME: need to investigate .Page.Store hasMermaid is set for homepage */}}
|
||||
{{- if and (.Page.Store.Get "hasMermaid") (not .Page.IsHome) -}}
|
||||
{{- $mermaidJS := resources.Get "lib/mermaid/mermaid.min.js" | fingerprint -}}
|
||||
<script defer src="{{ $mermaidJS.RelPermalink }}" integrity="{{ $mermaidJS.Data.Integrity }}"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const theme = document.documentElement.classList.contains("dark") ? "dark" : "default";
|
||||
mermaid.initialize({ startOnLoad: true, theme: theme });
|
||||
});
|
||||
</script>
|
||||
{{- end -}}
|
||||
|
||||
{{/* KaTex */}}
|
||||
{{- if .Page.Params.math -}}
|
||||
{{- $katexCSS := resources.Get "lib/katex/katex.min.css" | fingerprint -}}
|
||||
{{- $katexJS := resources.Get "lib/katex/katex.min.js" | fingerprint -}}
|
||||
{{- $mhchemJS := resources.Get "lib/katex/mhchem.min.js" | fingerprint -}}
|
||||
{{- $katexAutoRenderJS := resources.Get "lib/katex/auto-render.min.js" | fingerprint -}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ $katexCSS.RelPermalink }}" integrity="{{ $katexCSS.Data.Integrity }}" />
|
||||
<script defer src="{{ $katexJS.RelPermalink }}" integrity="{{ $katexJS.Data.Integrity }}"></script>
|
||||
<script defer src="{{ $katexAutoRenderJS.RelPermalink }}" integrity="{{ $katexAutoRenderJS.Data.Integrity }}"></script>
|
||||
<script defer src="{{ $mhchemJS.RelPermalink }}" integrity="{{ $mhchemJS.Data.Integrity }}"></script>
|
||||
{{ $katexFonts := resources.Match "lib/katex/fonts/*" }}
|
||||
{{- range $katexFonts -}}
|
||||
{{ .Publish }}
|
||||
{{- end -}}
|
||||
<script>
|
||||
// TODO: make render options configurable
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{ left: "$$", right: "$$", display: true },
|
||||
{ left: "$", right: "$", display: false },
|
||||
{ left: "\\(", right: "\\)", display: false },
|
||||
{ left: "\\[", right: "\\]", display: true },
|
||||
],
|
||||
throwOnError: false,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{ end }}
|
@ -26,7 +26,7 @@
|
||||
<div class="{{ $borderClass }} sticky bottom-0 flex flex-col items-start gap-2 pb-8 dark:border-neutral-800 contrast-more:border-t contrast-more:border-neutral-400 contrast-more:shadow-none contrast-more:dark:border-neutral-400">
|
||||
{{- if site.Params.editURL.enable -}}
|
||||
{{- $editURL := site.Params.editURL.base | default "" -}}
|
||||
{{- with .File -}}{{ $editURL = urls.JoinPath $editURL .Path }}{{- end -}}
|
||||
{{- with .File -}}{{ $editURL = urls.JoinPath $editURL (replace .Path "\\" "/") }}{{- end -}}
|
||||
{{- with .Params.editURL -}}{{ $editURL = .Params.editURL }}{{- end -}}
|
||||
<a class="text-xs font-medium text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100 contrast-more:text-gray-800 contrast-more:dark:text-gray-50" href="{{ $editURL }}" target="_blank" rel="noreferer">{{ $editThisPage }}</a>
|
||||
{{- end -}}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{{/* Split page raw content into fragments */}}
|
||||
{{ $page := . }}
|
||||
{{ $page := .context }}
|
||||
{{ $type := .type | default "content" }}
|
||||
|
||||
{{ $headingKeys := slice }}
|
||||
{{ $headingTitles := slice }}
|
||||
@ -22,24 +23,40 @@
|
||||
{{ $len := len $headingKeys }}
|
||||
{{ $data := dict }}
|
||||
|
||||
{{ if eq $len 0 }}
|
||||
{{ $data = $data | merge (dict "" $page.Plain) }}
|
||||
{{ else }}
|
||||
{{ range seq $len }}
|
||||
{{ $i := sub $len . }}
|
||||
{{ $headingKey := index $headingKeys $i }}
|
||||
{{ $headingTitle := index $headingTitles $i }}
|
||||
{{ if eq $type "content" }}
|
||||
{{/* Include full content of the page */}}
|
||||
{{ if eq $len 0 }}
|
||||
{{ $data = $data | merge (dict "" ($page.Plain | htmlUnescape | chomp)) }}
|
||||
{{ else }}
|
||||
{{/* Split the raw content from bottom to top */}}
|
||||
{{ range seq $len }}
|
||||
{{ $i := sub $len . }}
|
||||
{{ $headingKey := index $headingKeys $i }}
|
||||
{{ $headingTitle := index $headingTitles $i }}
|
||||
|
||||
{{ if eq $i 0 }}
|
||||
{{ $data = $data | merge (dict $headingKey ($content | markdownify | plainify)) }}
|
||||
{{ else }}
|
||||
{{ $parts := split $content (printf "\n%s\n" $headingTitle) }}
|
||||
{{ $lastPart := index $parts (sub (len $parts) 1) }}
|
||||
{{ if eq $i 0 }}
|
||||
{{ $data = $data | merge (dict $headingKey ($content | markdownify | plainify | htmlUnescape | chomp)) }}
|
||||
{{ else }}
|
||||
{{ $parts := split $content (printf "\n%s\n" $headingTitle) }}
|
||||
{{ $lastPart := index $parts (sub (len $parts) 1) }}
|
||||
|
||||
{{ $data = $data | merge (dict $headingKey ($lastPart | markdownify | plainify)) }}
|
||||
{{ $content = strings.TrimSuffix $lastPart $content }}
|
||||
{{ $content = strings.TrimSuffix (printf "\n%s\n" $headingTitle) $content }}
|
||||
{{ $data = $data | merge (dict $headingKey ($lastPart | markdownify | plainify | htmlUnescape | chomp)) }}
|
||||
{{ $content = strings.TrimSuffix $lastPart $content }}
|
||||
{{ $content = strings.TrimSuffix (printf "\n%s\n" $headingTitle) $content }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else if (eq $type "heading" ) }}
|
||||
{{/* Put heading keys with empty content to the data object */}}
|
||||
{{ $data = dict "" "" }}
|
||||
{{ range $headingKeys }}
|
||||
{{ $data = $data | merge (dict . "") }}
|
||||
{{ end }}
|
||||
{{ else if (eq $type "title") }}
|
||||
{{/* Use empty data object since title is included in search-data.json */}}
|
||||
{{ $data = $data | merge (dict "" "") }}
|
||||
{{ else if (eq $type "summary" ) }}
|
||||
{{ $data = $data | merge (dict "" ($page.Summary | plainify | htmlUnescape | chomp)) }}
|
||||
{{ end }}
|
||||
|
||||
{{ return $data }}
|
||||
|
@ -17,8 +17,8 @@
|
||||
<div class="overflow-x-auto mt-6 flex rounded-lg border py-2 ltr:pr-4 rtl:pl-4 contrast-more:border-current contrast-more:dark:border-current {{ $class }}">
|
||||
<div class="select-none text-xl ltr:pl-3 ltr:pr-2 rtl:pr-3 rtl:pl-2" style='font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";'>{{ $emoji }}</div>
|
||||
<div class="w-full min-w-0 leading-7">
|
||||
<p class="mt-6 leading-7 first:mt-0">
|
||||
<div class="mt-6 leading-7 first:mt-0">
|
||||
{{ .Inner | markdownify }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,7 +3,8 @@
|
||||
"dev:theme": "hugo server --logLevel=debug --config=hugo.yaml,../dev.toml --environment=theme --source=exampleSite --themesDir=../.. --disableFastRender -D --port 1313",
|
||||
"dev": "hugo server --source=exampleSite --themesDir=../.. --disableFastRender -D --port 1313",
|
||||
"build:css": "npx postcss --config postcss.config.js --env production assets/css/styles.css -o assets/css/compiled/main.css",
|
||||
"build": "hugo --gc --minify --themesDir=../.. --source=exampleSite"
|
||||
"build": "hugo --gc --minify --themesDir=../.. --source=exampleSite",
|
||||
"metrics": "hugo --themesDir=../.. --source=exampleSite --templateMetrics --templateMetricsHints"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
|
||||
|
Reference in New Issue
Block a user