Github messages for voidlinux
 help / color / mirror / Atom feed
* [PR PATCH] [RFC] Create a version of Manual.md in mdBook
@ 2022-02-26  2:49 classabbyamp
  2022-03-06  3:06 ` [PR PATCH] [Updated] " classabbyamp
                   ` (25 more replies)
  0 siblings, 26 replies; 27+ messages in thread
From: classabbyamp @ 2022-02-26  2:49 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1092 bytes --]

There is a new pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to set up some CI to check and deploy the generated book, as well as some tweaks in the area of theming.

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 32936 bytes --]

From c5c39afae060aa8d4e2dfb17c369eaf3828c4ed8 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/2] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 40b2321f7a25..5cb2e1d6c1f2 100644
--- a/Manual.md
+++ b/Manual.md
@@ -814,10 +814,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -825,7 +825,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1909,7 +1909,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 322e4fe5cb1dce4a3700c9991abcd1a36e1e633a Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/2] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 104 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 853 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..2e77c40f55b1
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("../Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
@ 2022-03-06  3:06 ` classabbyamp
  2022-03-15 19:17 ` classabbyamp
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-03-06  3:06 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1097 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to set up some CI to check and deploy the generated book, as well as some tweaks in the area of theming.

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 32936 bytes --]

From bdac568707de31fa8524805ff79bf34e712aa88f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/2] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 40b2321f7a25..5cb2e1d6c1f2 100644
--- a/Manual.md
+++ b/Manual.md
@@ -814,10 +814,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -825,7 +825,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1909,7 +1909,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 723d2db58a1a2c382cf0c50dbec163d3c8e0ead0 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/2] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 104 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 853 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..2e77c40f55b1
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("../Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
  2022-03-06  3:06 ` [PR PATCH] [Updated] " classabbyamp
@ 2022-03-15 19:17 ` classabbyamp
  2022-04-01 19:24 ` classabbyamp
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-03-15 19:17 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1097 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to set up some CI to check and deploy the generated book, as well as some tweaks in the area of theming.

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 32936 bytes --]

From 62b93c649e0597b206dcefc6d6634bf42271ee87 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/2] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 40b2321f7a25..5cb2e1d6c1f2 100644
--- a/Manual.md
+++ b/Manual.md
@@ -814,10 +814,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -825,7 +825,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1909,7 +1909,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 7a20bb7704547a589047e3eb07d36d255a4d322c Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/2] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 104 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 853 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..2e77c40f55b1
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("../Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
zcmV;Z7FX$sP)<h;3K|Lk000e1NJLTq0055w0055&1^@s6!()h(00004b3#c}2nYxW
zd<bNS00009a7bBm000SL000SL0ncLY;Q#;t8FWQhbW?9;ba!ELWdL_~cP?peYja~^
zaAhuUa%Y?FJQ@H179~kUK~#90?VWjaRn@)6Kfir$5@wasC%UX+TPJX;sMQt%v63Kl
zs3iBkqM{)R*sj(N+FFa1gF~s+zGZ8DidF#!^xYfkQ<O<+NP^W?v<^77z9O|aA{xdd
z_nzPT<0hDp>E`C_b8aA?wO9+1dw$<v;LASy?DO0EC+vkhT=nq2DJOL>!Xc_U0yr4h
z7uZKt`yuofWC-9iU6NFEiy-wVn-DeuA0fO$ihm|0x!!r-*%*t(QX&0Fp&(%=U=vqe
zGxDjChmm5m7&!rDG;lm=qxj8E;1%E{;3b23w*S`tYbQ;bRBu5mC)nf6^HA+WhewD^
zRMj(puK`0sOs8JawSv?rx?=Ny&3~SG;>^Yn^YN8lZ=R~Es>nxqA59>|nFtesFNU2{
zC)6qW1j--rcr;cN+ZuL4J)zf{C;n9YcuC2Hz!YG3*!g6^4n>y=a^JAju*ZvvijrX`
z)LnX&dG3Ga{vrJu`dzH(G~k%9^YM|5qB371a#!isOWzJVnM~;w=2^6A(f)aPdEY^}
z4A?L1d@SMtOV!g~#>U5<4m+8y$Z7M$YvLmf<|m4t2jqvHPmri+6~Z;KF|jpa=h6{5
zX`TnyJa}Lv6}b+f957+$6EZ7}XRa<CU;1p=iR?~JnP<t`B?ETu*!g{wS-{}1^T{C;
zs5FlGNiNNaa>hKB)s+{i=v?4{uye^_wyEgc&HXq3Iv3W9LO0Lin#Bircy|D2hn-7r
zK}FX&;!H0cQ~FGpd4y&jRh2|dqC!M|1MCx49=(_p!Yu>044f6#E09pk<Em>$)sFfj
za84NddlHJi2%HxyjJ+DPb|_CWuDG6x-a-0hVjn#~kmCedTUk>%J$UU9NmM6_5gq~h
zg_*~mf*|*K5pQPfYq1@{=!HzYHa<~Q9;VMgzM$x{DU<rvlrd8_+SUr)JkU1+Szx26
zoLM%m>~A(ja$@y1Ixf0WMbD*g4&)aHs_5$Y>i7hkA~|6mfY{jBa+GuEGmu{l5hIUP
z)>M|-637|zKzan~Gmx+3EBZf`)s+|72%Zyjp55u2MEj1Xs#9a*VvGC?nv>>%Pz=-n
zya#+py2@{-AW0yPbiw=}U?AywqJaVB<R?i%qGg3;|La%4oHY+vGtds;FCy}ys;(1}
zm%XI-c65C7eG$=&@~VmtmxvsR#}OiOxT41arvd|f%+m(-M$DO|V@p^05iF<81HKqY
zfoD;csH3adkH5x7#TtA}5rFtB@qGDEzKX+XC`F`qZshr`;{$c7Iwm$Q_Hs`H<-B<y
zO9Lru5$-i9^KfZV={xqaYoB;c{PRZ46!ml}!ihoacZMMUYBY_f^mHMySC|JfHqbUf
z?nSxF_5xXVh}XnN3Nj6tLAd+KK0{f%)!RCDTG6zupKN=Ld7#sk=q)Jo)zi7LqS$+3
z<dzAKta)T`eX71fkgE{(4?7=0?kg)SyD<Cfdl|cpKsykAQ=eCV(*>toP#1P?nG&yx
z55nO)g8UpiTc0yVkQrr#Wp`zFZ5Ft$Yh2g$vboj|nhbQl8l4pz6Wb709z7*q6(25+
z+ytCYc>j@iD0*Tx^+4GqEG2IF5*6mHoV{R47T5Q&8;h$K7p6?=Hc>e`41E<{w=KEt
zr0&jty8U{hWKP9*LB1&}mBn+)&+6{=eYB-?TxktM84cV*dTAk0MCIr~kwMpY`?~IY
zc%oZALXnsFY+Fo&ik9__K>iW0j-M?e^MRoOOMB|+nC|RU?as&}irh)-{Z#pVBapwu
z#>E~@rjnz9zXmLA#K^pPYv*OWu$i%WirtFoKzsEe`Jxgpamy!W8mo`anNl=my+(9A
z@Myr&#|+&*^omSBoY8IT>>Dcv*YD83L#JyKeYY^5iO1uP442!1AdB0#tI?w}d5X<w
z<Z0O9{hZE?1AVtJ--*Rysj{(UGg0OQC_BJ-CZox7mm|*^?j;9#q~0L&H^zM<kOdMo
ziC+R&1th1&GhglMaD^^M9wqrJx>st@Hv(CpY;4&q5xF}cIZ2t+bzOa^Q}dKuTXCEs
zQ?d~6n?(DJh^Y6jHxuEHf#@M6i>ntGcJ`UhM;=dq+T~|DrXR>7eIt;6#A30OI(h*C
zJ;r<D&F<{`9nN`*ub=h>N%9swFKFtU1NlsRP5fZ-<ayw8wzWj$l(Mm9e`)V~9gZrJ
z<Z}F}gzK9F`Alp~Y(vwjY1Ut5sOk?p`d+&u&o}3uw_mcpU<2u*Z@=gpfqbU2w(=HL
z{efkfq;brb+dGl0-H|6*UoahijRSp?Xy5UMdOrnhAFUG6q_(?A+0kuls-;BwCec0;
z8x?E7<5Dc&zErfLs;Vlo``6o;XM$UCB4D$wBp)^BS&Y9|5gi|`6_E!m%N(#NvgwT7
zzu3k+4rE=^nj!U$le*fn#$H83eZ!A|Pi$(aYMZ-Js|oXX*NqhL_B(X_9xMAKVD3tH
z{?Bd2?<rMPRgw41dk5rsc>}$OHxOZ~)97qVeVBSTm-}fYY7<wY{K}%lc1`L2Et{>h
zx{u}a_Czci2l%uB{k6sT-DYvs;)7BqRU#rq2%|sJkG>colCO>;Dxlyw9x_}S5;ci`
z2=bhYRwX65B8UT&GDVYmn>ca>FwBy~0CD6@;GyO(v@(x@v+OP$$Zf0L`BsP5OsJ}=
zifoE(imK`*9^QCC(zox<fB1Z0q@p8HrbaZ9O4KC&gz$$~#=NrFmGCwM0K|%7TNAa3
z-=nw|WhT?YJgw$D3MCeWw=0dmwHUukR8<nSi3>M5n_gGdMZjrf|N6xNic#XDYDc}A
zs7ZX=zf7}39laIUVo{>FWu@V+InNpHw2@xA&7mdat`+V*Gwj9hoXV=oBQ>ITqKvgD
z^Q5M9W~?ao4~rU|lBiAGjB>d(0guAy_-HM(_+RIVw0RWsSCcz!#_x<oZQ??8^jXWs
z0XR(@c|Kkp4{a%;sd(NVhKZm3wi#8Mx2(TC;oh^>V*GYdRY_DQT$F|Qzhd4IgGJ<l
zM0LXElTU|~jVXIY(dR8n>?+eR0MU5VA$=r>9Yhyei`@aLDwVaB^AI{ME3k%eWo1p}
zt>H+^eHO!?v~=mx0%$T%-RlF6#NKIFno{p!OR+m-aqZ$83AS*&4T^p*QJrvu)^!|b
z5n!t;PQlgzTTg)I+m`2ywj>0wc9nZ?p!>-yt1B;3^d~`Tc8D-LULF6>ASEY%eewqg
z_B!GSC&R8ekM|X8LhzWy*ll>I`k|v#^tK>1yFx_nj910K9H^#Bm95yum}6j<dBony
zRYtA-5WAzQ;)ruQxp&~?5FBm~Qc%U--@B*YvGCc`mX$Ws3Omfcg9F<(ySQfYg}}I=
z^tyu}r&m^2#)8rtxMkqez;+w41sUb4YfOoI@lfnFOZ)4x%We&%kytz)cT_Z|-`1<q
zIj*{a9Gu?Nz|B^B?EyzTb;NLE42M_}K;Q*ivD;|vFW&|NomX@l<;bIJk3KgjIT2ZB
zD|RFm8DTv24z?u#SWCVQ!X<%e^#E19I4CVoy*Dg|Kh!8{%{&GT#60me@q=lx@|n{J
z6Beyn6v$zgj_15?E4HFvFh<3ic?8zmirorjB01KCr4u5Nf=F=?LaF@Jo3>($kzqzr
zTg5|-Co|#$OR-xaDn&u)_=Qn(S`a$XQ==QP^~9y}IRj#=U&6)!PfWGBLhd_?o)Cmi
zQ!foG#dnGhGgxc3As^X^+nT20q!B@A`N&~GJ+ce3wd+r1#Q-DL%%g#H*d10s>_Bp}
zK}cuxKm1RJe?AByMXgj8Dq3JXdu@J#Y0umsgS-Az?h$b!LFH|$EH&SVwPtAQdt0J*
zRYdy-q2o8kF+ug_+GZoDsN@-eq?O=EApfYM637VWH(tuK@)Wc?iWXQ2EbK6vZjb0Y
z+WNd^Yb5iMy{2JFOOmOi&0P+=5KB+fif%JlG7kZnXLG;J?_#fN&K5h~{KlIf2BM^P
z=Cp!rH=tJTi9wA#Gf$k^D9As9(DDmK-wbS%pzt4-B5yMwn`}fDdu1MNQ1rQ=wERF2
zo4Xr!5s$|m>}?<4W-Kn|4VvVZ54RM%6@tBzOB)FCWFSHZF`V?yR{v2p8St!(JOCaX
zX)AV18ntnGTCP`+G>%yrh?2*l)?ynr7%<j4|9Y6iZN+ZMlyOu3rJ~hAX!*d3$zPuw
z)C=QdZN*mAw~Zp}Z3ze*VJr6T7)(}uGJ{4%gF3|>)J|=BQ|t|fh-dLhMXfo1cf5DK
zIPmwN^s+&a*Ipg->LUS4+WY%&V1wD`@U0txtb1#B!Pla0G~ZI}R>Well!|uQFCk=p
z5!54k{Lk_JfUPZ6n`0AVpPEE0kpi)Gw90@wGw`dHVz(hWCVD@xIw-yFpy;Zy!m`DI
z2{v*n=>w^JM@3(NU8S4)k_{06RRXD+gLLte8od;-Rl|}kpE}~q3{udDv9^D$d7Y|x
z$EY>)5O9fTY)tG875#3Inq4AHFCAO@e?dxmc;{FPuB?S7^C)U<SEs<&zj4DQf$SD&
zCN?&9A22sa&5lrXc64ks5u|3kI)11i$JvT6D$hZad7Pnxp2OaxIg;0y3iOl+Xlibz
z6545GP23tQj9nYJs!(PzdfmFhb#FnFd6r#vYrP;g-w)YpN6;Z6qNRnUmj>LQ^D~qi
zqlMASLeYlvtwmOzYJOsDQH1T)+SB92Gu&w-ZN={h5z*+ls4K`+f}2Ed0j5O9MSm8M
zUMu3O<EPrZWhuI%`S-ibBSx0n5=@IS&1U@0C@U<xUyYs!Sbc?|=&F>H`bxAg`cT)O
zwu2b4dm5U<E}!f=O1G(Xz(?3CrKpP()Q@=lszsYE#qSbTl|)Tqiio(tVSdFG<TaJx
zC&s(8Xw{<q^YZil30Nrs7G!-{VcD*it-D5^)vlWaU1dYGi6P1Qg6~_5-z6fVv2n3Q
z`ko%8s^3O<0?4+XL<(h@AZ6>ut~)wx;{fF6=U<K`<213X<sVu#E-7)#&jWeDqWH%J
z{bj_XSKbpy{ca{axaPqF9nUE?nA4heavnzeE3@?~`gc)z4&lkh`o<OKoqpc?0jJv`
z@ju6hN>bKiv4T_4Gh>CZ$D9A$s`o@a10Kz1#}2XQ-k}XUxfHm`Qv7by)O=(?)30>(
z-9Ze;V-U(94{wW#Zgn;~@8@#Y8%fGl*e(72VDo^@t?H24^cXF1%PT=jEhbS%Ufz)_
zX5IO&MUB0Rcy;_x5qS-;v&|;WEz!d0k6M1A?S5&odH^)Q5E_#=TGrUBFk<Fn*EkqV
z8(*|BPrV-UIAFPtluj*~Q_-uAMagAU^&afKApm%#w6L^I(S95Aq@TL5+x!AR_12(j
zefD&s<Dx57^jvHiNZa>AyT^Pu5px%owjsA*R2>8RZeKnT8yj1Wat{6*XiLP4v|Bpc
zu6dSTefQfy0EJ8PgNbu00y`aaPbWGqy3%h033A7|Md!AAKvahZ40*`SekITeqN?{4
zUw4VUEh2jvJ{zd9Q5!q#+|!|Xn)+h}v6*gPNhKeO{#UeD9+i@d_-LR-Q^rmCS4ZFM
z^k_5Bc)#eOc!tQyb#;T{<6Sq>^=JFY1Ro~Ro|AGqz58@(o+qwZ_!RI&mg3#z%zWOS
zPdf{jePl-uC(#Qh7ftT;9PKU-RTQJM=&EV7PY7I4>`uEO2(7(^EN@{ulA7$YLt>Za
zS$@reXBBL|ENz9rRmJYK-_kb%`9^mQB*;yj&2^f|=s4H>Na|-;I@_*21THIar!5#g
zZ$@uB<uqsMhJilVnZNTlnZ(S<Jda&{@4r<7-;HqLuy-3P`<}x5Ba=zAh+K8SDHmjP
zRA0siN$;auZ&G+U)6e_F*>!b;swcYTBLddlW9ecN{bcEw(u`kCWo({=n`jWsC1hWD
zmn|nL^5Vof<<Wq3_gFf(g>5rprZ=@5$cS!FN=w{nHv^Y<_c|X?;CDmV`qPA)2;_|C
zJ)J~#q8Q;3pr0V$DJv|yqdV7kyZ?b1I%pOFcJOO~06(Z>@XHh2ia=Tp?dj|qfs|#X
zV@v0C_xh|JhfA)#Xq0;9S&QYWU7><5GH}IGcforh<<Mg$U4QW*$)tC^pbxBY7g*i;
z)ETb2CYz&QvR+FmnNvQU@VE1Ris;;ee&)Y(si?jyPXE=6eaz0rD+PW;x_!e=&~w8y
zP&V{vA+E%ob{FJCZ_hfA+d+Q4!oB<Bu#@SCv)mcO8cBXn;WE(ucKddNik7XMy<kZw
z$z?&0%ro9C?^l4-04MpBPfytb+==n#uefHRtplsN&BSZVPg2iJ2R)z8yA1WJ5va$n
z04BH<2O9Jl;0r$F<0sD}dar8g!IkcPft)_tl1bNJd`L3oO%~-s;P`G_+G`Bt%lo5A
z*OniX@?;G##E*DBlLVd?<Vi7F#lC}{S$5g2^?r+tcgy?b<D4vb<3Y{_?Mds(USS~L
zUOpDjDW4!JOR&5J#0RzuJfrAKVBS#l&HPB}jmNIO_urXZ?7FUbI&b6t+Sqs~$kBp#
zEa_7zPQu@xJ?9PN>znIhxBOdzB-m{yYA<0s@CoUjoLdC_RFMYI2<drBKZX4z-OC}U
zpD(8k<a^me(|-5+=skVTbDf+uknc?`-dg>_%U>FQ{F{QDV<Y}vNCYJIrK00sd~5X!
zZ-$*rORN=K6}!{U6i8sLhv{BN&aDGkt=21XD^3SpOm04f^+s~)7G}NFv*g-~M&X&I
zz`?e)_7ZYt1hPJ<U2)CAS3KlYA${<3A3Y{tRN^IW`NXgj3FL|B3HP3jGIY=wfv{hs
z?kV&Gd8Bww`B`Bm5%>;;k~!tmRpqxKmUHY$ggOTbd@W|h>;-o@=~g~32CcmZ(huZP
z*L6c#Qwwcx)EU3~eu4M#)*FBy(zhOz4WA(Vc*X4b_Rmu4fY3MAopJ5OU-3Ne55Ngw
z=hAzS<r>i$E3cm4<;Fa#S%fLx%;<SD@;?7w;}xpR0tSYiPp+cyZv#JD?#{nI>|7v6
z%+t&Sx8gttovrEW#J+c9pRo<(_XYjAA(zT+bILr;q$}^_I-n%%TtX%Va*u;|O)eJt
z=cIX>ndnxG0iB}+*J?6Hs8{rU;O3R?g4e@NrXzCNJk3mS%a1phAA?Szch(AJN}a+2
z2j`|`?jPO>JDINN73OJX()AY~l1h5hNw;4*FzkGM<Zl8C^83sDT$<B#MXxeXGtqd|
z`Si7clSIu_&~MWDj)WXV-vwFZ7+tvZngu~0AmIzW(mX9GzG><}wmD}BW-{mmVBfHF
z>V$uQt`LzWgOA#}D&5n*_tNXl(~|M78!0gBPsNiGU>xb!AYm4xeFCgOWVtb^<;#At
z@U0MY@tr-&Ji8NhqxoCR;L)ny$*LS9$g#jE?A+!)23}NjEh5i}n&(!ycfUnI=Q)PK
z9&MfukO^+Z0hrXGj%N<_kb?vrCNNClbEHSA0U%A=Xf#QBC+aq!Uf>f&HVfVc57_`_
my~EB8%iIStI!$X&;{O3r3)blbBiM}q0000<MNUMnLSTX!m`rj2

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
  2022-03-06  3:06 ` [PR PATCH] [Updated] " classabbyamp
  2022-03-15 19:17 ` classabbyamp
@ 2022-04-01 19:24 ` classabbyamp
  2022-04-01 19:26 ` classabbyamp
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-01 19:24 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1097 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to set up some CI to check and deploy the generated book, as well as some tweaks in the area of theming.

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 35526 bytes --]

From 62b93c649e0597b206dcefc6d6634bf42271ee87 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 40b2321f7a25..5cb2e1d6c1f2 100644
--- a/Manual.md
+++ b/Manual.md
@@ -814,10 +814,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -825,7 +825,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1909,7 +1909,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 190a257370b4c04abde9e4d661ab009ea83f07c9 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 2f8d54d6fdec3322c476ca91bd7db4dfc530a945 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yaml | 25 +++++++++++++++++++++++++
 manual/book.toml              |  6 +++++-
 manual/ci/format.sh           | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yaml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yaml b/.github/workflows/manual.yaml
new file mode 100644
index 000000000000..89da21bfa29c
--- /dev/null
+++ b/.github/workflows/manual.yaml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (2 preceding siblings ...)
  2022-04-01 19:24 ` classabbyamp
@ 2022-04-01 19:26 ` classabbyamp
  2022-04-01 19:29 ` classabbyamp
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-01 19:26 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1097 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to set up some CI to check and deploy the generated book, as well as some tweaks in the area of theming.

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 35526 bytes --]

From 22c5dd2c365b0582adacbc19879fe3bc91f6579e Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index af5f698debae..80c825087930 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1910,7 +1910,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From cae8f800d3a4537226822e0f59fc8e0c977c555f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From cfa82687592309ce43e24867251a67946e978671 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yaml | 25 +++++++++++++++++++++++++
 manual/book.toml              |  6 +++++-
 manual/ci/format.sh           | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yaml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yaml b/.github/workflows/manual.yaml
new file mode 100644
index 000000000000..89da21bfa29c
--- /dev/null
+++ b/.github/workflows/manual.yaml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (3 preceding siblings ...)
  2022-04-01 19:26 ` classabbyamp
@ 2022-04-01 19:29 ` classabbyamp
  2022-04-01 19:30 ` classabbyamp
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-01 19:29 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1110 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to set up some CI to check and deploy the generated book, as well as some tweaks in the area of theming.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 35526 bytes --]

From 22c5dd2c365b0582adacbc19879fe3bc91f6579e Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index af5f698debae..80c825087930 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1910,7 +1910,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From cae8f800d3a4537226822e0f59fc8e0c977c555f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 269d90b652ea81af0907d9b3b09e1db869cb8a0b Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..f102790c5b8c
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdbook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (4 preceding siblings ...)
  2022-04-01 19:29 ` classabbyamp
@ 2022-04-01 19:30 ` classabbyamp
  2022-04-02  5:07 ` classabbyamp
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-01 19:30 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1110 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to set up some CI to check and deploy the generated book, as well as some tweaks in the area of theming.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 35526 bytes --]

From 22c5dd2c365b0582adacbc19879fe3bc91f6579e Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index af5f698debae..80c825087930 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1910,7 +1910,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From cae8f800d3a4537226822e0f59fc8e0c977c555f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 286f12e02c5ec30fa11420f0777d69b8aa402419 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (5 preceding siblings ...)
  2022-04-01 19:30 ` classabbyamp
@ 2022-04-02  5:07 ` classabbyamp
  2022-04-02 17:50 ` classabbyamp
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-02  5:07 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 1074 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

Note that this is not fully complete, but is a decent working prototype. There is still a little work to be done to deploy the generated book and some tweaks in the area of theming.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 69574 bytes --]

From 22c5dd2c365b0582adacbc19879fe3bc91f6579e Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index af5f698debae..80c825087930 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1910,7 +1910,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From cae8f800d3a4537226822e0f59fc8e0c977c555f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 286f12e02c5ec30fa11420f0777d69b8aa402419 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From 1f44e4070f4c8eac87009486e1f71221f11bb28e Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 177 +++--------
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 246 ++++++++++-----
 6 files changed, 829 insertions(+), 204 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..8d52e7b5073c 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,7 +263,7 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
@@ -323,97 +336,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..27401b487870 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,33 +1,72 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
@@ -39,7 +78,35 @@
 							</svg>
 						</button>
 					</li>
-				{{#if search_enabled}}
+					<li>
+						<button id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</button>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
 						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
@@ -47,10 +114,10 @@
 							</svg>
 						</button>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					{{/if}}
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (6 preceding siblings ...)
  2022-04-02  5:07 ` classabbyamp
@ 2022-04-02 17:50 ` classabbyamp
  2022-04-02 21:40 ` classabbyamp
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-02 17:50 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 945 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 69574 bytes --]

From 98686d2bfd9a8181320f82c1b4bc8a5263b76d7d Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8ec755499bbe..a3d3adb99cea 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1916,7 +1916,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From a1ed33eaad81e5f2a9d1dca30e15c7028893c925 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 05e3e18936c852755f150d6396d24ae7da295f99 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From 9119d2adf3b830d08f1bfe841f19b8eedbf33117 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 177 +++--------
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 246 ++++++++++-----
 6 files changed, 829 insertions(+), 204 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..8d52e7b5073c 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,7 +263,7 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
@@ -323,97 +336,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..27401b487870 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,33 +1,72 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
@@ -39,7 +78,35 @@
 							</svg>
 						</button>
 					</li>
-				{{#if search_enabled}}
+					<li>
+						<button id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</button>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
 						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
@@ -47,10 +114,10 @@
 							</svg>
 						</button>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					{{/if}}
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (7 preceding siblings ...)
  2022-04-02 17:50 ` classabbyamp
@ 2022-04-02 21:40 ` classabbyamp
  2022-04-07 17:40 ` classabbyamp
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-02 21:40 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 945 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 69574 bytes --]

From 6f8f13b48dfefd93a26e7e0840aba84e1dc3f141 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8ec755499bbe..a3d3adb99cea 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1916,7 +1916,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From f2083ed9177ec94195691fc96f9b5989f776ff98 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From b99ad3b9a590592371ce7dbc8b2ffa53a6ad5ca3 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From 572b3884ddbf2ac998fb4d53f87690e59aedb37f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 177 +++--------
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 246 ++++++++++-----
 6 files changed, 829 insertions(+), 204 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..8d52e7b5073c 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,7 +263,7 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
@@ -323,97 +336,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..27401b487870 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,33 +1,72 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
@@ -39,7 +78,35 @@
 							</svg>
 						</button>
 					</li>
-				{{#if search_enabled}}
+					<li>
+						<button id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</button>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
 						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
@@ -47,10 +114,10 @@
 							</svg>
 						</button>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					{{/if}}
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (8 preceding siblings ...)
  2022-04-02 21:40 ` classabbyamp
@ 2022-04-07 17:40 ` classabbyamp
  2022-04-19 15:28 ` classabbyamp
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-07 17:40 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 945 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 71757 bytes --]

From e061cc81aad955a539a1777ab10672928a0c112f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8ec755499bbe..a3d3adb99cea 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1916,7 +1916,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 31c46ed11215baa4bc305ad996c19547e3a21d62 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 5d447a50ee6910facc3fbc76c94ad7c88104bf4c Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From 7519edcbbc5f7fe9fbc43499bd7a289b9898198f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 183 ++++-------
 manual/src/theme/css/print.css     |  27 +-
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 254 ++++++++++-----
 7 files changed, 865 insertions(+), 209 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..3d682f8d972f 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,13 +263,19 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
 	margin: 0 auto;
 	width: 1.5em;
 }
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
 
 /* layout */
 
@@ -323,97 +342,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
index 717ccb80ed2c..2970c2b6c7ab 100644
--- a/manual/src/theme/css/print.css
+++ b/manual/src/theme/css/print.css
@@ -1,6 +1,7 @@
 
 #sidebar,
 #menu-bar,
+#void-nav,
 .nav-chapters,
 .mobile-nav-chapters {
     display: none;
@@ -23,13 +24,21 @@
 }
 
 code {
-    background-color: #666666;
+    background-color: #ddd;
     border-radius: 5px;
 
     /* Force background to be printed in Chrome */
     -webkit-print-color-adjust: exact;
 }
 
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
 pre > .buttons {
     z-index: 2;
 }
@@ -52,3 +61,19 @@ pre, code {
 svg {
     display: none !important;
 }
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..5679e803b1ba 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,56 +1,123 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
 					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
 					<li>
-						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
 							</svg>
-						</button>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
 					</li>
-				{{#if search_enabled}}
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
-						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
 							</svg>
-						</button>
+						</a>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] Create a version of Manual.md in mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (9 preceding siblings ...)
  2022-04-07 17:40 ` classabbyamp
@ 2022-04-19 15:28 ` classabbyamp
  2022-04-23  1:23 ` [PR PATCH] [Updated] [RFC] render Manual.md with mdBook classabbyamp
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-19 15:28 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 945 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] Create a version of Manual.md in mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 71757 bytes --]

From 8064ccae4a16999001ce7b1014611f71d1495b39 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8ec755499bbe..a3d3adb99cea 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1916,7 +1916,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From cd7a5c644c3f8b1709c396330294568bbf4d2a16 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..d8a5f5421625
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    manual = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in manual:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 6f35979b9bf9319fe683456f22183030ec613371 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From ae92ce0fd2bec6dab024ad1c9363bc7c3c348d65 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 183 ++++-------
 manual/src/theme/css/print.css     |  27 +-
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 254 ++++++++++-----
 7 files changed, 865 insertions(+), 209 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..3d682f8d972f 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,13 +263,19 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
 	margin: 0 auto;
 	width: 1.5em;
 }
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
 
 /* layout */
 
@@ -323,97 +342,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
index 717ccb80ed2c..2970c2b6c7ab 100644
--- a/manual/src/theme/css/print.css
+++ b/manual/src/theme/css/print.css
@@ -1,6 +1,7 @@
 
 #sidebar,
 #menu-bar,
+#void-nav,
 .nav-chapters,
 .mobile-nav-chapters {
     display: none;
@@ -23,13 +24,21 @@
 }
 
 code {
-    background-color: #666666;
+    background-color: #ddd;
     border-radius: 5px;
 
     /* Force background to be printed in Chrome */
     -webkit-print-color-adjust: exact;
 }
 
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
 pre > .buttons {
     z-index: 2;
 }
@@ -52,3 +61,19 @@ pre, code {
 svg {
     display: none !important;
 }
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..5679e803b1ba 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,56 +1,123 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
 					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
 					<li>
-						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
 							</svg>
-						</button>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
 					</li>
-				{{#if search_enabled}}
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
-						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
 							</svg>
-						</button>
+						</a>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (10 preceding siblings ...)
  2022-04-19 15:28 ` classabbyamp
@ 2022-04-23  1:23 ` classabbyamp
  2022-05-02  2:09 ` classabbyamp
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-04-23  1:23 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 71751 bytes --]

From affd0f2bcf3c7ff5053b396e945388dc58e42cc9 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8ec755499bbe..a3d3adb99cea 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1916,7 +1916,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 07ed583cc469456850d23a34dea866e7128572bb Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 1fe7716c1b38ac9050d58957abab3f7f4d6e1182 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..e47ec8fba57d
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From 9169ee2bb6749449fafa72e1c9d3e637fe72ac93 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 183 ++++-------
 manual/src/theme/css/print.css     |  27 +-
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 254 ++++++++++-----
 7 files changed, 865 insertions(+), 209 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..3d682f8d972f 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,13 +263,19 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
 	margin: 0 auto;
 	width: 1.5em;
 }
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
 
 /* layout */
 
@@ -323,97 +342,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
index 717ccb80ed2c..2970c2b6c7ab 100644
--- a/manual/src/theme/css/print.css
+++ b/manual/src/theme/css/print.css
@@ -1,6 +1,7 @@
 
 #sidebar,
 #menu-bar,
+#void-nav,
 .nav-chapters,
 .mobile-nav-chapters {
     display: none;
@@ -23,13 +24,21 @@
 }
 
 code {
-    background-color: #666666;
+    background-color: #ddd;
     border-radius: 5px;
 
     /* Force background to be printed in Chrome */
     -webkit-print-color-adjust: exact;
 }
 
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
 pre > .buttons {
     z-index: 2;
 }
@@ -52,3 +61,19 @@ pre, code {
 svg {
     display: none !important;
 }
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..5679e803b1ba 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,56 +1,123 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
 					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
 					<li>
-						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
 							</svg>
-						</button>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
 					</li>
-				{{#if search_enabled}}
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
-						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
 							</svg>
-						</button>
+						</a>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (11 preceding siblings ...)
  2022-04-23  1:23 ` [PR PATCH] [Updated] [RFC] render Manual.md with mdBook classabbyamp
@ 2022-05-02  2:09 ` classabbyamp
  2022-05-05 17:24 ` classabbyamp
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-05-02  2:09 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 71806 bytes --]

From f4801842258a31b84123b3bacc2c318f8e8eb6ad Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8ec755499bbe..a3d3adb99cea 100644
--- a/Manual.md
+++ b/Manual.md
@@ -815,10 +815,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -826,7 +826,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1916,7 +1916,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 80f14bd454477fd67a9d7ba1d77338159ee1c750 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From 2ac2afbcf356298b6b1f9c342a055aa03cbe0d48 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 24 ++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From d91c14a039c73a87572c6fb7dfa9cfee6643252c Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 183 ++++-------
 manual/src/theme/css/print.css     |  27 +-
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 254 ++++++++++-----
 7 files changed, 865 insertions(+), 209 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..3d682f8d972f 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,13 +263,19 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
 	margin: 0 auto;
 	width: 1.5em;
 }
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
 
 /* layout */
 
@@ -323,97 +342,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
index 717ccb80ed2c..2970c2b6c7ab 100644
--- a/manual/src/theme/css/print.css
+++ b/manual/src/theme/css/print.css
@@ -1,6 +1,7 @@
 
 #sidebar,
 #menu-bar,
+#void-nav,
 .nav-chapters,
 .mobile-nav-chapters {
     display: none;
@@ -23,13 +24,21 @@
 }
 
 code {
-    background-color: #666666;
+    background-color: #ddd;
     border-radius: 5px;
 
     /* Force background to be printed in Chrome */
     -webkit-print-color-adjust: exact;
 }
 
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
 pre > .buttons {
     z-index: 2;
 }
@@ -52,3 +61,19 @@ pre, code {
 svg {
     display: none !important;
 }
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..5679e803b1ba 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,56 +1,123 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
 					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
 					<li>
-						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
 							</svg>
-						</button>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
 					</li>
-				{{#if search_enabled}}
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
-						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
 							</svg>
-						</button>
+						</a>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (12 preceding siblings ...)
  2022-05-02  2:09 ` classabbyamp
@ 2022-05-05 17:24 ` classabbyamp
  2022-05-05 18:41 ` classabbyamp
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-05-05 17:24 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 71806 bytes --]

From c164863100b3da90d3a3f7d6e01f57453401822e Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/4] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8afe781f0ec1..b96193ef07e7 100644
--- a/Manual.md
+++ b/Manual.md
@@ -819,10 +819,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -830,7 +830,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1920,7 +1920,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 3fc267dcdd50d78e5cbed717cb4a1c1170f15e62 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/4] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                       |   1 +
 manual/book.toml                 |  13 +
 manual/generate_mdbook.py        | 107 ++++++++
 manual/src/SUMMARY.md            |   1 +
 manual/src/theme/book.js         | 104 ++++++++
 manual/src/theme/css/general.css | 419 +++++++++++++++++++++++++++++++
 manual/src/theme/css/print.css   |  54 ++++
 manual/src/theme/favicon.png     | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs       | 157 ++++++++++++
 9 files changed, 856 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..5ef5064d8928
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,13 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..853247bfc4c9
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..e0255a8281b1
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,419 @@
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: #333;
+	margin: 0;
+	background-color: #ffffff;
+}
+h1, h2, h3, h4, h5, h6 { color: #333; }
+a {
+	color: #478061;
+	text-decoration: none;
+}
+a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+code {
+	background: #fdf6e3;
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: #fdf6e3;
+	border: 1px solid #ccc;
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: #ebf4ef;
+	border: 1px solid #d1e6da;
+	border-left: none;
+	border-right: none;
+}
+
+li.js-unavailable {
+        background-color: #f6cf68;
+        border-radius: 10px;
+        margin-left: 1em;
+        padding-left: 1em;
+        padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+}
+table td {
+	padding: 3px 20px;
+	border: 1px #fafafa solid;
+}
+table thead {
+	background: #fafafa;
+}
+table thead td {
+	font-weight: 700;
+	border: none;
+}
+table thead tr {
+}
+/* Alternate background colors for rows */
+table tbody tr:nth-child(2n) {
+	background: #fafafa;
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: #478061;
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav button,
+#void-nav label {
+	fill: #fff;
+	height: 50px;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: #fff;
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: #fafafa;
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: #000;
+	display: block;
+}
+#sidebar a:hover {
+	color: #478061;
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: #478061;
+}
+
+#sidebar-toggle {
+	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: #ccc;
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: #333
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
+
+@media (prefers-color-scheme: dark) {
+	body {
+		color: #ccc;
+		background-color: #222;
+	}
+	h1, h2, h3, h4, h5, h6 { color: #ccc; }
+	a {
+		color: #62b086;
+	}
+	a:hover {
+		color: #ccc;
+	}
+
+	code {
+		background: inherit;
+	}
+	pre {
+		background: #353535;
+		border: 1px solid #111;
+	}
+
+	blockquote {
+		background: inherit;
+		border-left: .2em solid #ccc;
+		border-right: none;
+		border-top: none;
+		border-bottom: none;
+		padding-top: .5em;
+		padding-bottom: .5em;
+		padding-left: 1em;
+		padding-right: 1em;
+		margin-left: 1em;
+	}
+	blockquote code {
+		color: #62b086;
+	}
+        li.js-unavailable {
+                background-color: #f6cf68;
+                color: #000000;
+                border-radius: 10px;
+                margin-left: 1em;
+                padding-left: 1em;
+                padding-right: 1em;
+        }
+	table td {
+		border: 1px #2c2c2c solid;
+	}
+	table thead {
+		background: #2c2c2c;
+	}
+	table tbody tr:nth-child(2n) {
+		background: #2c2c2c;
+	}
+
+	/* nav */
+	#void-nav ul li a:hover,
+	#void-nav ul li a:focus,
+	#void-nav button:hover,
+	#void-nav button:focus,
+	#void-nav label:hover,
+	#void-nav label:focus {
+		background: #222;
+	}
+
+	#void-nav {
+		background: #295340;
+	}
+
+	/* sidebar  */
+	#sidebar {
+		background: #252525;
+	}
+	#sidebar a {
+		color: #ccc;
+	}
+	#sidebar a:hover {
+		color: #62b086;
+	}
+	#sidebar a.active {
+		color: #62b086;
+	}
+
+	/* search */
+	#searchbar {
+		background-color: #222;
+		color: #ccc;
+	}
+
+	/* chapter navigation */
+	.nav-chapters:hover {
+		fill: #fff
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..717ccb80ed2c
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,54 @@
+
+#sidebar,
+#menu-bar,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #666666;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..239c9c5728a1
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js">
+    <head>
+        <!-- Book generated using mdBook -->
+        <meta charset="utf-8">
+        <title>{{ title }}</title>
+        <meta name="description" content="{{ description }}">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <meta name="theme-color" content="#ffffff" />
+
+        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
+        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+    </head>
+    <body>
+        <!-- Provide site root to javascript -->
+        <script type="text/javascript">
+            var path_to_root = "{{ path_to_root }}";
+        </script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</button>
+					</li>
+				{{#if search_enabled}}
+					<li>
+						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</button>
+					</li>
+				{{/if}}
+                                        <noscript>
+                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
+                                        </noscript>
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+			<script type="text/javascript">
+				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+	});
+			</script>
+
+			<div id="page-wrapper">
+				{{#if search_enabled}}
+				<div id="search-wrapper" class="hidden">
+					<form id="searchbar-outer" class="searchbar-outer">
+						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+					</form>
+					<div id="searchresults-outer" class="searchresults-outer hidden">
+						<div id="searchresults-header" class="searchresults-header"></div>
+						<ul id="searchresults">
+						</ul>
+					</div>
+				</div>
+				{{/if}}
+
+				<main id="main">
+					{{{ content }}}
+				</main>
+
+				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+        {{#if livereload}}
+        <!-- Livereload script (if served using the cli tool) -->
+        <script type="text/javascript">
+            var socket = new WebSocket("{{{livereload}}}");
+            socket.onmessage = function (event) {
+                if (event.data === "reload") {
+                    socket.close();
+                    location.reload(true); // force reload from server (not from cache)
+                }
+            };
+
+            window.onbeforeunload = function() {
+                socket.close();
+            }
+        </script>
+        {{/if}}
+
+        {{#if search_js}}
+        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+        {{/if}}
+
+        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+    </body>
+</html>

From fb5ba5e238a5aa0f013dec8ec85631242ab98f5c Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@kb6.ee>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/4] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 manual/book.toml             |  6 +++++-
 manual/ci/format.sh          | 24 ++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/manual/book.toml b/manual/book.toml
index 5ef5064d8928..4272409788e3 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,9 +5,13 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

From a4d1e69728ef0053439a651057d050059bf18c9f Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Sat, 2 Apr 2022 01:05:46 -0400
Subject: [PATCH 4/4] manual/src/theme: update theme

---
 manual/book.toml                   |   4 +
 manual/src/theme/book.js           |  15 +
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 183 ++++-------
 manual/src/theme/css/print.css     |  27 +-
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/index.hbs         | 254 ++++++++++-----
 7 files changed, 865 insertions(+), 209 deletions(-)
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/variables.css

diff --git a/manual/book.toml b/manual/book.toml
index 4272409788e3..4ae4312c2d2e 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -11,6 +11,10 @@ command = "python3 manual/generate_mdbook.py"
 # TODO: this should be a submodule or something
 # to avoid de-syncing with void-docs/src/theme
 theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
 
 [output.linkcheck]
 optional = true
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
index 853247bfc4c9..d1a280f9eca3 100644
--- a/manual/src/theme/book.js
+++ b/manual/src/theme/book.js
@@ -3,6 +3,21 @@
 // Fix back button cache problem
 window.onunload = function () { };
 
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
 (function sidebar() {
     var html = document.querySelector("html");
     var sidebar = document.getElementById("sidebar");
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
index e0255a8281b1..3d682f8d972f 100644
--- a/manual/src/theme/css/general.css
+++ b/manual/src/theme/css/general.css
@@ -1,23 +1,25 @@
+@import 'variables.css';
+
 body {
 	font-family: 'Ubuntu', sans-serif;
 	font-size: 1rem;
 	line-height: 1.5;
-	color: #333;
+	color: var(--fg);
 	margin: 0;
-	background-color: #ffffff;
+	background-color: var(--bg);
 }
-h1, h2, h3, h4, h5, h6 { color: #333; }
-a {
-	color: #478061;
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
 	text-decoration: none;
 }
-a:hover {
-	color: #333;
+a:hover, a:visited:hover {
+	color: var(--links-hover);
 	text-decoration: underline;
 }
 
 code {
-	background: #fdf6e3;
+	background: var(--inline-code-color);
 	padding: 2px 4px;
 	border-radius: 4px;
 	white-space: pre-wrap;
@@ -30,8 +32,8 @@ pre code {
 pre {
 	padding: .5em;
 	margin: 1em 0;
-	background: #fdf6e3;
-	border: 1px solid #ccc;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
 	border-radius: 4px;
 }
 
@@ -39,41 +41,43 @@ blockquote {
 	margin: 20px 0;
 	padding: 0 20px;
 	padding-left: 1em;
-	background: #ebf4ef;
-	border: 1px solid #d1e6da;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
 	border-left: none;
 	border-right: none;
 }
 
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
 li.js-unavailable {
-        background-color: #f6cf68;
-        border-radius: 10px;
-        margin-left: 1em;
-        padding-left: 1em;
-        padding-right: 1em;
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
 }
 
 table {
 	border-collapse: collapse;
 	display: block;
 	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
 }
 table td {
 	padding: 3px 20px;
-	border: 1px #fafafa solid;
 }
 table thead {
-	background: #fafafa;
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
 }
 table thead td {
 	font-weight: 700;
-	border: none;
-}
-table thead tr {
 }
-/* Alternate background colors for rows */
 table tbody tr:nth-child(2n) {
-	background: #fafafa;
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
 }
 
 svg {
@@ -99,17 +103,19 @@ svg {
 #void-nav {
 	width: 100%;
 	min-height: 50px;
-	background: #478061;
+	background: var(--nav-bg);
 	font-size: 14px;
 
 	display: flex;
 	flex-direction: row;
 	flex-wrap: wrap;
 }
+#void-nav a,
 #void-nav button,
 #void-nav label {
-	fill: #fff;
+	fill: var(--nav-fg);
 	height: 50px;
+	min-height: 100%;
 	display: block;
 	line-height: 50px;
 	padding: 0 15px;
@@ -129,7 +135,7 @@ svg {
 	display: inline-block;
 }
 #void-nav ul li a {
-	color: #fff;
+	color: var(--nav-fg);
 	display: block;
 	padding: 0 15px;
 	line-height: 50px;
@@ -158,6 +164,13 @@ svg {
 	top: 0;
 }
 
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
 /* sidebar  */
 
 .sidebar-hidden #sidebar {
@@ -165,7 +178,7 @@ svg {
 }
 #sidebar {
 	padding: .5em;
-	background: #fafafa;
+	background: var(--sidebar-bg);
 	font-size: 0.875em;
 }
 #sidebar ol {
@@ -181,19 +194,19 @@ svg {
 	line-height: 1.9em;
 }
 #sidebar a {
-	color: #000;
+	color: var(--sidebar-fg);
 	display: block;
 }
 #sidebar a:hover {
-	color: #478061;
+	color: var(--sidebar-active);
 	text-decoration: none;
 }
 #sidebar a.active {
-	color: #478061;
+	color: var(--sidebar-active);
 }
 
 #sidebar-toggle {
-	display: none;
+ 	display: none;
 }
 
 /* search */
@@ -203,7 +216,7 @@ svg {
 	padding: 10px 16px;
 	margin: 5px 0;
 	border-radius: 3px;
-	border: 1px solid #aaa;
+	border: 1px solid var(--searchbar-border-color);
 }
 #searchresults-header {
 	font-weight: bold;
@@ -241,7 +254,7 @@ ul#searchresults span.teaser {
 	right: 15px;
 }
 .nav-chapters {
-	fill: #ccc;
+	fill: var(--nav-arrow-fg);
 	text-align: center;
 	text-decoration: none;
 	display: block;
@@ -250,13 +263,19 @@ ul#searchresults span.teaser {
 }
 .nav-chapters:hover {
 	text-decoration: none;
-	fill: #333
+	fill: var(--nav-fg-hover);
 }
 
 .nav-chapters svg {
 	margin: 0 auto;
 	width: 1.5em;
 }
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
 
 /* layout */
 
@@ -323,97 +342,3 @@ body {
 		display: table;
 	}
 }
-
-@media (prefers-color-scheme: dark) {
-	body {
-		color: #ccc;
-		background-color: #222;
-	}
-	h1, h2, h3, h4, h5, h6 { color: #ccc; }
-	a {
-		color: #62b086;
-	}
-	a:hover {
-		color: #ccc;
-	}
-
-	code {
-		background: inherit;
-	}
-	pre {
-		background: #353535;
-		border: 1px solid #111;
-	}
-
-	blockquote {
-		background: inherit;
-		border-left: .2em solid #ccc;
-		border-right: none;
-		border-top: none;
-		border-bottom: none;
-		padding-top: .5em;
-		padding-bottom: .5em;
-		padding-left: 1em;
-		padding-right: 1em;
-		margin-left: 1em;
-	}
-	blockquote code {
-		color: #62b086;
-	}
-        li.js-unavailable {
-                background-color: #f6cf68;
-                color: #000000;
-                border-radius: 10px;
-                margin-left: 1em;
-                padding-left: 1em;
-                padding-right: 1em;
-        }
-	table td {
-		border: 1px #2c2c2c solid;
-	}
-	table thead {
-		background: #2c2c2c;
-	}
-	table tbody tr:nth-child(2n) {
-		background: #2c2c2c;
-	}
-
-	/* nav */
-	#void-nav ul li a:hover,
-	#void-nav ul li a:focus,
-	#void-nav button:hover,
-	#void-nav button:focus,
-	#void-nav label:hover,
-	#void-nav label:focus {
-		background: #222;
-	}
-
-	#void-nav {
-		background: #295340;
-	}
-
-	/* sidebar  */
-	#sidebar {
-		background: #252525;
-	}
-	#sidebar a {
-		color: #ccc;
-	}
-	#sidebar a:hover {
-		color: #62b086;
-	}
-	#sidebar a.active {
-		color: #62b086;
-	}
-
-	/* search */
-	#searchbar {
-		background-color: #222;
-		color: #ccc;
-	}
-
-	/* chapter navigation */
-	.nav-chapters:hover {
-		fill: #fff
-	}
-}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
index 717ccb80ed2c..2970c2b6c7ab 100644
--- a/manual/src/theme/css/print.css
+++ b/manual/src/theme/css/print.css
@@ -1,6 +1,7 @@
 
 #sidebar,
 #menu-bar,
+#void-nav,
 .nav-chapters,
 .mobile-nav-chapters {
     display: none;
@@ -23,13 +24,21 @@
 }
 
 code {
-    background-color: #666666;
+    background-color: #ddd;
     border-radius: 5px;
 
     /* Force background to be printed in Chrome */
     -webkit-print-color-adjust: exact;
 }
 
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
 pre > .buttons {
     z-index: 2;
 }
@@ -52,3 +61,19 @@ pre, code {
 svg {
     display: none !important;
 }
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
index 239c9c5728a1..5679e803b1ba 100644
--- a/manual/src/theme/index.hbs
+++ b/manual/src/theme/index.hbs
@@ -1,56 +1,123 @@
 <!DOCTYPE HTML>
-<html lang="{{ language }}" class="sidebar-visible no-js">
-    <head>
-        <!-- Book generated using mdBook -->
-        <meta charset="utf-8">
-        <title>{{ title }}</title>
-        <meta name="description" content="{{ description }}">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <meta name="theme-color" content="#ffffff" />
-
-        <link rel="shortcut icon" href="{{ path_to_root }}{{ favicon_png }}">
-        <link rel="stylesheet" href="{{ path_to_root }}css/general.css">
-        <link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
-    </head>
-    <body>
-        <!-- Provide site root to javascript -->
-        <script type="text/javascript">
-            var path_to_root = "{{ path_to_root }}";
-        </script>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
 		<!-- Work around some values being stored in localStorage wrapped in quotes -->
 		<script type="text/javascript">
 			try {
+				var theme = localStorage.getItem('mdbook-theme');
 				var sidebar = localStorage.getItem('mdbook-sidebar');
 
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
 				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
 					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
 				}
 			} catch (e) { }
 		</script>
 
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
 		<header>
 			<nav id="void-nav">
 				<ul>
 					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
 					<li>
-						<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
 							</svg>
-						</button>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
 					</li>
-				{{#if search_enabled}}
+					{{/if}}
+					{{#if search_enabled}}
 					<li>
-						<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
 							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
 								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
 							</svg>
-						</button>
+						</a>
 					</li>
-				{{/if}}
-                                        <noscript>
-                                          <li class="js-unavailable">Search functionality requires JavaScript</li>
-                                        </noscript>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
 				</ul>
 				<ul id="nav-right">
 					<li><a href="https://www.voidlinux.org">Home</a></li>
@@ -65,7 +132,6 @@
 		</header>
 
 		<div id="content">
-
 			<!-- Hide / unhide sidebar before it is displayed -->
 			<script type="text/javascript">
 				var html = document.querySelector('html');
@@ -82,36 +148,59 @@
 				{{#toc}}{{/toc}}
 			</nav>
 
-			<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
-			<script type="text/javascript">
-				document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
-	document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
-	Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
-		link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
-	});
-			</script>
+			<div id="page-wrapper" class="page-wrapper">
+
 
-			<div id="page-wrapper">
-				{{#if search_enabled}}
-				<div id="search-wrapper" class="hidden">
-					<form id="searchbar-outer" class="searchbar-outer">
-						<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
-					</form>
-					<div id="searchresults-outer" class="searchresults-outer hidden">
-						<div id="searchresults-header" class="searchresults-header"></div>
-						<ul id="searchresults">
-						</ul>
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
 					</div>
-				</div>
-				{{/if}}
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
 
-				<main id="main">
-					{{{ content }}}
-				</main>
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
 
-				<nav id="nav-wide-wrapper" aria-label="Page navigation">
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
 					{{#previous}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
 							</svg>
@@ -119,7 +208,7 @@
 					{{/previous}}
 
 					{{#next}}
-						<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
 							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
 								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
 							</svg>
@@ -129,29 +218,36 @@
 			</div>
 		</div>
 
-        {{#if livereload}}
-        <!-- Livereload script (if served using the cli tool) -->
-        <script type="text/javascript">
-            var socket = new WebSocket("{{{livereload}}}");
-            socket.onmessage = function (event) {
-                if (event.data === "reload") {
-                    socket.close();
-                    location.reload(true); // force reload from server (not from cache)
-                }
-            };
-
-            window.onbeforeunload = function() {
-                socket.close();
-            }
-        </script>
-        {{/if}}
-
-        {{#if search_js}}
-        <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
-        {{/if}}
-
-        <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
-    </body>
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
 </html>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (13 preceding siblings ...)
  2022-05-05 17:24 ` classabbyamp
@ 2022-05-05 18:41 ` classabbyamp
  2022-05-17 17:09 ` classabbyamp
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-05-05 18:41 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 53773 bytes --]

From 7bbeae6a19edc3be5bc2028d7d3dcc14538df14b Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8afe781f0ec1..b96193ef07e7 100644
--- a/Manual.md
+++ b/Manual.md
@@ -819,10 +819,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -830,7 +830,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1920,7 +1920,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 26ce74b5f9f82b21281a638f1434eb4469494fa5 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                         |   1 +
 manual/book.toml                   |  17 +
 manual/generate_mdbook.py          | 107 +++++++
 manual/src/SUMMARY.md              |   1 +
 manual/src/theme/book.js           | 119 +++++++
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 344 ++++++++++++++++++++
 manual/src/theme/css/print.css     |  79 +++++
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/favicon.png       | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs         | 253 +++++++++++++++
 11 files changed, 1512 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/css/variables.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..8f15412495eb
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,17 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..d1a280f9eca3
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,119 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..3d682f8d972f
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,344 @@
+@import 'variables.css';
+
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: var(--fg);
+	margin: 0;
+	background-color: var(--bg);
+}
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
+	text-decoration: none;
+}
+a:hover, a:visited:hover {
+	color: var(--links-hover);
+	text-decoration: underline;
+}
+
+code {
+	background: var(--inline-code-color);
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
+	border-left: none;
+	border-right: none;
+}
+
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
+li.js-unavailable {
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
+}
+table td {
+	padding: 3px 20px;
+}
+table thead {
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
+}
+table thead td {
+	font-weight: 700;
+}
+table tbody tr:nth-child(2n) {
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: var(--nav-bg);
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav a,
+#void-nav button,
+#void-nav label {
+	fill: var(--nav-fg);
+	height: 50px;
+	min-height: 100%;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: var(--nav-fg);
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: var(--sidebar-bg);
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: var(--sidebar-fg);
+	display: block;
+}
+#sidebar a:hover {
+	color: var(--sidebar-active);
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: var(--sidebar-active);
+}
+
+#sidebar-toggle {
+ 	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid var(--searchbar-border-color);
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: var(--nav-arrow-fg);
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: var(--nav-fg-hover);
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..2970c2b6c7ab
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,79 @@
+
+#sidebar,
+#menu-bar,
+#void-nav,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #ddd;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
zcmV;Z7FX$sP)<h;3K|Lk000e1NJLTq0055w0055&1^@s6!()h(00004b3#c}2nYxW
zd<bNS00009a7bBm000SL000SL0ncLY;Q#;t8FWQhbW?9;ba!ELWdL_~cP?peYja~^
zaAhuUa%Y?FJQ@H179~kUK~#90?VWjaRn@)6Kfir$5@wasC%UX+TPJX;sMQt%v63Kl
zs3iBkqM{)R*sj(N+FFa1gF~s+zGZ8DidF#!^xYfkQ<O<+NP^W?v<^77z9O|aA{xdd
z_nzPT<0hDp>E`C_b8aA?wO9+1dw$<v;LASy?DO0EC+vkhT=nq2DJOL>!Xc_U0yr4h
z7uZKt`yuofWC-9iU6NFEiy-wVn-DeuA0fO$ihm|0x!!r-*%*t(QX&0Fp&(%=U=vqe
zGxDjChmm5m7&!rDG;lm=qxj8E;1%E{;3b23w*S`tYbQ;bRBu5mC)nf6^HA+WhewD^
zRMj(puK`0sOs8JawSv?rx?=Ny&3~SG;>^Yn^YN8lZ=R~Es>nxqA59>|nFtesFNU2{
zC)6qW1j--rcr;cN+ZuL4J)zf{C;n9YcuC2Hz!YG3*!g6^4n>y=a^JAju*ZvvijrX`
z)LnX&dG3Ga{vrJu`dzH(G~k%9^YM|5qB371a#!isOWzJVnM~;w=2^6A(f)aPdEY^}
z4A?L1d@SMtOV!g~#>U5<4m+8y$Z7M$YvLmf<|m4t2jqvHPmri+6~Z;KF|jpa=h6{5
zX`TnyJa}Lv6}b+f957+$6EZ7}XRa<CU;1p=iR?~JnP<t`B?ETu*!g{wS-{}1^T{C;
zs5FlGNiNNaa>hKB)s+{i=v?4{uye^_wyEgc&HXq3Iv3W9LO0Lin#Bircy|D2hn-7r
zK}FX&;!H0cQ~FGpd4y&jRh2|dqC!M|1MCx49=(_p!Yu>044f6#E09pk<Em>$)sFfj
za84NddlHJi2%HxyjJ+DPb|_CWuDG6x-a-0hVjn#~kmCedTUk>%J$UU9NmM6_5gq~h
zg_*~mf*|*K5pQPfYq1@{=!HzYHa<~Q9;VMgzM$x{DU<rvlrd8_+SUr)JkU1+Szx26
zoLM%m>~A(ja$@y1Ixf0WMbD*g4&)aHs_5$Y>i7hkA~|6mfY{jBa+GuEGmu{l5hIUP
z)>M|-637|zKzan~Gmx+3EBZf`)s+|72%Zyjp55u2MEj1Xs#9a*VvGC?nv>>%Pz=-n
zya#+py2@{-AW0yPbiw=}U?AywqJaVB<R?i%qGg3;|La%4oHY+vGtds;FCy}ys;(1}
zm%XI-c65C7eG$=&@~VmtmxvsR#}OiOxT41arvd|f%+m(-M$DO|V@p^05iF<81HKqY
zfoD;csH3adkH5x7#TtA}5rFtB@qGDEzKX+XC`F`qZshr`;{$c7Iwm$Q_Hs`H<-B<y
zO9Lru5$-i9^KfZV={xqaYoB;c{PRZ46!ml}!ihoacZMMUYBY_f^mHMySC|JfHqbUf
z?nSxF_5xXVh}XnN3Nj6tLAd+KK0{f%)!RCDTG6zupKN=Ld7#sk=q)Jo)zi7LqS$+3
z<dzAKta)T`eX71fkgE{(4?7=0?kg)SyD<Cfdl|cpKsykAQ=eCV(*>toP#1P?nG&yx
z55nO)g8UpiTc0yVkQrr#Wp`zFZ5Ft$Yh2g$vboj|nhbQl8l4pz6Wb709z7*q6(25+
z+ytCYc>j@iD0*Tx^+4GqEG2IF5*6mHoV{R47T5Q&8;h$K7p6?=Hc>e`41E<{w=KEt
zr0&jty8U{hWKP9*LB1&}mBn+)&+6{=eYB-?TxktM84cV*dTAk0MCIr~kwMpY`?~IY
zc%oZALXnsFY+Fo&ik9__K>iW0j-M?e^MRoOOMB|+nC|RU?as&}irh)-{Z#pVBapwu
z#>E~@rjnz9zXmLA#K^pPYv*OWu$i%WirtFoKzsEe`Jxgpamy!W8mo`anNl=my+(9A
z@Myr&#|+&*^omSBoY8IT>>Dcv*YD83L#JyKeYY^5iO1uP442!1AdB0#tI?w}d5X<w
z<Z0O9{hZE?1AVtJ--*Rysj{(UGg0OQC_BJ-CZox7mm|*^?j;9#q~0L&H^zM<kOdMo
ziC+R&1th1&GhglMaD^^M9wqrJx>st@Hv(CpY;4&q5xF}cIZ2t+bzOa^Q}dKuTXCEs
zQ?d~6n?(DJh^Y6jHxuEHf#@M6i>ntGcJ`UhM;=dq+T~|DrXR>7eIt;6#A30OI(h*C
zJ;r<D&F<{`9nN`*ub=h>N%9swFKFtU1NlsRP5fZ-<ayw8wzWj$l(Mm9e`)V~9gZrJ
z<Z}F}gzK9F`Alp~Y(vwjY1Ut5sOk?p`d+&u&o}3uw_mcpU<2u*Z@=gpfqbU2w(=HL
z{efkfq;brb+dGl0-H|6*UoahijRSp?Xy5UMdOrnhAFUG6q_(?A+0kuls-;BwCec0;
z8x?E7<5Dc&zErfLs;Vlo``6o;XM$UCB4D$wBp)^BS&Y9|5gi|`6_E!m%N(#NvgwT7
zzu3k+4rE=^nj!U$le*fn#$H83eZ!A|Pi$(aYMZ-Js|oXX*NqhL_B(X_9xMAKVD3tH
z{?Bd2?<rMPRgw41dk5rsc>}$OHxOZ~)97qVeVBSTm-}fYY7<wY{K}%lc1`L2Et{>h
zx{u}a_Czci2l%uB{k6sT-DYvs;)7BqRU#rq2%|sJkG>colCO>;Dxlyw9x_}S5;ci`
z2=bhYRwX65B8UT&GDVYmn>ca>FwBy~0CD6@;GyO(v@(x@v+OP$$Zf0L`BsP5OsJ}=
zifoE(imK`*9^QCC(zox<fB1Z0q@p8HrbaZ9O4KC&gz$$~#=NrFmGCwM0K|%7TNAa3
z-=nw|WhT?YJgw$D3MCeWw=0dmwHUukR8<nSi3>M5n_gGdMZjrf|N6xNic#XDYDc}A
zs7ZX=zf7}39laIUVo{>FWu@V+InNpHw2@xA&7mdat`+V*Gwj9hoXV=oBQ>ITqKvgD
z^Q5M9W~?ao4~rU|lBiAGjB>d(0guAy_-HM(_+RIVw0RWsSCcz!#_x<oZQ??8^jXWs
z0XR(@c|Kkp4{a%;sd(NVhKZm3wi#8Mx2(TC;oh^>V*GYdRY_DQT$F|Qzhd4IgGJ<l
zM0LXElTU|~jVXIY(dR8n>?+eR0MU5VA$=r>9Yhyei`@aLDwVaB^AI{ME3k%eWo1p}
zt>H+^eHO!?v~=mx0%$T%-RlF6#NKIFno{p!OR+m-aqZ$83AS*&4T^p*QJrvu)^!|b
z5n!t;PQlgzTTg)I+m`2ywj>0wc9nZ?p!>-yt1B;3^d~`Tc8D-LULF6>ASEY%eewqg
z_B!GSC&R8ekM|X8LhzWy*ll>I`k|v#^tK>1yFx_nj910K9H^#Bm95yum}6j<dBony
zRYtA-5WAzQ;)ruQxp&~?5FBm~Qc%U--@B*YvGCc`mX$Ws3Omfcg9F<(ySQfYg}}I=
z^tyu}r&m^2#)8rtxMkqez;+w41sUb4YfOoI@lfnFOZ)4x%We&%kytz)cT_Z|-`1<q
zIj*{a9Gu?Nz|B^B?EyzTb;NLE42M_}K;Q*ivD;|vFW&|NomX@l<;bIJk3KgjIT2ZB
zD|RFm8DTv24z?u#SWCVQ!X<%e^#E19I4CVoy*Dg|Kh!8{%{&GT#60me@q=lx@|n{J
z6Beyn6v$zgj_15?E4HFvFh<3ic?8zmirorjB01KCr4u5Nf=F=?LaF@Jo3>($kzqzr
zTg5|-Co|#$OR-xaDn&u)_=Qn(S`a$XQ==QP^~9y}IRj#=U&6)!PfWGBLhd_?o)Cmi
zQ!foG#dnGhGgxc3As^X^+nT20q!B@A`N&~GJ+ce3wd+r1#Q-DL%%g#H*d10s>_Bp}
zK}cuxKm1RJe?AByMXgj8Dq3JXdu@J#Y0umsgS-Az?h$b!LFH|$EH&SVwPtAQdt0J*
zRYdy-q2o8kF+ug_+GZoDsN@-eq?O=EApfYM637VWH(tuK@)Wc?iWXQ2EbK6vZjb0Y
z+WNd^Yb5iMy{2JFOOmOi&0P+=5KB+fif%JlG7kZnXLG;J?_#fN&K5h~{KlIf2BM^P
z=Cp!rH=tJTi9wA#Gf$k^D9As9(DDmK-wbS%pzt4-B5yMwn`}fDdu1MNQ1rQ=wERF2
zo4Xr!5s$|m>}?<4W-Kn|4VvVZ54RM%6@tBzOB)FCWFSHZF`V?yR{v2p8St!(JOCaX
zX)AV18ntnGTCP`+G>%yrh?2*l)?ynr7%<j4|9Y6iZN+ZMlyOu3rJ~hAX!*d3$zPuw
z)C=QdZN*mAw~Zp}Z3ze*VJr6T7)(}uGJ{4%gF3|>)J|=BQ|t|fh-dLhMXfo1cf5DK
zIPmwN^s+&a*Ipg->LUS4+WY%&V1wD`@U0txtb1#B!Pla0G~ZI}R>Well!|uQFCk=p
z5!54k{Lk_JfUPZ6n`0AVpPEE0kpi)Gw90@wGw`dHVz(hWCVD@xIw-yFpy;Zy!m`DI
z2{v*n=>w^JM@3(NU8S4)k_{06RRXD+gLLte8od;-Rl|}kpE}~q3{udDv9^D$d7Y|x
z$EY>)5O9fTY)tG875#3Inq4AHFCAO@e?dxmc;{FPuB?S7^C)U<SEs<&zj4DQf$SD&
zCN?&9A22sa&5lrXc64ks5u|3kI)11i$JvT6D$hZad7Pnxp2OaxIg;0y3iOl+Xlibz
z6545GP23tQj9nYJs!(PzdfmFhb#FnFd6r#vYrP;g-w)YpN6;Z6qNRnUmj>LQ^D~qi
zqlMASLeYlvtwmOzYJOsDQH1T)+SB92Gu&w-ZN={h5z*+ls4K`+f}2Ed0j5O9MSm8M
zUMu3O<EPrZWhuI%`S-ibBSx0n5=@IS&1U@0C@U<xUyYs!Sbc?|=&F>H`bxAg`cT)O
zwu2b4dm5U<E}!f=O1G(Xz(?3CrKpP()Q@=lszsYE#qSbTl|)Tqiio(tVSdFG<TaJx
zC&s(8Xw{<q^YZil30Nrs7G!-{VcD*it-D5^)vlWaU1dYGi6P1Qg6~_5-z6fVv2n3Q
z`ko%8s^3O<0?4+XL<(h@AZ6>ut~)wx;{fF6=U<K`<213X<sVu#E-7)#&jWeDqWH%J
z{bj_XSKbpy{ca{axaPqF9nUE?nA4heavnzeE3@?~`gc)z4&lkh`o<OKoqpc?0jJv`
z@ju6hN>bKiv4T_4Gh>CZ$D9A$s`o@a10Kz1#}2XQ-k}XUxfHm`Qv7by)O=(?)30>(
z-9Ze;V-U(94{wW#Zgn;~@8@#Y8%fGl*e(72VDo^@t?H24^cXF1%PT=jEhbS%Ufz)_
zX5IO&MUB0Rcy;_x5qS-;v&|;WEz!d0k6M1A?S5&odH^)Q5E_#=TGrUBFk<Fn*EkqV
z8(*|BPrV-UIAFPtluj*~Q_-uAMagAU^&afKApm%#w6L^I(S95Aq@TL5+x!AR_12(j
zefD&s<Dx57^jvHiNZa>AyT^Pu5px%owjsA*R2>8RZeKnT8yj1Wat{6*XiLP4v|Bpc
zu6dSTefQfy0EJ8PgNbu00y`aaPbWGqy3%h033A7|Md!AAKvahZ40*`SekITeqN?{4
zUw4VUEh2jvJ{zd9Q5!q#+|!|Xn)+h}v6*gPNhKeO{#UeD9+i@d_-LR-Q^rmCS4ZFM
z^k_5Bc)#eOc!tQyb#;T{<6Sq>^=JFY1Ro~Ro|AGqz58@(o+qwZ_!RI&mg3#z%zWOS
zPdf{jePl-uC(#Qh7ftT;9PKU-RTQJM=&EV7PY7I4>`uEO2(7(^EN@{ulA7$YLt>Za
zS$@reXBBL|ENz9rRmJYK-_kb%`9^mQB*;yj&2^f|=s4H>Na|-;I@_*21THIar!5#g
zZ$@uB<uqsMhJilVnZNTlnZ(S<Jda&{@4r<7-;HqLuy-3P`<}x5Ba=zAh+K8SDHmjP
zRA0siN$;auZ&G+U)6e_F*>!b;swcYTBLddlW9ecN{bcEw(u`kCWo({=n`jWsC1hWD
zmn|nL^5Vof<<Wq3_gFf(g>5rprZ=@5$cS!FN=w{nHv^Y<_c|X?;CDmV`qPA)2;_|C
zJ)J~#q8Q;3pr0V$DJv|yqdV7kyZ?b1I%pOFcJOO~06(Z>@XHh2ia=Tp?dj|qfs|#X
zV@v0C_xh|JhfA)#Xq0;9S&QYWU7><5GH}IGcforh<<Mg$U4QW*$)tC^pbxBY7g*i;
z)ETb2CYz&QvR+FmnNvQU@VE1Ris;;ee&)Y(si?jyPXE=6eaz0rD+PW;x_!e=&~w8y
zP&V{vA+E%ob{FJCZ_hfA+d+Q4!oB<Bu#@SCv)mcO8cBXn;WE(ucKddNik7XMy<kZw
z$z?&0%ro9C?^l4-04MpBPfytb+==n#uefHRtplsN&BSZVPg2iJ2R)z8yA1WJ5va$n
z04BH<2O9Jl;0r$F<0sD}dar8g!IkcPft)_tl1bNJd`L3oO%~-s;P`G_+G`Bt%lo5A
z*OniX@?;G##E*DBlLVd?<Vi7F#lC}{S$5g2^?r+tcgy?b<D4vb<3Y{_?Mds(USS~L
zUOpDjDW4!JOR&5J#0RzuJfrAKVBS#l&HPB}jmNIO_urXZ?7FUbI&b6t+Sqs~$kBp#
zEa_7zPQu@xJ?9PN>znIhxBOdzB-m{yYA<0s@CoUjoLdC_RFMYI2<drBKZX4z-OC}U
zpD(8k<a^me(|-5+=skVTbDf+uknc?`-dg>_%U>FQ{F{QDV<Y}vNCYJIrK00sd~5X!
zZ-$*rORN=K6}!{U6i8sLhv{BN&aDGkt=21XD^3SpOm04f^+s~)7G}NFv*g-~M&X&I
zz`?e)_7ZYt1hPJ<U2)CAS3KlYA${<3A3Y{tRN^IW`NXgj3FL|B3HP3jGIY=wfv{hs
z?kV&Gd8Bww`B`Bm5%>;;k~!tmRpqxKmUHY$ggOTbd@W|h>;-o@=~g~32CcmZ(huZP
z*L6c#Qwwcx)EU3~eu4M#)*FBy(zhOz4WA(Vc*X4b_Rmu4fY3MAopJ5OU-3Ne55Ngw
z=hAzS<r>i$E3cm4<;Fa#S%fLx%;<SD@;?7w;}xpR0tSYiPp+cyZv#JD?#{nI>|7v6
z%+t&Sx8gttovrEW#J+c9pRo<(_XYjAA(zT+bILr;q$}^_I-n%%TtX%Va*u;|O)eJt
z=cIX>ndnxG0iB}+*J?6Hs8{rU;O3R?g4e@NrXzCNJk3mS%a1phAA?Szch(AJN}a+2
z2j`|`?jPO>JDINN73OJX()AY~l1h5hNw;4*FzkGM<Zl8C^83sDT$<B#MXxeXGtqd|
z`Si7clSIu_&~MWDj)WXV-vwFZ7+tvZngu~0AmIzW(mX9GzG><}wmD}BW-{mmVBfHF
z>V$uQt`LzWgOA#}D&5n*_tNXl(~|M78!0gBPsNiGU>xb!AYm4xeFCgOWVtb^<;#At
z@U0MY@tr-&Ji8NhqxoCR;L)ny$*LS9$g#jE?A+!)23}NjEh5i}n&(!ycfUnI=Q)PK
z9&MfukO^+Z0hrXGj%N<_kb?vrCNNClbEHSA0U%A=Xf#QBC+aq!Uf>f&HVfVc57_`_
my~EB8%iIStI!$X&;{O3r3)blbBiM}q0000<MNUMnLSTX!m`rj2

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..5679e803b1ba
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var theme = localStorage.getItem('mdbook-theme');
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
+					<li>
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</a>
+					</li>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<div id="page-wrapper" class="page-wrapper">
+
+
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
+					</div>
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
+
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
+
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
+</html>

From f1bea5d812aa4c34d9f694af49832477d1d98ecd Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 Manual.md                    |  2 +-
 manual/book.toml             |  7 ++++++-
 manual/ci/format.sh          | 24 ++++++++++++++++++++++++
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/Manual.md b/Manual.md
index b96193ef07e7..d91f71f9510e 100644
--- a/Manual.md
+++ b/Manual.md
@@ -2152,7 +2152,7 @@ otherwise the `debug` packages won't have debugging symbols.
 <a id="contributing"></a>
 ### Contributing via git
 
-To get started, [fork](https://help.github.com/articles/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
+To get started, [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
 
     $ git clone git@github.com:<user>/void-packages.git
 
diff --git a/manual/book.toml b/manual/book.toml
index 8f15412495eb..2a98cbe327af 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
@@ -15,3 +15,8 @@ default-theme = "void-light"
 preferred-dark-theme = "void-dark"
 edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
 limit-results = 100
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
+exclude = [ "https://docs.github.com/en/get-started/quickstart/fork-a-repo" ]
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (14 preceding siblings ...)
  2022-05-05 18:41 ` classabbyamp
@ 2022-05-17 17:09 ` classabbyamp
  2022-05-28  7:03 ` classabbyamp
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-05-17 17:09 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 53773 bytes --]

From cfcde44978a6564719a9d23731c0cb7ca8618a70 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8afe781f0ec1..b96193ef07e7 100644
--- a/Manual.md
+++ b/Manual.md
@@ -819,10 +819,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -830,7 +830,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1920,7 +1920,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 12c533298d1939ab59238343743453d7b5719038 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                         |   1 +
 manual/book.toml                   |  17 +
 manual/generate_mdbook.py          | 107 +++++++
 manual/src/SUMMARY.md              |   1 +
 manual/src/theme/book.js           | 119 +++++++
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 344 ++++++++++++++++++++
 manual/src/theme/css/print.css     |  79 +++++
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/favicon.png       | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs         | 253 +++++++++++++++
 11 files changed, 1512 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/css/variables.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..8f15412495eb
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,17 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..d1a280f9eca3
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,119 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..3d682f8d972f
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,344 @@
+@import 'variables.css';
+
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: var(--fg);
+	margin: 0;
+	background-color: var(--bg);
+}
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
+	text-decoration: none;
+}
+a:hover, a:visited:hover {
+	color: var(--links-hover);
+	text-decoration: underline;
+}
+
+code {
+	background: var(--inline-code-color);
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
+	border-left: none;
+	border-right: none;
+}
+
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
+li.js-unavailable {
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
+}
+table td {
+	padding: 3px 20px;
+}
+table thead {
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
+}
+table thead td {
+	font-weight: 700;
+}
+table tbody tr:nth-child(2n) {
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: var(--nav-bg);
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav a,
+#void-nav button,
+#void-nav label {
+	fill: var(--nav-fg);
+	height: 50px;
+	min-height: 100%;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: var(--nav-fg);
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: var(--sidebar-bg);
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: var(--sidebar-fg);
+	display: block;
+}
+#sidebar a:hover {
+	color: var(--sidebar-active);
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: var(--sidebar-active);
+}
+
+#sidebar-toggle {
+ 	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid var(--searchbar-border-color);
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: var(--nav-arrow-fg);
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: var(--nav-fg-hover);
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..2970c2b6c7ab
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,79 @@
+
+#sidebar,
+#menu-bar,
+#void-nav,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #ddd;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..5679e803b1ba
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var theme = localStorage.getItem('mdbook-theme');
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
+					<li>
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</a>
+					</li>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<div id="page-wrapper" class="page-wrapper">
+
+
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
+					</div>
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
+
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
+
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
+</html>

From 02baf7086e1b5038b11260ef297a65715d9a5288 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 Manual.md                    |  2 +-
 manual/book.toml             |  7 ++++++-
 manual/ci/format.sh          | 24 ++++++++++++++++++++++++
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/Manual.md b/Manual.md
index b96193ef07e7..d91f71f9510e 100644
--- a/Manual.md
+++ b/Manual.md
@@ -2152,7 +2152,7 @@ otherwise the `debug` packages won't have debugging symbols.
 <a id="contributing"></a>
 ### Contributing via git
 
-To get started, [fork](https://help.github.com/articles/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
+To get started, [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
 
     $ git clone git@github.com:<user>/void-packages.git
 
diff --git a/manual/book.toml b/manual/book.toml
index 8f15412495eb..2a98cbe327af 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
@@ -15,3 +15,8 @@ default-theme = "void-light"
 preferred-dark-theme = "void-dark"
 edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
 limit-results = 100
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
+exclude = [ "https://docs.github.com/en/get-started/quickstart/fork-a-repo" ]
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (15 preceding siblings ...)
  2022-05-17 17:09 ` classabbyamp
@ 2022-05-28  7:03 ` classabbyamp
  2022-05-28 20:05 ` classabbyamp
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-05-28  7:03 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 53773 bytes --]

From 018d4680c781f1979ee03a969150ceba17b8b2f5 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8afe781f0ec1..b96193ef07e7 100644
--- a/Manual.md
+++ b/Manual.md
@@ -819,10 +819,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -830,7 +830,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1920,7 +1920,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 78f818fb776ac2a4aff8812a01accf4d759931b1 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                         |   1 +
 manual/book.toml                   |  17 +
 manual/generate_mdbook.py          | 107 +++++++
 manual/src/SUMMARY.md              |   1 +
 manual/src/theme/book.js           | 119 +++++++
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 344 ++++++++++++++++++++
 manual/src/theme/css/print.css     |  79 +++++
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/favicon.png       | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs         | 253 +++++++++++++++
 11 files changed, 1512 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/css/variables.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..8f15412495eb
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,17 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..d1a280f9eca3
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,119 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..3d682f8d972f
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,344 @@
+@import 'variables.css';
+
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: var(--fg);
+	margin: 0;
+	background-color: var(--bg);
+}
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
+	text-decoration: none;
+}
+a:hover, a:visited:hover {
+	color: var(--links-hover);
+	text-decoration: underline;
+}
+
+code {
+	background: var(--inline-code-color);
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
+	border-left: none;
+	border-right: none;
+}
+
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
+li.js-unavailable {
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
+}
+table td {
+	padding: 3px 20px;
+}
+table thead {
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
+}
+table thead td {
+	font-weight: 700;
+}
+table tbody tr:nth-child(2n) {
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: var(--nav-bg);
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav a,
+#void-nav button,
+#void-nav label {
+	fill: var(--nav-fg);
+	height: 50px;
+	min-height: 100%;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: var(--nav-fg);
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: var(--sidebar-bg);
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: var(--sidebar-fg);
+	display: block;
+}
+#sidebar a:hover {
+	color: var(--sidebar-active);
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: var(--sidebar-active);
+}
+
+#sidebar-toggle {
+ 	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid var(--searchbar-border-color);
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: var(--nav-arrow-fg);
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: var(--nav-fg-hover);
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..2970c2b6c7ab
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,79 @@
+
+#sidebar,
+#menu-bar,
+#void-nav,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #ddd;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..5679e803b1ba
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var theme = localStorage.getItem('mdbook-theme');
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
+					<li>
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</a>
+					</li>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<div id="page-wrapper" class="page-wrapper">
+
+
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
+					</div>
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
+
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
+
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
+</html>

From e01505bcca9840d49ee12cf027982c7f236cdaaa Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 Manual.md                    |  2 +-
 manual/book.toml             |  7 ++++++-
 manual/ci/format.sh          | 24 ++++++++++++++++++++++++
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 manual/ci/format.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3e4ce6a2cf61
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./manual/ci/format.sh
+
diff --git a/Manual.md b/Manual.md
index b96193ef07e7..d91f71f9510e 100644
--- a/Manual.md
+++ b/Manual.md
@@ -2152,7 +2152,7 @@ otherwise the `debug` packages won't have debugging symbols.
 <a id="contributing"></a>
 ### Contributing via git
 
-To get started, [fork](https://help.github.com/articles/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
+To get started, [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
 
     $ git clone git@github.com:<user>/void-packages.git
 
diff --git a/manual/book.toml b/manual/book.toml
index 8f15412495eb..2a98cbe327af 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
@@ -15,3 +15,8 @@ default-theme = "void-light"
 preferred-dark-theme = "void-dark"
 edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
 limit-results = 100
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
+exclude = [ "https://docs.github.com/en/get-started/quickstart/fork-a-repo" ]
diff --git a/manual/ci/format.sh b/manual/ci/format.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/manual/ci/format.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (16 preceding siblings ...)
  2022-05-28  7:03 ` classabbyamp
@ 2022-05-28 20:05 ` classabbyamp
  2022-05-29  0:12 ` classabbyamp
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-05-28 20:05 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 53808 bytes --]

From 018d4680c781f1979ee03a969150ceba17b8b2f5 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8afe781f0ec1..b96193ef07e7 100644
--- a/Manual.md
+++ b/Manual.md
@@ -819,10 +819,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -830,7 +830,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1920,7 +1920,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 78f818fb776ac2a4aff8812a01accf4d759931b1 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                         |   1 +
 manual/book.toml                   |  17 +
 manual/generate_mdbook.py          | 107 +++++++
 manual/src/SUMMARY.md              |   1 +
 manual/src/theme/book.js           | 119 +++++++
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 344 ++++++++++++++++++++
 manual/src/theme/css/print.css     |  79 +++++
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/favicon.png       | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs         | 253 +++++++++++++++
 11 files changed, 1512 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/css/variables.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..8f15412495eb
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,17 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..d1a280f9eca3
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,119 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..3d682f8d972f
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,344 @@
+@import 'variables.css';
+
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: var(--fg);
+	margin: 0;
+	background-color: var(--bg);
+}
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
+	text-decoration: none;
+}
+a:hover, a:visited:hover {
+	color: var(--links-hover);
+	text-decoration: underline;
+}
+
+code {
+	background: var(--inline-code-color);
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
+	border-left: none;
+	border-right: none;
+}
+
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
+li.js-unavailable {
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
+}
+table td {
+	padding: 3px 20px;
+}
+table thead {
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
+}
+table thead td {
+	font-weight: 700;
+}
+table tbody tr:nth-child(2n) {
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: var(--nav-bg);
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav a,
+#void-nav button,
+#void-nav label {
+	fill: var(--nav-fg);
+	height: 50px;
+	min-height: 100%;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: var(--nav-fg);
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: var(--sidebar-bg);
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: var(--sidebar-fg);
+	display: block;
+}
+#sidebar a:hover {
+	color: var(--sidebar-active);
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: var(--sidebar-active);
+}
+
+#sidebar-toggle {
+ 	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid var(--searchbar-border-color);
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: var(--nav-arrow-fg);
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: var(--nav-fg-hover);
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..2970c2b6c7ab
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,79 @@
+
+#sidebar,
+#menu-bar,
+#void-nav,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #ddd;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..5679e803b1ba
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var theme = localStorage.getItem('mdbook-theme');
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
+					<li>
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</a>
+					</li>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<div id="page-wrapper" class="page-wrapper">
+
+
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
+					</div>
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
+
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
+
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
+</html>

From 8470b844fac4df336df0a7ec13102bc0d52c4980 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 Manual.md                    |  2 +-
 common/travis/manual-ci.sh   | 24 ++++++++++++++++++++++++
 manual/book.toml             |  7 ++++++-
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 common/travis/manual-ci.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..9e999d9f43b0
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -yu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./common/travis/manual-ci.sh
+
diff --git a/Manual.md b/Manual.md
index b96193ef07e7..d91f71f9510e 100644
--- a/Manual.md
+++ b/Manual.md
@@ -2152,7 +2152,7 @@ otherwise the `debug` packages won't have debugging symbols.
 <a id="contributing"></a>
 ### Contributing via git
 
-To get started, [fork](https://help.github.com/articles/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
+To get started, [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
 
     $ git clone git@github.com:<user>/void-packages.git
 
diff --git a/common/travis/manual-ci.sh b/common/travis/manual-ci.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/common/travis/manual-ci.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi
diff --git a/manual/book.toml b/manual/book.toml
index 8f15412495eb..2a98cbe327af 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
@@ -15,3 +15,8 @@ default-theme = "void-light"
 preferred-dark-theme = "void-dark"
 edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
 limit-results = 100
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
+exclude = [ "https://docs.github.com/en/get-started/quickstart/fork-a-repo" ]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (17 preceding siblings ...)
  2022-05-28 20:05 ` classabbyamp
@ 2022-05-29  0:12 ` classabbyamp
  2022-06-26 23:49 ` classabbyamp
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-05-29  0:12 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 53809 bytes --]

From 018d4680c781f1979ee03a969150ceba17b8b2f5 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 8afe781f0ec1..b96193ef07e7 100644
--- a/Manual.md
+++ b/Manual.md
@@ -819,10 +819,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -830,7 +830,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1920,7 +1920,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 78f818fb776ac2a4aff8812a01accf4d759931b1 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                         |   1 +
 manual/book.toml                   |  17 +
 manual/generate_mdbook.py          | 107 +++++++
 manual/src/SUMMARY.md              |   1 +
 manual/src/theme/book.js           | 119 +++++++
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 344 ++++++++++++++++++++
 manual/src/theme/css/print.css     |  79 +++++
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/favicon.png       | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs         | 253 +++++++++++++++
 11 files changed, 1512 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/css/variables.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..8f15412495eb
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,17 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..d1a280f9eca3
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,119 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..3d682f8d972f
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,344 @@
+@import 'variables.css';
+
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: var(--fg);
+	margin: 0;
+	background-color: var(--bg);
+}
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
+	text-decoration: none;
+}
+a:hover, a:visited:hover {
+	color: var(--links-hover);
+	text-decoration: underline;
+}
+
+code {
+	background: var(--inline-code-color);
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
+	border-left: none;
+	border-right: none;
+}
+
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
+li.js-unavailable {
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
+}
+table td {
+	padding: 3px 20px;
+}
+table thead {
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
+}
+table thead td {
+	font-weight: 700;
+}
+table tbody tr:nth-child(2n) {
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: var(--nav-bg);
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav a,
+#void-nav button,
+#void-nav label {
+	fill: var(--nav-fg);
+	height: 50px;
+	min-height: 100%;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: var(--nav-fg);
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: var(--sidebar-bg);
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: var(--sidebar-fg);
+	display: block;
+}
+#sidebar a:hover {
+	color: var(--sidebar-active);
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: var(--sidebar-active);
+}
+
+#sidebar-toggle {
+ 	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid var(--searchbar-border-color);
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: var(--nav-arrow-fg);
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: var(--nav-fg-hover);
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..2970c2b6c7ab
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,79 @@
+
+#sidebar,
+#menu-bar,
+#void-nav,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #ddd;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
zcmV;Z7FX$sP)<h;3K|Lk000e1NJLTq0055w0055&1^@s6!()h(00004b3#c}2nYxW
zd<bNS00009a7bBm000SL000SL0ncLY;Q#;t8FWQhbW?9;ba!ELWdL_~cP?peYja~^
zaAhuUa%Y?FJQ@H179~kUK~#90?VWjaRn@)6Kfir$5@wasC%UX+TPJX;sMQt%v63Kl
zs3iBkqM{)R*sj(N+FFa1gF~s+zGZ8DidF#!^xYfkQ<O<+NP^W?v<^77z9O|aA{xdd
z_nzPT<0hDp>E`C_b8aA?wO9+1dw$<v;LASy?DO0EC+vkhT=nq2DJOL>!Xc_U0yr4h
z7uZKt`yuofWC-9iU6NFEiy-wVn-DeuA0fO$ihm|0x!!r-*%*t(QX&0Fp&(%=U=vqe
zGxDjChmm5m7&!rDG;lm=qxj8E;1%E{;3b23w*S`tYbQ;bRBu5mC)nf6^HA+WhewD^
zRMj(puK`0sOs8JawSv?rx?=Ny&3~SG;>^Yn^YN8lZ=R~Es>nxqA59>|nFtesFNU2{
zC)6qW1j--rcr;cN+ZuL4J)zf{C;n9YcuC2Hz!YG3*!g6^4n>y=a^JAju*ZvvijrX`
z)LnX&dG3Ga{vrJu`dzH(G~k%9^YM|5qB371a#!isOWzJVnM~;w=2^6A(f)aPdEY^}
z4A?L1d@SMtOV!g~#>U5<4m+8y$Z7M$YvLmf<|m4t2jqvHPmri+6~Z;KF|jpa=h6{5
zX`TnyJa}Lv6}b+f957+$6EZ7}XRa<CU;1p=iR?~JnP<t`B?ETu*!g{wS-{}1^T{C;
zs5FlGNiNNaa>hKB)s+{i=v?4{uye^_wyEgc&HXq3Iv3W9LO0Lin#Bircy|D2hn-7r
zK}FX&;!H0cQ~FGpd4y&jRh2|dqC!M|1MCx49=(_p!Yu>044f6#E09pk<Em>$)sFfj
za84NddlHJi2%HxyjJ+DPb|_CWuDG6x-a-0hVjn#~kmCedTUk>%J$UU9NmM6_5gq~h
zg_*~mf*|*K5pQPfYq1@{=!HzYHa<~Q9;VMgzM$x{DU<rvlrd8_+SUr)JkU1+Szx26
zoLM%m>~A(ja$@y1Ixf0WMbD*g4&)aHs_5$Y>i7hkA~|6mfY{jBa+GuEGmu{l5hIUP
z)>M|-637|zKzan~Gmx+3EBZf`)s+|72%Zyjp55u2MEj1Xs#9a*VvGC?nv>>%Pz=-n
zya#+py2@{-AW0yPbiw=}U?AywqJaVB<R?i%qGg3;|La%4oHY+vGtds;FCy}ys;(1}
zm%XI-c65C7eG$=&@~VmtmxvsR#}OiOxT41arvd|f%+m(-M$DO|V@p^05iF<81HKqY
zfoD;csH3adkH5x7#TtA}5rFtB@qGDEzKX+XC`F`qZshr`;{$c7Iwm$Q_Hs`H<-B<y
zO9Lru5$-i9^KfZV={xqaYoB;c{PRZ46!ml}!ihoacZMMUYBY_f^mHMySC|JfHqbUf
z?nSxF_5xXVh}XnN3Nj6tLAd+KK0{f%)!RCDTG6zupKN=Ld7#sk=q)Jo)zi7LqS$+3
z<dzAKta)T`eX71fkgE{(4?7=0?kg)SyD<Cfdl|cpKsykAQ=eCV(*>toP#1P?nG&yx
z55nO)g8UpiTc0yVkQrr#Wp`zFZ5Ft$Yh2g$vboj|nhbQl8l4pz6Wb709z7*q6(25+
z+ytCYc>j@iD0*Tx^+4GqEG2IF5*6mHoV{R47T5Q&8;h$K7p6?=Hc>e`41E<{w=KEt
zr0&jty8U{hWKP9*LB1&}mBn+)&+6{=eYB-?TxktM84cV*dTAk0MCIr~kwMpY`?~IY
zc%oZALXnsFY+Fo&ik9__K>iW0j-M?e^MRoOOMB|+nC|RU?as&}irh)-{Z#pVBapwu
z#>E~@rjnz9zXmLA#K^pPYv*OWu$i%WirtFoKzsEe`Jxgpamy!W8mo`anNl=my+(9A
z@Myr&#|+&*^omSBoY8IT>>Dcv*YD83L#JyKeYY^5iO1uP442!1AdB0#tI?w}d5X<w
z<Z0O9{hZE?1AVtJ--*Rysj{(UGg0OQC_BJ-CZox7mm|*^?j;9#q~0L&H^zM<kOdMo
ziC+R&1th1&GhglMaD^^M9wqrJx>st@Hv(CpY;4&q5xF}cIZ2t+bzOa^Q}dKuTXCEs
zQ?d~6n?(DJh^Y6jHxuEHf#@M6i>ntGcJ`UhM;=dq+T~|DrXR>7eIt;6#A30OI(h*C
zJ;r<D&F<{`9nN`*ub=h>N%9swFKFtU1NlsRP5fZ-<ayw8wzWj$l(Mm9e`)V~9gZrJ
z<Z}F}gzK9F`Alp~Y(vwjY1Ut5sOk?p`d+&u&o}3uw_mcpU<2u*Z@=gpfqbU2w(=HL
z{efkfq;brb+dGl0-H|6*UoahijRSp?Xy5UMdOrnhAFUG6q_(?A+0kuls-;BwCec0;
z8x?E7<5Dc&zErfLs;Vlo``6o;XM$UCB4D$wBp)^BS&Y9|5gi|`6_E!m%N(#NvgwT7
zzu3k+4rE=^nj!U$le*fn#$H83eZ!A|Pi$(aYMZ-Js|oXX*NqhL_B(X_9xMAKVD3tH
z{?Bd2?<rMPRgw41dk5rsc>}$OHxOZ~)97qVeVBSTm-}fYY7<wY{K}%lc1`L2Et{>h
zx{u}a_Czci2l%uB{k6sT-DYvs;)7BqRU#rq2%|sJkG>colCO>;Dxlyw9x_}S5;ci`
z2=bhYRwX65B8UT&GDVYmn>ca>FwBy~0CD6@;GyO(v@(x@v+OP$$Zf0L`BsP5OsJ}=
zifoE(imK`*9^QCC(zox<fB1Z0q@p8HrbaZ9O4KC&gz$$~#=NrFmGCwM0K|%7TNAa3
z-=nw|WhT?YJgw$D3MCeWw=0dmwHUukR8<nSi3>M5n_gGdMZjrf|N6xNic#XDYDc}A
zs7ZX=zf7}39laIUVo{>FWu@V+InNpHw2@xA&7mdat`+V*Gwj9hoXV=oBQ>ITqKvgD
z^Q5M9W~?ao4~rU|lBiAGjB>d(0guAy_-HM(_+RIVw0RWsSCcz!#_x<oZQ??8^jXWs
z0XR(@c|Kkp4{a%;sd(NVhKZm3wi#8Mx2(TC;oh^>V*GYdRY_DQT$F|Qzhd4IgGJ<l
zM0LXElTU|~jVXIY(dR8n>?+eR0MU5VA$=r>9Yhyei`@aLDwVaB^AI{ME3k%eWo1p}
zt>H+^eHO!?v~=mx0%$T%-RlF6#NKIFno{p!OR+m-aqZ$83AS*&4T^p*QJrvu)^!|b
z5n!t;PQlgzTTg)I+m`2ywj>0wc9nZ?p!>-yt1B;3^d~`Tc8D-LULF6>ASEY%eewqg
z_B!GSC&R8ekM|X8LhzWy*ll>I`k|v#^tK>1yFx_nj910K9H^#Bm95yum}6j<dBony
zRYtA-5WAzQ;)ruQxp&~?5FBm~Qc%U--@B*YvGCc`mX$Ws3Omfcg9F<(ySQfYg}}I=
z^tyu}r&m^2#)8rtxMkqez;+w41sUb4YfOoI@lfnFOZ)4x%We&%kytz)cT_Z|-`1<q
zIj*{a9Gu?Nz|B^B?EyzTb;NLE42M_}K;Q*ivD;|vFW&|NomX@l<;bIJk3KgjIT2ZB
zD|RFm8DTv24z?u#SWCVQ!X<%e^#E19I4CVoy*Dg|Kh!8{%{&GT#60me@q=lx@|n{J
z6Beyn6v$zgj_15?E4HFvFh<3ic?8zmirorjB01KCr4u5Nf=F=?LaF@Jo3>($kzqzr
zTg5|-Co|#$OR-xaDn&u)_=Qn(S`a$XQ==QP^~9y}IRj#=U&6)!PfWGBLhd_?o)Cmi
zQ!foG#dnGhGgxc3As^X^+nT20q!B@A`N&~GJ+ce3wd+r1#Q-DL%%g#H*d10s>_Bp}
zK}cuxKm1RJe?AByMXgj8Dq3JXdu@J#Y0umsgS-Az?h$b!LFH|$EH&SVwPtAQdt0J*
zRYdy-q2o8kF+ug_+GZoDsN@-eq?O=EApfYM637VWH(tuK@)Wc?iWXQ2EbK6vZjb0Y
z+WNd^Yb5iMy{2JFOOmOi&0P+=5KB+fif%JlG7kZnXLG;J?_#fN&K5h~{KlIf2BM^P
z=Cp!rH=tJTi9wA#Gf$k^D9As9(DDmK-wbS%pzt4-B5yMwn`}fDdu1MNQ1rQ=wERF2
zo4Xr!5s$|m>}?<4W-Kn|4VvVZ54RM%6@tBzOB)FCWFSHZF`V?yR{v2p8St!(JOCaX
zX)AV18ntnGTCP`+G>%yrh?2*l)?ynr7%<j4|9Y6iZN+ZMlyOu3rJ~hAX!*d3$zPuw
z)C=QdZN*mAw~Zp}Z3ze*VJr6T7)(}uGJ{4%gF3|>)J|=BQ|t|fh-dLhMXfo1cf5DK
zIPmwN^s+&a*Ipg->LUS4+WY%&V1wD`@U0txtb1#B!Pla0G~ZI}R>Well!|uQFCk=p
z5!54k{Lk_JfUPZ6n`0AVpPEE0kpi)Gw90@wGw`dHVz(hWCVD@xIw-yFpy;Zy!m`DI
z2{v*n=>w^JM@3(NU8S4)k_{06RRXD+gLLte8od;-Rl|}kpE}~q3{udDv9^D$d7Y|x
z$EY>)5O9fTY)tG875#3Inq4AHFCAO@e?dxmc;{FPuB?S7^C)U<SEs<&zj4DQf$SD&
zCN?&9A22sa&5lrXc64ks5u|3kI)11i$JvT6D$hZad7Pnxp2OaxIg;0y3iOl+Xlibz
z6545GP23tQj9nYJs!(PzdfmFhb#FnFd6r#vYrP;g-w)YpN6;Z6qNRnUmj>LQ^D~qi
zqlMASLeYlvtwmOzYJOsDQH1T)+SB92Gu&w-ZN={h5z*+ls4K`+f}2Ed0j5O9MSm8M
zUMu3O<EPrZWhuI%`S-ibBSx0n5=@IS&1U@0C@U<xUyYs!Sbc?|=&F>H`bxAg`cT)O
zwu2b4dm5U<E}!f=O1G(Xz(?3CrKpP()Q@=lszsYE#qSbTl|)Tqiio(tVSdFG<TaJx
zC&s(8Xw{<q^YZil30Nrs7G!-{VcD*it-D5^)vlWaU1dYGi6P1Qg6~_5-z6fVv2n3Q
z`ko%8s^3O<0?4+XL<(h@AZ6>ut~)wx;{fF6=U<K`<213X<sVu#E-7)#&jWeDqWH%J
z{bj_XSKbpy{ca{axaPqF9nUE?nA4heavnzeE3@?~`gc)z4&lkh`o<OKoqpc?0jJv`
z@ju6hN>bKiv4T_4Gh>CZ$D9A$s`o@a10Kz1#}2XQ-k}XUxfHm`Qv7by)O=(?)30>(
z-9Ze;V-U(94{wW#Zgn;~@8@#Y8%fGl*e(72VDo^@t?H24^cXF1%PT=jEhbS%Ufz)_
zX5IO&MUB0Rcy;_x5qS-;v&|;WEz!d0k6M1A?S5&odH^)Q5E_#=TGrUBFk<Fn*EkqV
z8(*|BPrV-UIAFPtluj*~Q_-uAMagAU^&afKApm%#w6L^I(S95Aq@TL5+x!AR_12(j
zefD&s<Dx57^jvHiNZa>AyT^Pu5px%owjsA*R2>8RZeKnT8yj1Wat{6*XiLP4v|Bpc
zu6dSTefQfy0EJ8PgNbu00y`aaPbWGqy3%h033A7|Md!AAKvahZ40*`SekITeqN?{4
zUw4VUEh2jvJ{zd9Q5!q#+|!|Xn)+h}v6*gPNhKeO{#UeD9+i@d_-LR-Q^rmCS4ZFM
z^k_5Bc)#eOc!tQyb#;T{<6Sq>^=JFY1Ro~Ro|AGqz58@(o+qwZ_!RI&mg3#z%zWOS
zPdf{jePl-uC(#Qh7ftT;9PKU-RTQJM=&EV7PY7I4>`uEO2(7(^EN@{ulA7$YLt>Za
zS$@reXBBL|ENz9rRmJYK-_kb%`9^mQB*;yj&2^f|=s4H>Na|-;I@_*21THIar!5#g
zZ$@uB<uqsMhJilVnZNTlnZ(S<Jda&{@4r<7-;HqLuy-3P`<}x5Ba=zAh+K8SDHmjP
zRA0siN$;auZ&G+U)6e_F*>!b;swcYTBLddlW9ecN{bcEw(u`kCWo({=n`jWsC1hWD
zmn|nL^5Vof<<Wq3_gFf(g>5rprZ=@5$cS!FN=w{nHv^Y<_c|X?;CDmV`qPA)2;_|C
zJ)J~#q8Q;3pr0V$DJv|yqdV7kyZ?b1I%pOFcJOO~06(Z>@XHh2ia=Tp?dj|qfs|#X
zV@v0C_xh|JhfA)#Xq0;9S&QYWU7><5GH}IGcforh<<Mg$U4QW*$)tC^pbxBY7g*i;
z)ETb2CYz&QvR+FmnNvQU@VE1Ris;;ee&)Y(si?jyPXE=6eaz0rD+PW;x_!e=&~w8y
zP&V{vA+E%ob{FJCZ_hfA+d+Q4!oB<Bu#@SCv)mcO8cBXn;WE(ucKddNik7XMy<kZw
z$z?&0%ro9C?^l4-04MpBPfytb+==n#uefHRtplsN&BSZVPg2iJ2R)z8yA1WJ5va$n
z04BH<2O9Jl;0r$F<0sD}dar8g!IkcPft)_tl1bNJd`L3oO%~-s;P`G_+G`Bt%lo5A
z*OniX@?;G##E*DBlLVd?<Vi7F#lC}{S$5g2^?r+tcgy?b<D4vb<3Y{_?Mds(USS~L
zUOpDjDW4!JOR&5J#0RzuJfrAKVBS#l&HPB}jmNIO_urXZ?7FUbI&b6t+Sqs~$kBp#
zEa_7zPQu@xJ?9PN>znIhxBOdzB-m{yYA<0s@CoUjoLdC_RFMYI2<drBKZX4z-OC}U
zpD(8k<a^me(|-5+=skVTbDf+uknc?`-dg>_%U>FQ{F{QDV<Y}vNCYJIrK00sd~5X!
zZ-$*rORN=K6}!{U6i8sLhv{BN&aDGkt=21XD^3SpOm04f^+s~)7G}NFv*g-~M&X&I
zz`?e)_7ZYt1hPJ<U2)CAS3KlYA${<3A3Y{tRN^IW`NXgj3FL|B3HP3jGIY=wfv{hs
z?kV&Gd8Bww`B`Bm5%>;;k~!tmRpqxKmUHY$ggOTbd@W|h>;-o@=~g~32CcmZ(huZP
z*L6c#Qwwcx)EU3~eu4M#)*FBy(zhOz4WA(Vc*X4b_Rmu4fY3MAopJ5OU-3Ne55Ngw
z=hAzS<r>i$E3cm4<;Fa#S%fLx%;<SD@;?7w;}xpR0tSYiPp+cyZv#JD?#{nI>|7v6
z%+t&Sx8gttovrEW#J+c9pRo<(_XYjAA(zT+bILr;q$}^_I-n%%TtX%Va*u;|O)eJt
z=cIX>ndnxG0iB}+*J?6Hs8{rU;O3R?g4e@NrXzCNJk3mS%a1phAA?Szch(AJN}a+2
z2j`|`?jPO>JDINN73OJX()AY~l1h5hNw;4*FzkGM<Zl8C^83sDT$<B#MXxeXGtqd|
z`Si7clSIu_&~MWDj)WXV-vwFZ7+tvZngu~0AmIzW(mX9GzG><}wmD}BW-{mmVBfHF
z>V$uQt`LzWgOA#}D&5n*_tNXl(~|M78!0gBPsNiGU>xb!AYm4xeFCgOWVtb^<;#At
z@U0MY@tr-&Ji8NhqxoCR;L)ny$*LS9$g#jE?A+!)23}NjEh5i}n&(!ycfUnI=Q)PK
z9&MfukO^+Z0hrXGj%N<_kb?vrCNNClbEHSA0U%A=Xf#QBC+aq!Uf>f&HVfVc57_`_
my~EB8%iIStI!$X&;{O3r3)blbBiM}q0000<MNUMnLSTX!m`rj2

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..5679e803b1ba
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var theme = localStorage.getItem('mdbook-theme');
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
+					<li>
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</a>
+					</li>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<div id="page-wrapper" class="page-wrapper">
+
+
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
+					</div>
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
+
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
+
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
+</html>

From 48edf7709625be810a46e738a04031756710d351 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 Manual.md                    |  2 +-
 common/travis/manual-ci.sh   | 24 ++++++++++++++++++++++++
 manual/book.toml             |  7 ++++++-
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 common/travis/manual-ci.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3b3cd21486c7
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -Syu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./common/travis/manual-ci.sh
+
diff --git a/Manual.md b/Manual.md
index b96193ef07e7..d91f71f9510e 100644
--- a/Manual.md
+++ b/Manual.md
@@ -2152,7 +2152,7 @@ otherwise the `debug` packages won't have debugging symbols.
 <a id="contributing"></a>
 ### Contributing via git
 
-To get started, [fork](https://help.github.com/articles/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
+To get started, [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
 
     $ git clone git@github.com:<user>/void-packages.git
 
diff --git a/common/travis/manual-ci.sh b/common/travis/manual-ci.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/common/travis/manual-ci.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi
diff --git a/manual/book.toml b/manual/book.toml
index 8f15412495eb..2a98cbe327af 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
@@ -15,3 +15,8 @@ default-theme = "void-light"
 preferred-dark-theme = "void-dark"
 edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
 limit-results = 100
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
+exclude = [ "https://docs.github.com/en/get-started/quickstart/fork-a-repo" ]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (18 preceding siblings ...)
  2022-05-29  0:12 ` classabbyamp
@ 2022-06-26 23:49 ` classabbyamp
  2022-07-17 18:33 ` [PR REVIEW] " ericonr
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-06-26 23:49 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 53809 bytes --]

From 74dbfd8b67f8aeb9a7545f18acf09067b2efc273 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index 26e3b9f43413..d69d7838c65d 100644
--- a/Manual.md
+++ b/Manual.md
@@ -824,10 +824,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -835,7 +835,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1925,7 +1925,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From b00c0765965c8c975a3d4a9c766809f6bc8723b5 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                         |   1 +
 manual/book.toml                   |  17 +
 manual/generate_mdbook.py          | 107 +++++++
 manual/src/SUMMARY.md              |   1 +
 manual/src/theme/book.js           | 119 +++++++
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 344 ++++++++++++++++++++
 manual/src/theme/css/print.css     |  79 +++++
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/favicon.png       | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs         | 253 +++++++++++++++
 11 files changed, 1512 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/css/variables.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..8f15412495eb
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,17 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..d1a280f9eca3
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,119 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..3d682f8d972f
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,344 @@
+@import 'variables.css';
+
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: var(--fg);
+	margin: 0;
+	background-color: var(--bg);
+}
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
+	text-decoration: none;
+}
+a:hover, a:visited:hover {
+	color: var(--links-hover);
+	text-decoration: underline;
+}
+
+code {
+	background: var(--inline-code-color);
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
+	border-left: none;
+	border-right: none;
+}
+
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
+li.js-unavailable {
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
+}
+table td {
+	padding: 3px 20px;
+}
+table thead {
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
+}
+table thead td {
+	font-weight: 700;
+}
+table tbody tr:nth-child(2n) {
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: var(--nav-bg);
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav a,
+#void-nav button,
+#void-nav label {
+	fill: var(--nav-fg);
+	height: 50px;
+	min-height: 100%;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: var(--nav-fg);
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: var(--sidebar-bg);
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: var(--sidebar-fg);
+	display: block;
+}
+#sidebar a:hover {
+	color: var(--sidebar-active);
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: var(--sidebar-active);
+}
+
+#sidebar-toggle {
+ 	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid var(--searchbar-border-color);
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: var(--nav-arrow-fg);
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: var(--nav-fg-hover);
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..2970c2b6c7ab
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,79 @@
+
+#sidebar,
+#menu-bar,
+#void-nav,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #ddd;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
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

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..5679e803b1ba
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var theme = localStorage.getItem('mdbook-theme');
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
+					<li>
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</a>
+					</li>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<div id="page-wrapper" class="page-wrapper">
+
+
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
+					</div>
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
+
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
+
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
+</html>

From 21b0327964022e4684bd3de34d016b0d7de460b1 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 Manual.md                    |  2 +-
 common/travis/manual-ci.sh   | 24 ++++++++++++++++++++++++
 manual/book.toml             |  7 ++++++-
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 common/travis/manual-ci.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..3b3cd21486c7
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -Syu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck vmdfmt git
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./common/travis/manual-ci.sh
+
diff --git a/Manual.md b/Manual.md
index d69d7838c65d..726c15910498 100644
--- a/Manual.md
+++ b/Manual.md
@@ -2157,7 +2157,7 @@ otherwise the `debug` packages won't have debugging symbols.
 <a id="contributing"></a>
 ### Contributing via git
 
-To get started, [fork](https://help.github.com/articles/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
+To get started, [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
 
     $ git clone git@github.com:<user>/void-packages.git
 
diff --git a/common/travis/manual-ci.sh b/common/travis/manual-ci.sh
new file mode 100755
index 000000000000..fac251ec728c
--- /dev/null
+++ b/common/travis/manual-ci.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Format them
+printf "Formatting tree"
+vmdfmt -l -w Manual.md
+
+git config --global --add safe.directory "${PWD}"
+
+# Check Status
+if [ ! -z "$(git status --porcelain)" ] ; then
+    git diff
+    echo "Working directory not clean, files to be formatted:"
+    git status
+    VMDFMT=1
+fi
+
+# Generate exit value
+if [ ! -z $VMDFMT ] || [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi
diff --git a/manual/book.toml b/manual/book.toml
index 8f15412495eb..2a98cbe327af 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
@@ -15,3 +15,8 @@ default-theme = "void-light"
 preferred-dark-theme = "void-dark"
 edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
 limit-results = 100
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
+exclude = [ "https://docs.github.com/en/get-started/quickstart/fork-a-repo" ]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR REVIEW] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (19 preceding siblings ...)
  2022-06-26 23:49 ` classabbyamp
@ 2022-07-17 18:33 ` ericonr
  2022-07-17 18:34 ` ericonr
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ericonr @ 2022-07-17 18:33 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 469 bytes --]

New review comment by ericonr on void-packages repository

https://github.com/void-linux/void-packages/pull/35856#discussion_r922879397

Comment:
This isn't actually working correctly:

> error: Unsupported node type HTMLSpan ignored

Folks have expressed dislike towards vmdfmt, since it can lead to single line changes moving things around in a whole paragraph. I don't think it's worth implementing it here. Manual.md is actually very far from conforming to it.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (20 preceding siblings ...)
  2022-07-17 18:33 ` [PR REVIEW] " ericonr
@ 2022-07-17 18:34 ` ericonr
  2022-07-18  5:13 ` [PR PATCH] [Updated] " classabbyamp
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: ericonr @ 2022-07-17 18:34 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 203 bytes --]

New comment by ericonr on void-packages repository

https://github.com/void-linux/void-packages/pull/35856#issuecomment-1186586276

Comment:
This looks mostly ok to me, though. It's an interesting idea!

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Updated] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (21 preceding siblings ...)
  2022-07-17 18:34 ` ericonr
@ 2022-07-18  5:13 ` classabbyamp
  2022-07-18  5:13 ` [PR REVIEW] " classabbyamp
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-07-18  5:13 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

There is an updated pull request by classabbyamp against master on the void-packages repository

https://github.com/classabbyamp/void-packages manual-mdbook
https://github.com/void-linux/void-packages/pull/35856

[RFC] render Manual.md with mdBook
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

A patch file from https://github.com/void-linux/void-packages/pull/35856.patch is attached

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-manual-mdbook-35856.patch --]
[-- Type: text/x-diff, Size: 53466 bytes --]

From 433e505c3a5fc7f5db7b5bbea460e9e749c98b70 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:35:29 -0500
Subject: [PATCH 1/3] Manual.md: make heading levels match Table of Contents

---
 Manual.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Manual.md b/Manual.md
index d8358565893c..b4e8d45015ee 100644
--- a/Manual.md
+++ b/Manual.md
@@ -824,10 +824,10 @@ not a particular package is present in `makedepends` or `hostmakedepends`, that
 package shouldn't be added as a build time dependency.
 
 <a id="repositories"></a>
-#### Repositories
+### Repositories
 
 <a id="repo_by_branch"></a>
-##### Repositories defined by Branch
+#### Repositories defined by Branch
 
 The global repository takes the name of
 the current branch, except if the name of the branch is master. Then the resulting
@@ -835,7 +835,7 @@ repository will be at the global scope. The usage scenario is that the user can
 update multiple packages in a second branch without polluting his local repository.
 
 <a id="pkg_defined_repo"></a>
-##### Package defined Repositories
+#### Package defined Repositories
 
 The second way to define a repository is by setting the `repository` variable in
 a template. This way the maintainer can define repositories for a specific
@@ -1925,7 +1925,7 @@ If it is running under another architecture it tries to use the host's `install-
 utility.
 
 <a id="triggers_initramfs_regenerate"></a>
-### initramfs-regenerate
+#### initramfs-regenerate
 
 The initramfs-regenerate trigger will trigger the regeneration of all kernel
 initramfs images after package installation or removal. The trigger must be

From 61d575960e5280ccea75c1f56dd4c0874eac8c48 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 25 Feb 2022 21:36:12 -0500
Subject: [PATCH 2/3] manual/: Add script to generate mdbook of Manual.md

---
 .gitignore                         |   1 +
 manual/book.toml                   |  17 +
 manual/generate_mdbook.py          | 107 +++++++
 manual/src/SUMMARY.md              |   1 +
 manual/src/theme/book.js           | 119 +++++++
 manual/src/theme/css/chrome.css    | 484 +++++++++++++++++++++++++++++
 manual/src/theme/css/general.css   | 344 ++++++++++++++++++++
 manual/src/theme/css/print.css     |  79 +++++
 manual/src/theme/css/variables.css | 107 +++++++
 manual/src/theme/favicon.png       | Bin 0 -> 5800 bytes
 manual/src/theme/index.hbs         | 253 +++++++++++++++
 11 files changed, 1512 insertions(+)
 create mode 100644 manual/book.toml
 create mode 100644 manual/generate_mdbook.py
 create mode 100644 manual/src/SUMMARY.md
 create mode 100644 manual/src/theme/book.js
 create mode 100644 manual/src/theme/css/chrome.css
 create mode 100644 manual/src/theme/css/general.css
 create mode 100644 manual/src/theme/css/print.css
 create mode 100644 manual/src/theme/css/variables.css
 create mode 100644 manual/src/theme/favicon.png
 create mode 100644 manual/src/theme/index.hbs

diff --git a/.gitignore b/.gitignore
index 958f644eee94..52806ca91964 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ etc/conf.*
 etc/virtual
 etc/xbps.d/custom
 .xbps-checkvers*.plist
+/manual/book/
diff --git a/manual/book.toml b/manual/book.toml
new file mode 100644
index 000000000000..8f15412495eb
--- /dev/null
+++ b/manual/book.toml
@@ -0,0 +1,17 @@
+[book]
+authors = ["The Void Linux Team"]
+multilingual = false
+src = "src"
+title = "XBPS Source Packages Manual"
+
+[preprocessor.gen_manual]
+command = "python3 generate_mdbook.py"
+
+[output.html]
+# TODO: this should be a submodule or something
+# to avoid de-syncing with void-docs/src/theme
+theme = "src/theme"
+default-theme = "void-light"
+preferred-dark-theme = "void-dark"
+edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
+limit-results = 100
diff --git a/manual/generate_mdbook.py b/manual/generate_mdbook.py
new file mode 100644
index 000000000000..20c04cda857e
--- /dev/null
+++ b/manual/generate_mdbook.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+from dataclasses import asdict, dataclass, field
+import json
+from pathlib import Path
+import re
+import sys
+from typing import Optional
+
+
+HEADING_RE = re.compile(r"###? (?:\w|\d)")
+ANCHOR_RE = re.compile(r'<a id=".+"></a>')
+REL_LINK_RE = re.compile(r"\[(.*)\]\(\./(.*)\)")
+
+
+@dataclass
+class Chapter:
+    name: str
+    content: str
+    path: str
+    source_path: str
+    number: Optional[list[int]] = None
+    sub_items: list['Chapter'] = field(default_factory=list)
+    parent_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class Section:
+    Chapter: Chapter
+
+
+@dataclass
+class Book:
+    sections: list[Section]
+
+
+def slugify(s: str) -> str:
+    return s.lower().replace(" ", "-").replace(".", "-").replace("`", "")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "supports":
+            raise SystemExit
+
+    doc = Path("Manual.md").read_text().split("\n")
+
+    sections = []
+    curr_chapter = []
+    hlevel = None
+    heading = None
+    fn = None
+    in_section = False
+
+    for line in doc:
+        line = REL_LINK_RE.sub(r"[\1](https://github.com/void-linux/void-packages/blob/master/\2)", line)
+
+        if HEADING_RE.match(line):
+            if in_section:
+                if ANCHOR_RE.search(curr_chapter[-1]):
+                    del curr_chapter[-1]
+
+                sections.append(
+                    Section(
+                        Chapter(
+                            name=heading,
+                            number=num,
+                            content="\n".join(curr_chapter),
+                            path=fn,
+                            source_path=fn,
+                        )
+                    )
+                )
+            curr_chapter = ["#" + line.lstrip("#")]
+            hlevel, heading = line.split(" ", maxsplit=1)
+            fn = f"./{slugify(heading)}.md"
+
+            match len(hlevel):
+                # intro/help
+                case 2:
+                    num = None
+                # other "top" level headings
+                case 3:
+                    num = [ len(sections) ]
+                case _:
+                    continue
+
+            in_section = True
+        elif in_section:
+            curr_chapter.append(line.replace("####", "##", 1))
+
+    # get the last section
+    sections.append(
+        Section(
+            Chapter(
+                name=heading,
+                number=num,
+                content="\n".join(curr_chapter),
+                path=fn,
+                source_path=fn,
+            )
+        )
+    )
+
+    # mdBook requires this key in the emitted json, for reasons unknown
+    book = asdict(Book(sections)) | {"__non_exhaustive": None}
+    print(json.dumps(book))
diff --git a/manual/src/SUMMARY.md b/manual/src/SUMMARY.md
new file mode 100644
index 000000000000..73fa82d6e79c
--- /dev/null
+++ b/manual/src/SUMMARY.md
@@ -0,0 +1 @@
+<!-- leave this file empty. mdBook needs it but it will not be used -->
diff --git a/manual/src/theme/book.js b/manual/src/theme/book.js
new file mode 100644
index 000000000000..d1a280f9eca3
--- /dev/null
+++ b/manual/src/theme/book.js
@@ -0,0 +1,119 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+(function theme() {
+    var html = document.querySelector("html");
+    var themeToggleButton = document.getElementById("theme-toggle");
+
+    themeToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("void-light")) {
+            html.classList.replace("void-light", "void-dark");
+            localStorage.setItem('mdbook-theme', "void-dark");
+        } else {
+            html.classList.replace("void-dark", "void-light");
+            localStorage.setItem('mdbook-theme', "void-light");
+        }
+    });
+})();
+
+(function sidebar() {
+    var html = document.querySelector("html");
+    var sidebar = document.getElementById("sidebar");
+    var sidebarLinks = document.querySelectorAll('#sidebar a');
+    var sidebarToggleButton = document.getElementById("sidebar-toggle");
+    var firstContact = null;
+
+    function showSidebar() {
+        html.classList.remove('sidebar-hidden')
+        html.classList.add('sidebar-visible');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', 0);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', true);
+        sidebar.setAttribute('aria-hidden', false);
+        try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+    }
+
+    function hideSidebar() {
+        html.classList.remove('sidebar-visible')
+        html.classList.add('sidebar-hidden');
+        Array.from(sidebarLinks).forEach(function (link) {
+            link.setAttribute('tabIndex', -1);
+        });
+        sidebarToggleButton.setAttribute('aria-expanded', false);
+        sidebar.setAttribute('aria-hidden', true);
+        try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+    }
+
+    // Toggle sidebar
+    sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+        if (html.classList.contains("sidebar-hidden")) {
+            showSidebar();
+        } else if (html.classList.contains("sidebar-visible")) {
+            hideSidebar();
+        } else {
+            if (getComputedStyle(sidebar)['transform'] === 'none') {
+                hideSidebar();
+            } else {
+                showSidebar();
+            }
+        }
+    });
+
+    document.addEventListener('touchstart', function (e) {
+        firstContact = {
+            x: e.touches[0].clientX,
+            time: Date.now()
+        };
+    }, { passive: true });
+
+    document.addEventListener('touchmove', function (e) {
+        if (!firstContact)
+            return;
+
+        var curX = e.touches[0].clientX;
+        var xDiff = curX - firstContact.x,
+            tDiff = Date.now() - firstContact.time;
+
+        if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+                showSidebar();
+            else if (xDiff < 0 && curX < 300)
+                hideSidebar();
+
+            firstContact = null;
+        }
+    }, { passive: true });
+
+    // Scroll sidebar to current active section
+    var activeSection = sidebar.querySelector(".active");
+    if (activeSection) {
+        sidebar.scrollTop = activeSection.offsetTop;
+    }
+})();
+
+(function chapterNavigation() {
+    document.addEventListener('keydown', function (e) {
+        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+        if (window.search && window.search.hasFocus()) { return; }
+
+        switch (e.key) {
+            case 'ArrowRight':
+                e.preventDefault();
+                var nextButton = document.querySelector('.nav-chapters.next');
+                if (nextButton) {
+                    window.location.href = nextButton.href;
+                }
+                break;
+            case 'ArrowLeft':
+                e.preventDefault();
+                var previousButton = document.querySelector('.nav-chapters.previous');
+                if (previousButton) {
+                    window.location.href = previousButton.href;
+                }
+                break;
+        }
+    });
+})();
diff --git a/manual/src/theme/css/chrome.css b/manual/src/theme/css/chrome.css
new file mode 100644
index 000000000000..544e7718ed9d
--- /dev/null
+++ b/manual/src/theme/css/chrome.css
@@ -0,0 +1,484 @@
+/* CSS for UI elements (a.k.a. chrome) */
+
+@import 'variables.css';
+
+::-webkit-scrollbar {
+	background: var(--bg);
+}
+::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+html {
+	scrollbar-color: var(--scrollbar) var(--bg);
+}
+#searchresults a,
+.content a:link,
+a:visited,
+a > .hljs {
+	color: var(--links);
+}
+
+/* Menu Bar */
+
+#menu-bar,
+#menu-bar-hover-placeholder {
+	z-index: 101;
+	margin: auto calc(0px - var(--page-padding));
+}
+#menu-bar {
+	position: relative;
+	display: flex;
+	flex-wrap: wrap;
+	background-color: var(--bg);
+	border-bottom-color: var(--bg);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+#menu-bar.sticky,
+.js #menu-bar-hover-placeholder:hover + #menu-bar,
+.js #menu-bar:hover,
+.js.sidebar-visible #menu-bar {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0 !important;
+}
+#menu-bar-hover-placeholder {
+	position: sticky;
+	position: -webkit-sticky;
+	top: 0;
+	height: var(--menu-bar-height);
+}
+#menu-bar.bordered {
+	border-bottom-color: var(--table-border-color);
+}
+#menu-bar i, #menu-bar .icon-button {
+	position: relative;
+	padding: 0 8px;
+	z-index: 10;
+	line-height: var(--menu-bar-height);
+	cursor: pointer;
+	transition: color 0.5s;
+}
+@media only screen and (max-width: 420px) {
+	#menu-bar i, #menu-bar .icon-button {
+		padding: 0 5px;
+	}
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	padding: 0;
+	color: inherit;
+}
+.icon-button i {
+	margin: 0;
+}
+
+.right-buttons {
+	margin: 0 15px;
+}
+.right-buttons a {
+	text-decoration: none;
+}
+
+.left-buttons {
+	display: flex;
+	margin: 0 5px;
+}
+.no-js .left-buttons {
+	display: none;
+}
+
+.menu-title {
+	display: inline-block;
+	font-weight: 200;
+	font-size: 2.4rem;
+	line-height: var(--menu-bar-height);
+	text-align: center;
+	margin: 0;
+	flex: 1;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+.js .menu-title {
+	cursor: pointer;
+}
+
+.menu-bar,
+.menu-bar:visited,
+.nav-chapters,
+.nav-chapters:visited,
+.mobile-nav-chapters,
+.mobile-nav-chapters:visited,
+.menu-bar .icon-button,
+.menu-bar a i {
+	color: var(--icons);
+}
+
+.menu-bar i:hover,
+.menu-bar .icon-button:hover,
+.nav-chapters:hover,
+.mobile-nav-chapters i:hover {
+	color: var(--icons-hover);
+}
+
+/* Nav Icons */
+
+.nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	margin: 0;
+	max-width: 150px;
+	min-width: 90px;
+
+	display: flex;
+	justify-content: center;
+	align-content: center;
+	flex-direction: column;
+
+	transition: color 0.5s, background-color 0.5s;
+}
+
+.nav-chapters:hover {
+	text-decoration: none;
+	transition: color 0.15s, color 0.15s;
+}
+
+.nav-wrapper {
+	margin-top: 50px;
+	display: none;
+}
+
+.mobile-nav-chapters {
+	font-size: 2.5em;
+	text-align: center;
+	text-decoration: none;
+	width: 90px;
+	border-radius: 5px;
+	background-color: var(--sidebar-bg);
+}
+
+.previous {
+	float: left;
+}
+
+.next {
+	float: right;
+	right: var(--page-padding);
+}
+
+@media only screen and (max-width: 1080px) {
+	.nav-wide-wrapper { display: none; }
+	.nav-wrapper { display: block; }
+}
+
+@media only screen and (max-width: 1380px) {
+	.sidebar-visible .nav-wide-wrapper { display: none; }
+	.sidebar-visible .nav-wrapper { display: block; }
+}
+
+/* Inline code */
+
+:not(pre) > .hljs {
+	display: inline;
+	padding: 0.1em 0.3em;
+	border-radius: 3px;
+}
+
+:not(pre):not(a) > .hljs {
+	color: var(--inline-code-color);
+	overflow-x: initial;
+}
+
+a:hover > .hljs {
+	text-decoration: underline;
+}
+
+pre {
+	position: relative;
+}
+pre > .buttons {
+	position: absolute;
+	z-index: 100;
+	right: 5px;
+	top: 5px;
+
+	color: var(--sidebar-fg);
+	cursor: pointer;
+}
+pre > .buttons :hover {
+	color: var(--sidebar-active);
+}
+pre > .buttons i {
+	margin-left: 8px;
+}
+pre > .buttons button {
+	color: inherit;
+	background: transparent;
+	border: none;
+	cursor: inherit;
+}
+pre > .result {
+	margin-top: 10px;
+}
+
+/* Search */
+
+#searchresults a {
+	text-decoration: none;
+}
+
+mark {
+	border-radius: 2px;
+	padding: 0 3px 1px 3px;
+	margin: 0 -3px -1px -3px;
+	background-color: var(--search-mark-bg);
+	transition: background-color 300ms linear;
+	cursor: pointer;
+}
+
+mark.fade-out {
+	background-color: rgba(0,0,0,0) !important;
+	cursor: auto;
+}
+
+.searchbar-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+}
+
+#searchbar {
+	width: 100%;
+	margin: 5px auto 0px auto;
+	padding: 10px 16px;
+	transition: box-shadow 300ms ease-in-out;
+	border: 1px solid var(--searchbar-border-color);
+	border-radius: 3px;
+	background-color: var(--searchbar-bg);
+	color: var(--searchbar-fg);
+}
+#searchbar:focus,
+#searchbar.active {
+	box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+.searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+	color: var(--searchresults-header-fg);
+}
+
+.searchresults-outer {
+	margin-left: auto;
+	margin-right: auto;
+	max-width: var(--content-max-width);
+	border-bottom: 1px dashed var(--searchresults-border-color);
+}
+
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults li.focus {
+	background-color: var(--searchresults-li-bg);
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+ul#searchresults span.teaser em {
+	font-weight: bold;
+	font-style: normal;
+}
+
+/* Sidebar */
+
+.sidebar {
+	position: fixed;
+	left: 0;
+	top: 0;
+	bottom: 0;
+	width: var(--sidebar-width);
+	font-size: 0.875em;
+	box-sizing: border-box;
+	-webkit-overflow-scrolling: touch;
+	overscroll-behavior-y: contain;
+	background-color: var(--sidebar-bg);
+	color: var(--sidebar-fg);
+}
+.sidebar-resizing {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.js:not(.sidebar-resizing) .sidebar {
+	transition: transform 0.3s; /* Animation: slide away */
+}
+.sidebar code {
+	line-height: 2em;
+}
+/* .sidebar .sidebar-scrollbox {
+	overflow-y: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding: 10px 10px;
+} */
+.sidebar .sidebar-resize-handle {
+	position: absolute;
+	cursor: col-resize;
+	width: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+}
+.js .sidebar .sidebar-resize-handle {
+	cursor: col-resize;
+	width: 5px;
+}
+.sidebar-hidden .sidebar {
+	transform: translateX(calc(0px - var(--sidebar-width)));
+}
+.sidebar::-webkit-scrollbar {
+	background: var(--sidebar-bg);
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background: var(--scrollbar);
+}
+
+.chapter {
+	list-style: none outside none;
+	padding-left: 0;
+	line-height: 2.2em;
+}
+
+.chapter ol {
+	width: 100%;
+}
+
+.chapter li {
+	display: flex;
+	color: var(--sidebar-non-existant);
+}
+.chapter li a {
+	display: block;
+	padding: 0;
+	text-decoration: none;
+	color: var(--sidebar-fg);
+}
+
+.chapter li a:hover {
+	color: var(--sidebar-active);
+}
+
+.chapter li a.active {
+	color: var(--sidebar-active);
+}
+
+.chapter li > a.toggle {
+	cursor: pointer;
+	display: block;
+	margin-left: auto;
+	padding: 0 10px;
+	user-select: none;
+	opacity: 0.68;
+}
+
+.chapter li > a.toggle div {
+	transition: transform 0.5s;
+}
+
+/* collapse the section */
+.chapter li:not(.expanded) + li > ol {
+	display: none;
+}
+
+.chapter li.chapter-item {
+	line-height: 1.5em;
+	margin-top: 0.6em;
+}
+
+.chapter li.expanded > a.toggle div {
+	transform: rotate(90deg);
+}
+
+.spacer {
+	width: 100%;
+	height: 3px;
+	margin: 5px 0px;
+}
+.chapter .spacer {
+	background-color: var(--sidebar-spacer);
+}
+
+@media (-moz-touch-enabled: 1), (pointer: coarse) {
+	.chapter li a { padding: 5px 0; }
+	.spacer { margin: 10px 0; }
+}
+
+.section {
+	list-style: none outside none;
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+
+/* Theme Menu Popup */
+
+.theme-popup {
+	position: absolute;
+	left: 10px;
+	top: var(--menu-bar-height);
+	z-index: 1000;
+	border-radius: 4px;
+	font-size: 0.7em;
+	color: var(--fg);
+	background: var(--theme-popup-bg);
+	border: 1px solid var(--theme-popup-border);
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	display: none;
+}
+.theme-popup .default {
+	color: var(--icons);
+}
+.theme-popup .theme {
+	width: 100%;
+	border: 0;
+	margin: 0;
+	padding: 2px 10px;
+	line-height: 25px;
+	white-space: nowrap;
+	text-align: left;
+	cursor: pointer;
+	color: inherit;
+	background: inherit;
+	font-size: inherit;
+}
+.theme-popup .theme:hover {
+	background-color: var(--theme-hover);
+}
+.theme-popup .theme:hover:first-child,
+.theme-popup .theme:hover:last-child {
+	border-top-left-radius: inherit;
+	border-top-right-radius: inherit;
+}
diff --git a/manual/src/theme/css/general.css b/manual/src/theme/css/general.css
new file mode 100644
index 000000000000..3d682f8d972f
--- /dev/null
+++ b/manual/src/theme/css/general.css
@@ -0,0 +1,344 @@
+@import 'variables.css';
+
+body {
+	font-family: 'Ubuntu', sans-serif;
+	font-size: 1rem;
+	line-height: 1.5;
+	color: var(--fg);
+	margin: 0;
+	background-color: var(--bg);
+}
+h1, h2, h3, h4, h5, h6 { color: var(--fg); }
+a, a:visited {
+	color: var(--links);
+	text-decoration: none;
+}
+a:hover, a:visited:hover {
+	color: var(--links-hover);
+	text-decoration: underline;
+}
+
+code {
+	background: var(--inline-code-color);
+	padding: 2px 4px;
+	border-radius: 4px;
+	white-space: pre-wrap;
+	overflow-wrap: break-word;
+}
+pre code {
+	padding: 0;
+	border-radius: 0;
+}
+pre {
+	padding: .5em;
+	margin: 1em 0;
+	background: var(--inline-code-color);
+	border: 1px solid var(--code-border);
+	border-radius: 4px;
+}
+
+blockquote {
+	margin: 20px 0;
+	padding: 0 20px;
+	padding-left: 1em;
+	background: var(--quote-bg);
+	border: 1px solid var(--quote-border);
+	border-left: none;
+	border-right: none;
+}
+
+blockquote code {
+	background: var(--quote-code-bg);
+}
+
+li.js-unavailable {
+		background-color: #f6cf68;
+		border-radius: 10px;
+		margin-left: 1em;
+		padding-left: 1em;
+		padding-right: 1em;
+}
+
+table {
+	border-collapse: collapse;
+	display: block;
+	overflow-y: auto;
+	border: 1px var(--table-border-color) solid;
+}
+table td {
+	padding: 3px 20px;
+}
+table thead {
+	background: var(--table-header-bg);
+	color: var(--table-header-fg);
+}
+table thead td {
+	font-weight: 700;
+}
+table tbody tr:nth-child(2n) {
+	/* Alternate background colors for rows */
+	background: var(--table-alternate-bg);
+}
+
+svg {
+	position: relative;
+	top: .125em;
+	width: 1em;
+	height: auto;
+}
+
+.hidden {
+	display: none;
+}
+
+.icon-button {
+	border: none;
+	background: none;
+	cursor: pointer;
+	padding: 1em;
+}
+
+/* void navigation */
+
+#void-nav {
+	width: 100%;
+	min-height: 50px;
+	background: var(--nav-bg);
+	font-size: 14px;
+
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+#void-nav a,
+#void-nav button,
+#void-nav label {
+	fill: var(--nav-fg);
+	height: 50px;
+	min-height: 100%;
+	display: block;
+	line-height: 50px;
+	padding: 0 15px;
+	font-size: 1.2em;
+}
+#void-nav ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+#void-nav ul#nav-right {
+	margin-left: auto;
+}
+
+#void-nav ul li {
+	display: inline-block;
+}
+#void-nav ul li a {
+	color: var(--nav-fg);
+	display: block;
+	padding: 0 15px;
+	line-height: 50px;
+	font-size: 1.2em;
+	text-decoration: none
+}
+#void-nav ul li a:hover,
+#void-nav ul li a:focus,
+#void-nav button:hover,
+#void-nav button:focus,
+#void-nav label:hover,
+#void-nav label:focus {
+	background: #000;
+}
+
+#skip-to-content {
+	position: absolute;
+	left: -999px;
+	top: -999px;
+}
+
+#skip-to-content:active,
+#skip-to-content:focus {
+	position: relative;
+	left: 0;
+	top: 0;
+}
+
+#icon-theme-light {
+	display: var(--theme-toggle-light);
+}
+#icon-theme-dark {
+	display: var(--theme-toggle-dark);
+}
+
+/* sidebar  */
+
+.sidebar-hidden #sidebar {
+	display: none;
+}
+#sidebar {
+	padding: .5em;
+	background: var(--sidebar-bg);
+	font-size: 0.875em;
+}
+#sidebar ol {
+	list-style: none;
+	margin: 0;
+}
+#sidebar ol.chapter {
+	padding: 0;
+	line-height: 2.2em;
+}
+#sidebar ol.section {
+	padding-left: 20px;
+	line-height: 1.9em;
+}
+#sidebar a {
+	color: var(--sidebar-fg);
+	display: block;
+}
+#sidebar a:hover {
+	color: var(--sidebar-active);
+	text-decoration: none;
+}
+#sidebar a.active {
+	color: var(--sidebar-active);
+}
+
+#sidebar-toggle {
+ 	display: none;
+}
+
+/* search */
+
+#searchbar {
+	width: 100%;
+	padding: 10px 16px;
+	margin: 5px 0;
+	border-radius: 3px;
+	border: 1px solid var(--searchbar-border-color);
+}
+#searchresults-header {
+	font-weight: bold;
+	font-size: 1em;
+	padding: 18px 0 0 5px;
+}
+ul#searchresults {
+	list-style: none;
+	padding-left: 20px;
+}
+ul#searchresults li {
+	margin: 10px 0px;
+	padding: 2px;
+	border-radius: 2px;
+}
+ul#searchresults span.teaser {
+	display: block;
+	clear: both;
+	margin: 5px 0 0 20px;
+	font-size: 0.8em;
+}
+
+/* chapter navigation */
+
+#nav-wide-wrapper {
+	max-width: 800px;
+	margin: 0 auto;
+	margin-top: 50px;
+}
+.previous {
+	float: left;
+}
+.next {
+	float: right;
+	right: 15px;
+}
+.nav-chapters {
+	fill: var(--nav-arrow-fg);
+	text-align: center;
+	text-decoration: none;
+	display: block;
+	max-width: 150px;
+	min-width: 90px;
+}
+.nav-chapters:hover {
+	text-decoration: none;
+	fill: var(--nav-fg-hover);
+}
+
+.nav-chapters svg {
+	margin: 0 auto;
+	width: 1.5em;
+}
+.mobile-nav-chapters {
+    fill: var(--nav-arrow-fg);
+}
+.mobile-nav-chapters:hover {
+	fill: var(--nav-fg-hover);
+}
+
+/* layout */
+
+body {
+	box-sizing: border-box;
+}
+#content {
+	display: flex;
+	flex-direction: row;
+	width: 100%;
+}
+#page-wrapper {
+	--content-padding: 10px;
+	padding: 0 var(--content-padding);
+	width: calc(100% - var(--content-padding) * 2);
+}
+#search-wrapper,
+#page-wrapper main {
+	width: 100%;
+	max-width: 800px;
+	margin: 0 auto;
+}
+#sidebar {
+	max-width: 300px;
+	flex-shrink: 0;
+}
+
+/* 300px + 800px + 2*90px + 15px */
+@media only screen and (min-width: 1295px) {
+	.sidebar-visible #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-visible .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+}
+/* 800px + 2*90px + 15px */
+@media only screen and (min-width: 995px) {
+	.sidebar-hidden #nav-wide-wrapper {
+		max-width: none;
+		margin: 0;
+	}
+	.sidebar-hidden .nav-chapters {
+		background: none;
+		position: fixed;
+		top: 50px;
+		bottom: 0;
+		margin: 0;
+		justify-content: center;
+		align-content: center;
+		display: flex;
+		flex-direction: column;
+	}
+	table {
+		display: table;
+	}
+}
diff --git a/manual/src/theme/css/print.css b/manual/src/theme/css/print.css
new file mode 100644
index 000000000000..2970c2b6c7ab
--- /dev/null
+++ b/manual/src/theme/css/print.css
@@ -0,0 +1,79 @@
+
+#sidebar,
+#menu-bar,
+#void-nav,
+.nav-chapters,
+.mobile-nav-chapters {
+    display: none;
+}
+
+#page-wrapper.page-wrapper {
+    transform: none;
+    margin-left: 0px;
+    overflow-y: initial;
+}
+
+#content {
+    max-width: none;
+    margin: 0;
+    padding: 0;
+}
+
+.page {
+    overflow-y: initial;
+}
+
+code {
+    background-color: #ddd;
+    border-radius: 5px;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+
+pre {
+    background-color: #ddd;
+
+    /* Force background to be printed in Chrome */
+    -webkit-print-color-adjust: exact;
+}
+
+pre > .buttons {
+    z-index: 2;
+}
+
+a, a:visited, a:active, a:hover {
+    color: #4183c4;
+    text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    page-break-inside: avoid;
+    page-break-after: avoid;
+}
+
+pre, code {
+    page-break-inside: avoid;
+    white-space: pre-wrap;
+}
+
+svg {
+    display: none !important;
+}
+
+table {
+    color: black;
+    border-color: black;
+    background-color: unset;
+}
+
+table thead tr {
+    color: black;
+    background-color: unset;
+}
+
+table tbody tr {
+    background-color: unset;
+}
+
diff --git a/manual/src/theme/css/variables.css b/manual/src/theme/css/variables.css
new file mode 100644
index 000000000000..ea306becc5fb
--- /dev/null
+++ b/manual/src/theme/css/variables.css
@@ -0,0 +1,107 @@
+
+/* Globals */
+
+:root {
+	--sidebar-width: 300px;
+	--page-padding: 15px;
+	--content-max-width: 750px;
+	--menu-bar-height: 50px;
+	--void-green: #478061;
+	--void-dark-green: #62b086;
+	--void-light: #fafafa;
+	--void-dark: #252525;
+}
+
+/* Themes */
+
+.void-light {
+	--bg: #ffffff;
+	--fg: #333;
+
+	--sidebar-bg: var(--void-light);
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: var(--void-green);
+	--nav-fg: var(--bg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #000;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #fdf6e3;
+	--code-border: #ccc;
+
+	--theme-toggle-light: none;
+	--theme-toggle-dark: inherit;
+
+	--quote-bg: #ebf4ef;
+	--quote-border: #d1e6da;
+	--quote-code-bg: var(--inline-code-color);
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: var(--void-light);
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
+
+.void-dark {
+	--bg: #222;
+	--fg: #ccc;
+
+	--sidebar-bg: #252525;
+	--sidebar-fg: var(--fg);
+	--sidebar-active: var(--links);
+
+	--nav-bg: #295340;
+	--nav-fg: var(--fg);
+	--nav-arrow-fg: var(--fg);
+	--nav-fg-hover: #fff;
+
+	--scrollbar: var(--sidebar-fg);
+
+	--icons: #737480;
+	--icons-hover: #b7b9cc;
+
+	--links: var(--void-dark-green);
+	--links-hover: var(--fg);
+
+	--inline-code-color: #353535;
+	--code-border: #111;
+
+	--theme-toggle-light: inherit;
+	--theme-toggle-dark: none;
+
+	--quote-bg: #293d35;
+	--quote-border: #22362e;
+	--quote-code-bg: #2a2a2a;
+
+	--table-border-color: var(--void-green);
+	--table-header-bg: var(--void-green);
+	--table-header-fg: #fff;
+	--table-alternate-bg: #2c2c2c;
+
+	--searchbar-border-color: #aaa;
+	--searchbar-bg: var(--bg);
+	--searchbar-fg: var(--fg);
+	--searchbar-shadow-color: #d4c89f;
+	--searchresults-header-fg: #666;
+	--searchresults-border-color: #888;
+	--searchresults-li-bg: #252932;
+	--search-mark-bg: #e3b171;
+}
diff --git a/manual/src/theme/favicon.png b/manual/src/theme/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec045d08445cdd73c7413e01a89754827593f
GIT binary patch
literal 5800
zcmV;Z7FX$sP)<h;3K|Lk000e1NJLTq0055w0055&1^@s6!()h(00004b3#c}2nYxW
zd<bNS00009a7bBm000SL000SL0ncLY;Q#;t8FWQhbW?9;ba!ELWdL_~cP?peYja~^
zaAhuUa%Y?FJQ@H179~kUK~#90?VWjaRn@)6Kfir$5@wasC%UX+TPJX;sMQt%v63Kl
zs3iBkqM{)R*sj(N+FFa1gF~s+zGZ8DidF#!^xYfkQ<O<+NP^W?v<^77z9O|aA{xdd
z_nzPT<0hDp>E`C_b8aA?wO9+1dw$<v;LASy?DO0EC+vkhT=nq2DJOL>!Xc_U0yr4h
z7uZKt`yuofWC-9iU6NFEiy-wVn-DeuA0fO$ihm|0x!!r-*%*t(QX&0Fp&(%=U=vqe
zGxDjChmm5m7&!rDG;lm=qxj8E;1%E{;3b23w*S`tYbQ;bRBu5mC)nf6^HA+WhewD^
zRMj(puK`0sOs8JawSv?rx?=Ny&3~SG;>^Yn^YN8lZ=R~Es>nxqA59>|nFtesFNU2{
zC)6qW1j--rcr;cN+ZuL4J)zf{C;n9YcuC2Hz!YG3*!g6^4n>y=a^JAju*ZvvijrX`
z)LnX&dG3Ga{vrJu`dzH(G~k%9^YM|5qB371a#!isOWzJVnM~;w=2^6A(f)aPdEY^}
z4A?L1d@SMtOV!g~#>U5<4m+8y$Z7M$YvLmf<|m4t2jqvHPmri+6~Z;KF|jpa=h6{5
zX`TnyJa}Lv6}b+f957+$6EZ7}XRa<CU;1p=iR?~JnP<t`B?ETu*!g{wS-{}1^T{C;
zs5FlGNiNNaa>hKB)s+{i=v?4{uye^_wyEgc&HXq3Iv3W9LO0Lin#Bircy|D2hn-7r
zK}FX&;!H0cQ~FGpd4y&jRh2|dqC!M|1MCx49=(_p!Yu>044f6#E09pk<Em>$)sFfj
za84NddlHJi2%HxyjJ+DPb|_CWuDG6x-a-0hVjn#~kmCedTUk>%J$UU9NmM6_5gq~h
zg_*~mf*|*K5pQPfYq1@{=!HzYHa<~Q9;VMgzM$x{DU<rvlrd8_+SUr)JkU1+Szx26
zoLM%m>~A(ja$@y1Ixf0WMbD*g4&)aHs_5$Y>i7hkA~|6mfY{jBa+GuEGmu{l5hIUP
z)>M|-637|zKzan~Gmx+3EBZf`)s+|72%Zyjp55u2MEj1Xs#9a*VvGC?nv>>%Pz=-n
zya#+py2@{-AW0yPbiw=}U?AywqJaVB<R?i%qGg3;|La%4oHY+vGtds;FCy}ys;(1}
zm%XI-c65C7eG$=&@~VmtmxvsR#}OiOxT41arvd|f%+m(-M$DO|V@p^05iF<81HKqY
zfoD;csH3adkH5x7#TtA}5rFtB@qGDEzKX+XC`F`qZshr`;{$c7Iwm$Q_Hs`H<-B<y
zO9Lru5$-i9^KfZV={xqaYoB;c{PRZ46!ml}!ihoacZMMUYBY_f^mHMySC|JfHqbUf
z?nSxF_5xXVh}XnN3Nj6tLAd+KK0{f%)!RCDTG6zupKN=Ld7#sk=q)Jo)zi7LqS$+3
z<dzAKta)T`eX71fkgE{(4?7=0?kg)SyD<Cfdl|cpKsykAQ=eCV(*>toP#1P?nG&yx
z55nO)g8UpiTc0yVkQrr#Wp`zFZ5Ft$Yh2g$vboj|nhbQl8l4pz6Wb709z7*q6(25+
z+ytCYc>j@iD0*Tx^+4GqEG2IF5*6mHoV{R47T5Q&8;h$K7p6?=Hc>e`41E<{w=KEt
zr0&jty8U{hWKP9*LB1&}mBn+)&+6{=eYB-?TxktM84cV*dTAk0MCIr~kwMpY`?~IY
zc%oZALXnsFY+Fo&ik9__K>iW0j-M?e^MRoOOMB|+nC|RU?as&}irh)-{Z#pVBapwu
z#>E~@rjnz9zXmLA#K^pPYv*OWu$i%WirtFoKzsEe`Jxgpamy!W8mo`anNl=my+(9A
z@Myr&#|+&*^omSBoY8IT>>Dcv*YD83L#JyKeYY^5iO1uP442!1AdB0#tI?w}d5X<w
z<Z0O9{hZE?1AVtJ--*Rysj{(UGg0OQC_BJ-CZox7mm|*^?j;9#q~0L&H^zM<kOdMo
ziC+R&1th1&GhglMaD^^M9wqrJx>st@Hv(CpY;4&q5xF}cIZ2t+bzOa^Q}dKuTXCEs
zQ?d~6n?(DJh^Y6jHxuEHf#@M6i>ntGcJ`UhM;=dq+T~|DrXR>7eIt;6#A30OI(h*C
zJ;r<D&F<{`9nN`*ub=h>N%9swFKFtU1NlsRP5fZ-<ayw8wzWj$l(Mm9e`)V~9gZrJ
z<Z}F}gzK9F`Alp~Y(vwjY1Ut5sOk?p`d+&u&o}3uw_mcpU<2u*Z@=gpfqbU2w(=HL
z{efkfq;brb+dGl0-H|6*UoahijRSp?Xy5UMdOrnhAFUG6q_(?A+0kuls-;BwCec0;
z8x?E7<5Dc&zErfLs;Vlo``6o;XM$UCB4D$wBp)^BS&Y9|5gi|`6_E!m%N(#NvgwT7
zzu3k+4rE=^nj!U$le*fn#$H83eZ!A|Pi$(aYMZ-Js|oXX*NqhL_B(X_9xMAKVD3tH
z{?Bd2?<rMPRgw41dk5rsc>}$OHxOZ~)97qVeVBSTm-}fYY7<wY{K}%lc1`L2Et{>h
zx{u}a_Czci2l%uB{k6sT-DYvs;)7BqRU#rq2%|sJkG>colCO>;Dxlyw9x_}S5;ci`
z2=bhYRwX65B8UT&GDVYmn>ca>FwBy~0CD6@;GyO(v@(x@v+OP$$Zf0L`BsP5OsJ}=
zifoE(imK`*9^QCC(zox<fB1Z0q@p8HrbaZ9O4KC&gz$$~#=NrFmGCwM0K|%7TNAa3
z-=nw|WhT?YJgw$D3MCeWw=0dmwHUukR8<nSi3>M5n_gGdMZjrf|N6xNic#XDYDc}A
zs7ZX=zf7}39laIUVo{>FWu@V+InNpHw2@xA&7mdat`+V*Gwj9hoXV=oBQ>ITqKvgD
z^Q5M9W~?ao4~rU|lBiAGjB>d(0guAy_-HM(_+RIVw0RWsSCcz!#_x<oZQ??8^jXWs
z0XR(@c|Kkp4{a%;sd(NVhKZm3wi#8Mx2(TC;oh^>V*GYdRY_DQT$F|Qzhd4IgGJ<l
zM0LXElTU|~jVXIY(dR8n>?+eR0MU5VA$=r>9Yhyei`@aLDwVaB^AI{ME3k%eWo1p}
zt>H+^eHO!?v~=mx0%$T%-RlF6#NKIFno{p!OR+m-aqZ$83AS*&4T^p*QJrvu)^!|b
z5n!t;PQlgzTTg)I+m`2ywj>0wc9nZ?p!>-yt1B;3^d~`Tc8D-LULF6>ASEY%eewqg
z_B!GSC&R8ekM|X8LhzWy*ll>I`k|v#^tK>1yFx_nj910K9H^#Bm95yum}6j<dBony
zRYtA-5WAzQ;)ruQxp&~?5FBm~Qc%U--@B*YvGCc`mX$Ws3Omfcg9F<(ySQfYg}}I=
z^tyu}r&m^2#)8rtxMkqez;+w41sUb4YfOoI@lfnFOZ)4x%We&%kytz)cT_Z|-`1<q
zIj*{a9Gu?Nz|B^B?EyzTb;NLE42M_}K;Q*ivD;|vFW&|NomX@l<;bIJk3KgjIT2ZB
zD|RFm8DTv24z?u#SWCVQ!X<%e^#E19I4CVoy*Dg|Kh!8{%{&GT#60me@q=lx@|n{J
z6Beyn6v$zgj_15?E4HFvFh<3ic?8zmirorjB01KCr4u5Nf=F=?LaF@Jo3>($kzqzr
zTg5|-Co|#$OR-xaDn&u)_=Qn(S`a$XQ==QP^~9y}IRj#=U&6)!PfWGBLhd_?o)Cmi
zQ!foG#dnGhGgxc3As^X^+nT20q!B@A`N&~GJ+ce3wd+r1#Q-DL%%g#H*d10s>_Bp}
zK}cuxKm1RJe?AByMXgj8Dq3JXdu@J#Y0umsgS-Az?h$b!LFH|$EH&SVwPtAQdt0J*
zRYdy-q2o8kF+ug_+GZoDsN@-eq?O=EApfYM637VWH(tuK@)Wc?iWXQ2EbK6vZjb0Y
z+WNd^Yb5iMy{2JFOOmOi&0P+=5KB+fif%JlG7kZnXLG;J?_#fN&K5h~{KlIf2BM^P
z=Cp!rH=tJTi9wA#Gf$k^D9As9(DDmK-wbS%pzt4-B5yMwn`}fDdu1MNQ1rQ=wERF2
zo4Xr!5s$|m>}?<4W-Kn|4VvVZ54RM%6@tBzOB)FCWFSHZF`V?yR{v2p8St!(JOCaX
zX)AV18ntnGTCP`+G>%yrh?2*l)?ynr7%<j4|9Y6iZN+ZMlyOu3rJ~hAX!*d3$zPuw
z)C=QdZN*mAw~Zp}Z3ze*VJr6T7)(}uGJ{4%gF3|>)J|=BQ|t|fh-dLhMXfo1cf5DK
zIPmwN^s+&a*Ipg->LUS4+WY%&V1wD`@U0txtb1#B!Pla0G~ZI}R>Well!|uQFCk=p
z5!54k{Lk_JfUPZ6n`0AVpPEE0kpi)Gw90@wGw`dHVz(hWCVD@xIw-yFpy;Zy!m`DI
z2{v*n=>w^JM@3(NU8S4)k_{06RRXD+gLLte8od;-Rl|}kpE}~q3{udDv9^D$d7Y|x
z$EY>)5O9fTY)tG875#3Inq4AHFCAO@e?dxmc;{FPuB?S7^C)U<SEs<&zj4DQf$SD&
zCN?&9A22sa&5lrXc64ks5u|3kI)11i$JvT6D$hZad7Pnxp2OaxIg;0y3iOl+Xlibz
z6545GP23tQj9nYJs!(PzdfmFhb#FnFd6r#vYrP;g-w)YpN6;Z6qNRnUmj>LQ^D~qi
zqlMASLeYlvtwmOzYJOsDQH1T)+SB92Gu&w-ZN={h5z*+ls4K`+f}2Ed0j5O9MSm8M
zUMu3O<EPrZWhuI%`S-ibBSx0n5=@IS&1U@0C@U<xUyYs!Sbc?|=&F>H`bxAg`cT)O
zwu2b4dm5U<E}!f=O1G(Xz(?3CrKpP()Q@=lszsYE#qSbTl|)Tqiio(tVSdFG<TaJx
zC&s(8Xw{<q^YZil30Nrs7G!-{VcD*it-D5^)vlWaU1dYGi6P1Qg6~_5-z6fVv2n3Q
z`ko%8s^3O<0?4+XL<(h@AZ6>ut~)wx;{fF6=U<K`<213X<sVu#E-7)#&jWeDqWH%J
z{bj_XSKbpy{ca{axaPqF9nUE?nA4heavnzeE3@?~`gc)z4&lkh`o<OKoqpc?0jJv`
z@ju6hN>bKiv4T_4Gh>CZ$D9A$s`o@a10Kz1#}2XQ-k}XUxfHm`Qv7by)O=(?)30>(
z-9Ze;V-U(94{wW#Zgn;~@8@#Y8%fGl*e(72VDo^@t?H24^cXF1%PT=jEhbS%Ufz)_
zX5IO&MUB0Rcy;_x5qS-;v&|;WEz!d0k6M1A?S5&odH^)Q5E_#=TGrUBFk<Fn*EkqV
z8(*|BPrV-UIAFPtluj*~Q_-uAMagAU^&afKApm%#w6L^I(S95Aq@TL5+x!AR_12(j
zefD&s<Dx57^jvHiNZa>AyT^Pu5px%owjsA*R2>8RZeKnT8yj1Wat{6*XiLP4v|Bpc
zu6dSTefQfy0EJ8PgNbu00y`aaPbWGqy3%h033A7|Md!AAKvahZ40*`SekITeqN?{4
zUw4VUEh2jvJ{zd9Q5!q#+|!|Xn)+h}v6*gPNhKeO{#UeD9+i@d_-LR-Q^rmCS4ZFM
z^k_5Bc)#eOc!tQyb#;T{<6Sq>^=JFY1Ro~Ro|AGqz58@(o+qwZ_!RI&mg3#z%zWOS
zPdf{jePl-uC(#Qh7ftT;9PKU-RTQJM=&EV7PY7I4>`uEO2(7(^EN@{ulA7$YLt>Za
zS$@reXBBL|ENz9rRmJYK-_kb%`9^mQB*;yj&2^f|=s4H>Na|-;I@_*21THIar!5#g
zZ$@uB<uqsMhJilVnZNTlnZ(S<Jda&{@4r<7-;HqLuy-3P`<}x5Ba=zAh+K8SDHmjP
zRA0siN$;auZ&G+U)6e_F*>!b;swcYTBLddlW9ecN{bcEw(u`kCWo({=n`jWsC1hWD
zmn|nL^5Vof<<Wq3_gFf(g>5rprZ=@5$cS!FN=w{nHv^Y<_c|X?;CDmV`qPA)2;_|C
zJ)J~#q8Q;3pr0V$DJv|yqdV7kyZ?b1I%pOFcJOO~06(Z>@XHh2ia=Tp?dj|qfs|#X
zV@v0C_xh|JhfA)#Xq0;9S&QYWU7><5GH}IGcforh<<Mg$U4QW*$)tC^pbxBY7g*i;
z)ETb2CYz&QvR+FmnNvQU@VE1Ris;;ee&)Y(si?jyPXE=6eaz0rD+PW;x_!e=&~w8y
zP&V{vA+E%ob{FJCZ_hfA+d+Q4!oB<Bu#@SCv)mcO8cBXn;WE(ucKddNik7XMy<kZw
z$z?&0%ro9C?^l4-04MpBPfytb+==n#uefHRtplsN&BSZVPg2iJ2R)z8yA1WJ5va$n
z04BH<2O9Jl;0r$F<0sD}dar8g!IkcPft)_tl1bNJd`L3oO%~-s;P`G_+G`Bt%lo5A
z*OniX@?;G##E*DBlLVd?<Vi7F#lC}{S$5g2^?r+tcgy?b<D4vb<3Y{_?Mds(USS~L
zUOpDjDW4!JOR&5J#0RzuJfrAKVBS#l&HPB}jmNIO_urXZ?7FUbI&b6t+Sqs~$kBp#
zEa_7zPQu@xJ?9PN>znIhxBOdzB-m{yYA<0s@CoUjoLdC_RFMYI2<drBKZX4z-OC}U
zpD(8k<a^me(|-5+=skVTbDf+uknc?`-dg>_%U>FQ{F{QDV<Y}vNCYJIrK00sd~5X!
zZ-$*rORN=K6}!{U6i8sLhv{BN&aDGkt=21XD^3SpOm04f^+s~)7G}NFv*g-~M&X&I
zz`?e)_7ZYt1hPJ<U2)CAS3KlYA${<3A3Y{tRN^IW`NXgj3FL|B3HP3jGIY=wfv{hs
z?kV&Gd8Bww`B`Bm5%>;;k~!tmRpqxKmUHY$ggOTbd@W|h>;-o@=~g~32CcmZ(huZP
z*L6c#Qwwcx)EU3~eu4M#)*FBy(zhOz4WA(Vc*X4b_Rmu4fY3MAopJ5OU-3Ne55Ngw
z=hAzS<r>i$E3cm4<;Fa#S%fLx%;<SD@;?7w;}xpR0tSYiPp+cyZv#JD?#{nI>|7v6
z%+t&Sx8gttovrEW#J+c9pRo<(_XYjAA(zT+bILr;q$}^_I-n%%TtX%Va*u;|O)eJt
z=cIX>ndnxG0iB}+*J?6Hs8{rU;O3R?g4e@NrXzCNJk3mS%a1phAA?Szch(AJN}a+2
z2j`|`?jPO>JDINN73OJX()AY~l1h5hNw;4*FzkGM<Zl8C^83sDT$<B#MXxeXGtqd|
z`Si7clSIu_&~MWDj)WXV-vwFZ7+tvZngu~0AmIzW(mX9GzG><}wmD}BW-{mmVBfHF
z>V$uQt`LzWgOA#}D&5n*_tNXl(~|M78!0gBPsNiGU>xb!AYm4xeFCgOWVtb^<;#At
z@U0MY@tr-&Ji8NhqxoCR;L)ny$*LS9$g#jE?A+!)23}NjEh5i}n&(!ycfUnI=Q)PK
z9&MfukO^+Z0hrXGj%N<_kb?vrCNNClbEHSA0U%A=Xf#QBC+aq!Uf>f&HVfVc57_`_
my~EB8%iIStI!$X&;{O3r3)blbBiM}q0000<MNUMnLSTX!m`rj2

literal 0
HcmV?d00001

diff --git a/manual/src/theme/index.hbs b/manual/src/theme/index.hbs
new file mode 100644
index 000000000000..5679e803b1ba
--- /dev/null
+++ b/manual/src/theme/index.hbs
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
+	<head>
+		<!-- Book generated using mdBook -->
+		<meta charset="UTF-8">
+		<title>{{ title }}</title>
+		{{#if is_print }}
+		<meta name="robots" content="noindex" />
+		{{/if}}
+		{{#if base_url}}
+		<base href="{{ base_url }}">
+		{{/if}}
+
+		<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+		<meta name="description" content="{{ description }}">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<meta name="theme-color" content="#ffffff" />
+
+		<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
+		<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
+		<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
+	</head>
+	<body>
+		<!-- Provide site root to javascript -->
+		<script type="text/javascript">
+			var path_to_root = "{{ path_to_root }}";
+			var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
+		</script>
+		<!-- Work around some values being stored in localStorage wrapped in quotes -->
+		<script type="text/javascript">
+			try {
+				var theme = localStorage.getItem('mdbook-theme');
+				var sidebar = localStorage.getItem('mdbook-sidebar');
+
+				if (theme.startsWith('"') && theme.endsWith('"')) {
+					localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
+				}
+				if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
+					localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
+				}
+			} catch (e) { }
+		</script>
+
+		<!-- Set the theme before any content is loaded, prevents flash -->
+		<script type="text/javascript">
+			var theme;
+			try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
+			if (theme === null || theme === undefined) { theme = default_theme; }
+			var html = document.querySelector('html');
+			html.classList.remove('no-js')
+			html.classList.remove('{{ default_theme }}')
+			html.classList.add(theme);
+			html.classList.add('js');
+		</script>
+
+		<!-- Hide / unhide sidebar before it is displayed -->
+		<script type="text/javascript">
+			var html = document.querySelector('html');
+			var sidebar = 'hidden';
+			if (document.body.clientWidth >= 1080) {
+				try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+				sidebar = sidebar || 'visible';
+			}
+			html.classList.remove('sidebar-visible');
+			html.classList.add("sidebar-" + sidebar);
+		</script>
+
+		<header>
+			<nav id="void-nav">
+				<ul>
+					<li><a id="skip-to-content" tabindex="1" href="#main">Skip to content</a></li>
+					<li>
+						<a id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M1 3v2h18V3zm0 8h18V9H1zm0 6h18v-2H1z"/>
+							</svg>
+						</a>
+					</li>
+					<li>
+						<a id="theme-toggle" class="icon-button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
+							<svg id="icon-theme-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/>
+							</svg>
+							<svg id="icon-theme-dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/>
+							</svg>
+						</a>
+					</li>
+					{{#if print_enable}}
+					<li>
+						<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+								<path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if git_repository_edit_url}}
+					<li>
+						<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
+								<path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/>
+							</svg>
+						</a>
+					</li>
+					{{/if}}
+					{{#if search_enabled}}
+					<li>
+						<a id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
+							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+								<path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46C10.79 14.43 9.21 15 7.5 15 3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/>
+							</svg>
+						</a>
+					</li>
+					<noscript>
+						<li class="js-unavailable">Search functionality requires JavaScript</li>
+					</noscript>
+					{{/if}}
+				</ul>
+				<ul id="nav-right">
+					<li><a href="https://www.voidlinux.org">Home</a></li>
+					<li><a href="https://www.voidlinux.org/news/">News</a></li>
+					<li><a href="https://www.voidlinux.org/download/">Download</a></li>
+					<li><a href="https://www.voidlinux.org/packages/">Packages</a></li>
+					<li><a href="https://docs.voidlinux.org">Documentation</a></li>
+					<li><a href="https://man.voidlinux.org/">Manual Pages</a></li>
+					<li><a href="https://github.com/void-linux">GitHub</a></li>
+				</ul>
+			</nav>
+		</header>
+
+		<div id="content">
+			<!-- Hide / unhide sidebar before it is displayed -->
+			<script type="text/javascript">
+				var html = document.querySelector('html');
+				var sidebar = 'hidden';
+				if (document.body.clientWidth >= 1080) {
+					try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
+					sidebar = sidebar || 'visible';
+				}
+				html.classList.remove('sidebar-visible');
+				html.classList.add("sidebar-" + sidebar);
+			</script>
+
+			<nav id="sidebar" aria-label="Table of contents">
+				{{#toc}}{{/toc}}
+			</nav>
+
+			<div id="page-wrapper" class="page-wrapper">
+
+
+					{{#if search_enabled}}
+					<div id="search-wrapper" class="hidden">
+						<form id="searchbar-outer" class="searchbar-outer">
+							<input type="search" name="search" id="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
+						</form>
+						<div id="searchresults-outer" class="searchresults-outer hidden">
+							<div id="searchresults-header" class="searchresults-header"></div>
+							<ul id="searchresults">
+							</ul>
+						</div>
+					</div>
+					{{/if}}
+
+					<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
+					<script type="text/javascript">
+						document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
+						document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
+						Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
+							link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
+						});
+					</script>
+
+					<main id="main">
+						{{{ content }}}
+					</main>
+
+					<nav class="nav-wrapper" aria-label="Page navigation">
+						<!-- Mobile navigation buttons -->
+						{{#previous}}
+							<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+								</svg>
+							</a>
+						{{/previous}}
+
+						{{#next}}
+							<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+								<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+									<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+								</svg>
+							</a>
+						{{/next}}
+
+						<div style="clear: both"></div>
+					</nav>
+
+				<nav class="nav-wide-wrapper" aria-label="Page navigation">
+					{{#previous}}
+						<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
+							</svg>
+						</a>
+					{{/previous}}
+
+					{{#next}}
+						<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
+							<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+								<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
+							</svg>
+						</a>
+					{{/next}}
+				</nav>
+			</div>
+		</div>
+
+		{{#if livereload}}
+		<!-- Livereload script (if served using the cli tool) -->
+		<script type="text/javascript">
+			var socket = new WebSocket("{{{livereload}}}");
+			socket.onmessage = function (event) {
+				if (event.data === "reload") {
+					socket.close();
+					location.reload(true); // force reload from server (not from cache)
+				}
+			};
+
+			window.onbeforeunload = function() {
+				socket.close();
+			}
+		</script>
+		{{/if}}
+
+		{{#if search_js}}
+		<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
+		<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
+		{{/if}}
+
+		<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
+		{{#if is_print}}
+		<script type="text/javascript">
+		window.addEventListener('load', function() {
+			window.setTimeout(window.print, 100);
+		});
+		</script>
+		{{/if}}
+	</body>
+</html>

From abe7d2f943a1527ea6deec9263831026f62b55a3 Mon Sep 17 00:00:00 2001
From: classabbyamp <dev@placeviolette.net>
Date: Fri, 1 Apr 2022 15:24:18 -0400
Subject: [PATCH 3/3] manual/: add linkcheck, CI for checking Manual.md changes

---
 .github/workflows/manual.yml | 25 +++++++++++++++++++++++++
 Manual.md                    |  2 +-
 common/travis/manual-ci.sh   | 10 ++++++++++
 manual/book.toml             |  7 ++++++-
 4 files changed, 42 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/manual.yml
 create mode 100755 common/travis/manual-ci.sh

diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml
new file mode 100644
index 000000000000..5d6a00ce06bc
--- /dev/null
+++ b/.github/workflows/manual.yml
@@ -0,0 +1,25 @@
+name: Check Manual Changes
+
+on:
+  pull_request:
+    paths:
+      - 'Manual.md'
+      - 'manual/**'
+
+jobs:
+  only:
+    name: Check formatting and links
+    runs-on: ubuntu-latest
+    container:
+      image: ghcr.io/void-linux/void-linux:latest-mini-x86_64-musl
+    steps:
+      - name: Prepare container
+        run: |
+          xbps-install -Syu || xbps-install -Syu xbps
+          xbps-install -yu
+          xbps-install -y mdBook mdbook-linkcheck
+      - uses: actions/checkout@v1
+        with:
+          fetch-depth: 200
+      - run: ./common/travis/manual-ci.sh
+
diff --git a/Manual.md b/Manual.md
index b4e8d45015ee..cd950fafb0bc 100644
--- a/Manual.md
+++ b/Manual.md
@@ -2157,7 +2157,7 @@ otherwise the `debug` packages won't have debugging symbols.
 <a id="contributing"></a>
 ### Contributing via git
 
-To get started, [fork](https://help.github.com/articles/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
+To get started, [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the void-linux `void-packages` git repository on GitHub and clone it:
 
     $ git clone git@github.com:<user>/void-packages.git
 
diff --git a/common/travis/manual-ci.sh b/common/travis/manual-ci.sh
new file mode 100755
index 000000000000..34281e42bf12
--- /dev/null
+++ b/common/travis/manual-ci.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+echo "Checking links"
+RUST_LOG=linkcheck=debug mdbook build manual
+LINKCHECK=$?
+
+# Generate exit value
+if [ ! $LINKCHECK -eq 0 ] ; then
+    exit 2
+fi
diff --git a/manual/book.toml b/manual/book.toml
index 8f15412495eb..2a98cbe327af 100644
--- a/manual/book.toml
+++ b/manual/book.toml
@@ -5,7 +5,7 @@ src = "src"
 title = "XBPS Source Packages Manual"
 
 [preprocessor.gen_manual]
-command = "python3 generate_mdbook.py"
+command = "python3 manual/generate_mdbook.py"
 
 [output.html]
 # TODO: this should be a submodule or something
@@ -15,3 +15,8 @@ default-theme = "void-light"
 preferred-dark-theme = "void-dark"
 edit-url-template = "https://github.com/void-linux/void-packages/edit/master/Manual.md"
 limit-results = 100
+
+[output.linkcheck]
+optional = true
+follow-web-links = true
+exclude = [ "https://docs.github.com/en/get-started/quickstart/fork-a-repo" ]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR REVIEW] [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (22 preceding siblings ...)
  2022-07-18  5:13 ` [PR PATCH] [Updated] " classabbyamp
@ 2022-07-18  5:13 ` classabbyamp
  2022-08-21 18:47 ` classabbyamp
  2022-08-21 18:47 ` [PR PATCH] [Closed]: " classabbyamp
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-07-18  5:13 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 185 bytes --]

New review comment by classabbyamp on void-packages repository

https://github.com/void-linux/void-packages/pull/35856#discussion_r922976374

Comment:
removed, it's only linkcheck now.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (23 preceding siblings ...)
  2022-07-18  5:13 ` [PR REVIEW] " classabbyamp
@ 2022-08-21 18:47 ` classabbyamp
  2022-08-21 18:47 ` [PR PATCH] [Closed]: " classabbyamp
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-08-21 18:47 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 226 bytes --]

New comment by classabbyamp on void-packages repository

https://github.com/void-linux/void-packages/pull/35856#issuecomment-1221600550

Comment:
kinda lost interest in this, may reconsider at a later date if i have more time

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PR PATCH] [Closed]: [RFC] render Manual.md with mdBook
  2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
                   ` (24 preceding siblings ...)
  2022-08-21 18:47 ` classabbyamp
@ 2022-08-21 18:47 ` classabbyamp
  25 siblings, 0 replies; 27+ messages in thread
From: classabbyamp @ 2022-08-21 18:47 UTC (permalink / raw)
  To: ml

[-- Attachment #1: Type: text/plain, Size: 762 bytes --]

There's a closed pull request on the void-packages repository

[RFC] render Manual.md with mdBook
https://github.com/void-linux/void-packages/pull/35856

Description:
This idea was mentioned once on IRC, so I thought I might take a stab at it and submit it for discussion.

I set out to create a version of `Manual.md` that is a bit more user/noob-friendly than a giant monolith of a document, while still preserving the workflow surrounding it. Thus, I created a mdBook pre-processor that processes Manual.md and builds the book, without the need for converting Manual.md *into* an mdBook.

A live example of what is currently generated by mdBook is available here: https://book.qrm.cat/

The only thing this needs is a deployment mechanism.

[ci skip]

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2022-08-21 18:47 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-26  2:49 [PR PATCH] [RFC] Create a version of Manual.md in mdBook classabbyamp
2022-03-06  3:06 ` [PR PATCH] [Updated] " classabbyamp
2022-03-15 19:17 ` classabbyamp
2022-04-01 19:24 ` classabbyamp
2022-04-01 19:26 ` classabbyamp
2022-04-01 19:29 ` classabbyamp
2022-04-01 19:30 ` classabbyamp
2022-04-02  5:07 ` classabbyamp
2022-04-02 17:50 ` classabbyamp
2022-04-02 21:40 ` classabbyamp
2022-04-07 17:40 ` classabbyamp
2022-04-19 15:28 ` classabbyamp
2022-04-23  1:23 ` [PR PATCH] [Updated] [RFC] render Manual.md with mdBook classabbyamp
2022-05-02  2:09 ` classabbyamp
2022-05-05 17:24 ` classabbyamp
2022-05-05 18:41 ` classabbyamp
2022-05-17 17:09 ` classabbyamp
2022-05-28  7:03 ` classabbyamp
2022-05-28 20:05 ` classabbyamp
2022-05-29  0:12 ` classabbyamp
2022-06-26 23:49 ` classabbyamp
2022-07-17 18:33 ` [PR REVIEW] " ericonr
2022-07-17 18:34 ` ericonr
2022-07-18  5:13 ` [PR PATCH] [Updated] " classabbyamp
2022-07-18  5:13 ` [PR REVIEW] " classabbyamp
2022-08-21 18:47 ` classabbyamp
2022-08-21 18:47 ` [PR PATCH] [Closed]: " classabbyamp

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).