Compare commits

...

114 Commits

Author SHA1 Message Date
Xin
d35a471e7b chore: prepare release 2025-08-14 22:25:49 +08:00
Xin
b9bcf5b470 chore: update fa and ja translations 2025-08-14 22:07:15 +08:00
Xin
24debf9b0c chore: update ja translation 2025-08-14 19:28:16 +08:00
Xin
f79b1d262c chore: update zh-cn translation 2025-08-14 19:07:25 +08:00
Xin
8773081fae Add notable new features to v0.10.md, including dropdown menu support, enhanced search experience, and blog list pagination. Updated FlexSearch upgrade details for clarity and improved migration guide by removing redundant breaking change notes. 2025-08-14 17:11:37 +08:00
Xin
273e1e3db9 Update v0.10.md to enhance upgrade instructions, clarify breaking changes, and improve overall readability. Adjusted formatting for consistency and added details on asset management and user experience improvements. 2025-08-14 16:28:36 +08:00
Xin
477b98e151 Enhance v0.10 release documentation with detailed upgrade instructions and migration guide. Added Tailwind theme variable customization section and clarified breaking changes. Improved clarity on CSS class prefix changes for better user experience. 2025-08-14 14:49:55 +08:00
Xin
d6dd2550b1 Update v0.10.md to refine upgrade instructions and enhance blog features. Added synchronized tab switching and pagination controls, while improving the search experience and table of contents navigation. 2025-08-14 09:32:58 +08:00
Xin
cc6d7e6e94 Update v0.10.md 2025-08-14 01:48:52 +08:00
Xin
7d9d07bfdf docs(blog): prepare for v0.10 release post 2025-08-14 01:43:35 +08:00
Xin
8025de9d9d docs(getting-started): fix instructions for using git submodule (#693)
* docs(getting-started): fix instructions for using git submodule

* docs(getting-started): update instructions for adding Hextra theme as a Git submodule
2025-08-14 00:14:53 +08:00
Xin
9f70e8a87b refactor(mermaid): enhance Mermaid JS loading logic for flexibility (#745)
* refactor(mermaid): enhance Mermaid JS loading logic for flexibility

- Updated the Mermaid JS loader to support configurable remote or local asset loading based on site parameters.
- Improved error handling for asset retrieval and added comments for clarity on behavior based on different configurations.
- Ensured that the default behavior falls back to a CDN if no base is provided, enhancing usability and maintainability.

* chore: clean up comments

* fix: comment typo
2025-08-14 00:00:03 +08:00
Xin
6823cc1fe8 refactor(css): add prefix to component classes for consistency (#744)
* refactor(navbar): add hextra prefix to navbar and hamburger menu classes

- Updated CSS class names from `nav-container` to `hextra-nav-container` and `hamburger-menu` to `hextra-hamburger-menu` for improved clarity and consistency across the project.
- Adjusted related JavaScript and documentation to reflect the new class names.

* refactor(search): update class names for search components

- Renamed CSS classes from `search-wrapper`, `search-input`, `active`, `no-result`, `prefix`, `excerpt`, and `match` to `hextra-search-wrapper`, `hextra-search-input`, `hextra-search-active`, `hextra-search-no-result`, `hextra-search-prefix`, `hextra-search-excerpt`, and `hextra-search-match` for improved clarity and consistency.
- Updated JavaScript selectors to match the new class names, ensuring functionality remains intact.
- Adjusted HTML structure to reflect the new class naming convention.

* refactor(search): update search component class names for consistency

- Renamed existing search-related CSS classes to include the `hextra` prefix for improved clarity and consistency.
- Added optional nested classes for enhanced customization of the search UI, including titles, active states, and result snippets.
- Removed outdated breadcrumb section as no specific class is available.

* refactor(sidebar): rename sidebar classes for consistency

- Updated CSS and JavaScript to replace `sidebar-container` with `hextra-sidebar-container` and `sidebar-active-item` with `hextra-sidebar-active-item` for improved clarity and consistency across the project.
- Adjusted related documentation to reflect the new class names.

* refactor(language & theme): update class names for consistency

- Renamed language switcher and theme toggle classes to include the `hextra` prefix for improved clarity and consistency across the project.
- Updated related JavaScript selectors and documentation to reflect the new class names.

* refactor(css & html): rename classes for consistency and clarity

- Updated various CSS class names to include the `hextra` prefix, enhancing consistency across the project. This includes renaming classes such as `content` to `hextra-content`, `filename` to `hextra-code-filename`, and `steps` to `hextra-steps`.
- Adjusted related HTML and JavaScript to reflect the new class names, ensuring functionality and styling remain intact.
- Updated documentation to include the new class names for better clarity.

* refactor(language): update class names for language options

- Renamed the `language-options` class to `hextra-language-options` for consistency with the existing `hextra` prefix convention.
- Updated the corresponding HTML to reflect the new class name, ensuring clarity and uniformity across the project.

* refactor(css & html): rename classes for consistency and clarity

- Renamed CSS classes to include the `hextra` prefix, such as changing `subheading-anchor` to `hextra-subheading-anchor` and `footnotes` to `hextra-footnotes`, enhancing consistency across the project.
- Updated related HTML and documentation to reflect the new class names, ensuring clarity and uniformity.

* feat(typography): add styling for horizontal lines

- Introduced new styles for horizontal lines to enhance visual separation in content. The styles apply margin and border color adjustments, ensuring consistency with the overall design.

* feat(blog): add draft release announcement for Hextra v0.10.0

- Created a new markdown file for the draft release announcement of Hextra v0.10.0, including upgrade instructions and author details.
- The announcement is currently marked as a draft and may be updated before the official release.

* refactor(css & html): rename `hextra-content` class to `content` for consistency

- Updated the `hextra-content` class to simply `content` across various HTML files and CSS, enhancing clarity and consistency in the codebase.
- Adjusted the `package.json` script for the development server to include the `-F` flag for better functionality.

* refactor(typography & markdown): enhance table styling and markdown syntax

- Updated CSS for tables to improve styling, including adjustments to margins, borders, and text properties for better readability.
- Revised markdown documentation to standardize table formatting and improve clarity, including consistent syntax for headers and lists.
- Enhanced examples in the documentation to reflect the updated styling and ensure accurate representation of output.

* fix(blog): update text color for improved accessibility

- Modified the text color in the blog single layout to enhance readability in dark mode by adding a dark text color class.
- Ensured consistency in styling for better user experience across different themes.

* docs(blog): update draft release announcement for Hextra v0.10.0

- Revised the "What's New" section to include a TODO placeholder for future updates.
- Added a comprehensive migration guide detailing the CSS class prefix changes to enhance consistency and avoid conflicts.
- Updated the announcement to reflect the new class naming conventions for various components.

* chore: rebuild css
2025-08-13 22:55:38 +08:00
Xin
953042a0c0 docs(customization): add docs for layout css vars 2025-08-13 16:01:09 +08:00
Xin
b4d7d982f9 docs(config): add documentation for navbar dropdown menu 2025-08-13 15:41:37 +08:00
Xin
9a20f07d4e 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.
2025-08-13 15:14:46 +08:00
Ludovic Fernandez
b6864a0c19 fix(search): unescape title (#741) 2025-08-12 20:02:35 +08:00
Ludovic Fernandez
532cbcce10 feat: search in all headings (#740) 2025-08-12 08:52:21 +08:00
Xin
30866e328c chore: enhance tags appearance in toc and minor updates (#739)
* chore: remove standard development mode command from CLAUDE.md

* docs(toc): add TOC scroll functionality documentation

- Added detailed comments to toc-scroll.js explaining the purpose and functionality of the TOC scroll spy feature.
- Clarified the requirements for proper operation, including the need for a .hextra-toc element and matching heading IDs.

* refactor(fragments): enhance fragment processing and documentation

- Improved the fragments.html partial to better handle page content splitting into searchable fragments based on headings.
- Added comprehensive documentation within the file, detailing parameters, return values, and examples for clarity.
- Updated content handling to ensure whitespace is trimmed

* refactor(tags): enhance tag rendering and styling in toc

- Updated the tags.html partial to utilize a context variable for improved accessibility.
- Enhanced the styling of tag links for better visual consistency across different themes.
- Modified toc.html and list.html to pass the context to the tags partial, ensuring consistent rendering.

* chore: run `task css`
2025-08-11 17:36:37 +08:00
Xin
0bb59d6f49 feat(toc): add Table of Contents scroll highlighting (#738)
* feat(toc): add Table of Contents scroll highlighting

- Introduced a new toc.css file for styling the Table of Contents with active link highlighting.
- Implemented toc-scroll.js to manage scroll behavior and link activation based on viewport visibility.
- Updated core.html to include the new JavaScript file and ensure proper loading of the Table of Contents functionality.

* chore: lower root heading font weight for TOC

* chore: run `task css`

* chore: update dependencies in package.json and package-lock.json to version 4.1.11 for @tailwindcss/postcss and tailwindcss, and update @jridgewell packages to latest versions
2025-08-11 16:04:53 +08:00
Ludovic Fernandez
025dd1f211 fix: search results linking to wrong spot on page (#737) 2025-08-11 14:23:30 +08:00
Xin
af78002014 feat: support llms.txt output (#736)
* feat(llms): add llms txt output format

- Introduced a new output format 'llms' in the configuration.
- Updated the example site to utilize the new 'llms' format for the home output.
- Added a new layout file 'llms.txt' for rendering content in the LLMS format.

* docs: add llms.txt support documentation

- Introduced a new section in the configuration guide detailing how to enable the llms.txt output format for improved accessibility to large language models.
- Provided example configuration for adding the 'llms' output format in the site's hugo.yaml.
- Explained the content structure of the generated llms.txt file, including site title, section listings, page summaries, and direct links.

* Update configuration.md
2025-08-10 23:15:21 +08:00
Xin
096f0d9c22 fix(favicon): dynamic favicon switching based on color scheme in js (#735)
* fix(favicon): dynamic favicon switching based on color scheme in js

* refactor(favicon): simplify favicon logic and ensure dynamic switching based on color scheme

* docs(favicon): enhance favicon setup instructions with dark mode support and adaptive SVG guidance
2025-08-10 23:04:19 +08:00
Xin
7ac1d59e9f chore: add CLAUDE.md 2025-08-09 14:58:54 +08:00
lzmyhzy
ea17ae6cbd fix: giscus theme will be switched to dark when no 'color-theme' field (#723) 2025-08-06 09:01:00 +09:00
Xin
1081ab8d7f fix: tab not switching on non-synced mode (#732)
* fix: tab not switching on non-synced mode

* fix(tabs): improve tab synchronization logic and clean up data attributes

* refactor(tabs): streamline tab synchronization logic
2025-08-05 21:37:42 +09:00
Xin
46290e10e7 feat(pagination): add blog pagination component (#725)
* feat(pagination): add blog pagination component

- Introduced a new blog pagination component for improved navigation on list pages.
- Updated the blog list layout to utilize pagination, allowing for better content organization.
- Added a new parameter for pagination size in the configuration file.

* chore: increase pager size for blog
2025-07-24 22:26:17 +01:00
Xin
7b27743159 feat(tabs): implement synchronized tabs switching (#700)
* Sync tabs across groups

* feat(tabs): add optional synchronization

* Move tabs sync setting under page params

* fix: spacing between title and site title (#704)

* docs: document configure opengraph image (#706)

* [Docs] document using og:image

* Make example title page match others

* clarify wording

* chore: update tailwind css to latest version 4.1.8 (#703)

* fix: wrong SRI hash for katex.css (#702)

* Correct URL given in 'dev.toml'

* stylesheet 'katex.css': fix SRI hash

* fix(build): run npm update to fix postcss complaint

* feat(tags): improve usability of tags (#698)

* feat(tags): improve usability of tags

* Tags can be shown also at docs
* Documented tag-related config flags
* Added example tags to the site
* Made rendered tags active

* Move tags listing to ToC

* Hide tags section on no tags

* feat(math): add optional MathJax support (#707)

* feat: add MathJax option

* docs: move math engine note

* refactor: update LaTeX documentation and improve MathJax integration

- Adjusted LaTeX documentation for clarity and formatting.
- Enhanced MathJax configuration in the templates to support both KaTeX and MathJax rendering.
- Removed deprecated comments and streamlined the script loading process for MathJax.
- Updated the passthrough extension settings in the Hugo configuration for better compatibility with LaTeX math expressions.

* docs: simplify LaTeX documentation and clarify configuration steps

- Updated LaTeX documentation to reflect that KaTeX is enabled by default, removing the need for manual activation.
- Added examples for using LaTeX math expressions and clarified the configuration for the passthrough extension in Hugo.
- Enhanced MathJax section to emphasize its use as an alternative rendering engine.

* fix(tabs): add null check for panels container and update example items

* fix(tabs): improve tab group key handling and add validation for items parameter

* refactor(tabs): comment out sync option in configuration and adjust tab formatting in documentation

---------

Co-authored-by: hobobandy <30026704+hobobandy@users.noreply.github.com>
Co-authored-by: Matt Dodson <47385188+MattDodsonEnglish@users.noreply.github.com>
Co-authored-by: Andreas Deininger <adeininger@urbanonline.de>
Co-authored-by: yuri <1969yuri1969@gmail.com>
2025-07-21 21:16:44 +01:00
Floren Munteanu
c9795867c4 refactor: extract navbar title as individual partial component (#718)
* feat: navbar title

* fix: navbar title

---------

Co-authored-by: Floren Munteanu <19806136+fmunteanu@users.noreply.github.com>
2025-07-21 20:43:21 +01:00
Jaechan Lee
7610118c04 fix(i18n): Update translation in ko.yaml (#720) 2025-07-19 16:01:39 +01:00
yuri
05fd0129c2 fix(i18n): add missing tags translations (#708) 2025-06-15 10:29:43 +01:00
Xin
7031718449 feat(math): add optional MathJax support (#707)
* feat: add MathJax option

* docs: move math engine note

* refactor: update LaTeX documentation and improve MathJax integration

- Adjusted LaTeX documentation for clarity and formatting.
- Enhanced MathJax configuration in the templates to support both KaTeX and MathJax rendering.
- Removed deprecated comments and streamlined the script loading process for MathJax.
- Updated the passthrough extension settings in the Hugo configuration for better compatibility with LaTeX math expressions.

* docs: simplify LaTeX documentation and clarify configuration steps

- Updated LaTeX documentation to reflect that KaTeX is enabled by default, removing the need for manual activation.
- Added examples for using LaTeX math expressions and clarified the configuration for the passthrough extension in Hugo.
- Enhanced MathJax section to emphasize its use as an alternative rendering engine.
2025-06-14 14:36:10 +01:00
yuri
e22b8d5c0e feat(tags): improve usability of tags (#698)
* feat(tags): improve usability of tags

* Tags can be shown also at docs
* Documented tag-related config flags
* Added example tags to the site
* Made rendered tags active

* Move tags listing to ToC

* Hide tags section on no tags
2025-06-13 22:09:05 +01:00
Xin
32a55bb8ee fix(build): run npm update to fix postcss complaint 2025-06-13 21:57:08 +01:00
Andreas Deininger
b43870a538 fix: wrong SRI hash for katex.css (#702)
* Correct URL given in 'dev.toml'

* stylesheet 'katex.css': fix SRI hash
2025-06-10 23:48:03 +01:00
Andreas Deininger
9c2a9f600b chore: update tailwind css to latest version 4.1.8 (#703) 2025-06-10 23:46:53 +01:00
Matt Dodson
7385fe9e2a docs: document configure opengraph image (#706)
* [Docs] document using og:image

* Make example title page match others

* clarify wording
2025-06-10 23:45:19 +01:00
hobobandy
b1d40c4a2d fix: spacing between title and site title (#704) 2025-06-07 14:31:25 +01:00
Xin
40b1c5f2f1 docs: update hugo version in deploy guide (#699) 2025-06-04 00:38:38 +01:00
Xin
3a13d44d3c chore: update Hugo version to 0.147.7 and improve nav-menu script 2025-06-01 17:37:37 +01:00
Xin
c24d55ee40 feat: child menu support in navbar (#695)
* feat: implement child menu support in main navbar

- Added a new JavaScript file for handling dropdown functionality in the navbar.
- Implemented event listeners for toggling dropdowns, closing them on outside clicks, and dismissing with the Escape key.
- Updated navbar HTML to support dropdown items with children, enhancing the navigation experience.
- Adjusted core script imports to include the new dropdown functionality.

* chore: update menu identifiers and add missing translations for development versions

* chore: update hugo stats

* chore: update script name

* chore: update menu item names to include arrows for external links
2025-06-01 17:33:45 +01:00
yuri
a44de285b2 chore: adapt theme to new template system (#696)
* Adapted theme's layout to [Hugo v0.146.0](https://gohugo.io/templates/new-templatesystem-overview/)
* Bumped minimal Hugo version to v0.146.0
2025-06-01 00:25:39 +01:00
yuri
c8a231b650 fix(build): update NPM to resolve postcss issue (#694)
`npm update` fixed broken development which logged:

>  postcss: Error: Loading PostCSS Plugin failed: Cannot find module '../lightningcss.linux-x64-gnu.node
2025-05-31 22:38:43 +01:00
Xin
5a6fa55d0a fix: missing variables and custom styles css imports for theme dev 2025-05-31 19:58:09 +01:00
Andreas Deininger
c497ef700e docs(chore): switch to new template system (Hugo v0.146.0) (#681) 2025-05-31 16:48:23 +01:00
Xin
41140af6fa fix: CSS loading logic for production and theme dev environments (#691)
* fix: CSS loading logic for production and theme dev environments

- Updated the CSS loading logic to differentiate between production and theme environments.

* fix: streamline CSS loading logic for production and development environments

- Refactored the CSS loading logic to ensure proper handling of stylesheets in both production and development modes.
- Consolidated the CSS concatenation and minification process for production, while simplifying the development loading process.

* fix: simplify development CSS loading in head partial

- Updated the CSS loading logic in head.html to streamline the process for development environments by renaming the variable for clarity and ensuring proper handling of stylesheets.
2025-05-31 00:56:32 +01:00
PrintN
33129ca59f docs(showcase): Add Beginner Privacy (#688)
* docs(showcase): Add Beginner Privacy

* docs(showcase): Add Beginner Privacy

* docs(showcase): Add Beginner Privacy
2025-05-31 00:18:11 +01:00
Keith Stockdale
72c383ef5c feat: allow authors to set the value of reversePagination in page front matter (#674)
* Allow authors to set the reverse pagination setting of a page using front matter

* Adding some documentation of the reversePagination front matter custom parameter along with an example of how to use it and why you might want to use it
2025-05-23 01:23:25 +01:00
miniwater
51b5de23c7 fix: alternative text of images should not be repeated as text (#669)
* Add centering for custom footers

* Update layouts/partials/footer.html

* Fix alt duplication

* Update navbar.html

---------

Co-authored-by: Xin <fuxin1997@gmail.com>
Co-authored-by: Xin <xin@imfing.com>
2025-05-23 01:09:38 +01:00
Xin
befce4cd9a refactor: improve width handling and introduce CSS variables (#678)
* refactor: update page width handling and CSS structure

- Changed the default page width from 1280px to 80rem in hugo.yaml.
- Replaced dynamic page width partials with a new CSS class 'hextra-max-page-width' across multiple layout files for consistency.
- Introduced a new head-config-css.html partial for managing CSS styles related to page width.
- Removed the outdated page-width utility partial to streamline the codebase.

* feat: introduce CSS variables for layout widths and update footer/navbar styles

- Added a new configs.css file to define CSS variables for page, navbar, and footer widths.
- Updated footer and navbar partials to utilize the new CSS classes for consistent width management.
- Refactored head-config-css.html to include the new navbar width variable.
- Enhanced the overall styling structure for better maintainability and responsiveness.

* Refactor: Rename configs.css to variables.css and update references

Remove head-config-css.html and update references to use variables.css instead of configs.css

* Update assets/css/variables.css

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update layouts/partials/head.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-23 00:57:12 +01:00
Matt Dodson
128235e7e2 feat(seo): add page param to block Google indexing
Add a `noindex` param to page front matter to block Google indexing for specific pages or directories. This is useful for avoiding duplicate content in search results or preventing indexing of pages that shouldn't appear in search engines.

- Adjust `head.html` for optional `noindex` param (default: false)
- Document usage in `configuration.md` and clarify Flexsearch section
2025-05-17 22:35:26 +01:00
miniwater
f194bc64e4 fix: centering custom footers (#649)
* Add centering for custom footers

* Update layouts/partials/footer.html

---------

Co-authored-by: Xin <fuxin1997@gmail.com>
2025-04-13 10:35:39 +01:00
Zabriskije
1cc02a6931 fix: phantom scroll when params.footer.enable is set to false (#658)
* Phantom scroll fix

* fix: adjust menu height in CSS and update sidebar max height reference

* chore: regenerate css

---------

Co-authored-by: Xin <xin@imfing.com>
2025-04-09 23:11:19 +01:00
Justin Chen
1eb4b9ea23 fix: link render hook produces dangling links when base URL has additional segments (#660)
* fix: link render hook produces dangling links when base URL has additional segments

* fix: incorrect fragment character in image render hook
2025-04-09 22:47:02 +01:00
Justin Chen
d08b077acd feat: make image render hook aware of assets directory (#657) 2025-04-04 00:14:05 +01:00
Justin Chen
1f88cff7d4 fix: KaTeX fonts are not fetched from CDN (#656) 2025-04-04 00:07:12 +01:00
Xin
83fda0109f fix: enhanced encoder for CJK mixed content (#653) 2025-03-31 08:43:41 +01:00
Xin
7fdb6a3a07 chore: upgrade flexsearch to 0.8 (#652)
* chore: upgrade flexsearch to 0.8.x

- use cdn to download flexsearch js bundle

* chore: remove flexsearch.bundle.min.js
2025-03-30 21:52:41 +01:00
Xin
8021437f77 chore: use CDN to download mermaid js (#651)
* chore: download js assets during build time

* chore: remove CDN asset download task and update Mermaid script fingerprinting
2025-03-30 21:20:50 +01:00
Andreas Deininger
b700825943 chore: server side rendering of math equations (#589) 2025-03-30 18:05:35 +01:00
Xin
c74d44492e chore: migrate to Tailwind CSS v4.0 (#632)
* chore: migrate PostCSS configuration to v4

* chore: update dependencies in package.json and package-lock.json

- Bump versions for autoprefixer, postcss, postcss-cli, prettier, and tailwindcss.
- Remove unused dependencies to streamline the project.

* chore: add @tailwindcss/postcss and update package-lock.json

* fix: update import paths in CSS files for consistency

* fix: change prefix from `hx-` to `hx:`

* chore: migrate primary color theme variables to CSS

* fix: remove unnecessary text decoration property from anchor styles in typography CSS

* fix: update CSS styles for improved consistency and clarity across components

* chore: clean up package.json and package-lock.json, remove unused dependencies, and update CSS imports for better organization

* fix: bulk replace prefix `hx-` with `hx:`

* fix: update tailwind css prefix

* fix: styling consistent issues

- steps counter fix in v4
- removed tailwind.css
- update hr border colors
- fix button cursor in v4
- fix border colors in various places

* fix: update class prefixes for consistency in menu and sidebar components

* fix: refine CSS classes and transitions for navbar and sidebar components

- Updated hamburger menu styles for improved animation and structure.
- Adjusted sidebar transition duration for smoother effects.
- Standardized class prefixes for consistency across components.

* fix: update border color in hero badge component for improved styling consistency

* fix: update tab button cursor style for improved user interaction

* chore: recompile css

* fix: dark mode color not applied for before / after elements

* fix: docs navigation

* chore: recompile CSS

* chore: update Tailwind CSS and PostCSS dependencies to version 4.0.17, recompile CSS, and add safelist

* fix: typo in class name and add back decoration-from-font for typography a tags

* fix: update class syntax for Tailwind CSS compatibility in mermaid code block
2025-03-29 12:51:42 +00:00
Ajay Kidave
32f7f6d33f docs(showcase): update Clace image url (#646) 2025-03-29 12:43:01 +00:00
Xin
38c8ee1168 fix: mermaid script was skipped on home page (#637) 2025-03-27 22:40:24 +00:00
Xin
17de708c9f chore(build): build main docs site from v0.9.6 2025-03-27 22:23:38 +00:00
Xin
a338c363ed chore(ci): add support for building docs for multiple versions (#633)
* chore(ci): add support for building docs for multiple versions

* chore(ci): enable fetching tags in GitHub Actions workflow

* chore(build): add v0.8.6 version to build script
2025-03-26 21:55:31 +00:00
Ajay Kidave
c3ce3b67e6 docs(showcase): Added Clace to showcase (#623) 2025-03-23 10:12:13 +00:00
Xin
9d57dbd9cd chore: update hugo to 0.145.0 in devcontainer.json
[skip ci]
2025-03-11 21:43:41 +00:00
Xin
a2718d8aa3 fix: use InnerDeindent for filetree shortcode rendering (#613) 2025-03-11 21:42:16 +00:00
Xin
2b83a3762f Merge pull request #607 from maxbischoff/fix-codeblock-copy-button-in-details
fix: copy-button not being rendered in details blocks
2025-03-10 23:05:43 +00:00
Maximilian Bischoff
aad859d72e fix copy-button not being rendered in details blocks 2025-03-10 20:12:30 +01:00
Pat David
6a2f11d780 fix: sidebar more menu item link with multilingual (#594)
* sidebar: fix more menu item with multilingual

Fixes #593

Correctly parses multilingual URL in sidebar, particularly if
lang code follows a nested sub directory.

For instance, GH pages hosting where the baseurl might be of the
format `https://<USERNAME>.github.io/<REPO>/`.

* Update sidebar.html

---------

Co-authored-by: Xin <xin@imfing.com>
2025-03-01 08:39:36 +00:00
Xin
662d9202dc chore: bump minimum hugo version 2025-02-26 23:33:11 +00:00
Xin
9f9ddd69ab fix: sanitize heading title to prevent html tags displayed on mobile (#591) 2025-02-26 23:26:21 +00:00
Xin
96b2f6145d feat: add optional pagination control for blog articles (#590) 2025-02-26 23:13:43 +00:00
Xin
c2286c9dd1 refactor: modularize scripts partial into separate components (#587)
* refactor: modularize scripts partial into separate components

* fix: conditionally load Mermaid and KaTeX scripts
2025-02-26 08:39:26 +00:00
Xin
49b1cd11ee feat: hide navbar on mobile when heading links clicked (#584) 2025-02-25 18:48:25 +00:00
Xin
b2e6c30c7f chore: remove unused hx-mx-2 margin utility class 2025-02-25 08:12:44 +00:00
Nick Gracilla
8eb348323d docs: fix typo in documentation on customization.md (#582) 2025-02-23 10:06:25 +00:00
Connor Lanigan
ab56c66ae5 fix: misaligned page title when logo is not displayed (#578) 2025-02-17 17:15:35 +00:00
yuri
ddc017b8dc docs: mention Hugo Figure shortcode (#572) 2025-02-13 11:22:41 +08:00
Khosrow Moossavi
ce837dca42 docs(showcase): add Regolith Desktop (#568) 2025-02-13 11:21:59 +08:00
yuri
c60d1f5de7 docs: describe page last modification date (#562) 2025-01-23 20:58:03 +00:00
Mir Sobhan
bbe3c46320 chore: add telegram icon (#558)
* Update icons.yaml | add telegram icon

* chore: update telegram icon

---------

Co-authored-by: Xin <xin@imfing.com>
2025-01-21 21:57:43 +00:00
Gaëtan Steininger
cb3373d500 fix: missing doctype error on 404 page with htmltest scan (#554)
* Fix missing doctype error on 404 page with htmltest scan

* format 404.html

---------

Co-authored-by: Xin <xin@imfing.com>
2025-01-19 19:35:24 +00:00
Xin
56f28e6f14 chore: 2024 -> 2025 2025-01-19 11:50:14 +00:00
Xin
14036ffea6 feat: enhance FlexSearch encoding for CJK support (#553)
- Added support for CJK (Chinese, Japanese, Korean) languages in FlexSearch encoding.
- Introduced `isCJK` function to detect language and select appropriate encoding method.
- Implemented `encodeCJK` and `encodeDefault` functions for different tokenization strategies.
2025-01-18 18:54:54 +00:00
Jan Zerfowski
a1232ecf9f fix: skip image process on svgs and remote images (#551) 2025-01-18 17:05:13 +00:00
Nikolay Dubina
a933f464f5 docs: fix wrong param footer (#543)
it has changed 709a407b2e/layouts/partials/footer.html (L40C40-L40C56)
2025-01-07 10:51:00 +00:00
Xin
709a407b2e feat: add tags support (#542)
* feat: basic tags support

* feat: add term page

* chore: update css
2025-01-05 20:33:56 +00:00
Xin
876eb3abff chore(docs): remove redundant backticks 2025-01-05 19:03:13 +00:00
Jan Zerfowski
a27f6eef5e refactor: use generic .Process for card processing and allow bundled images (#538)
* Change processing method to more generic hugo .Process and allow resources from local .Page.Resources as well as global resources

* Add comments

* Fix double curly brackets error

Co-authored-by: Xin <fuxin1997@gmail.com>

* Maintain backward compatibility by using defaulting $process to $methd $options

https://github.com/imfing/hextra/pull/538#discussion_r1903250264

* Use printf instead of delimit

Co-authored-by: Xin <fuxin1997@gmail.com>

---------

Co-authored-by: Xin <fuxin1997@gmail.com>
2025-01-05 19:00:00 +00:00
stoic-hugo
323f4c4b44 i18n: update sw.yaml (#541) 2025-01-05 18:59:20 +00:00
Xin
b8f617f1b0 docs(i18n): add ja translation
- translated via llm
2024-12-31 01:01:56 +00:00
Xin
9632c4d05a docs: update zh-cn translations 2024-12-31 00:34:20 +00:00
Xin
594b1f190c docs: add instruction for enable and configure passthrough for latex (#530) 2024-12-30 23:13:23 +00:00
Xin
12815aaddc docs: documentation updates for alerts and syntax highlighting 2024-12-28 16:23:58 +00:00
Xin
e532637cbc fix: correct tabs button color on hover in dark mode (#526)
* fix: tabs toggle button color in dark mode

* chore: update css
2024-12-28 15:51:35 +00:00
Xin
79bb4504a0 feat: add support for icon in badge shortcode (#525)
* feat: add icon support to badge shortcodes

- Introduced an optional icon parameter to both badge.html files.
- Updated the badge rendering to include the icon alongside the content.
- Enhanced the layout for better alignment of icon and text.

* feat: add new feature shortcode

* docs: update badge doc
2024-12-28 13:50:22 +00:00
strowk
26a298da5d feat: support link in code block title (#523)
* feat: support link in code block title

* refactor: simplify code block filename link implementation

* docs: update syntax-highlighting.md

* chore: rename `filename_uri_base` to `base_url`

[skip ci]

* refactor: use `base_url` int code block implementation

---------

Co-authored-by: Xin <xin@imfing.com>
2024-12-28 12:41:23 +00:00
Xin
cf61e606c1 docs: update customization instructions for footer section 2024-12-26 23:50:18 +00:00
Wonchae Yang
0716533699 feat: add giscus theme customization (#522)
Co-authored-by: Xin <xin@imfing.com>
2024-12-26 12:29:21 +00:00
Attila Greguss
9efcda2fdd feat: support custom footer section (#518)
* Add support for custom footer

* amend how the custom footer section is displayed

* Add missing class

* add missing class

* Remove context variable and flag in hugo.yaml

* update hugo_stats

* Only show footer section for copyright and PoweredBy if they are enabled

* Add missing compiled css

* Added necessary variables for correct styling of the custom footer in some cases

* make padding consistent for copyright section and custom footer

* chore: update css

* chore: clean up css

* docs: update customization instructions

* fix: footer padding

---------

Co-authored-by: Xin <xin@imfing.com>
2024-12-25 21:25:47 +00:00
Attila Greguss
655148f329 docs: add advanced theme customization instruction (#519)
Some checks failed
Deploy Hugo site to Pages / build (push) Has been cancelled
Deploy Hugo site to Pages / deploy (push) Has been cancelled
* Add docs on how to customize theme

* fix heading level

* extend description

* Add missing classes

* Update exampleSite/content/docs/advanced/customization.md

---------

Co-authored-by: Xin <fuxin1997@gmail.com>
2024-12-18 22:48:42 +00:00
Xin
b6d14afca3 docs(showcase): update link for lutheran-confessions
fixes #520
2024-12-18 22:35:52 +00:00
Attila Greguss
bc778ee243 fix: only display footer switches section if it actually has content (#517)
Some checks failed
Deploy Hugo site to Pages / build (push) Has been cancelled
Deploy Hugo site to Pages / deploy (push) Has been cancelled
2024-12-16 22:21:15 +00:00
Xin
f377609eba docs: update HUGO_VERSION to 0.138.0 in deployment documentation
Some checks are pending
Deploy Hugo site to Pages / build (push) Waiting to run
Deploy Hugo site to Pages / deploy (push) Blocked by required conditions
2024-12-15 20:32:54 +00:00
Xin
a9b992436e chore: update HUGO_VERSION to 0.138.0 in GitHub Actions workflow
* add FUNDING.yml
2024-12-15 20:26:11 +00:00
dependabot[bot]
7f5a7f2f5a chore(deps-dev): bump cross-spawn from 7.0.3 to 7.0.6 (#515) 2024-12-15 16:33:44 +00:00
dependabot[bot]
30fddec3fa chore(deps): bump nanoid from 3.3.7 to 3.3.8 (#514)
Some checks are pending
Deploy Hugo site to Pages / build (push) Waiting to run
Deploy Hugo site to Pages / deploy (push) Blocked by required conditions
2024-12-15 14:20:52 +00:00
Xin
68dd327312 feat: support github style alerts (#513)
Some checks are pending
Deploy Hugo site to Pages / build (push) Waiting to run
Deploy Hugo site to Pages / deploy (push) Blocked by required conditions
* feat: basic github style alerts support

* feat: implement github style alert

* chore: re-generate css

* chore: add missing prettier config

* docs: add alerts instructions to markdown

* chore: revert "docs: add alerts instructions to markdown"

This reverts commit 3a70540e0b.

* chore: redo "docs: add alerts instructions to markdown""

This reverts commit 8399373747.

* chore(build): bump hugo version
2024-12-14 23:44:10 +00:00
Floren Munteanu
0c90c1aa50 feat: add hextra hero-section shortcode (#390)
Some checks failed
Deploy Hugo site to Pages / build (push) Has been cancelled
Deploy Hugo site to Pages / deploy (push) Has been cancelled
* Implement hextra hero-section

* Implement header size

* Update layouts/shortcodes/hextra/hero-section.html

* Update layouts/shortcodes/hextra/hero-section.html

* Update layouts/shortcodes/hextra/hero-section.html

* Update layouts/shortcodes/hextra/hero-section.html

---------

Co-authored-by: Xin <fuxin1997@gmail.com>
2024-12-10 23:58:36 +00:00
Xin
fe2271b60b docs(showcase): add "Model Context Protocol Specification"
Some checks failed
Deploy Hugo site to Pages / build (push) Has been cancelled
Deploy Hugo site to Pages / deploy (push) Has been cancelled
2024-12-08 12:38:44 +00:00
Torbjørn Pedersen
bd34a5bad3 fix: rerender mermaid diagrams on theme change (#509)
Make mermaid render diagrams on manual toggle between themes, after initial page load
2024-12-08 12:29:28 +00:00
Nishant Srivastava
0dcf7e7a40 chore(icons): add bluesky icon (#505)
Some checks failed
Deploy Hugo site to Pages / build (push) Has been cancelled
Deploy Hugo site to Pages / deploy (push) Has been cancelled
* Update icons.yaml to add bsky icon

Added BlueSky icon to socials

* update the fill color to current color

* Update data/icons.yaml

---------

Co-authored-by: Xin <fuxin1997@gmail.com>
2024-12-04 07:34:02 +00:00
361 changed files with 11567 additions and 10383 deletions

View File

@@ -3,7 +3,7 @@
"features": { "features": {
"ghcr.io/devcontainers/features/hugo:1": { "ghcr.io/devcontainers/features/hugo:1": {
"extended": true, "extended": true,
"version": "0.131.0" "version": "0.147.7"
}, },
"ghcr.io/devcontainers/features/node:1": {} "ghcr.io/devcontainers/features/node:1": {}
}, },
@@ -18,5 +18,7 @@
} }
}, },
"postCreateCommand": "npm install", "postCreateCommand": "npm install",
"forwardPorts": [1313] "forwardPorts": [
} 1313
]
}

15
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# These are supported funding model platforms
github: imfing
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -1,5 +1,5 @@
# Sample workflow for building and deploying a Hugo site to GitHub Pages # Build and deploy Hextra docs site to GitHub Pages
name: Deploy Hugo site to Pages name: Deploy Hextra docs site to Pages
on: on:
# Runs on pushes targeting the default branch # Runs on pushes targeting the default branch
@@ -31,39 +31,43 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
HUGO_VERSION: 0.136.5 HUGO_VERSION: 0.147.7
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # fetch all history for .GitInfo and .Lastmod fetch-depth: 0 # fetch all history for .GitInfo and .Lastmod
fetch-tags: true
submodules: recursive submodules: recursive
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: '1.23' go-version: "1.24"
- name: Setup Pages - name: Setup Pages
id: pages id: pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v5
- name: Setup Hugo - name: Setup Hugo
run: | run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb && sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Build with Hugo
- name: Make build script executable
run: chmod +x ./build.sh
- name: Build all site versions
env: env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production HUGO_ENVIRONMENT: production
HUGO_ENV: production HUGO_ENV: production
# Use the latest release of the theme to build exampleSite
run: | run: |
cd exampleSite && rm go.mod ./build.sh "${{ steps.pages.outputs.base_url }}"
hugo mod init github.com/imfing/hextra/exampleSite
hugo mod get -u github.com/imfing/hextra
hugo --minify --baseURL "${{ steps.pages.outputs.base_url }}/"
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v3
with: with:
path: ./exampleSite/public path: ./public
# Deployment job # Deployment job
deploy: deploy:

View File

@@ -1,4 +1,7 @@
{ {
"plugins": [
"prettier-plugin-go-template"
],
"goTemplateBracketSpacing": true, "goTemplateBracketSpacing": true,
"htmlWhitespaceSensitivity": "css", "htmlWhitespaceSensitivity": "css",
"printWidth": 200, "printWidth": 200,

161
CLAUDE.md Normal file
View File

@@ -0,0 +1,161 @@
# CLAUDE.md
This file provides guidance to [Claude Code](claude.ai/code) when working with code in this repository.
## Project Overview
Hextra is a modern, responsive Hugo theme designed for creating documentation websites, technical blogs, and static sites. Built with Tailwind CSS and inspired by Nextra, it offers features like full-text search, dark mode, multi-language support, and extensive customization options.
## Development Commands
### Development Server
```bash
# Start development server with theme reloading (recommended for theme development)
npm run dev:theme
# Using Task runner
task dev
```
### Building
```bash
# Build the example site
npm run build
# or
task build
# Build CSS assets only
npm run build:css
# or
task css
```
### Alternative Task Commands
The project uses Task runner (`taskfile.yaml`) for simplified commands:
- `task dev` - Start development server (runs `npm run dev:theme`)
- `task build` - Build example site
- `task css` - Compile CSS (depends on build)
## Architecture Overview
### Hugo Theme Structure
- **Base Layout**: `layouts/baseof.html` wraps all pages
- **Specialized Layouts**: `layouts/docs/`, `layouts/blog/`, `layouts/hextra-home.html`
- **Partials**: Reusable components in `layouts/_partials/`
- Core UI: `navbar.html`, `sidebar.html`, `footer.html`, `breadcrumb.html`, `toc.html`
- Utilities: `layouts/_partials/utils/` for helper functions
- Custom overrides: `layouts/_partials/custom/` for user customizations
- **Shortcodes**: Custom Markdown extensions in `layouts/_shortcodes/`
- **Render Hooks**: Custom Markdown rendering in `layouts/_markup/` for codeblocks, headings, images, and links
### Asset Organization
```
assets/
├── css/
│ ├── styles.css # Main stylesheet
│ ├── compiled/main.css # Built CSS output (generated)
│ ├── components/ # Component-specific styles
│ └── chroma/ # Syntax highlighting themes
├── js/ # JavaScript components
└── lib/ # External libraries
```
### Key Components
- **Search**: FlexSearch-powered full-text search (`assets/js/flexsearch.js`)
- **Navigation**: Responsive navbar and auto-generated sidebar
- **Theme Toggle**: Dark/light mode switching
- **Internationalization**: 20+ language support in `i18n/`
### Content Features
- **Shortcodes**: `callout`, `card`, `cards`, `tabs`, `details`, `steps`, `jupyter`, `filetree`
- **Code Features**: Syntax highlighting (Chroma), copy buttons, line numbers via render hooks
- **SEO**: Open Graph, Twitter Cards, structured data
- **Performance**: Minimal JavaScript, optimized CSS with Tailwind
## Development Workflow
### Example Site Development
The `exampleSite/` directory serves as both documentation and testing ground:
- Test new features here before releasing
- Configuration examples in `exampleSite/hugo.yaml` showing multi-language setup
- Content examples demonstrate all theme capabilities
- Run from exampleSite with: `hugo server --themesDir=../..`
### CSS Development Workflow
- Source: `assets/css/styles.css` (main stylesheet)
- Build process: Tailwind CSS → PostCSS → `assets/css/compiled/main.css`
- Component styles organized in `assets/css/components/`
- Chroma syntax highlighting themes in `assets/css/chroma/`
- CSS compilation requires Node.js dependencies (PostCSS, Tailwind CSS v4+)
### Customization Points
- Custom partials: `layouts/_partials/custom/`
- Custom CSS: `assets/css/custom.css`
- Site-specific overrides: Copy any layout to your site's `layouts/` directory
## Configuration & Requirements
### Theme Requirements
- Hugo minimum version: 0.146.0 (extended version required - see `theme.toml`)
- Go 1.20+ (as specified in `go.mod`)
- Node.js for CSS compilation (PostCSS, Tailwind CSS v4+)
### Key Configuration Files
- `exampleSite/hugo.yaml` - Example Hugo configuration with multi-language setup
- `postcss.config.mjs` - PostCSS configuration for CSS processing
- `package.json` - Node.js dependencies and build scripts
- `taskfile.yaml` - Task runner configuration
### Development Environment
- Default Hugo development server: Port 1313
- Development server runs with `--disableFastRender -D` for better development experience
- Theme development uses `--logLevel=debug` for detailed logging
### Multi-language Support
- Configure languages in `hugo.yaml` (supports 20+ languages including RTL)
- Translation files in `i18n/` directory (e.g., `en.yaml`, `fa.yaml`, `ja.yaml`, `zh-cn.yaml`)
- Example supports English, Persian (RTL), Japanese, and Simplified Chinese
## Theme Development Guidelines
### Hugo Theme Conventions
- Theme files in this repository override Hugo defaults
- Follow Hugo's theme development guidelines for compatibility
- Maintain backward compatibility with existing configurations
### JavaScript & Performance
- All JavaScript components are designed to have minimal footprint
- Core JS components: `theme.js`, `search.js`, `nav-menu.js`, `code-copy.js`
- FlexSearch powers offline full-text search functionality
### CSS Architecture
- Uses Tailwind CSS v4+ with PostCSS processing
- Component-based CSS organization in `assets/css/components/`
- Compiled output goes to `assets/css/compiled/main.css`
- Prettier formatting for Go templates and code consistency
### Testing & Quality Assurance
- Test all changes in `exampleSite/` before releasing
- Use `npm run dev:theme` for theme development with hot reloading
- Format code with `npx prettier --write .` before committing
- Verify multi-language functionality across supported languages

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,3 @@
.hextra-badge { .hextra-badge {
@apply hx-inline-flex hx-items-center; @apply hx:inline-flex hx:items-center;
} }

View File

@@ -2,6 +2,6 @@
(-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px)) (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
) { ) {
.hextra-code-copy-btn { .hextra-code-copy-btn {
@apply hx-backdrop-blur-md hx-bg-opacity-[.85] dark:hx-bg-opacity-80; @apply hx:backdrop-blur-md hx:opacity-85 hx:dark:opacity-80;
} }
} }

View File

@@ -1,16 +1,16 @@
.hextra-jupyter-code-cell { .hextra-jupyter-code-cell {
scrollbar-gutter: auto; scrollbar-gutter: auto;
@apply hx-mt-6; @apply hx:mt-6;
.hextra-jupyter-code-cell-outputs-container { .hextra-jupyter-code-cell-outputs-container {
@apply hx-text-xs hx-overflow-hidden; @apply hx:text-xs hx:overflow-hidden;
.hextra-jupyter-code-cell-outputs { .hextra-jupyter-code-cell-outputs {
@apply hx-overflow-auto hx-max-h-[50vh]; @apply hx:overflow-auto hx:max-h-[50vh];
pre { pre {
@apply hx-text-xs hx-overflow-auto hx-max-w-full; @apply hx:text-xs hx:overflow-auto hx:max-w-full;
} }
} }
} }

View File

@@ -1,55 +1,50 @@
nav { nav {
.search-wrapper { .search-wrapper {
@apply hx-hidden md:hx-inline-block; @apply hx:hidden hx:md:inline-block;
} }
} }
@supports ( @supports (
(-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px)) (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
) { ) {
.nav-container-blur { .hextra-nav-container-blur {
@apply hx-backdrop-blur-md hx-bg-white/[.85] dark:!hx-bg-dark/80; @apply hx:backdrop-blur-md hx:bg-white/[.85] hx:dark:bg-dark/80!;
} }
} }
.hamburger-menu svg { /* Hamburger Menu - Flattened Structure */
g { .hextra-hamburger-menu svg g {
@apply hx-origin-center; @apply hx:origin-center hx:transition-all hx:duration-100 hx:ease-out;
transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); }
}
path { .hextra-hamburger-menu svg path {
opacity: 1; @apply hx:opacity-100 hx:transition-all hx:duration-100 hx:ease-out hx:delay-100;
transition: }
transform 0.2s cubic-bezier(0.25, 1, 0.5, 1) 0.2s,
opacity 0.2s ease 0.2s; .hextra-hamburger-menu svg.open path {
} @apply hx:transition-transform hx:duration-100 hx:ease-out hx:delay-0;
}
&.open {
path { .hextra-hamburger-menu svg.open g {
transition: @apply hx:transition-transform hx:duration-100 hx:ease-out hx:delay-100;
transform 0.2s cubic-bezier(0.25, 1, 0.5, 1), }
opacity 0s ease 0.2s;
} .hextra-hamburger-menu svg.open > path {
g { @apply hx:opacity-0;
transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1) 0.2s; }
}
} .hextra-hamburger-menu svg.open > g:nth-of-type(1) {
@apply hx:rotate-45;
&.open > { }
path {
@apply hx-opacity-0; .hextra-hamburger-menu svg.open > g:nth-of-type(1) path {
} @apply hx:translate-y-1;
g:nth-of-type(1) { }
@apply hx-rotate-45;
path { .hextra-hamburger-menu svg.open > g:nth-of-type(2) {
transform: translate3d(0, 4px, 0); @apply hx:-rotate-45;
} }
}
g:nth-of-type(2) { .hextra-hamburger-menu svg.open > g:nth-of-type(2) path {
@apply -hx-rotate-45; @apply hx:-translate-y-1;
path {
transform: translate3d(0, -4px, 0);
}
}
}
} }

View File

@@ -4,18 +4,18 @@
scrollbar-gutter: stable; scrollbar-gutter: stable;
&::-webkit-scrollbar { &::-webkit-scrollbar {
@apply hx-w-3 hx-h-3; @apply hx:w-3 hx:h-3;
} }
&::-webkit-scrollbar-track { &::-webkit-scrollbar-track {
@apply hx-bg-transparent; @apply hx:bg-transparent;
} }
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
@apply hx-rounded-[10px]; @apply hx:rounded-[10px];
} }
&:hover::-webkit-scrollbar-thumb { &:hover::-webkit-scrollbar-thumb {
border: 3px solid transparent; border: 3px solid transparent;
background-color: var(--tw-shadow-color); background-color: var(--tw-shadow-color);
background-clip: content-box; background-clip: content-box;
@apply hx-shadow-neutral-500/20 hover:hx-shadow-neutral-500/40; @apply hx:shadow-neutral-500/20 hx:hover:shadow-neutral-500/40;
} }
} }

View File

@@ -1,38 +1,38 @@
.search-wrapper { .hextra-search-wrapper {
li { li {
@apply hx-mx-2.5 hx-break-words hx-rounded-md contrast-more:hx-border hx-text-gray-800 contrast-more:hx-border-transparent dark:hx-text-gray-300; @apply hx:mx-2.5 hx:break-words hx:rounded-md hx:contrast-more:border hx:text-gray-800 hx:contrast-more:border-transparent hx:dark:text-gray-300;
a { a {
@apply hx-block hx-scroll-m-12 hx-px-2.5 hx-py-2; @apply hx:block hx:scroll-m-12 hx:px-2.5 hx:py-2;
} }
.title { .hextra-search-title {
@apply hx-text-base hx-font-semibold hx-leading-5; @apply hx:text-base hx:font-semibold hx:leading-5;
} }
.active { .hextra-search-active {
@apply hx-rounded-md hx-bg-primary-500/10 contrast-more:hx-border-primary-500; @apply hx:rounded-md hx:bg-primary-500/10 hx:contrast-more:border-primary-500;
} }
} }
.no-result { .hextra-search-no-result {
@apply hx-block hx-select-none hx-p-8 hx-text-center hx-text-sm hx-text-gray-400; @apply hx:block hx:select-none hx:p-8 hx:text-center hx:text-sm hx:text-gray-400;
} }
.prefix { .hextra-search-prefix {
@apply hx-mx-2.5 hx-mb-2 hx-mt-6 hx-select-none hx-border-b hx-border-black/10 hx-px-2.5 hx-pb-1.5 hx-text-xs hx-font-semibold @apply hx:mx-2.5 hx:mb-2 hx:mt-6 hx:select-none hx:border-b hx:border-black/10 hx:px-2.5 hx:pb-1.5 hx:text-xs hx:font-semibold
hx-uppercase hx-text-gray-500 first:hx-mt-0 dark:hx-border-white/20 dark:hx-text-gray-300 contrast-more:hx-border-gray-600 hx:uppercase hx:text-gray-500 hx:first:mt-0 hx:dark:border-white/20 hx:dark:text-gray-300 hx:contrast-more:border-gray-600
contrast-more:hx-text-gray-900 contrast-more:dark:hx-border-gray-50 contrast-more:dark:hx-text-gray-50; hx:contrast-more:text-gray-900 hx:contrast-more:dark:border-gray-50 hx:contrast-more:dark:text-gray-50;
} }
.excerpt { .hextra-search-excerpt {
@apply hx-overflow-hidden hx-text-ellipsis hx-mt-1 hx-text-sm hx-leading-[1.35rem] hx-text-gray-600 dark:hx-text-gray-400 contrast-more:dark:hx-text-gray-50; @apply hx:overflow-hidden hx:text-ellipsis hx:mt-1 hx:text-sm hx:leading-[1.35rem] hx:text-gray-600 hx:dark:text-gray-400 hx:contrast-more:dark:text-gray-50;
display: -webkit-box; display: -webkit-box;
line-clamp: 1; line-clamp: 1;
-webkit-line-clamp: 1; -webkit-line-clamp: 1;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
.match { .hextra-search-match {
@apply hx-text-primary-600; @apply hx:text-primary-600;
} }
} }

View File

@@ -1,21 +1,21 @@
@media (max-width: 767px) { @media (max-width: 48rem) {
.sidebar-container { .hextra-sidebar-container {
@apply hx-fixed hx-pt-[calc(var(--navbar-height))] hx-top-0 hx-w-full hx-bottom-0 hx-z-[15] hx-overscroll-contain hx-bg-white dark:hx-bg-dark; @apply hx:fixed hx:pt-[calc(var(--navbar-height))] hx:top-0 hx:w-full hx:bottom-0 hx:z-[15] hx:overscroll-contain hx:bg-white hx:dark:bg-dark;
transition: transform 0.8s cubic-bezier(0.52, 0.16, 0.04, 1); transition: transform 0.4s cubic-bezier(0.52, 0.16, 0.04, 1);
will-change: transform, opacity; will-change: transform, opacity;
contain: layout style; contain: layout style;
backface-visibility: hidden; backface-visibility: hidden;
} }
} }
.sidebar-container { .hextra-sidebar-container {
li > div { li > div {
@apply hx-h-0; @apply hx:h-0;
} }
li.open > div { li.open > div {
@apply hx-h-auto hx-pt-1; @apply hx:h-auto hx:pt-1;
} }
li.open > a > span > svg > path { li.open > a > span > svg > path {
@apply hx-rotate-90; @apply hx:rotate-90;
} }
} }

View File

@@ -1,17 +1,22 @@
.steps h3 { .hextra-steps {
counter-increment: step; :where(h2, h3, h4, h5, h6):not(.no-step-marker) {
counter-increment: step;
&:before { @apply hx:ltr:before:ml-[-41px] hx:rtl:before:mr-[-44px];
@apply hx-absolute hx-w-[33px] hx-h-[33px]; /* https://github.com/tailwindlabs/tailwindcss/issues/15597#issuecomment-2582673546 */
@apply hx-border-4 hx-border-white hx-bg-gray-100 dark:hx-border-dark dark:hx-bg-neutral-800; @apply hx:before:bg-gray-100 hx:dark:before:bg-neutral-800;
@apply hx-rounded-full hx-text-neutral-400 hx-text-base hx-font-normal hx-text-center -hx-indent-px; @apply hx:before:border-4 hx:before:border-white hx:dark:before:border-dark;
@apply hx-mt-[3px] ltr:hx-ml-[-41px] rtl:hx-mr-[-44px]; &:before {
content: counter(step); content: counter(step);
@apply hx:absolute hx:size-[33px];
@apply hx:rounded-full hx:text-neutral-400 hx:text-base hx:font-normal hx:text-center hx:-indent-px;
}
} }
} }
:lang(fa) .steps h3 { :lang(fa) .hextra-steps {
&:before { :where(h2, h3, h4, h5, h6):not(.no-step-marker) {
content: counter(step, persian); &:before {
content: counter(step, persian);
}
} }
} }

View File

@@ -0,0 +1,4 @@
/* Table of Contents Scroll Spy Styles */
.hextra-toc a.hextra-toc-active {
@apply hx:text-gray-900! hx:dark:text-gray-50! hx:transition-all hx:duration-200;
}

View File

@@ -1,53 +1,53 @@
/* Code syntax highlight */ /* Code syntax highlight */
@import "chroma/light.css"; @import "./chroma/light.css";
@import "chroma/dark.css"; @import "./chroma/dark.css";
.hextra-code-block { .hextra-code-block {
@apply hx-text-[.9em] hx-leading-5; @apply hx:text-[.9em] hx:leading-5;
pre { pre {
@apply hx-text-[.9em] hx-bg-primary-700/5 hx-overflow-x-auto hx-font-medium hx-subpixel-antialiased dark:hx-bg-primary-300/10 contrast-more:hx-border contrast-more:hx-border-primary-900/20 contrast-more:hx-contrast-150 contrast-more:dark:hx-border-primary-100/40; @apply hx:text-[.9em] hx:bg-primary-700/5 hx:overflow-x-auto hx:font-medium hx:subpixel-antialiased hx:dark:bg-primary-300/10 hx:contrast-more:border hx:contrast-more:border-primary-900/20 hx:contrast-more:contrast-150 hx:contrast-more:dark:border-primary-100/40;
} }
.filename { .hextra-code-filename {
@apply hx-absolute hx-top-0 hx-z-[1] hx-w-full hx-truncate hx-rounded-t-xl hx-bg-primary-700/5 hx-py-2 hx-px-4 hx-text-xs hx-text-gray-700 dark:hx-bg-primary-300/10 dark:hx-text-gray-200; @apply hx:absolute hx:top-0 hx:z-[1] hx:w-full hx:truncate hx:rounded-t-xl hx:bg-primary-700/5 hx:py-2 hx:px-4 hx:text-xs hx:text-gray-700 hx:dark:bg-primary-300/10 hx:dark:text-gray-200;
} }
.filename + pre:not(.lntable pre) { .hextra-code-filename + pre:not(.lntable pre) {
/* Override padding for code blocks with filename but no highlight */ /* Override padding for code blocks with filename but no highlight */
@apply hx-pt-12; @apply hx:pt-12;
} }
} }
.hextra-code-block pre:not(.lntable pre) { .hextra-code-block pre:not(.lntable pre) {
@apply hx-px-4 hx-mb-4 hx-py-4 hx-rounded-xl; @apply hx:px-4 hx:mb-4 hx:py-4 hx:rounded-xl;
} }
.hextra-code-block div:nth-of-type(2) pre { .hextra-code-block div:nth-of-type(2) pre {
@apply hx-pt-12 hx-pb-4; @apply hx:pt-12 hx:pb-4;
} }
.chroma { .chroma {
.lntable { .lntable {
@apply hx-m-0 hx-block hx-w-auto hx-overflow-auto hx-rounded-xl; @apply hx:m-0 hx:block hx:w-auto hx:overflow-auto hx:rounded-xl;
pre { pre {
@apply hx-pt-4 hx-pb-4; @apply hx:pt-4 hx:pb-4;
} }
} }
.ln, .ln,
.lnt:not(.hl > .lnt), .lnt:not(.hl > .lnt),
.hl:not(.line) { .hl:not(.line) {
@apply hx-pl-4 hx-pr-4 hx-min-w-[2.6rem] hx-text-neutral-600 dark:hx-text-neutral-300; @apply hx:pl-4 hx:pr-4 hx:min-w-[2.6rem] hx:text-neutral-600 hx:dark:text-neutral-300;
} }
.lntd { .lntd {
@apply hx-p-0 hx-align-top; @apply hx:p-0 hx:align-top;
} }
.lntd:last-of-type { .lntd:last-of-type {
@apply hx-w-full; @apply hx:w-full;
} }
/* LineHighlight */ /* LineHighlight */
.hl { .hl {
@apply hx-block hx-w-full hx-bg-primary-800/10; @apply hx:block hx:w-full hx:bg-primary-800/10;
} }
} }

1
assets/css/safelist.txt Normal file
View File

@@ -0,0 +1 @@
hx:max-w-full

View File

@@ -1,26 +1,27 @@
@import "tailwind.css"; @import "tailwindcss" prefix(hx);
@import "typography.css"; @custom-variant dark (&:where(.dark, .dark *));
@import "highlight.css";
@import "components/cards.css"; @theme {
@import "components/steps.css"; --color-primary-50: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 47));
@import "components/search.css"; --color-primary-100: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 44));
@import "components/sidebar.css"; --color-primary-200: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 36));
@import "components/navbar.css"; --color-primary-300: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 27));
@import "components/scrollbar.css"; --color-primary-400: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 16));
@import "components/code-copy.css"; --color-primary-500: hsl(var(--primary-hue) var(--primary-saturation) var(--primary-lightness));
@import "components/hextra/feature-grid.css"; --color-primary-600: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 45));
@import "components/jupyter.css"; --color-primary-700: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 39));
@import "components/badge.css"; --color-primary-800: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 32));
--color-primary-900: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 24));
--color-dark: #111;
}
html { html {
@apply hx-text-base hx-antialiased; @apply hx:text-base hx:antialiased;
font-feature-settings: "rlig" 1, "calt" 1, "ss01" 1;
-webkit-tap-highlight-color: transparent;
} }
body { body {
@apply hx-w-full hx-bg-white dark:hx-bg-dark dark:hx-text-gray-100; @apply hx:w-full hx:bg-white hx:dark:bg-dark hx:dark:text-gray-100;
} }
:root { :root {
@@ -28,7 +29,7 @@ body {
--primary-saturation: 100%; --primary-saturation: 100%;
--primary-lightness: 50%; --primary-lightness: 50%;
--navbar-height: 4rem; --navbar-height: 4rem;
--menu-height: 3.75rem; --menu-height: 3.75rem; /* 60px */
} }
.dark { .dark {
@@ -36,3 +37,17 @@ body {
--primary-saturation: 100%; --primary-saturation: 100%;
--primary-lightness: 50%; --primary-lightness: 50%;
} }
@import "./typography.css";
@import "./highlight.css";
@import "./components/cards.css";
@import "./components/steps.css";
@import "./components/search.css";
@import "./components/sidebar.css";
@import "./components/navbar.css";
@import "./components/scrollbar.css";
@import "./components/code-copy.css";
@import "./components/hextra/feature-grid.css";
@import "./components/jupyter.css";
@import "./components/badge.css";
@import "./components/toc.css";

View File

@@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -1,126 +1,136 @@
.content { .content {
:where(h1):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100; @apply hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100;
} }
:where(h2):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-font-semibold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100 hx-mt-10 hx-border-b hx-pb-1 hx-text-3xl hx-border-neutral-200/70 contrast-more:hx-border-neutral-400 dark:hx-border-primary-100/10 contrast-more:dark:hx-border-neutral-400; @apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-10 hx:border-b hx:pb-1 hx:text-3xl hx:border-neutral-200/70 hx:contrast-more:border-neutral-400 hx:dark:border-primary-100/10 hx:contrast-more:dark:border-neutral-400;
} }
:where(h3):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-font-semibold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100 hx-mt-8 hx-text-2xl; @apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-2xl;
} }
:where(h4):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-font-semibold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100 hx-mt-8 hx-text-xl; @apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-xl;
} }
:where(h5):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(h5):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-font-semibold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100 hx-mt-8 hx-text-lg; @apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-lg;
} }
:where(h6):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(h6):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-font-semibold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100 hx-mt-8 hx-text-base; @apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-base;
} }
:where(p):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(p):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-mt-6 hx-leading-7 first:hx-mt-0; @apply hx:mt-6 hx:leading-7 hx:first:mt-0;
} }
:where(a):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(a):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-text-primary-600 hx-underline hx-decoration-from-font [text-underline-position:from-font]; @apply hx:text-primary-600 hx:underline hx:decoration-from-font;
} }
:where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-mt-6 hx-border-gray-300 hx-italic hx-text-gray-700 dark:hx-border-gray-700 dark:hx-text-gray-400 first:hx-mt-0 ltr:hx-border-l-2 ltr:hx-pl-6 rtl:hx-border-r-2 rtl:hx-pr-6; @apply hx:mt-6 hx:border-gray-300 hx:italic hx:text-gray-700 hx:dark:border-gray-700 hx:dark:text-gray-400 hx:first:mt-0 hx:ltr:border-l-2 hx:ltr:pl-6 hx:rtl:border-r-2 hx:rtl:pr-6;
} }
:where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) { :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
@apply hx-bg-primary-700/5 hx-mb-4 hx-overflow-x-auto hx-rounded-xl hx-font-medium hx-subpixel-antialiased dark:hx-bg-primary-300/10 hx-text-[.9em] contrast-more:hx-border contrast-more:hx-border-primary-900/20 contrast-more:hx-contrast-150 contrast-more:dark:hx-border-primary-100/40 hx-py-4; @apply hx:bg-primary-700/5 hx:mb-4 hx:overflow-x-auto hx:rounded-xl hx:font-medium hx:subpixel-antialiased hx:dark:bg-primary-300/10 hx:text-[.9em] hx:contrast-more:border hx:contrast-more:border-primary-900/20 hx:contrast-more:contrast-150 hx:contrast-more:dark:border-primary-100/40 hx:py-4;
} }
:where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *)) { :where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *)) {
@apply hx-border-black hx-border-opacity-[0.04] hx-bg-opacity-[0.03] hx-bg-black hx-break-words hx-rounded-md hx-border hx-py-0.5 hx-px-[.25em] hx-text-[.9em] dark:hx-border-white/10 dark:hx-bg-white/10; @apply hx:border-black/4 hx:bg-black/3 hx:break-words hx:rounded-md hx:border hx:py-0.5 hx:px-[.25em] hx:text-[.9em] hx:dark:border-white/10 hx:dark:bg-white/10;
} }
:where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) { :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) {
@apply hx-block hx-overflow-x-auto hx-mt-6 hx-p-0 first:hx-mt-0; @apply hx:block hx:overflow-x-auto hx:my-6 hx:p-0 hx:first:mt-0 hx:w-full hx:text-sm hx:leading-5;
tr { thead {
@apply hx-m-0 hx-border-t hx-border-gray-300 hx-p-0 dark:hx-border-gray-600 even:hx-bg-gray-100 even:dark:hx-bg-gray-600/20; @apply hx:border-b hx:border-gray-200 hx:dark:border-neutral-800;
}
tbody tr {
@apply hx:m-0 hx:border-b hx:border-gray-100 hx:dark:border-neutral-800/50;
} }
th { th {
@apply hx-m-0 hx-border hx-border-gray-300 hx-px-4 hx-py-2 hx-font-semibold dark:hx-border-gray-600; @apply hx:m-0 hx:p-2 hx:font-semibold hx:first:pl-0 hx:last:pr-0;
} }
td { td {
@apply hx-m-0 hx-border hx-border-gray-300 hx-px-4 hx-py-2 dark:hx-border-gray-600; @apply hx:m-0 hx:p-2 hx:first:pl-0 hx:last:pr-0;
} }
} }
:where(ol):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-mt-6 hx-list-decimal first:hx-mt-0 ltr:hx-ml-6 rtl:hx-mr-6; @apply hx:mt-6 hx:list-decimal hx:first:mt-0 hx:ltr:ml-6 hx:rtl:mr-6;
li { li {
@apply hx-my-2; @apply hx:my-2;
} }
} }
:where(ul):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-mt-6 hx-list-disc first:hx-mt-0 ltr:hx-ml-6 rtl:hx-mr-6; @apply hx:mt-6 hx:list-disc hx:first:mt-0 hx:ltr:ml-6 hx:rtl:mr-6;
li { li {
@apply hx-my-2; @apply hx:my-2;
} }
} }
/* This CSS rule targets the first nested unordered (ul) or ordered (ol) list /* This CSS rule targets the first nested unordered (ul) or ordered (ol) list
inside the list item (li) of any parent ul or ol. inside the list item (li) of any parent ul or ol.
The rule sets the top margin of the selected list to zero. */ The rule sets the top margin of the selected list to zero. */
:where(ul, ol) > li > :where(ul, ol):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(ul, ol) > li > :where(ul, ol):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-mt-0; @apply hx:mt-0;
} }
:where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-border-black hx-border-opacity-[0.04] hx-bg-opacity-[0.03] hx-bg-black hx-break-words hx-rounded-md hx-border hx-py-0.5 hx-px-[.25em] hx-text-[.9em] dark:hx-border-white/10 dark:hx-bg-white/10; @apply hx:border-black/4 hx:bg-black/3 hx:break-words hx:rounded-md hx:border hx:py-0.5 hx:px-[.25em] hx:text-[.9em] hx:dark:border-white/10 hx:dark:bg-white/10;
} }
:where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) { :where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
@apply hx-bg-transparent hx-rounded-none dark:hx-bg-transparent; @apply hx:bg-transparent hx:rounded-none hx:dark:bg-transparent;
} }
:where(img):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(img):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx-mx-auto hx-my-4 hx-rounded-md; @apply hx:mx-auto hx:my-4 hx:rounded-md;
} }
:where(figure):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)) {
figcaption { figcaption {
@apply hx-text-sm hx-text-gray-500 dark:hx-text-gray-400 hx-mt-2 hx-block hx-text-center; @apply hx:text-sm hx:text-gray-500 hx:dark:text-gray-400 hx:mt-2 hx:block hx:text-center;
} }
} }
/* Definition list */ /* Definition list */
:where(dl):not(:where([class~=not-prose],[class~=not-prose] *)) { :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)) {
dt { dt {
@apply hx-mt-6 hx-font-semibold; @apply hx:mt-6 hx:font-semibold;
} }
dd { dd {
@apply hx-my-2 hx-ps-6; @apply hx:my-2 hx:ps-6;
} }
} }
/* Horizontal line */
:where(hr):not(:where([class~=not-prose],[class~=not-prose] *)) {
@apply hx:my-10 hx:first:mt-0 hx:last:mb-0 hx:border-gray-200 hx:dark:border-neutral-800;
}
.footnotes { .footnotes {
@apply hx-mt-12 hx-text-sm; @apply hx:mt-12 hx:text-sm;
hr {
@apply hx:border-gray-200 hx:dark:border-neutral-800;
}
} }
} .subheading-anchor {
@apply hx:opacity-0 hx:transition-opacity hx:ltr:ml-1 hx:rtl:mr-1;
.subheading-anchor {
@apply hx-opacity-0 hx-transition-opacity ltr:hx-ml-1 rtl:hx-mr-1; span:target + &,
:hover > &,
span:target + &, &:focus {
:hover > &, @apply hx:opacity-100;
&:focus { }
@apply hx-opacity-100;
} span + &,
:hover > & {
span + &, @apply hx:no-underline!;
:hover > & { }
@apply !hx-no-underline;
} @apply hx:after:text-gray-300 hx:dark:after:text-neutral-700;
&:after {
&:after { @apply hx:content-['#'] hx:px-1;
@apply hx-content-['#'] hx-px-1; span:target + & {
@apply hx-text-gray-300 dark:hx-text-neutral-700; @apply hx:text-gray-400;
span:target + & { @apply hx:dark:text-neutral-500;
@apply hx-text-gray-400; }
@apply dark:hx-text-neutral-500;
} }
} }
} }
article details > summary { article details > summary {
&::-webkit-details-marker { &::-webkit-details-marker {
@apply hx-hidden; @apply hx:hidden;
} }
&::before { &::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='hx-h-5 hx-w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd' /%3E%3C/svg%3E"); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='hx:h-5 hx:w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd' /%3E%3C/svg%3E");
height: 1.2em; height: 1.2em;
width: 1.2em; width: 1.2em;
vertical-align: -4px; vertical-align: -4px;

30
assets/css/variables.css Normal file
View File

@@ -0,0 +1,30 @@
/* Hugo template to derive CSS variables from site and page parameters */
/* Do not remove the following comment. It is used by Hugo to render CSS variables.
{{- $pageWidth := .Params.width | default .Site.Params.page.width -}}
{{- $maxPageWidth := cond (eq $pageWidth "wide") "90rem" (cond (eq $pageWidth "full") "100%" "80rem") -}}
{{- $navbarWidth := .Site.Params.navbar.width -}}
{{- $maxNavbarWidth := cond (eq $navbarWidth "wide") "90rem" (cond (eq $navbarWidth "full") "100%" "80rem") -}}
{{- $footerWidth := .Site.Params.footer.width -}}
{{- $maxFooterWidth := cond (eq $footerWidth "wide") "90rem" (cond (eq $footerWidth "full") "100%" "80rem") -}}
*/
:root {
--hextra-max-page-width: {{ $maxPageWidth }};
--hextra-max-navbar-width: {{ $maxNavbarWidth }};
--hextra-max-footer-width: {{ $maxFooterWidth }};
}
.hextra-max-page-width {
max-width: var(--hextra-max-page-width);
}
.hextra-max-navbar-width {
max-width: var(--hextra-max-navbar-width);
}
.hextra-max-footer-width {
max-width: var(--hextra-max-footer-width);
}

View File

@@ -5,9 +5,9 @@ document.addEventListener("DOMContentLoaded", function () {
if (backToTop) { if (backToTop) {
document.addEventListener("scroll", (e) => { document.addEventListener("scroll", (e) => {
if (window.scrollY > 300) { if (window.scrollY > 300) {
backToTop.classList.remove("hx-opacity-0"); backToTop.classList.remove("hx:opacity-0");
} else { } else {
backToTop.classList.add("hx-opacity-0"); backToTop.classList.add("hx:opacity-0");
} }
}); });
} }

View File

@@ -27,8 +27,8 @@ document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) { document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) {
// Add copy and success icons // Add copy and success icons
button.querySelector('.copy-icon')?.appendChild(getCopyIcon()); button.querySelector('.hextra-copy-icon')?.appendChild(getCopyIcon());
button.querySelector('.success-icon')?.appendChild(getSuccessIcon()); button.querySelector('.hextra-success-icon')?.appendChild(getSuccessIcon());
// Add click event listener for copy button // Add click event listener for copy button
button.addEventListener('click', function (e) { button.addEventListener('click', function (e) {

22
assets/js/favicon.js Normal file
View File

@@ -0,0 +1,22 @@
// {{ $faviconDarkExists := fileExists (path.Join "static" "favicon-dark.svg") }}
(function () {
const faviconEl = document.getElementById("favicon-svg");
const faviconDarkExists = "{{ $faviconDarkExists }}" === "true";
if (faviconEl && faviconDarkExists) {
const lightFavicon = '{{ "favicon.svg" | relURL }}';
const darkFavicon = '{{ "favicon-dark.svg" | relURL }}';
const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
function updateFavicon(e) {
faviconEl.href = e.matches ? darkFavicon : lightFavicon;
}
// Set favicon on load
updateFavicon(darkModeQuery);
// Listen for system preference changes
darkModeQuery.addEventListener("change", updateFavicon);
}
})();

View File

@@ -3,10 +3,10 @@
// Change shortcut key to cmd+k on Mac, iPad or iPhone. // Change shortcut key to cmd+k on Mac, iPad or iPhone.
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
if (/iPad|iPhone|Macintosh/.test(navigator.userAgent)) { if (/iPad|iPhone|Macintosh/.test(navigator.userAgent)) {
// select the kbd element under the .search-wrapper class // select the kbd element under the .hextra-search-wrapper class
const keys = document.querySelectorAll(".search-wrapper kbd"); const keys = document.querySelectorAll(".hextra-search-wrapper kbd");
keys.forEach(key => { keys.forEach(key => {
key.innerHTML = '<span class="hx-text-xs">⌘</span>K'; key.innerHTML = '<span class="hx:text-xs">⌘</span>K';
}); });
} }
}); });
@@ -22,7 +22,7 @@ document.addEventListener("DOMContentLoaded", function () {
(function () { (function () {
const searchDataURL = '{{ $searchData.RelPermalink }}'; const searchDataURL = '{{ $searchData.RelPermalink }}';
const inputElements = document.querySelectorAll('.search-input'); const inputElements = document.querySelectorAll('.hextra-search-input');
for (const el of inputElements) { for (const el of inputElements) {
el.addEventListener('focus', init); el.addEventListener('focus', init);
el.addEventListener('keyup', search); el.addEventListener('keyup', search);
@@ -30,7 +30,7 @@ document.addEventListener("DOMContentLoaded", function () {
el.addEventListener('input', handleInputChange); el.addEventListener('input', handleInputChange);
} }
const shortcutElements = document.querySelectorAll('.search-wrapper kbd'); const shortcutElements = document.querySelectorAll('.hextra-search-wrapper kbd');
function setShortcutElementsOpacity(opacity) { function setShortcutElementsOpacity(opacity) {
shortcutElements.forEach(el => { shortcutElements.forEach(el => {
@@ -45,12 +45,12 @@ document.addEventListener("DOMContentLoaded", function () {
// Get the search wrapper, input, and results elements. // Get the search wrapper, input, and results elements.
function getActiveSearchElement() { function getActiveSearchElement() {
const inputs = Array.from(document.querySelectorAll('.search-wrapper')).filter(el => el.clientHeight > 0); const inputs = Array.from(document.querySelectorAll('.hextra-search-wrapper')).filter(el => el.clientHeight > 0);
if (inputs.length === 1) { if (inputs.length === 1) {
return { return {
wrapper: inputs[0], wrapper: inputs[0],
inputElement: inputs[0].querySelector('.search-input'), inputElement: inputs[0].querySelector('.hextra-search-input'),
resultsElement: inputs[0].querySelector('.search-results') resultsElement: inputs[0].querySelector('.hextra-search-results')
}; };
} }
return undefined; return undefined;
@@ -103,7 +103,7 @@ document.addEventListener("DOMContentLoaded", function () {
const { resultsElement } = getActiveSearchElement(); const { resultsElement } = getActiveSearchElement();
if (!resultsElement) return { result: undefined, index: -1 }; if (!resultsElement) return { result: undefined, index: -1 };
const result = resultsElement.querySelector('.active'); const result = resultsElement.querySelector('.hextra-search-active');
if (!result) return { result: undefined, index: -1 }; if (!result) return { result: undefined, index: -1 };
const index = parseInt(result.dataset.index, 10); const index = parseInt(result.dataset.index, 10);
@@ -116,10 +116,10 @@ document.addEventListener("DOMContentLoaded", function () {
if (!resultsElement) return; if (!resultsElement) return;
const { result: activeResult } = getActiveResult(); const { result: activeResult } = getActiveResult();
activeResult && activeResult.classList.remove('active'); activeResult && activeResult.classList.remove('hextra-search-active');
const result = resultsElement.querySelector(`[data-index="${index}"]`); const result = resultsElement.querySelector(`[data-index="${index}"]`);
if (result) { if (result) {
result.classList.add('active'); result.classList.add('hextra-search-active');
result.focus(); result.focus();
} }
} }
@@ -143,7 +143,7 @@ document.addEventListener("DOMContentLoaded", function () {
function hideSearchResults() { function hideSearchResults() {
const { resultsElement } = getActiveSearchElement(); const { resultsElement } = getActiveSearchElement();
if (!resultsElement) return; if (!resultsElement) return;
resultsElement.classList.add('hx-hidden'); resultsElement.classList.add('hx:hidden');
} }
// Handle keyboard events. // Handle keyboard events.
@@ -195,8 +195,17 @@ document.addEventListener("DOMContentLoaded", function () {
*/ */
async function preloadIndex() { async function preloadIndex() {
const tokenize = '{{- site.Params.search.flexsearch.tokenize | default "forward" -}}'; const tokenize = '{{- site.Params.search.flexsearch.tokenize | default "forward" -}}';
// https://github.com/TryGhost/Ghost/pull/21148
const regex = new RegExp(
`[\u{4E00}-\u{9FFF}\u{3040}-\u{309F}\u{30A0}-\u{30FF}\u{AC00}-\u{D7A3}\u{3400}-\u{4DBF}\u{20000}-\u{2A6DF}\u{2A700}-\u{2B73F}\u{2B740}-\u{2B81F}\u{2B820}-\u{2CEAF}\u{2CEB0}-\u{2EBEF}\u{30000}-\u{3134F}\u{31350}-\u{323AF}\u{2EBF0}-\u{2EE5F}\u{F900}-\u{FAFF}\u{2F800}-\u{2FA1F}]|[0-9A-Za-zа\u00C0-\u017F\u0400-\u04FF\u0600-\u06FF\u0980-\u09FF\u1E00-\u1EFF\u0590-\u05FF]+`,
'mug'
);
const encode = (str) => { return ('' + str).toLowerCase().match(regex) ?? []; }
window.pageIndex = new FlexSearch.Document({ window.pageIndex = new FlexSearch.Document({
tokenize, tokenize,
encode,
cache: 100, cache: 100,
document: { document: {
id: 'id', id: 'id',
@@ -207,12 +216,15 @@ document.addEventListener("DOMContentLoaded", function () {
window.sectionIndex = new FlexSearch.Document({ window.sectionIndex = new FlexSearch.Document({
tokenize, tokenize,
encode,
cache: 100, cache: 100,
document: { document: {
id: 'id', id: 'id',
store: ['title', 'content', 'url', 'display', 'crumb'], store: ['title', 'content', 'url', 'display', 'crumb'],
index: "content", index: "content",
tag: 'pageId' tag: [{
field: "pageId"
}]
} }
}); });
@@ -225,7 +237,7 @@ document.addEventListener("DOMContentLoaded", function () {
const urlParts = route.split('/').filter(x => x != "" && !x.startsWith('#')); const urlParts = route.split('/').filter(x => x != "" && !x.startsWith('#'));
let crumb = ''; let crumb = '';
let searchUrl = '/' let searchUrl = '/';
for (let i = 0; i < urlParts.length; i++) { for (let i = 0; i < urlParts.length; i++) {
const urlPart = urlParts[i]; const urlPart = urlParts[i];
searchUrl += urlPart + '/' searchUrl += urlPart + '/'
@@ -304,7 +316,7 @@ document.addEventListener("DOMContentLoaded", function () {
while (resultsElement.firstChild) { while (resultsElement.firstChild) {
resultsElement.removeChild(resultsElement.firstChild); resultsElement.removeChild(resultsElement.firstChild);
} }
resultsElement.classList.remove('hx-hidden'); resultsElement.classList.remove('hx:hidden');
const pageResults = window.pageIndex.search(query, 5, { enrich: true, suggest: true })[0]?.result || []; const pageResults = window.pageIndex.search(query, 5, { enrich: true, suggest: true })[0]?.result || [];
@@ -316,7 +328,7 @@ document.addEventListener("DOMContentLoaded", function () {
pageTitleMatches[i] = 0; pageTitleMatches[i] = 0;
// Show the top 5 results for each page // Show the top 5 results for each page
const sectionResults = window.sectionIndex.search(query, 5, { enrich: true, suggest: true, tag: `page_${result.id}` })[0]?.result || []; const sectionResults = window.sectionIndex.search(query, 5, { enrich: true, suggest: true, tag: { 'pageId': `page_${result.id}` } })[0]?.result || [];
let isFirstItemOfPage = true let isFirstItemOfPage = true
const occurred = {} const occurred = {}
@@ -372,7 +384,7 @@ document.addEventListener("DOMContentLoaded", function () {
if (!resultsElement) return; if (!resultsElement) return;
if (!results.length) { if (!results.length) {
resultsElement.innerHTML = `<span class="no-result">{{ $noResultsFound | safeHTML }}</span>`; resultsElement.innerHTML = `<span class="hextra-search-no-result">{{ $noResultsFound | safeHTML }}</span>`;
return; return;
} }
@@ -406,14 +418,14 @@ document.addEventListener("DOMContentLoaded", function () {
const result = results[i]; const result = results[i];
if (result.prefix) { if (result.prefix) {
fragment.appendChild(createElement(` fragment.appendChild(createElement(`
<div class="prefix">${result.prefix}</div>`)); <div class="hextra-search-prefix">${result.prefix}</div>`));
} }
let li = createElement(` let li = createElement(`
<li> <li>
<a data-index="${i}" href="${result.route}" class=${i === 0 ? "active" : ""}> <a data-index="${i}" href="${result.route}" class=${i === 0 ? "hextra-search-active" : ""}>
<div class="title">`+ highlightMatches(result.children.title, query) + `</div>` + <div class="hextra-search-title">`+ highlightMatches(result.children.title, query) + `</div>` +
(result.children.content ? (result.children.content ?
`<div class="excerpt">` + highlightMatches(result.children.content, query) + `</div>` : '') + ` `<div class="hextra-search-excerpt">` + highlightMatches(result.children.content, query) + `</div>` : '') + `
</a> </a>
</li>`); </li>`);
li.addEventListener('mousemove', handleMouseMove); li.addEventListener('mousemove', handleMouseMove);

View File

@@ -1,11 +1,11 @@
(function () { (function () {
const languageSwitchers = document.querySelectorAll('.language-switcher'); const languageSwitchers = document.querySelectorAll('.hextra-language-switcher');
languageSwitchers.forEach((switcher) => { languageSwitchers.forEach((switcher) => {
switcher.addEventListener('click', (e) => { switcher.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
switcher.dataset.state = switcher.dataset.state === 'open' ? 'closed' : 'open'; switcher.dataset.state = switcher.dataset.state === 'open' ? 'closed' : 'open';
const optionsElement = switcher.nextElementSibling; const optionsElement = switcher.nextElementSibling;
optionsElement.classList.toggle('hx-hidden'); optionsElement.classList.toggle('hx:hidden');
// Calculate position of language options element // Calculate position of language options element
const switcherRect = switcher.getBoundingClientRect(); const switcherRect = switcher.getBoundingClientRect();
@@ -17,11 +17,11 @@
// Dismiss language switcher when clicking outside // Dismiss language switcher when clicking outside
document.addEventListener('click', (e) => { document.addEventListener('click', (e) => {
if (e.target.closest('.language-switcher') === null) { if (e.target.closest('.hextra-language-switcher') === null) {
languageSwitchers.forEach((switcher) => { languageSwitchers.forEach((switcher) => {
switcher.dataset.state = 'closed'; switcher.dataset.state = 'closed';
const optionsElement = switcher.nextElementSibling; const optionsElement = switcher.nextElementSibling;
optionsElement.classList.add('hx-hidden'); optionsElement.classList.add('hx:hidden');
}); });
} }
}); });

View File

@@ -1,49 +1,40 @@
// Hamburger menu for mobile navigation // Hamburger menu for mobile navigation
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
const menu = document.querySelector('.hamburger-menu'); const menu = document.querySelector('.hextra-hamburger-menu');
const overlay = document.querySelector('.mobile-menu-overlay'); const sidebarContainer = document.querySelector('.hextra-sidebar-container');
const sidebarContainer = document.querySelector('.sidebar-container');
// Initialize the overlay
const overlayClasses = ['hx-fixed', 'hx-inset-0', 'hx-z-10', 'hx-bg-black/80', 'dark:hx-bg-black/60'];
overlay.classList.add('hx-bg-transparent');
overlay.classList.remove("hx-hidden", ...overlayClasses);
function toggleMenu() { function toggleMenu() {
// Toggle the hamburger menu // Toggle the hamburger menu
menu.querySelector('svg').classList.toggle('open'); menu.querySelector('svg').classList.toggle('open');
// When the menu is open, we want to show the navigation sidebar // When the menu is open, we want to show the navigation sidebar
sidebarContainer.classList.toggle('max-md:[transform:translate3d(0,-100%,0)]'); sidebarContainer.classList.toggle('hx:max-md:[transform:translate3d(0,-100%,0)]');
sidebarContainer.classList.toggle('max-md:[transform:translate3d(0,0,0)]'); sidebarContainer.classList.toggle('hx:max-md:[transform:translate3d(0,0,0)]');
// When the menu is open, we want to prevent the body from scrolling // When the menu is open, we want to prevent the body from scrolling
document.body.classList.toggle('hx-overflow-hidden'); document.body.classList.toggle('hx:overflow-hidden');
document.body.classList.toggle('md:hx-overflow-auto'); document.body.classList.toggle('hx:md:overflow-auto');
} }
menu.addEventListener('click', (e) => { menu.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
toggleMenu(); toggleMenu();
if (overlay.classList.contains('hx-bg-transparent')) {
// Show the overlay
overlay.classList.add(...overlayClasses);
overlay.classList.remove('hx-bg-transparent');
} else {
// Hide the overlay
overlay.classList.remove(...overlayClasses);
overlay.classList.add('hx-bg-transparent');
}
}); });
overlay.addEventListener('click', (e) => { // Select all anchor tags in the sidebar container
e.preventDefault(); const sidebarLinks = sidebarContainer.querySelectorAll('a');
toggleMenu();
// Hide the overlay // Add click event listener to each anchor tag
overlay.classList.remove(...overlayClasses); sidebarLinks.forEach(link => {
overlay.classList.add('hx-bg-transparent'); link.addEventListener('click', (e) => {
// Check if the href attribute contains a hash symbol (links to a heading)
if (link.getAttribute('href') && link.getAttribute('href').startsWith('#')) {
// Only dismiss overlay on mobile view
if (window.innerWidth < 768) {
toggleMenu();
}
}
});
}); });
}); });

62
assets/js/nav-menu.js Normal file
View File

@@ -0,0 +1,62 @@
(function () {
const hiddenClass = "hx:hidden";
const dropdownToggles = document.querySelectorAll(".hextra-nav-menu-toggle");
dropdownToggles.forEach((toggle) => {
toggle.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
// Close all other dropdowns first
dropdownToggles.forEach((otherToggle) => {
if (otherToggle !== toggle) {
otherToggle.dataset.state = "closed";
const otherMenuItems = otherToggle.nextElementSibling;
otherMenuItems.classList.add(hiddenClass);
}
});
// Toggle current dropdown
const isOpen = toggle.dataset.state === "open";
toggle.dataset.state = isOpen ? "closed" : "open";
const menuItemsElement = toggle.nextElementSibling;
if (!isOpen) {
// Position dropdown centered with toggle
menuItemsElement.style.position = "absolute";
menuItemsElement.style.top = "100%";
menuItemsElement.style.left = "50%";
menuItemsElement.style.transform = "translateX(-50%)";
menuItemsElement.style.zIndex = "1000";
// Show dropdown
menuItemsElement.classList.remove(hiddenClass);
} else {
// Hide dropdown
menuItemsElement.classList.add(hiddenClass);
}
});
});
// Dismiss dropdown when clicking outside
document.addEventListener("click", (e) => {
if (e.target.closest(".hextra-nav-menu-toggle") === null) {
dropdownToggles.forEach((toggle) => {
toggle.dataset.state = "closed";
const menuItemsElement = toggle.nextElementSibling;
menuItemsElement.classList.add(hiddenClass);
});
}
});
// Close dropdowns on escape key
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
dropdownToggles.forEach((toggle) => {
toggle.dataset.state = "closed";
const menuItemsElement = toggle.nextElementSibling;
menuItemsElement.classList.add(hiddenClass);
});
}
});
})();

View File

@@ -17,8 +17,8 @@ function enableCollapsibles() {
} }
function scrollToActiveItem() { function scrollToActiveItem() {
const sidebarScrollbar = document.querySelector("aside.sidebar-container > .hextra-scrollbar"); const sidebarScrollbar = document.querySelector("aside.hextra-sidebar-container > .hextra-scrollbar");
const activeItems = document.querySelectorAll(".sidebar-active-item"); const activeItems = document.querySelectorAll(".hextra-sidebar-active-item");
const visibleActiveItem = Array.from(activeItems).find(function (activeItem) { const visibleActiveItem = Array.from(activeItems).find(function (activeItem) {
return activeItem.getBoundingClientRect().height > 0; return activeItem.getBoundingClientRect().height > 0;
}); });

View File

@@ -1,20 +1,57 @@
document.querySelectorAll('.hextra-tabs-toggle').forEach(function (button) { (function () {
button.addEventListener('click', function (e) { function updateGroup(container, index) {
// set parent tabs to unselected const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle'));
const tabs = Array.from(e.target.parentElement.querySelectorAll('.hextra-tabs-toggle')); tabs.forEach((tab, i) => {
tabs.map(tab => tab.dataset.state = ''); tab.dataset.state = i === index ? 'selected' : '';
if (i === index) {
// set current tab to selected tab.setAttribute('aria-selected', 'true');
e.target.dataset.state = 'selected'; tab.tabIndex = 0;
} else {
// set all panels to unselected tab.removeAttribute('aria-selected');
const panelsContainer = e.target.parentElement.parentElement.nextElementSibling; tab.removeAttribute('tabindex');
Array.from(panelsContainer.children).forEach(function (panel) { }
panel.dataset.state = '';
}); });
const panelsContainer = container.parentElement.nextElementSibling;
if (!panelsContainer) return;
Array.from(panelsContainer.children).forEach((panel, i) => {
panel.dataset.state = i === index ? 'selected' : '';
if (i === index) {
panel.tabIndex = 0;
} else {
panel.removeAttribute('tabindex');
}
});
}
const panelId = e.target.getAttribute('aria-controls'); const syncGroups = document.querySelectorAll('[data-tab-group]');
const panel = panelsContainer.querySelector(`#${panelId}`);
panel.dataset.state = 'selected'; syncGroups.forEach((group) => {
const key = encodeURIComponent(group.dataset.tabGroup);
const saved = localStorage.getItem('hextra-tab-' + key);
if (saved !== null) {
updateGroup(group, parseInt(saved, 10));
}
}); });
});
document.querySelectorAll('.hextra-tabs-toggle').forEach((button) => {
button.addEventListener('click', function (e) {
const container = e.target.parentElement;
const index = Array.from(container.querySelectorAll('.hextra-tabs-toggle')).indexOf(
e.target
);
if (container.dataset.tabGroup) {
// Sync behavior: update all tab groups with the same name
const tabGroupValue = container.dataset.tabGroup;
const key = encodeURIComponent(tabGroupValue);
document
.querySelectorAll('[data-tab-group="' + tabGroupValue + '"]')
.forEach((grp) => updateGroup(grp, index));
localStorage.setItem('hextra-tab-' + key, index.toString());
} else {
// Non-sync behavior: update only this specific tab group
updateGroup(container, index);
}
});
});
})();

View File

@@ -2,7 +2,7 @@
(function () { (function () {
const defaultTheme = '{{ site.Params.theme.default | default `system`}}' const defaultTheme = '{{ site.Params.theme.default | default `system`}}'
const themeToggleButtons = document.querySelectorAll(".theme-toggle"); const themeToggleButtons = document.querySelectorAll(".hextra-theme-toggle");
// Change the icons of the buttons based on previous settings or system theme // Change the icons of the buttons based on previous settings or system theme
if ( if (

68
assets/js/toc-scroll.js Normal file
View File

@@ -0,0 +1,68 @@
/**
* TOC Scroll - Highlights active TOC links based on visible headings
*
* Uses Intersection Observer to track heading visibility and applies
* 'hextra-toc-active' class to corresponding TOC links. Selects the
* topmost heading when multiple are visible.
*
* Requires: .hextra-toc element, matching heading IDs, toc.css styles
*/
document.addEventListener("DOMContentLoaded", function () {
const toc = document.querySelector(".hextra-toc");
if (!toc) return;
const tocLinks = toc.querySelectorAll('a[href^="#"]');
if (tocLinks.length === 0) return;
const headingIds = Array.from(tocLinks).map((link) => link.getAttribute("href").substring(1));
const headings = headingIds.map((id) => document.getElementById(id)).filter(Boolean);
if (headings.length === 0) return;
let currentActiveLink = null;
// Create intersection observer
const observer = new IntersectionObserver(
(entries) => {
const visibleHeadings = entries.filter((entry) => entry.isIntersecting).map((entry) => entry.target);
if (visibleHeadings.length === 0) return;
// Find the heading closest to the top of the viewport
const topMostHeading = visibleHeadings.reduce((closest, heading) => {
const headingTop = heading.getBoundingClientRect().top;
const closestTop = closest.getBoundingClientRect().top;
return Math.abs(headingTop) < Math.abs(closestTop) ? heading : closest;
});
const targetId = topMostHeading.id;
const targetLink = toc.querySelector(`a[href="#${targetId}"]`);
if (targetLink && targetLink !== currentActiveLink) {
// Remove active class from previous link
if (currentActiveLink) {
currentActiveLink.classList.remove("hextra-toc-active");
}
// Add active class to current link
targetLink.classList.add("hextra-toc-active");
currentActiveLink = targetLink;
}
},
{
rootMargin: "-20px 0px -80% 0px", // Adjust sensitivity
threshold: [0, 0.1, 0.5, 1],
}
);
// Observe all headings
headings.forEach((heading) => observer.observe(heading));
// Handle edge case: if no headings are visible on initial load
setTimeout(() => {
if (!currentActiveLink && tocLinks.length > 0) {
tocLinks[0].classList.add("hextra-toc-active");
currentActiveLink = tocLinks[0];
}
}, 100);
});

View File

@@ -1,39 +0,0 @@
/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/flexsearch@0.7.31/dist/flexsearch.bundle.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
/**!
* FlexSearch.js v0.7.31 (Bundle)
* Copyright 2018-2022 Nextapps GmbH
* Author: Thomas Wilkerling
* Licence: Apache-2.0
* https://github.com/nextapps-de/flexsearch
*/
(function _f(self){'use strict';try{if(module)self=module}catch(e){}self._factory=_f;var t;function u(a){return"undefined"!==typeof a?a:!0}function aa(a){const b=Array(a);for(let c=0;c<a;c++)b[c]=v();return b}function v(){return Object.create(null)}function ba(a,b){return b.length-a.length}function x(a){return"string"===typeof a}function C(a){return"object"===typeof a}function D(a){return"function"===typeof a};function ca(a,b){var c=da;if(a&&(b&&(a=E(a,b)),this.H&&(a=E(a,this.H)),this.J&&1<a.length&&(a=E(a,this.J)),c||""===c)){a=a.split(c);if(this.filter){b=this.filter;c=a.length;const d=[];for(let e=0,f=0;e<c;e++){const g=a[e];g&&!b[g]&&(d[f++]=g)}a=d}return a}return a}const da=/[\p{Z}\p{S}\p{P}\p{C}]+/u,ea=/[\u0300-\u036f]/g;
function fa(a,b){const c=Object.keys(a),d=c.length,e=[];let f="",g=0;for(let h=0,k,m;h<d;h++)k=c[h],(m=a[k])?(e[g++]=F(b?"(?!\\b)"+k+"(\\b|_)":k),e[g++]=m):f+=(f?"|":"")+k;f&&(e[g++]=F(b?"(?!\\b)("+f+")(\\b|_)":"("+f+")"),e[g]="");return e}function E(a,b){for(let c=0,d=b.length;c<d&&(a=a.replace(b[c],b[c+1]),a);c+=2);return a}function F(a){return new RegExp(a,"g")}function ha(a){let b="",c="";for(let d=0,e=a.length,f;d<e;d++)(f=a[d])!==c&&(b+=c=f);return b};var ja={encode:ia,F:!1,G:""};function ia(a){return ca.call(this,(""+a).toLowerCase(),!1)};const ka={},G={};function la(a){I(a,"add");I(a,"append");I(a,"search");I(a,"update");I(a,"remove")}function I(a,b){a[b+"Async"]=function(){const c=this,d=arguments;var e=d[d.length-1];let f;D(e)&&(f=e,delete d[d.length-1]);e=new Promise(function(g){setTimeout(function(){c.async=!0;const h=c[b].apply(c,d);c.async=!1;g(h)})});return f?(e.then(f),this):e}};function ma(a,b,c,d){const e=a.length;let f=[],g,h,k=0;d&&(d=[]);for(let m=e-1;0<=m;m--){const n=a[m],w=n.length,q=v();let r=!g;for(let l=0;l<w;l++){const p=n[l],z=p.length;if(z)for(let B=0,A,y;B<z;B++)if(y=p[B],g){if(g[y]){if(!m)if(c)c--;else if(f[k++]=y,k===b)return f;if(m||d)q[y]=1;r=!0}if(d&&(A=(h[y]||0)+1,h[y]=A,A<e)){const H=d[A-2]||(d[A-2]=[]);H[H.length]=y}}else q[y]=1}if(d)g||(h=q);else if(!r)return[];g=q}if(d)for(let m=d.length-1,n,w;0<=m;m--){n=d[m];w=n.length;for(let q=0,r;q<w;q++)if(r=
n[q],!g[r]){if(c)c--;else if(f[k++]=r,k===b)return f;g[r]=1}}return f}function na(a,b){const c=v(),d=v(),e=[];for(let f=0;f<a.length;f++)c[a[f]]=1;for(let f=0,g;f<b.length;f++){g=b[f];for(let h=0,k;h<g.length;h++)k=g[h],c[k]&&!d[k]&&(d[k]=1,e[e.length]=k)}return e};function J(a){this.l=!0!==a&&a;this.cache=v();this.h=[]}function oa(a,b,c){C(a)&&(a=a.query);let d=this.cache.get(a);d||(d=this.search(a,b,c),this.cache.set(a,d));return d}J.prototype.set=function(a,b){if(!this.cache[a]){var c=this.h.length;c===this.l?delete this.cache[this.h[c-1]]:c++;for(--c;0<c;c--)this.h[c]=this.h[c-1];this.h[0]=a}this.cache[a]=b};J.prototype.get=function(a){const b=this.cache[a];if(this.l&&b&&(a=this.h.indexOf(a))){const c=this.h[a-1];this.h[a-1]=this.h[a];this.h[a]=c}return b};const qa={memory:{charset:"latin:extra",D:3,B:4,m:!1},performance:{D:3,B:3,s:!1,context:{depth:2,D:1}},match:{charset:"latin:extra",G:"reverse"},score:{charset:"latin:advanced",D:20,B:3,context:{depth:3,D:9}},"default":{}};function ra(a,b,c,d,e,f,g){setTimeout(function(){const h=a(c?c+"."+d:d,JSON.stringify(g));h&&h.then?h.then(function(){b.export(a,b,c,e,f+1)}):b.export(a,b,c,e,f+1)})};function K(a,b){if(!(this instanceof K))return new K(a);var c;if(a){x(a)?a=qa[a]:(c=a.preset)&&(a=Object.assign({},c[c],a));c=a.charset;var d=a.lang;x(c)&&(-1===c.indexOf(":")&&(c+=":default"),c=G[c]);x(d)&&(d=ka[d])}else a={};let e,f,g=a.context||{};this.encode=a.encode||c&&c.encode||ia;this.register=b||v();this.D=e=a.resolution||9;this.G=b=c&&c.G||a.tokenize||"strict";this.depth="strict"===b&&g.depth;this.l=u(g.bidirectional);this.s=f=u(a.optimize);this.m=u(a.fastupdate);this.B=a.minlength||1;this.C=
a.boost;this.map=f?aa(e):v();this.A=e=g.resolution||1;this.h=f?aa(e):v();this.F=c&&c.F||a.rtl;this.H=(b=a.matcher||d&&d.H)&&fa(b,!1);this.J=(b=a.stemmer||d&&d.J)&&fa(b,!0);if(c=b=a.filter||d&&d.filter){c=b;d=v();for(let h=0,k=c.length;h<k;h++)d[c[h]]=1;c=d}this.filter=c;this.cache=(b=a.cache)&&new J(b)}t=K.prototype;t.append=function(a,b){return this.add(a,b,!0)};
t.add=function(a,b,c,d){if(b&&(a||0===a)){if(!d&&!c&&this.register[a])return this.update(a,b);b=this.encode(b);if(d=b.length){const m=v(),n=v(),w=this.depth,q=this.D;for(let r=0;r<d;r++){let l=b[this.F?d-1-r:r];var e=l.length;if(l&&e>=this.B&&(w||!n[l])){var f=L(q,d,r),g="";switch(this.G){case "full":if(2<e){for(f=0;f<e;f++)for(var h=e;h>f;h--)if(h-f>=this.B){var k=L(q,d,r,e,f);g=l.substring(f,h);M(this,n,g,k,a,c)}break}case "reverse":if(1<e){for(h=e-1;0<h;h--)g=l[h]+g,g.length>=this.B&&M(this,n,
g,L(q,d,r,e,h),a,c);g=""}case "forward":if(1<e){for(h=0;h<e;h++)g+=l[h],g.length>=this.B&&M(this,n,g,f,a,c);break}default:if(this.C&&(f=Math.min(f/this.C(b,l,r)|0,q-1)),M(this,n,l,f,a,c),w&&1<d&&r<d-1)for(e=v(),g=this.A,f=l,h=Math.min(w+1,d-r),e[f]=1,k=1;k<h;k++)if((l=b[this.F?d-1-r-k:r+k])&&l.length>=this.B&&!e[l]){e[l]=1;const p=this.l&&l>f;M(this,m,p?f:l,L(g+(d/2>g?0:1),d,r,h-1,k-1),a,c,p?l:f)}}}}this.m||(this.register[a]=1)}}return this};
function L(a,b,c,d,e){return c&&1<a?b+(d||0)<=a?c+(e||0):(a-1)/(b+(d||0))*(c+(e||0))+1|0:0}function M(a,b,c,d,e,f,g){let h=g?a.h:a.map;if(!b[c]||g&&!b[c][g])a.s&&(h=h[d]),g?(b=b[c]||(b[c]=v()),b[g]=1,h=h[g]||(h[g]=v())):b[c]=1,h=h[c]||(h[c]=[]),a.s||(h=h[d]||(h[d]=[])),f&&h.includes(e)||(h[h.length]=e,a.m&&(a=a.register[e]||(a.register[e]=[]),a[a.length]=h))}
t.search=function(a,b,c){c||(!b&&C(a)?(c=a,a=c.query):C(b)&&(c=b));let d=[],e;let f,g=0;if(c){a=c.query||a;b=c.limit;g=c.offset||0;var h=c.context;f=c.suggest}if(a&&(a=this.encode(""+a),e=a.length,1<e)){c=v();var k=[];for(let n=0,w=0,q;n<e;n++)if((q=a[n])&&q.length>=this.B&&!c[q])if(this.s||f||this.map[q])k[w++]=q,c[q]=1;else return d;a=k;e=a.length}if(!e)return d;b||(b=100);h=this.depth&&1<e&&!1!==h;c=0;let m;h?(m=a[0],c=1):1<e&&a.sort(ba);for(let n,w;c<e;c++){w=a[c];h?(n=sa(this,d,f,b,g,2===e,w,
m),f&&!1===n&&d.length||(m=w)):n=sa(this,d,f,b,g,1===e,w);if(n)return n;if(f&&c===e-1){k=d.length;if(!k){if(h){h=0;c=-1;continue}return d}if(1===k)return ta(d[0],b,g)}}return ma(d,b,g,f)};
function sa(a,b,c,d,e,f,g,h){let k=[],m=h?a.h:a.map;a.s||(m=ua(m,g,h,a.l));if(m){let n=0;const w=Math.min(m.length,h?a.A:a.D);for(let q=0,r=0,l,p;q<w;q++)if(l=m[q])if(a.s&&(l=ua(l,g,h,a.l)),e&&l&&f&&(p=l.length,p<=e?(e-=p,l=null):(l=l.slice(e),e=0)),l&&(k[n++]=l,f&&(r+=l.length,r>=d)))break;if(n){if(f)return ta(k,d,0);b[b.length]=k;return}}return!c&&k}function ta(a,b,c){a=1===a.length?a[0]:[].concat.apply([],a);return c||a.length>b?a.slice(c,c+b):a}
function ua(a,b,c,d){c?(d=d&&b>c,a=(a=a[d?b:c])&&a[d?c:b]):a=a[b];return a}t.contain=function(a){return!!this.register[a]};t.update=function(a,b){return this.remove(a).add(a,b)};
t.remove=function(a,b){const c=this.register[a];if(c){if(this.m)for(let d=0,e;d<c.length;d++)e=c[d],e.splice(e.indexOf(a),1);else N(this.map,a,this.D,this.s),this.depth&&N(this.h,a,this.A,this.s);b||delete this.register[a];if(this.cache){b=this.cache;for(let d=0,e,f;d<b.h.length;d++)f=b.h[d],e=b.cache[f],e.includes(a)&&(b.h.splice(d--,1),delete b.cache[f])}}return this};
function N(a,b,c,d,e){let f=0;if(a.constructor===Array)if(e)b=a.indexOf(b),-1!==b?1<a.length&&(a.splice(b,1),f++):f++;else{e=Math.min(a.length,c);for(let g=0,h;g<e;g++)if(h=a[g])f=N(h,b,c,d,e),d||f||delete a[g]}else for(let g in a)(f=N(a[g],b,c,d,e))||delete a[g];return f}t.searchCache=oa;
t.export=function(a,b,c,d,e){let f,g;switch(e||(e=0)){case 0:f="reg";if(this.m){g=v();for(let h in this.register)g[h]=1}else g=this.register;break;case 1:f="cfg";g={doc:0,opt:this.s?1:0};break;case 2:f="map";g=this.map;break;case 3:f="ctx";g=this.h;break;default:return}ra(a,b||this,c,f,d,e,g);return!0};t.import=function(a,b){if(b)switch(x(b)&&(b=JSON.parse(b)),a){case "cfg":this.s=!!b.opt;break;case "reg":this.m=!1;this.register=b;break;case "map":this.map=b;break;case "ctx":this.h=b}};la(K.prototype);function va(a){a=a.data;var b=self._index;const c=a.args;var d=a.task;switch(d){case "init":d=a.options||{};a=a.factory;b=d.encode;d.cache=!1;b&&0===b.indexOf("function")&&(d.encode=Function("return "+b)());a?(Function("return "+a)()(self),self._index=new self.FlexSearch.Index(d),delete self.FlexSearch):self._index=new K(d);break;default:a=a.id,b=b[d].apply(b,c),postMessage("search"===d?{id:a,msg:b}:{id:a})}};let wa=0;function O(a){if(!(this instanceof O))return new O(a);var b;a?D(b=a.encode)&&(a.encode=b.toString()):a={};(b=(self||window)._factory)&&(b=b.toString());const c="undefined"===typeof window&&self.exports,d=this;this.o=xa(b,c,a.worker);this.h=v();if(this.o){if(c)this.o.on("message",function(e){d.h[e.id](e.msg);delete d.h[e.id]});else this.o.onmessage=function(e){e=e.data;d.h[e.id](e.msg);delete d.h[e.id]};this.o.postMessage({task:"init",factory:b,options:a})}}P("add");P("append");P("search");
P("update");P("remove");function P(a){O.prototype[a]=O.prototype[a+"Async"]=function(){const b=this,c=[].slice.call(arguments);var d=c[c.length-1];let e;D(d)&&(e=d,c.splice(c.length-1,1));d=new Promise(function(f){setTimeout(function(){b.h[++wa]=f;b.o.postMessage({task:a,id:wa,args:c})})});return e?(d.then(e),this):d}}
function xa(a,b,c){let d;try{d=b?eval('new (require("worker_threads")["Worker"])("../dist/node/node.js")'):a?new Worker(URL.createObjectURL(new Blob(["onmessage="+va.toString()],{type:"text/javascript"}))):new Worker(x(c)?c:"worker/worker.js",{type:"module"})}catch(e){}return d};function Q(a){if(!(this instanceof Q))return new Q(a);var b=a.document||a.doc||a,c;this.K=[];this.h=[];this.A=[];this.register=v();this.key=(c=b.key||b.id)&&S(c,this.A)||"id";this.m=u(a.fastupdate);this.C=(c=b.store)&&!0!==c&&[];this.store=c&&v();this.I=(c=b.tag)&&S(c,this.A);this.l=c&&v();this.cache=(c=a.cache)&&new J(c);a.cache=!1;this.o=a.worker;this.async=!1;c=v();let d=b.index||b.field||b;x(d)&&(d=[d]);for(let e=0,f,g;e<d.length;e++)f=d[e],x(f)||(g=f,f=f.field),g=C(g)?Object.assign({},a,g):a,
this.o&&(c[f]=new O(g),c[f].o||(this.o=!1)),this.o||(c[f]=new K(g,this.register)),this.K[e]=S(f,this.A),this.h[e]=f;if(this.C)for(a=b.store,x(a)&&(a=[a]),b=0;b<a.length;b++)this.C[b]=S(a[b],this.A);this.index=c}function S(a,b){const c=a.split(":");let d=0;for(let e=0;e<c.length;e++)a=c[e],0<=a.indexOf("[]")&&(a=a.substring(0,a.length-2))&&(b[d]=!0),a&&(c[d++]=a);d<c.length&&(c.length=d);return 1<d?c:c[0]}function T(a,b){if(x(b))a=a[b];else for(let c=0;a&&c<b.length;c++)a=a[b[c]];return a}
function U(a,b,c,d,e){a=a[e];if(d===c.length-1)b[e]=a;else if(a)if(a.constructor===Array)for(b=b[e]=Array(a.length),e=0;e<a.length;e++)U(a,b,c,d,e);else b=b[e]||(b[e]=v()),e=c[++d],U(a,b,c,d,e)}function V(a,b,c,d,e,f,g,h){if(a=a[g])if(d===b.length-1){if(a.constructor===Array){if(c[d]){for(b=0;b<a.length;b++)e.add(f,a[b],!0,!0);return}a=a.join(" ")}e.add(f,a,h,!0)}else if(a.constructor===Array)for(g=0;g<a.length;g++)V(a,b,c,d,e,f,g,h);else g=b[++d],V(a,b,c,d,e,f,g,h)}t=Q.prototype;
t.add=function(a,b,c){C(a)&&(b=a,a=T(b,this.key));if(b&&(a||0===a)){if(!c&&this.register[a])return this.update(a,b);for(let d=0,e,f;d<this.h.length;d++)f=this.h[d],e=this.K[d],x(e)&&(e=[e]),V(b,e,this.A,0,this.index[f],a,e[0],c);if(this.I){let d=T(b,this.I),e=v();x(d)&&(d=[d]);for(let f=0,g,h;f<d.length;f++)if(g=d[f],!e[g]&&(e[g]=1,h=this.l[g]||(this.l[g]=[]),!c||!h.includes(a)))if(h[h.length]=a,this.m){const k=this.register[a]||(this.register[a]=[]);k[k.length]=h}}if(this.store&&(!c||!this.store[a])){let d;
if(this.C){d=v();for(let e=0,f;e<this.C.length;e++)f=this.C[e],x(f)?d[f]=b[f]:U(b,d,f,0,f[0])}this.store[a]=d||b}}return this};t.append=function(a,b){return this.add(a,b,!0)};t.update=function(a,b){return this.remove(a).add(a,b)};
t.remove=function(a){C(a)&&(a=T(a,this.key));if(this.register[a]){for(var b=0;b<this.h.length&&(this.index[this.h[b]].remove(a,!this.o),!this.m);b++);if(this.I&&!this.m)for(let c in this.l){b=this.l[c];const d=b.indexOf(a);-1!==d&&(1<b.length?b.splice(d,1):delete this.l[c])}this.store&&delete this.store[a];delete this.register[a]}return this};
t.search=function(a,b,c,d){c||(!b&&C(a)?(c=a,a=""):C(b)&&(c=b,b=0));let e=[],f=[],g,h,k,m,n,w,q=0;if(c)if(c.constructor===Array)k=c,c=null;else{a=c.query||a;k=(g=c.pluck)||c.index||c.field;m=c.tag;h=this.store&&c.enrich;n="and"===c.bool;b=c.limit||b||100;w=c.offset||0;if(m&&(x(m)&&(m=[m]),!a)){for(let l=0,p;l<m.length;l++)if(p=ya.call(this,m[l],b,w,h))e[e.length]=p,q++;return q?e:[]}x(k)&&(k=[k])}k||(k=this.h);n=n&&(1<k.length||m&&1<m.length);const r=!d&&(this.o||this.async)&&[];for(let l=0,p,z,B;l<
k.length;l++){let A;z=k[l];x(z)||(A=z,z=A.field,a=A.query||a,b=A.limit||b);if(r)r[l]=this.index[z].searchAsync(a,b,A||c);else{d?p=d[l]:p=this.index[z].search(a,b,A||c);B=p&&p.length;if(m&&B){const y=[];let H=0;n&&(y[0]=[p]);for(let X=0,pa,R;X<m.length;X++)if(pa=m[X],B=(R=this.l[pa])&&R.length)H++,y[y.length]=n?[R]:R;H&&(p=n?ma(y,b||100,w||0):na(p,y),B=p.length)}if(B)f[q]=z,e[q++]=p;else if(n)return[]}}if(r){const l=this;return new Promise(function(p){Promise.all(r).then(function(z){p(l.search(a,b,
c,z))})})}if(!q)return[];if(g&&(!h||!this.store))return e[0];for(let l=0,p;l<f.length;l++){p=e[l];p.length&&h&&(p=za.call(this,p));if(g)return p;e[l]={field:f[l],result:p}}return e};function ya(a,b,c,d){let e=this.l[a],f=e&&e.length-c;if(f&&0<f){if(f>b||c)e=e.slice(c,c+b);d&&(e=za.call(this,e));return{tag:a,result:e}}}function za(a){const b=Array(a.length);for(let c=0,d;c<a.length;c++)d=a[c],b[c]={id:d,doc:this.store[d]};return b}t.contain=function(a){return!!this.register[a]};t.get=function(a){return this.store[a]};
t.set=function(a,b){this.store[a]=b;return this};t.searchCache=oa;t.export=function(a,b,c,d,e){e||(e=0);d||(d=0);if(d<this.h.length){const f=this.h[d],g=this.index[f];b=this;setTimeout(function(){g.export(a,b,e?f:"",d,e++)||(d++,e=1,b.export(a,b,f,d,e))})}else{let f,g;switch(e){case 1:f="tag";g=this.l;break;case 2:f="store";g=this.store;break;default:return}ra(a,this,c,f,d,e,g)}};
t.import=function(a,b){if(b)switch(x(b)&&(b=JSON.parse(b)),a){case "tag":this.l=b;break;case "reg":this.m=!1;this.register=b;for(let d=0,e;d<this.h.length;d++)e=this.index[this.h[d]],e.register=b,e.m=!1;break;case "store":this.store=b;break;default:a=a.split(".");const c=a[0];a=a[1];c&&a&&this.index[c].import(a,b)}};la(Q.prototype);var Ba={encode:Aa,F:!1,G:""};const Ca=[F("[\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5]"),"a",F("[\u00e8\u00e9\u00ea\u00eb]"),"e",F("[\u00ec\u00ed\u00ee\u00ef]"),"i",F("[\u00f2\u00f3\u00f4\u00f5\u00f6\u0151]"),"o",F("[\u00f9\u00fa\u00fb\u00fc\u0171]"),"u",F("[\u00fd\u0177\u00ff]"),"y",F("\u00f1"),"n",F("[\u00e7c]"),"k",F("\u00df"),"s",F(" & ")," and "];function Aa(a){var b=a=""+a;b.normalize&&(b=b.normalize("NFD").replace(ea,""));return ca.call(this,b.toLowerCase(),!a.normalize&&Ca)};var Ea={encode:Da,F:!1,G:"strict"};const Fa=/[^a-z0-9]+/,Ga={b:"p",v:"f",w:"f",z:"s",x:"s","\u00df":"s",d:"t",n:"m",c:"k",g:"k",j:"k",q:"k",i:"e",y:"e",u:"o"};function Da(a){a=Aa.call(this,a).join(" ");const b=[];if(a){const c=a.split(Fa),d=c.length;for(let e=0,f,g=0;e<d;e++)if((a=c[e])&&(!this.filter||!this.filter[a])){f=a[0];let h=Ga[f]||f,k=h;for(let m=1;m<a.length;m++){f=a[m];const n=Ga[f]||f;n&&n!==k&&(h+=n,k=n)}b[g++]=h}}return b};var Ia={encode:Ha,F:!1,G:""};const Ja=[F("ae"),"a",F("oe"),"o",F("sh"),"s",F("th"),"t",F("ph"),"f",F("pf"),"f",F("(?![aeo])h(?![aeo])"),"",F("(?!^[aeo])h(?!^[aeo])"),""];function Ha(a,b){a&&(a=Da.call(this,a).join(" "),2<a.length&&(a=E(a,Ja)),b||(1<a.length&&(a=ha(a)),a&&(a=a.split(" "))));return a||[]};var La={encode:Ka,F:!1,G:""};const Ma=F("(?!\\b)[aeo]");function Ka(a){a&&(a=Ha.call(this,a,!0),1<a.length&&(a=a.replace(Ma,"")),1<a.length&&(a=ha(a)),a&&(a=a.split(" ")));return a||[]};G["latin:default"]=ja;G["latin:simple"]=Ba;G["latin:balance"]=Ea;G["latin:advanced"]=Ia;G["latin:extra"]=La;const W=self;let Y;const Z={Index:K,Document:Q,Worker:O,registerCharset:function(a,b){G[a]=b},registerLanguage:function(a,b){ka[a]=b}};(Y=W.define)&&Y.amd?Y([],function(){return Z}):W.exports?W.exports=Z:W.FlexSearch=Z;}(this));

View File

@@ -1 +0,0 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){r.d(o,{default:function(){return d}});var e=r(771),t=r.n(e);const n=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},i=/^\\begin{/;var a=function(e,t){let r;const o=[],a=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;r=e.search(a),-1!==r;){r>0&&(o.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const a=t.findIndex((t=>e.startsWith(t.left)));if(r=n(t[a].right,e,t[a].left.length),-1===r)break;const l=e.slice(0,r+t[a].right.length),s=i.test(l)?l:e.slice(t[a].left.length,r);o.push({type:"math",data:s,rawData:l,display:t[a].display}),e=e.slice(r+t[a].right.length)}return""!==e&&o.push({type:"text",data:e}),o};const l=function(e,n){const r=a(e,n.delimiters);if(1===r.length&&"text"===r[0].type)return null;const o=document.createDocumentFragment();for(let e=0;e<r.length;e++)if("text"===r[e].type)o.appendChild(document.createTextNode(r[e].data));else{const i=document.createElement("span");let a=r[e].data;n.displayMode=r[e].display;try{n.preProcess&&(a=n.preProcess(a)),t().render(a,i,n)}catch(i){if(!(i instanceof t().ParseError))throw i;n.errorCallback("KaTeX auto-render: Failed to parse `"+r[e].data+"` with ",i),o.appendChild(document.createTextNode(r[e].rawData));continue}o.appendChild(i)}return o},s=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const s=l(o,t);if(s){for(let e=0;e<a;e++)r.nextSibling.remove();n+=s.childNodes.length-1,e.replaceChild(s,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&s(r,t)}}};var d=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},s(e,n)}}(),o=o.default}()}));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

55
build.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
set -e
# Specify the base URL
BASE_URL=${1:-"http://localhost:1313"}
echo "Using base URL: $BASE_URL"
# Version configuration - modify these arrays to specify versions to build
# Format: "ref:display_name" (ref can be tag, branch, or commit hash, display name is what will appear in URL)
MAIN_VERSION="v0.9.6:latest"
VERSIONS=(
"main:latest" # latest version always builds from main
"v0.9.6:v0.9"
"v0.8.6:v0.8"
)
# Parse main version
IFS=':' read -r MAIN_REF MAIN_NAME <<< "$MAIN_VERSION"
# Ensure clean public directory
rm -rf public
mkdir -p public
mkdir -p public/versions
# Checkout and build main site
git checkout $MAIN_REF
GIT_HASH=$(git rev-parse --short HEAD)
echo "Building main site from $MAIN_REF (commit: $GIT_HASH)"
hugo \
--minify \
--themesDir=../.. --source=exampleSite \
--baseURL "$BASE_URL/" \
--destination=../public
# Build all versions
for VERSION in "${VERSIONS[@]}"; do
IFS=':' read -r REF NAME <<< "$VERSION"
git checkout $REF
GIT_HASH=$(git rev-parse --short HEAD)
echo "Building version $NAME from $REF (commit: $GIT_HASH)"
mkdir -p "public/versions/$NAME"
hugo \
--minify \
--themesDir=../.. --source=exampleSite \
--baseURL "$BASE_URL/versions/$NAME/" \
--destination="../public/versions/$NAME"
done
# Return to main branch
git checkout main
echo "Build completed"

View File

@@ -289,3 +289,5 @@ youtube: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill=
x-twitter: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg> x-twitter: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg>
linkedin: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg> linkedin: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
slack: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52zm1.271 0a2.527 2.527 0 0 1 2.521-2.52a2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52zm0 1.271a2.528 2.528 0 0 1 2.521 2.521a2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521zm10.122 2.521a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522zm-1.268 0a2.528 2.528 0 0 1-2.523 2.521a2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522zm-2.523 10.122a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522zm0-1.268a2.527 2.527 0 0 1-2.52-2.523a2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523z" /></svg> slack: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52zm1.271 0a2.527 2.527 0 0 1 2.521-2.52a2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52zm0 1.271a2.528 2.528 0 0 1 2.521 2.521a2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521zm10.122 2.521a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522zm-1.268 0a2.528 2.528 0 0 1-2.523 2.521a2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522zm-2.523 10.122a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522zm0-1.268a2.527 2.527 0 0 1-2.52-2.523a2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523z" /></svg>
bluesky: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 530"><path fill="currentColor" d="M136 44c66 50 138 151 164 205 26-54 98-155 164-205 48-36 126-64 126 25 0 18-10 149-16 170-21 74-96 93-163 81 117 20 147 86 82 153-122 125-176-32-189-72-3-8-4-11-4-8 0-3-1 0-4 8-13 40-67 197-189 72-65-67-35-133 82-153-67 12-142-7-163-81-6-21-16-152-16-170 0-89 78-61 126-25z"/></svg>
telegram: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M248 8C111.033 8 0 119.033 0 256s111.033 248 248 248 248-111.033 248-248S384.967 8 248 8m114.952 168.66c-3.732 39.215-19.881 134.378-28.1 178.3-3.476 18.584-10.322 24.816-16.948 25.425-14.4 1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25 5.342-39.5 3.652-3.793 67.107-61.51 68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608 69.142-14.845 10.194-26.894 9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7 18.45-13.7 108.446-47.248 144.628-62.3c68.872-28.647 83.183-33.623 92.511-33.789 2.052-.034 6.639.474 9.61 2.885a10.45 10.45 0 0 1 3.53 6.716 43.8 43.8 0 0 1 .417 9.769"/></svg>

Some files were not shown because too many files have changed in this diff Show More