Compare commits

...

24 Commits

Author SHA1 Message Date
Xin
9fc253dff5 chore: cache tree branch without leaf 2023-10-24 22:52:10 +01:00
Xin
0125822785 chore: move inline template to partial 2023-10-24 22:22:57 +01:00
Xin
1e1d1c8716 chore: add back mobile nav 2023-10-24 21:39:43 +01:00
Xin
1048ee47d7 fix: unescape html for link title 2023-10-24 20:41:50 +01:00
Xin
5842f893a3 chore: add toc in section tree json 2023-10-24 20:41:28 +01:00
Xin
78ce7c2f2e fix: link in sidebar extra component 2023-10-24 20:17:39 +01:00
Xin
0652772c15 refactor: pre-calculate section tree structure json 2023-10-24 00:17:48 +01:00
Xin
13e4eb3414 chore: add section-tree.html 2023-10-23 21:59:26 +01:00
Xin
529fcd8a62 chore: add metric command 2023-10-23 19:44:18 +01:00
Xin
21a13c49f9 chore: inline partial sidebar switches 2023-10-22 13:57:55 +01:00
Xin
98d0a3dc73 refactor: split out sidebar elements into partials 2023-10-22 11:17:26 +01:00
Xin
24f3178ea8 perf: cache scripts 2023-10-22 11:01:19 +01:00
Xin
230cc438b7 perf: cache sidebar footer 2023-10-21 23:22:45 +01:00
Xin
214cb7994f perf: cache sidebear-collapsible-button 2023-10-21 23:18:42 +01:00
Xin
792ad4b569 perf: cache partial "search.html" 2023-10-21 23:13:30 +01:00
Xin
97e6945c04 feat: add option to set default theme and hide toggle button (#146)
resolves #135 

Light / dark theme can be configured via:

```yaml
  theme:
    # light | dark | system
    default: system
    displayToggle: true
```
2023-10-21 22:18:04 +01:00
Xin
93cb788e52 feat(search): support different search index types (#145)
* add support for different search index types: `content | summary | heading | title`
* resolves #139
2023-10-21 21:00:39 +01:00
88b0f1b2ab fix: broken "edit this page" link in windows (#143) 2023-10-18 22:33:36 +01:00
a31b46f5e3 chore(i18n): add translations of Korean (#144)
* chore(i18n): add translations of Korean

* Update ko.yaml

---------

Co-authored-by: Xin <xin@imfing.com>
2023-10-18 22:32:28 +01:00
Sid
6641d36b98 docs: add using Hextra as Git submodule (#137)
* docs: update docs relating to getting started and Hugo modules

* docs: add docs to use Hextra as Git submodule, closes #107

* chore: use paragraph instead of callout, refactor docs

* Update getting-started.md

* Update getting-started.md

---------

Co-authored-by: Xin <xin@imfing.com>
2023-10-16 14:28:28 +01:00
e42d01898a fix: add missing translations (#142)
* fix: add missing translation in blog list

* fix: add translatable variables

* Update en.yaml

---------

Co-authored-by: Xin <xin@imfing.com>
2023-10-16 13:30:31 +01:00
Xin
6cd4c55613 fix: callout styling issue with markdown content (#141) 2023-10-13 00:09:57 +01:00
Xin
cb09b7ce1e fix: skip scroll event if no backToTop element (#138) 2023-10-12 23:25:34 +01:00
96c6ff073f chore(i18n): update zh-cn.yaml (#136) 2023-10-11 23:22:07 +01:00
28 changed files with 556 additions and 273 deletions

View File

@ -1,18 +1,22 @@
const backToTop = document.querySelector("#backToTop"); // Back to top button
document.addEventListener("scroll", (event) => { document.addEventListener("DOMContentLoaded", function () {
if (window.scrollY > 300) { const backToTop = document.querySelector("#backToTop");
backToTop.classList.remove("opacity-0"); if (backToTop) {
} else { document.addEventListener("scroll", (e) => {
backToTop.classList.add("opacity-0"); if (window.scrollY > 300) {
backToTop.classList.remove("opacity-0");
} else {
backToTop.classList.add("opacity-0");
}
});
} }
}); });
function scrollUp() { function scrollUp() {
window.scroll({ window.scroll({
top: 0, top: 0,
left: 0, left: 0,
behavior: 'smooth' behavior: "smooth",
}); });
} }

View File

@ -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 // Change the icons of the buttons based on previous settings or system theme
if ( if (
localStorage.getItem("color-theme") === "dark" || localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches) (!("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 = "dark");
themeToggleButtons.forEach((el) => el.dataset.theme = "light"); } else {
} themeToggleButtons.forEach((el) => el.dataset.theme = "light");
}
themeToggleButtons.forEach((el) => { // Add click event handler to the buttons
el.addEventListener("click", function () { themeToggleButtons.forEach((el) => {
if (localStorage.getItem("color-theme")) { el.addEventListener("click", function () {
if (localStorage.getItem("color-theme") === "light") { if (localStorage.getItem("color-theme")) {
document.documentElement.classList.add("dark"); if (localStorage.getItem("color-theme") === "light") {
document.documentElement.style.colorScheme = "dark"; setDarkTheme();
localStorage.setItem("color-theme", "dark"); localStorage.setItem("color-theme", "dark");
} else {
setLightTheme();
localStorage.setItem("color-theme", "light");
}
} else { } else {
document.documentElement.classList.remove("dark"); if (document.documentElement.classList.contains("dark")) {
document.documentElement.style.colorScheme = "light"; setLightTheme();
localStorage.setItem("color-theme", "light"); localStorage.setItem("color-theme", "light");
} else {
setDarkTheme();
localStorage.setItem("color-theme", "dark");
}
} }
} else { el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
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";
}); });
});
// 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"
);
}
});
})();

View File

@ -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 .Site.Pages "Kind" "in" (slice "page" "section") -}}
{{- $pages = where $pages "Params.excludeSearch" "!=" true -}} {{- $pages = where $pages "Params.excludeSearch" "!=" true -}}
{{- $pages = where $pages "Content" "!=" "" -}} {{- $pages = where $pages "Content" "!=" "" -}}
@ -7,7 +14,7 @@
{{- range $index, $page := $pages -}} {{- range $index, $page := $pages -}}
{{- $pageTitle := $page.LinkTitle | default $page.File.BaseFileName -}} {{- $pageTitle := $page.LinkTitle | default $page.File.BaseFileName -}}
{{- $pageLink := $page.RelPermalink -}} {{- $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)) -}} {{- $output = $output | merge (dict $pageLink (dict "title" $pageTitle "data" $data)) -}}
{{- end -}} {{- end -}}

View File

@ -19,18 +19,29 @@ We have provided a [GitHub Actions workflow](https://docs.github.com/en/pages/ge
## Start as New Project ## 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. 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.
Please refer to Hugo's [official installation guide](https://gohugo.io/installation/) for more details.
[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 %}} {{% steps %}}
### Initialize a new Hugo site ### Initialize a new Hugo site
```bash ```shell
$ hugo new site my-site --format=yaml $ 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 $ 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 ```yaml
module: module:
@ -55,7 +66,7 @@ module:
### Create your first content pages ### 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 ```shell
$ hugo new content/_index.md $ hugo new content/_index.md
@ -73,20 +84,101 @@ Voila! You can see your new site at `http://localhost:1313/`.
{{% /steps %}} {{% /steps %}}
## Update Theme
{{% details title="How to 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 ```shell
$ hugo mod get -u $ 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. See [Hugo Modules](https://gohugo.io/hugo-modules/use-modules/#update-all-modules) for more details.
{{% /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 ## Next

View File

@ -5,7 +5,7 @@ weight: 2
Hugo reads its configuration from `hugo.yaml` in the root of your Hugo site. 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. 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--> <!--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. 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. 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 ### Page Width
@ -193,11 +213,42 @@ params:
width: wide width: wide
``` ```
There are three available options: `full`, `wide`, and `normal`. There are three available options: `full`, `wide`, and `normal`. By default, the page width is set to `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. 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 ### Google Analytics

View File

@ -109,6 +109,11 @@ params:
# full (100%), wide (90rem), normal (1280px) # full (100%), wide (90rem), normal (1280px)
width: normal width: normal
theme:
# light | dark | system
default: system
displayToggle: true
footer: footer:
displayCopyright: true displayCopyright: true
displayPoweredBy: true displayPoweredBy: true
@ -117,8 +122,15 @@ params:
displayUpdatedDate: true displayUpdatedDate: true
dateFormat: "January 2, 2006" dateFormat: "January 2, 2006"
# Search
# flexsearch is enabled by default
search: search:
enable: true enable: true
type: flexsearch
flexsearch:
# index page by: content | summary | heading | title
index: content
editURL: editURL:
enable: true enable: true

View File

@ -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 →" editThisPage: "Edit this page on GitHub →"
lastUpdated: "Last updated on" lastUpdated: "Last updated on"
onThisPage: "On this page"
backToTop: "Scroll to top" readMore: "Read more →"
searchPlaceholder: "Search..."
copyright: "© 2023 Hextra Project."

9
i18n/ko.yaml Normal file
View File

@ -0,0 +1,9 @@
backToTop: "맨위로 스크롤"
changeLanguage: "언어변경"
changeTheme: "테마변경"
copyright: "© 2023 Hextra Project."
editThisPage: "Github에서 편집하기 →"
lastUpdated: "마지막 수정일자"
onThisPage: "페이지 목차"
readMore: "더보기 →"
searchPlaceholder: "검색..."

View File

@ -12,4 +12,6 @@ onThisPage: "此页上"
editThisPage: "在 GitHub 上编辑此页 →" editThisPage: "在 GitHub 上编辑此页 →"
lastUpdated: "最后更新于" lastUpdated: "最后更新于"
backToTop: "返回顶部"
copyright: "© 2023 Hextra Project." copyright: "© 2023 Hextra Project."

View File

@ -1,12 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ .Site.Language.Lang }}"> <html lang="{{ .Site.Language.Lang }}">
{{- partial "head.html" . -}} {{ partial "head.html" . -}}
<body dir="ltr"> <body dir="ltr">
{{- partial "navbar.html" . -}} {{ partial "navbar.html" . -}}
{{- block "main" . }}{{ end -}} {{- block "main" . }}{{ end -}}
{{- if or (eq .Site.Params.footer.enable nil) (.Site.Params.footer.enable) }} {{- if or (eq .Site.Params.footer.enable nil) (.Site.Params.footer.enable) }}
{{ partial "footer.html" . }} {{ partial "footer.html" . }}
{{ end }} {{ end -}}
</body> </body>
{{ partial "scripts.html" . }} {{ partialCached "scripts.html" . }}
{{ partial "third-party/scripts.html" . }}
</html> </html>

View File

@ -1,4 +1,5 @@
{{ define "main" }} {{ define "main" }}
{{- $readMore := (T "readMore") | default "Read more →" -}}
<div class='mx-auto flex {{ partial "utils/page-width" . }}'> <div class='mx-auto flex {{ partial "utils/page-width" . }}'>
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }} {{ 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)]"> <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> <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"> <p class="opacity-80 mt-6 leading-7">
{{- partial "utils/page-description" . }} {{- 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>
<p class="opacity-50 text-sm mt-6 leading-7">{{ partial "utils/format-date" .Date }}</p> <p class="opacity-50 text-sm mt-6 leading-7">{{ partial "utils/format-date" .Date }}</p>
</div> </div>

View File

@ -1,4 +1,5 @@
{{- $enableFooterSwitches := .Scratch.Get "enableFooterSwitches" | default false -}} {{- $enableFooterSwitches := .Scratch.Get "enableFooterSwitches" | default false -}}
{{- $displayThemeToggle := site.Params.theme.displayToggle | default true -}}
{{- $copyright := (T "copyright") | default "© 2023 Hextra." -}} {{- $copyright := (T "copyright") | default "© 2023 Hextra." -}}
@ -11,16 +12,19 @@
{{ end -}} {{ end -}}
{{- end -}} {{- end -}}
<footer class="hextra-footer bg-gray-100 pb-[env(safe-area-inset-bottom)] dark:bg-neutral-900 print:bg-transparent"> <footer class="hextra-footer bg-gray-100 pb-[env(safe-area-inset-bottom)] dark:bg-neutral-900 print:bg-transparent">
{{- if $enableFooterSwitches }} {{- if $enableFooterSwitches -}}
<div class='mx-auto flex gap-2 py-2 px-4 {{ $footerWidth }}'> <div class="mx-auto flex gap-2 py-2 px-4 {{ $footerWidth }}">
{{- partial "language-switch.html" (dict "context" .) -}} {{- partial "language-switch.html" (dict "context" .) -}}
{{- partial "theme-toggle.html" -}} {{- with $displayThemeToggle }}{{ partial "theme-toggle.html" }}{{ end -}}
</div> </div>
{{ end -}} {{- if or site.IsMultiLingual $displayThemeToggle -}}
<hr class="dark:border-neutral-800" /> <hr class="dark:border-neutral-800" />
{{- end -}}
{{- end -}}
<div <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"> <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 }} {{- if (.Site.Params.footer.displayPoweredBy | default true) }}<div class="font-semibold">{{ template "theme-credit" . }}</div>{{ end }}

View File

@ -33,13 +33,25 @@
<script> <script>
/* Initialize light/dark mode */ /* 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.classList.add("dark");
document.documentElement.style.colorScheme = "dark"; document.documentElement.style.colorScheme = "dark";
} else { }
const setLightTheme = () => {
document.documentElement.classList.remove("dark"); document.documentElement.classList.remove("dark");
document.documentElement.style.colorScheme = "light"; 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> </script>
{{ partial "custom/head-end.html" . }} {{ partial "custom/head-end.html" . }}

View File

@ -30,7 +30,7 @@
{{- $currentPage := . -}} {{- $currentPage := . -}}
{{- range .Site.Menus.main -}} {{- range .Site.Menus.main -}}
{{- if eq .Params.type "search" -}} {{- if eq .Params.type "search" -}}
{{- partial "search.html" (dict "params" .Params) -}} {{- partialCached "search.html" $currentPage -}}
{{- else -}} {{- else -}}
{{- $external := strings.HasPrefix .URL "http" -}} {{- $external := strings.HasPrefix .URL "http" -}}
{{/* Display icon menu item */}} {{/* Display icon menu item */}}

View File

@ -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" -}} {{- $jsMenu := resources.Get "js/menu.js" -}}
{{- $jsTabs := resources.Get "js/tabs.js" -}} {{- $jsTabs := resources.Get "js/tabs.js" -}}
{{- $jsLang := resources.Get "js/lang.js" -}} {{- $jsLang := resources.Get "js/lang.js" -}}
@ -13,57 +13,20 @@
{{- end -}} {{- end -}}
<script defer src="{{ $scripts.RelPermalink }}" integrity="{{ $scripts.Data.Integrity }}"></script> <script defer src="{{ $scripts.RelPermalink }}" integrity="{{ $scripts.Data.Integrity }}"></script>
{{/* FlexSearch */}}
{{/* Search */}}
{{- if (site.Params.search.enable | default true) -}} {{- if (site.Params.search.enable | default true) -}}
{{- $jsSearchScript := printf "%s.search.js" .Language.Lang -}} {{- $searchType := site.Params.search.type | default "flexsearch" -}}
{{- $jsSearch := resources.Get "js/flexsearch.js" | resources.ExecuteAsTemplate $jsSearchScript . -}} {{- if eq $searchType "flexsearch" -}}
{{- if hugo.IsProduction -}} {{- $jsSearchScript := printf "%s.search.js" .Language.Lang -}}
{{- $jsSearch = $jsSearch | minify | fingerprint -}} {{- $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 -}} {{- 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 -}} {{- 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 }}

View File

@ -6,134 +6,40 @@
{{- $sidebarClass := cond $disableSidebar (cond $displayPlaceholder "md:hidden xl:block" "md:hidden") "md:sticky" -}} {{- $sidebarClass := cond $disableSidebar (cond $displayPlaceholder "md:hidden xl:block" "md:hidden") "md:sticky" -}}
{{- $navRoot := cond (eq site.Home.Type "docs") site.Home $context.FirstSection -}} {{- $navRoot := cond (eq site.Home.Type "docs") site.Home $context.FirstSection -}}
{{- $navPages := union $navRoot.RegularPages $navRoot.Sections -}}
{{- $pageURL := $context.RelPermalink -}} {{- $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 }}"> <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 --> {{/* Search bar on small screen */}}
<div class="px-4 pt-4 md:hidden"> {{- partialCached "sidebar/mobile-search" . -}}
{{ partial "search.html" }}
</div>
<div class="hextra-scrollbar overflow-y-auto overflow-x-hidden p-4 grow md:h-[calc(100vh-var(--navbar-height)-var(--menu-height))]"> <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"> <ul class="flex flex-col gap-1 md:hidden">
<!-- Nav --> {{/* Mobile Navigation */}}
{{ template "sidebar-main" (dict "context" site.Home "pageURL" $pageURL "page" $context "toc" true) -}} {{ $treeMobile := partialCached "sidebar/section-tree" site.Home site.Home }}
{{ template "sidebar-footer" }} {{ partial "sidebar/render-tree" (dict "context" site.Home "page" $context "tree" ($treeMobile | unmarshal)) }}
{{ partialCached "sidebar/extra" $context }}
</ul> </ul>
<!-- Sidebar on large screen --> {{/* Sidebar on large screen */}}
{{- if $disableSidebar -}} <ul class="flex flex-col gap-1 max-md:hidden">
{{- if $displayPlaceholder }}<div class="max-xl:hidden h-0 w-64 shrink-0"></div>{{ end -}} {{ $tree := partialCached "sidebar/section-tree" $navRoot $navRoot }}
{{ .context.Scratch.Set "enableFooterSwitches" true }} {{ partial "sidebar/render-tree" (dict "context" $navRoot "page" $context "tree" ($tree | unmarshal)) }}
{{- else -}} {{ partialCached "sidebar/extra" $context }}
<ul class="flex flex-col gap-1 max-md:hidden"> </ul>
{{ 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 -}}
</div> </div>
{{ partial "sidebar/switches" (dict "context" $context "disableSidebar" $disableSidebar) }}
</aside> </aside>
{{- define "sidebar-main" -}} {{- define "partials/sidebar/mobile-search" -}}
{{ template "sidebar-tree" (dict "context" .context "level" 0 "page" .page "pageURL" .pageURL "toc" (.toc | default false)) }} <div class="px-4 pt-4 md:hidden">
{{- end -}} {{- partialCached "search.html" . -}}
</div>
{{- 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 -}}
{{- end -}} {{- end -}}
{{- define "sidebar-item-link" -}} {{- define "sidebar-item-link" -}}
{{- $external := strings.HasPrefix .link "http" -}} {{- $external := strings.HasPrefix .link "http" -}}
{{- $open := .open | default true -}}
<a <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] 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 }} {{- if .active }}
@ -144,21 +50,30 @@
href="{{ .link }}" href="{{ .link }}"
{{ if $external }}target="_blank" rel="noreferer"{{ end }} {{ if $external }}target="_blank" rel="noreferer"{{ end }}
> >
{{- .title -}} {{- .title | htmlUnescape | safeHTML -}}
{{- with .context }} {{- with .context }}
{{- if or .RegularPages .Sections }} {{- if or .RegularPages .Sections .section }}{{ partialCached "sidebar/collapsible-button" . }}{{ end -}}
<span class="hextra-sidebar-collapsible-button">
{{- template "sidebear-collapsible-button" -}}
</span>
{{- end }}
{{ end -}} {{ end -}}
</a> </a>
{{- end -}} {{- end -}}
{{- define "sidebar-separator" -}} {{- define "partials/sidebar/switches" -}}
<div class="mt-4 border-t py-4 dark:border-neutral-800 contrast-more:border-neutral-400 dark:contrast-more:border-neutral-400" /> {{- $context := .context -}}
{{- end -}} {{- $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" -}} {{ if or site.IsMultiLingual $displayThemeToggle }}
<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> <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 -}} {{- end -}}

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

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

View File

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

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

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

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

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

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

View File

@ -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"> <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 -}} {{- if site.Params.editURL.enable -}}
{{- $editURL := site.Params.editURL.base | default "" -}} {{- $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 -}} {{- 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> <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 -}} {{- end -}}

View File

@ -1,5 +1,6 @@
{{/* Split page raw content into fragments */}} {{/* Split page raw content into fragments */}}
{{ $page := . }} {{ $page := .context }}
{{ $type := .type | default "content" }}
{{ $headingKeys := slice }} {{ $headingKeys := slice }}
{{ $headingTitles := slice }} {{ $headingTitles := slice }}
@ -22,24 +23,40 @@
{{ $len := len $headingKeys }} {{ $len := len $headingKeys }}
{{ $data := dict }} {{ $data := dict }}
{{ if eq $len 0 }} {{ if eq $type "content" }}
{{ $data = $data | merge (dict "" $page.Plain) }} {{/* Include full content of the page */}}
{{ else }} {{ if eq $len 0 }}
{{ range seq $len }} {{ $data = $data | merge (dict "" ($page.Plain | htmlUnescape | chomp)) }}
{{ $i := sub $len . }} {{ else }}
{{ $headingKey := index $headingKeys $i }} {{/* Split the raw content from bottom to top */}}
{{ $headingTitle := index $headingTitles $i }} {{ range seq $len }}
{{ $i := sub $len . }}
{{ $headingKey := index $headingKeys $i }}
{{ $headingTitle := index $headingTitles $i }}
{{ if eq $i 0 }} {{ if eq $i 0 }}
{{ $data = $data | merge (dict $headingKey ($content | markdownify | plainify)) }} {{ $data = $data | merge (dict $headingKey ($content | markdownify | plainify | htmlUnescape | chomp)) }}
{{ else }} {{ else }}
{{ $parts := split $content (printf "\n%s\n" $headingTitle) }} {{ $parts := split $content (printf "\n%s\n" $headingTitle) }}
{{ $lastPart := index $parts (sub (len $parts) 1) }} {{ $lastPart := index $parts (sub (len $parts) 1) }}
{{ $data = $data | merge (dict $headingKey ($lastPart | markdownify | plainify)) }} {{ $data = $data | merge (dict $headingKey ($lastPart | markdownify | plainify | htmlUnescape | chomp)) }}
{{ $content = strings.TrimSuffix $lastPart $content }} {{ $content = strings.TrimSuffix $lastPart $content }}
{{ $content = strings.TrimSuffix (printf "\n%s\n" $headingTitle) $content }} {{ $content = strings.TrimSuffix (printf "\n%s\n" $headingTitle) $content }}
{{ end }}
{{ end }} {{ 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 }} {{ end }}
{{ return $data }} {{ return $data }}

View File

@ -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="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="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"> <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 }} {{ .Inner | markdownify }}
</p> </div>
</div> </div>
</div> </div>

View File

@ -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: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", "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: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": { "devDependencies": {
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e", "@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",