From b76d1000b6bce844f36fcad44820a82bec28a58e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Thu, 14 Nov 2024 10:57:32 +0100 Subject: [PATCH 1/2] book: pin to mdbook v0.4.40 for now --- .github/workflows/rust.yml | 14 ++++++++++---- book/update-theme.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2e85f1ec..6a6b95c1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -110,10 +110,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: taiki-e/install-action@v2 - with: - tool: mdbook - - run: mdbook build book + - name: Generate "book/theme/index.hbs" as "skeleton" of the generated pages. + run: ./update-theme.py + working-directory: book + - name: Install mdbook + run: | + mkdir -p $HOME/bin + curl --location --silent --show-error --fail https://github.com/cargo-bins/cargo-quickinstall/releases/download/mdbook-0.4.40/mdbook-0.4.40-x86_64-unknown-linux-gnu.tar.gz | tar -xzvvf - -C $HOME/bin + - name: Convert the book to HTML + run: $HOME/bin/mdbook build + working-directory: book DevSkim: name: DevSkim diff --git a/book/update-theme.py b/book/update-theme.py index 8c127e32..54b4a08b 100755 --- a/book/update-theme.py +++ b/book/update-theme.py @@ -12,7 +12,7 @@ INDEX_HBS_DOMAIN = "api.github.com" INDEX_HBS_PORT = 443 INDEX_HBS_PROTO = "GET" -INDEX_HBS_PATH = "/repos/rust-lang/mdBook/contents/src/theme/index.hbs" +INDEX_HBS_PATH = "/repos/rust-lang/mdBook/contents/src/theme/index.hbs?ref=v0.4.40" INDEX_HBS_DATA = None INDEX_HBS_HEADERS = { "user-agent": "Update index.hbs for +https://github.com/rinja-rs/rinja", From a9e0132b286cf24a47b965544041f82050e2ee16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Thu, 14 Nov 2024 11:06:40 +0100 Subject: [PATCH 2/2] book: add toc --- book/book.toml | 2 + book/src/filters.md | 36 ----------- book/theme/.gitignore | 2 +- book/theme/LICENSE.md | 7 ++ book/theme/pagetoc.css | 109 +++++++++++++++++++++++++++++++ book/theme/pagetoc.js | 143 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 262 insertions(+), 37 deletions(-) create mode 100644 book/theme/LICENSE.md create mode 100644 book/theme/pagetoc.css create mode 100644 book/theme/pagetoc.js diff --git a/book/book.toml b/book/book.toml index 3f1ccf17..332b42d2 100644 --- a/book/book.toml +++ b/book/book.toml @@ -11,3 +11,5 @@ edition = "2021" [output.html] git-repository-url = "https://github.com/rinja-rs/rinja/tree/master/book" edit-url-template = "https://github.com/rinja-rs/rinja/tree/master/book/{path}" +additional-css = ["theme/pagetoc.css"] +additional-js = ["theme/pagetoc.js"] diff --git a/book/src/filters.md b/book/src/filters.md index 0c8d2182..632efcba 100644 --- a/book/src/filters.md +++ b/book/src/filters.md @@ -17,42 +17,6 @@ Rinja has a collection of built-in filters, documented below, but can also inclu Additionally, the `json` filter is included in the built-in filters, but is disabled by default. Enable it with Cargo features (see below for more information). -**Table of contents** - -* **[Built-in filters][#built-in-filters]:** - - * [`capitalize`][#capitalize] - * [`center`][#center] - * [`deref`][#deref] - * [`escape|e`][#escape] - * [`filesizeformat`][#filesizeformat] - * [`fmt`][#fmt] - * [`format`][#format] - * [`indent`][#indent] - * [`join`][#join] - * [`linebreaks`][#linebreaks] - * [`linebreaksbr`][#linebreaksbr] - * [`lower|lowercase`][#lower] - * [`paragraphbreaks`][#paragraphbreaks] - * [`pluralize`][#pluralize] - * [`ref`][#ref] - * [`safe`][#safe] - * [`title`][#title] - * [`trim`][#trim] - * [`truncate`][#truncate] - * [`upper|uppercase`][#upper] - * [`urlencode`][#urlencode] - * [`wordcount`][#wordcount] - -* **[Optional / feature gated filters][#optional-filters]:** - [`json|tojson`][#json], - -* **[Custom filters][#custom-filters]** - -* **[HTML-safe types][#html-safe-types]** - - * **[Safe output of custom filters][#safe-output-of-custom-filters]** - ## Built-In Filters [#built-in-filters]: #built-in-filters diff --git a/book/theme/.gitignore b/book/theme/.gitignore index 72e8ffc0..982ce4d1 100644 --- a/book/theme/.gitignore +++ b/book/theme/.gitignore @@ -1 +1 @@ -* +index.hbs diff --git a/book/theme/LICENSE.md b/book/theme/LICENSE.md new file mode 100644 index 00000000..0687dd46 --- /dev/null +++ b/book/theme/LICENSE.md @@ -0,0 +1,7 @@ +`pagetoc.css` and `pagetoc.js` where copied from the [Trunk] project in revision [4c9d85f6f04a31a272c71db8df12e142c76311fb]. +Author: Jens Reimann ([@ctron]) and the Trunk developers. +SPDX-License-Identifier: MIT/Apache-2.0. + +[Trunk]: +[@ctron]: +[4c9d85f6f04a31a272c71db8df12e142c76311fb]: diff --git a/book/theme/pagetoc.css b/book/theme/pagetoc.css new file mode 100644 index 00000000..6222d0e0 --- /dev/null +++ b/book/theme/pagetoc.css @@ -0,0 +1,109 @@ +main { + display: none; +} + +:root { + --toc-width: 270px; + --center-content-toc-shift: calc(-1 * var(--toc-width) / 2); +} + +.nav-chapters { + /* adjust width of buttons that bring to the previous or the next page */ + min-width: 50px; +} + +.previous { + /* + adjust the space between the left sidebar or the left side of the screen + and the button that leads to the previous page + */ + margin-left: var(--page-padding); +} + +@media only screen { + main.wrapped { + display: flex; + } + + @media (max-width: 1179px) { + .sidebar-hidden .sidetoc { + display: none; + } + } + + @media (max-width: 1439px) { + .sidebar-visible .sidetoc { + display: none; + } + } + + @media (1180px <= width <= 1439px) { + .sidebar-hidden main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + @media (1440px <= width <= 1700px) { + .sidebar-visible main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + .content-wrap { + overflow-y: auto; + width: 100%; + } + + .sidetoc { + margin-top: 20px; + margin-left: 10px; + margin-right: auto; + } + .pagetoc { + position: fixed; + /* adjust TOC width */ + width: var(--toc-width); + height: calc(100vh - var(--menu-bar-height) - 0.67em * 4); + overflow: auto; + } + .pagetoc a { + border-left: 1px solid var(--sidebar-bg); + color: var(--fg) !important; + display: block; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 10px; + text-align: left; + text-decoration: none; + } + .pagetoc a:hover, + .pagetoc a.active { + background: var(--sidebar-bg); + color: var(--sidebar-fg) !important; + } + .pagetoc .active { + background: var(--sidebar-bg); + color: var(--sidebar-fg); + font-weight: bold; + } + .pagetoc .pagetoc-H2 { + padding-left: 20px; + font-size: 90%; + } + .pagetoc .pagetoc-H3 { + padding-left: 40px; + font-size: 90%; + } + .pagetoc .pagetoc-H4 { + padding-left: 60px; + font-size: 90%; + } +} + +@media print { + .sidetoc { + display: none; + } +} diff --git a/book/theme/pagetoc.js b/book/theme/pagetoc.js new file mode 100644 index 00000000..27dad021 --- /dev/null +++ b/book/theme/pagetoc.js @@ -0,0 +1,143 @@ +function forEach(elems, fun) { + Array.prototype.forEach.call(elems, fun); +} + +function getPagetoc(){ + const pagetoc = document.getElementsByClassName("pagetoc")[0]; + + if (pagetoc) { + return pagetoc; + } + + return autoCreatePagetoc(); +} + +function autoCreatePagetoc() { + const main = document.querySelector("#content > main"); + + const content = document.createElement("div"); + content.classList.add("content-wrap"); + content.append(...main.childNodes); + + main.appendChild(content); + main.classList.add("wrapped"); + + main.insertAdjacentHTML("beforeend", ` +
+ +
+ `); + + return document.getElementsByClassName("pagetoc")[0] +} + +function getPagetocElems() { + return getPagetoc().children; +} + +function getHeaders(){ + return document.getElementsByClassName("header") +} + +// Un-active everything when you click it +function forPagetocElem(fun) { + forEach(getPagetocElems(), fun); +} + +function getRect(element) { + return element.getBoundingClientRect(); +} + +function overflowTop(container, element) { + return getRect(container).top - getRect(element).top; +} + +function overflowBottom(container, element) { + return getRect(container).bottom - getRect(element).bottom; +} + +var activeHref = location.href; + +var updateFunction = function (elem = undefined) { + var id = elem; + + if (!id && location.href != activeHref) { + activeHref = location.href; + forPagetocElem(function (el) { + if (el.href === activeHref) { + id = el; + } + }); + } + + if (!id) { + var elements = getHeaders(); + let margin = window.innerHeight / 3; + + forEach(elements, function (el, i, arr) { + if (!id && getRect(el).top >= 0) { + if (getRect(el).top < margin) { + id = el; + } else { + id = arr[Math.max(0, i - 1)]; + } + } + // a very long last section + // its heading is over the screen + if (!id && i == arr.length - 1) { + id = el + } + }); + } + + forPagetocElem(function (el) { + el.classList.remove("active"); + }); + + if (!id) return; + + forPagetocElem(function (el) { + if (id.href.localeCompare(el.href) == 0) { + el.classList.add("active"); + let pagetoc = getPagetoc(); + if (overflowTop(pagetoc, el) > 0) { + pagetoc.scrollTop = el.offsetTop; + } + if (overflowBottom(pagetoc, el) < 0) { + pagetoc.scrollTop -= overflowBottom(pagetoc, el); + } + } + }); +}; + +let elements = getHeaders(); + +if (elements.length > 1) { + // Populate sidebar on load + window.addEventListener("load", function () { + var pagetoc = getPagetoc(); + var elements = getHeaders(); + forEach(elements, function (el) { + var link = document.createElement("a"); + link.appendChild(document.createTextNode(el.text)); + link.href = el.hash; + link.classList.add("pagetoc-" + el.parentElement.tagName); + pagetoc.appendChild(link); + link.onclick = function () { + updateFunction(link); + }; + }); + updateFunction(); + }); + + // Handle active elements on scroll + window.addEventListener("scroll", function () { + updateFunction(); + }); +} else { + getPagetoc(); + const sidetoc = document.getElementsByClassName("sidetoc"); + if (sidetoc.length > 0 ) { + document.getElementsByClassName("sidetoc")[0].remove(); + } +}