From 9a20f07d4e51686e5ae1623b34ef6ae0d45b454a Mon Sep 17 00:00:00 2001
From: Xin <5097752+imfing@users.noreply.github.com>
Date: Wed, 13 Aug 2025 15:14:46 +0800
Subject: [PATCH] feat(math): support local katex assets and improve docs
(#742)
* refactor(math): update LaTeX guide and enhance KaTeX integration
- Revised the LaTeX documentation for clarity and improved structure, changing section titles and descriptions for better understanding.
- Added support for chemistry expressions using the mhchem extension.
- Introduced a new KaTeX CSS loader partial to streamline the integration of KaTeX, allowing for configurable remote or local asset loading.
- Updated the head partial to utilize the new KaTeX loader, enhancing the flexibility of math rendering options.
* refactor(mathjax): simplify MathJax script URL configuration
- Removed the dynamic version assignment for MathJax and set a fixed version in the script URL for consistency and clarity.
* docs(latex): enhance LaTeX guide and clarify chemistry expressions
- Updated section titles for better clarity, changing "Supported Functions" to "Chemistry Expressions."
- Improved description of the mhchem extension for rendering chemistry equations.
- Removed redundant instructions regarding the passthrough extension in Hugo.
- Corrected minor typographical errors in references to MathJax.
---
exampleSite/content/docs/guide/latex.md | 91 +++++++++++++++++--------
layouts/_partials/head.html | 20 +-----
layouts/_partials/scripts/katex.html | 88 ++++++++++++++++++++++++
layouts/_partials/scripts/mathjax.html | 3 +-
4 files changed, 155 insertions(+), 47 deletions(-)
create mode 100644 layouts/_partials/scripts/katex.html
diff --git a/exampleSite/content/docs/guide/latex.md b/exampleSite/content/docs/guide/latex.md
index 849d719..0238bd2 100644
--- a/exampleSite/content/docs/guide/latex.md
+++ b/exampleSite/content/docs/guide/latex.md
@@ -3,32 +3,35 @@ title: "LaTeX"
weight: 4
---
-By default, \(\KaTeX\) is used for rendering LaTeX math expressions.
-No manual activation is needed, you can start using LaTeX math expressions in your Markdown content right away.
+LaTeX math expressions are rendered using \(\KaTeX\) by default. Simply start including them in your Markdown content without any manual configurations.
-## Example
+## Usage
-Both inline and separate paragraph LaTeX math expressions are supported in the Markdown content.
+You can use LaTeX for both inline expressions and for larger blocks of text.
-### Inline
+### Inline Math
+
+To include an expression within a line of text, wrap it in `\(` and `\)` delimiters.
```markdown {filename="page.md"}
-This \(\sigma(z) = \frac{1}{1 + e^{-z}}\) is inline.
+This \(\sigma(z) = \frac{1}{1 + e^{-z}}\) is an inline expression.
```
-This \( \sigma(z) = \frac{1}{1 + e^{-z}} \) is inline.
+This \( \sigma(z) = \frac{1}{1 + e^{-z}} \) is an inline expression.
-### Separate Paragraph
+### Display Math
+
+For expressions that you want to stand on their own in a separate paragraph, use `$$` delimiters.
```markdown {filename="page.md"}
-$$F(\omega) = \int_{-\infty}^{\infty} f(t) e^{-j\omega t} \, dt$$
+$$F(\omega) = \int_{-\infty}^{\infty} f(t)\, e^{-j \omega t} \, dt$$
```
will be rendered as:
-$$F(\omega) = \int\_{-\infty}^{\infty} f(t) e^{-j\omega t} \, dt$$
+$$F(\omega) = \int_{-\infty}^{\infty} f(t)\, e^{-j \omega t} \, dt$$
-For example, using the aligned environment:
+You can also use LaTeX environments like `aligned` for multi-line expressions.
```latex {filename="page.md"}
$$
@@ -52,6 +55,23 @@ $$
\end{aligned}
$$
+For a list of supported functions, see [KaTeX supported functions](https://katex.org/docs/supported.html).
+
+### Chemistry Expressions
+
+The [mhchem][mhchem] extension is enabled by default, allowing you to easily render chemistry equations and formulas.
+
+Inline: \(\ce{H2O}\) is water.
+
+Separate paragraph:
+
+```markdown {filename="page.md"}
+$$\ce{Hg^2+ ->[I-] HgI2 ->[I-] [Hg^{II}I4]^2-}$$
+```
+
+will be rendered as:
+
+$$\ce{Hg^2+ ->[I-] HgI2 ->[I-] [Hg^{II}I4]^2-}$$
## Configuration
@@ -69,35 +89,47 @@ markup:
enable: true
```
-## Supported Functions
+### Math Engine
-For a list of supported functions, see [KaTeX supported functions](https://katex.org/docs/supported.html).
+[KaTeX][katex] is the default engine used to render LaTeX math expressions during the build process supported by [Hugo][hugo-transform-tomath].
-## Chemistry
+The default is KaTeX, but you can also switch to [MathJax][mathjax] if you need features only available in MathJax.
-Chemistry expressions are supported via [mhchem](https://mhchem.github.io/MathJax-mhchem/) extension.
+#### KaTeX
-Inline: \(\ce{H2O}\) is water.
+The default setup requires no configuration. Hugo fetches the KaTeX CSS from the CDN.
+If you need to pin a specific version of KaTeX or use local assets, you can do so in your `hugo.yaml` file.
-Separate paragraph:
+##### Override CDN base URL
-```markdown {filename="page.md"}
-$$\ce{Hg^2+ ->[I-] HgI2 ->[I-] [Hg^{II}I4]^2-}$$
+```yaml {filename="hugo.yaml"}
+params:
+ math:
+ engine: katex
+ katex:
+ base: "https://cdn.jsdelivr.net/npm/katex@0.16.22/dist"
```
-will be rendered as:
+##### Use local assets
-$$\ce{Hg^2+ ->[I-] HgI2 ->[I-] [Hg^{II}I4]^2-}$$
+You can also place the css file under `assets` and publish additional font files required by KaTeX.
+```yaml {filename="hugo.yaml"}
+params:
+ math:
+ engine: katex
+ katex:
+ css: "css/katex.min.css"
+ assets:
+ - "fonts/KaTeX_Main-Regular.woff2"
+ # Add other font files here
+```
-## Math Engine
+It will load the KaTeX CSS file from `assets/css/katex.min.css` instead of downloading from CDN.
-### MathJax
+#### MathJax
-By default, [KaTeX][katex] is used for rendering LaTeX math expressions during the build process, which is preferred.
-Alternatively, you can use [MathJax][mathjax] to render math expressions.
-
-To use it instead, add the following to the configuration `hugo.yaml` file:
+Alternatively, you can use [MathJax][mathjax] to render math expressions:
```yaml {filename="hugo.yaml"}
params:
@@ -105,5 +137,10 @@ params:
engine: mathjax
```
+> [!NOTE]
+> You can further customize MathJax (for example, adjust loader options, or change the CDN/source) by overriding the template at `layouts/_partials/scripts/mathjax.html` in your project. Hugo will use your version instead of the theme's default.
+
[katex]: https://katex.org/
[mathjax]: https://www.mathjax.org/
+[mhchem]: https://mhchem.github.io/MathJax-mhchem/
+[hugo-transform-tomath]: https://gohugo.io/functions/transform/tomath/
diff --git a/layouts/_partials/head.html b/layouts/_partials/head.html
index 0848533..e3430ba 100644
--- a/layouts/_partials/head.html
+++ b/layouts/_partials/head.html
@@ -83,27 +83,11 @@
}
-
+
{{ $noop := .WordCount -}}
{{- $engine := site.Params.math.engine | default "katex" -}}
{{ if and (.Page.Store.Get "hasMath") (eq $engine "katex") -}}
-
- {{ $katexBaseUrl := "https://cdn.jsdelivr.net/npm/katex@latest/dist" }}
- {{ $katexCssUrl := printf "%s/katex%s.css" $katexBaseUrl (cond hugo.IsProduction ".min" "") -}}
- {{ $katexFontPattern := "url(fonts/" }}
- {{ $katexFontSubstituted := printf "url(%s/fonts/" $katexBaseUrl }}
-
- {{ with try (resources.GetRemote $katexCssUrl) -}}
- {{ with .Err -}}
- {{ errorf "Could not retrieve KaTeX css file from %s. Reason: %s." $katexCssUrl . -}}
- {{ else with .Value -}}
- {{ $katexCssContent := strings.Replace .Content $katexFontPattern $katexFontSubstituted }}
- {{ with resources.FromString (printf "css/katex%s.css" (cond hugo.IsProduction ".min" "")) $katexCssContent -}}
- {{ $cssHash := . | fingerprint "sha512" -}}
-
- {{ end -}}
- {{ end -}}
- {{ end -}}
+ {{ partialCached "scripts/katex.html" . -}}
{{ else if and (.Page.Store.Get "hasMath") (eq $engine "mathjax") -}}
{{ partialCached "scripts/mathjax.html" . -}}
{{ end -}}
diff --git a/layouts/_partials/scripts/katex.html b/layouts/_partials/scripts/katex.html
new file mode 100644
index 0000000..69e7384
--- /dev/null
+++ b/layouts/_partials/scripts/katex.html
@@ -0,0 +1,88 @@
+{{- /* KaTeX CSS loader
+
+ Behavior (driven by site.params.math.katex):
+ - base (remote URL) + optional css:
+ - Construct remote CSS URL: "{{ base }}/{{ css | default "katex[.min].css" }}".
+ - Fetch via resources.GetRemote, rewrite font URLs to "{{ base }}/fonts/...".
+ - Build and fingerprint; emit .
+ - base (local path or not set) + css (asset path):
+ - Read CSS from Hugo assets via resources.Get; DO NOT rewrite font URLs.
+ - Build and fingerprint; emit .
+ - base (local path) only (no css):
+ - Link directly to "{{ base }}/katex[.min].css" (no processing).
+ - Nothing set:
+ - Default to CDN latest base; same as remote path above.
+
+ Additional:
+ - assets: optional list to publish extra assets. CSS/JS get tags with integrity (JS loads async).
+*/ -}}
+{{- $noop := .WordCount -}}
+
+{{- $katexBase := "https://cdn.jsdelivr.net/npm/katex@latest/dist" -}}
+{{- with site.Params.math.katex.base -}}
+ {{- $katexBase = . -}}
+{{- end -}}
+
+{{- $katexCssAsset := "" -}}
+{{- with site.Params.math.katex.css -}}
+ {{- $katexCssAsset = . -}}
+{{- end -}}
+
+{{- $s := newScratch -}}
+{{- $isRemoteBase := or (strings.HasPrefix $katexBase "http://") (strings.HasPrefix $katexBase "https://") -}}
+
+{{- /* CSS retrieval consolidated: get raw CSS from either local asset or remote, then process once */ -}}
+{{- $minSuffix := cond hugo.IsProduction ".min" "" -}}
+{{- if $isRemoteBase -}}
+ {{- $cssPath := cond (ne $katexCssAsset "") $katexCssAsset (printf "katex%s.css" $minSuffix) -}}
+ {{- $katexCssUrl := printf "%s/%s" $katexBase $cssPath -}}
+ {{- with try (resources.GetRemote $katexCssUrl) -}}
+ {{- with .Err -}}
+ {{- errorf "Could not retrieve KaTeX css file from %s. Reason: %s." $katexCssUrl . -}}
+ {{- else with .Value -}}
+ {{- $s.Set "katexCssValue" .Content -}}
+ {{- end -}}
+ {{- end -}}
+{{- else if $katexCssAsset -}}
+ {{- with resources.Get $katexCssAsset -}}
+ {{- $s.Set "katexCssValue" .Content -}}
+ {{- else -}}
+ {{- errorf "KaTeX css asset not found at %q" $katexCssAsset -}}
+ {{- end -}}
+{{- end -}}
+
+{{- with $s.Get "katexCssValue" -}}
+ {{- $cssContent := . -}}
+ {{- if $isRemoteBase -}}
+ {{- $fontPattern := "url(fonts/" -}}
+ {{- $fontSub := printf "url(%s/fonts/" $katexBase -}}
+ {{- $cssContent = strings.Replace $cssContent $fontPattern $fontSub -}}
+ {{- end -}}
+ {{- with resources.FromString (printf "css/katex%s.css" $minSuffix) $cssContent -}}
+ {{- $css := . | fingerprint "sha512" -}}
+
+ {{- end -}}
+{{- else -}}
+ {{- if not $isRemoteBase -}}
+ {{- $cssPath := cond (ne $katexCssAsset "") $katexCssAsset (printf "katex%s.css" $minSuffix) -}}
+
+ {{- end -}}
+{{- end -}}
+
+{{- /* Optionally publish files (fonts, css, js, etc.) from assets and emit tags for css/js with integrity and crossorigin */ -}}
+{{- with site.Params.math.katex.assets -}}
+ {{- range . -}}
+ {{- with resources.Get . -}}
+ {{- $name := .Name | lower -}}
+ {{- if strings.HasSuffix $name ".css" -}}
+ {{- $built := . | fingerprint "sha512" -}}
+
+ {{- else if or (strings.HasSuffix $name ".js") (strings.HasSuffix $name ".mjs") -}}
+ {{- $built := . | fingerprint "sha512" -}}
+
+ {{- else -}}
+ {{- .Publish -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+{{- end -}}
diff --git a/layouts/_partials/scripts/mathjax.html b/layouts/_partials/scripts/mathjax.html
index cc129af..a05b543 100644
--- a/layouts/_partials/scripts/mathjax.html
+++ b/layouts/_partials/scripts/mathjax.html
@@ -1,6 +1,5 @@
{{/* MathJax */}}
-{{ $mathjaxVersion := site.Params.math.mathjaxVersion | default "3" -}}
-{{ $mathjaxJsUrl := printf "https://cdn.jsdelivr.net/npm/mathjax@%s/es5/tex-chtml.js" $mathjaxVersion -}}
+{{ $mathjaxJsUrl := "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js" -}}