jekyll-theme-docsteer 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +107 -0
- data/_data/navigation.yml +44 -0
- data/_includes/buy-me-a-coffee.html +10 -0
- data/_includes/doc-footer.html +38 -0
- data/_includes/faq-list.html +52 -0
- data/_includes/footer.html +43 -0
- data/_includes/head-custom.html +4 -0
- data/_includes/head.html +61 -0
- data/_includes/navbar.html +70 -0
- data/_includes/scripts.html +22 -0
- data/_includes/search-modal.html +24 -0
- data/_includes/sidebar.html +48 -0
- data/_includes/toc.html +11 -0
- data/_layouts/default.html +45 -0
- data/_layouts/doc.html +37 -0
- data/_layouts/faq.html +51 -0
- data/_layouts/home.html +74 -0
- data/_layouts/page.html +13 -0
- data/_sass/docsteer/_base.scss +56 -0
- data/_sass/docsteer/_components.scss +166 -0
- data/_sass/docsteer/_content.scss +300 -0
- data/_sass/docsteer/_faq.scss +140 -0
- data/_sass/docsteer/_footer.scss +51 -0
- data/_sass/docsteer/_home.scss +184 -0
- data/_sass/docsteer/_layout.scss +52 -0
- data/_sass/docsteer/_lightbox.scss +73 -0
- data/_sass/docsteer/_navbar.scss +169 -0
- data/_sass/docsteer/_print.scss +13 -0
- data/_sass/docsteer/_reset.scss +57 -0
- data/_sass/docsteer/_responsive.scss +31 -0
- data/_sass/docsteer/_search.scss +98 -0
- data/_sass/docsteer/_sidebar.scss +159 -0
- data/_sass/docsteer/_skins.scss +193 -0
- data/_sass/docsteer/_syntax.scss +72 -0
- data/_sass/docsteer/_toc.scss +46 -0
- data/_sass/docsteer/_tokens.scss +96 -0
- data/_sass/docsteer/_utilities.scss +18 -0
- data/_sass/docsteer.scss +22 -0
- data/assets/css/main.scss +4 -0
- data/assets/images/favicon.svg +14 -0
- data/assets/images/logo.svg +14 -0
- data/assets/js/lightbox.js +96 -0
- data/assets/js/main.js +275 -0
- data/assets/js/search.js +186 -0
- data/assets/vendor/fontawesome/NOTICE.md +18 -0
- data/assets/vendor/fontawesome/css/all.min.css +9 -0
- data/assets/vendor/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
- data/assets/vendor/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
- data/assets/vendor/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
- data/assets/vendor/fontawesome/webfonts/fa-v4compatibility.woff2 +0 -0
- metadata +190 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// ── Design tokens (type, sizing, radius, motion) ─────────────
|
|
2
|
+
// Colours are NOT here — see _skins.scss.
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
// Type — system stack, ordered so each OS lands on its best UI face.
|
|
6
|
+
// No webfont request: nothing to block, nothing to FOUT.
|
|
7
|
+
--font-sans: "Inter var", Inter, -apple-system, BlinkMacSystemFont,
|
|
8
|
+
"Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial,
|
|
9
|
+
"Apple Color Emoji", "Segoe UI Emoji", sans-serif;
|
|
10
|
+
--font-display: var(--font-sans);
|
|
11
|
+
--font-mono: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Code",
|
|
12
|
+
"JetBrains Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
13
|
+
|
|
14
|
+
--fs-root: 16px;
|
|
15
|
+
--lh-body: 1.7; // long-form reading
|
|
16
|
+
--lh-tight: 1.25; // headings
|
|
17
|
+
--measure: 74ch; // max comfortable line length
|
|
18
|
+
|
|
19
|
+
// Type scale (major third-ish, fluid at the top end)
|
|
20
|
+
--fs-xs: 0.75rem;
|
|
21
|
+
--fs-sm: 0.8125rem;
|
|
22
|
+
--fs-base: 1rem;
|
|
23
|
+
--fs-md: 1.0625rem;
|
|
24
|
+
--fs-lg: 1.1875rem;
|
|
25
|
+
--fs-h4: 1.0625rem;
|
|
26
|
+
--fs-h3: 1.3125rem;
|
|
27
|
+
--fs-h2: clamp(1.4rem, 1.2rem + 0.7vw, 1.65rem);
|
|
28
|
+
--fs-h1: clamp(1.95rem, 1.55rem + 1.7vw, 2.6rem);
|
|
29
|
+
|
|
30
|
+
// Tracking — tighter as type grows
|
|
31
|
+
--tr-tight: -0.022em;
|
|
32
|
+
--tr-snug: -0.014em;
|
|
33
|
+
--tr-wide: 0.06em;
|
|
34
|
+
|
|
35
|
+
// Spacing scale
|
|
36
|
+
--sp-1: 4px;
|
|
37
|
+
--sp-2: 8px;
|
|
38
|
+
--sp-3: 12px;
|
|
39
|
+
--sp-4: 16px;
|
|
40
|
+
--sp-5: 24px;
|
|
41
|
+
--sp-6: 32px;
|
|
42
|
+
--sp-7: 48px;
|
|
43
|
+
--sp-8: 64px;
|
|
44
|
+
--sp-9: 96px;
|
|
45
|
+
|
|
46
|
+
// Radius
|
|
47
|
+
--r-sm: 6px;
|
|
48
|
+
--r-md: 10px;
|
|
49
|
+
--r-lg: 14px;
|
|
50
|
+
--r-xl: 20px;
|
|
51
|
+
--r-pill: 999px;
|
|
52
|
+
|
|
53
|
+
// Layout metrics
|
|
54
|
+
--nav-h: 60px;
|
|
55
|
+
--sidebar-w: 282px;
|
|
56
|
+
--toc-w: 232px;
|
|
57
|
+
--content-max: 78ch;
|
|
58
|
+
--shell-max: 1440px;
|
|
59
|
+
|
|
60
|
+
// Motion
|
|
61
|
+
--ease: cubic-bezier(.4, 0, .2, 1);
|
|
62
|
+
--ease-out: cubic-bezier(.16, 1, .3, 1);
|
|
63
|
+
--dur: .16s;
|
|
64
|
+
--dur-lg: .28s;
|
|
65
|
+
|
|
66
|
+
// Elevation — hairline-first. Shadows stay tight and tinted per skin,
|
|
67
|
+
// so cards read as crisp rather than blurry.
|
|
68
|
+
--shadow-sm: 0 1px 2px hsl(var(--shadow-color) / .06);
|
|
69
|
+
--shadow-md:
|
|
70
|
+
0 1px 2px hsl(var(--shadow-color) / .06),
|
|
71
|
+
0 6px 16px -4px hsl(var(--shadow-color) / .10);
|
|
72
|
+
--shadow-lg:
|
|
73
|
+
0 1px 2px hsl(var(--shadow-color) / .07),
|
|
74
|
+
0 12px 32px -8px hsl(var(--shadow-color) / .16);
|
|
75
|
+
|
|
76
|
+
--focus-ring:
|
|
77
|
+
0 0 0 2px var(--bg),
|
|
78
|
+
0 0 0 4px hsl(var(--brand-h) var(--brand-s) 55% / .55);
|
|
79
|
+
|
|
80
|
+
color-scheme: light;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
:root[data-mode="dark"],
|
|
84
|
+
:root:not([data-mode="light"]) {
|
|
85
|
+
@media (prefers-color-scheme: dark) { color-scheme: dark; }
|
|
86
|
+
}
|
|
87
|
+
:root[data-mode="dark"] { color-scheme: dark; }
|
|
88
|
+
|
|
89
|
+
@media (prefers-reduced-motion: reduce) {
|
|
90
|
+
*, *::before, *::after {
|
|
91
|
+
animation-duration: .001ms !important;
|
|
92
|
+
animation-iteration-count: 1 !important;
|
|
93
|
+
transition-duration: .001ms !important;
|
|
94
|
+
scroll-behavior: auto !important;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// ── Utilities ────────────────────────────────────────────────
|
|
2
|
+
.visually-hidden {
|
|
3
|
+
position: absolute !important;
|
|
4
|
+
width: 1px; height: 1px;
|
|
5
|
+
padding: 0; margin: -1px;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
clip: rect(0 0 0 0);
|
|
8
|
+
white-space: nowrap;
|
|
9
|
+
border: 0;
|
|
10
|
+
}
|
|
11
|
+
.text-center { text-align: center; }
|
|
12
|
+
.text-soft { color: var(--text-soft); }
|
|
13
|
+
.mt-0 { margin-top: 0 !important; }
|
|
14
|
+
.mb-0 { margin-bottom: 0 !important; }
|
|
15
|
+
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--sp-4); }
|
|
16
|
+
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--sp-4); }
|
|
17
|
+
.no-lightbox { cursor: default; }
|
|
18
|
+
.lead { font-size: 1.12rem; color: var(--text-soft); }
|
data/_sass/docsteer.scss
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// DocSteer — main stylesheet
|
|
2
|
+
// All colour values live in _skins.scss. Sizing tokens live in _tokens.scss.
|
|
3
|
+
|
|
4
|
+
@import "docsteer/tokens";
|
|
5
|
+
@import "docsteer/skins";
|
|
6
|
+
@import "docsteer/reset";
|
|
7
|
+
@import "docsteer/base";
|
|
8
|
+
@import "docsteer/layout";
|
|
9
|
+
@import "docsteer/navbar";
|
|
10
|
+
@import "docsteer/sidebar";
|
|
11
|
+
@import "docsteer/toc";
|
|
12
|
+
@import "docsteer/content";
|
|
13
|
+
@import "docsteer/components";
|
|
14
|
+
@import "docsteer/faq";
|
|
15
|
+
@import "docsteer/footer";
|
|
16
|
+
@import "docsteer/home";
|
|
17
|
+
@import "docsteer/search";
|
|
18
|
+
@import "docsteer/lightbox";
|
|
19
|
+
@import "docsteer/syntax";
|
|
20
|
+
@import "docsteer/utilities";
|
|
21
|
+
@import "docsteer/responsive";
|
|
22
|
+
@import "docsteer/print";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="DocSteer">
|
|
2
|
+
<title>DocSteer</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="ds-bg" x1="0" y1="0" x2="1" y2="1">
|
|
5
|
+
<stop offset="0" stop-color="#1668f0"/>
|
|
6
|
+
<stop offset="1" stop-color="#00b4d8"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
<rect width="64" height="64" rx="14" fill="url(#ds-bg)"/>
|
|
10
|
+
<!-- Compass needle: solid north, translucent south. One shape, so it still
|
|
11
|
+
reads at 16px where a letterform would turn to mush. -->
|
|
12
|
+
<path d="M32 11 L42 32 L22 32 Z" fill="#ffffff"/>
|
|
13
|
+
<path d="M22 32 L42 32 L32 53 Z" fill="#ffffff" opacity=".45"/>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="DocSteer">
|
|
2
|
+
<title>DocSteer</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="ds-logo-bg" x1="0" y1="0" x2="1" y2="1">
|
|
5
|
+
<stop offset="0" stop-color="#1668f0"/>
|
|
6
|
+
<stop offset="1" stop-color="#00b4d8"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
<!-- Same mark as the favicon. The navbar renders this beside the site title,
|
|
10
|
+
so it is the mark only — never a wordmark, or the name shows twice. -->
|
|
11
|
+
<rect width="64" height="64" rx="14" fill="url(#ds-logo-bg)"/>
|
|
12
|
+
<path d="M32 11 L42 32 L22 32 Z" fill="#ffffff"/>
|
|
13
|
+
<path d="M22 32 L42 32 L32 53 Z" fill="#ffffff" opacity=".45"/>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* DocSteer — image lightbox (no dependencies) */
|
|
2
|
+
(function () {
|
|
3
|
+
"use strict";
|
|
4
|
+
var DocSteer = window.DocSteer || {};
|
|
5
|
+
if (!DocSteer.lightbox) return;
|
|
6
|
+
|
|
7
|
+
var box = document.getElementById("lightbox");
|
|
8
|
+
if (!box) return;
|
|
9
|
+
|
|
10
|
+
var scope = document.querySelector(".doc-content") || document.querySelector(".prose");
|
|
11
|
+
if (!scope) return;
|
|
12
|
+
|
|
13
|
+
var imgs = Array.prototype.slice.call(scope.querySelectorAll("img"))
|
|
14
|
+
.filter(function (img) {
|
|
15
|
+
return !img.classList.contains("no-lightbox") &&
|
|
16
|
+
!img.closest("a") &&
|
|
17
|
+
(img.naturalWidth === 0 || img.naturalWidth > 40);
|
|
18
|
+
});
|
|
19
|
+
if (!imgs.length) return;
|
|
20
|
+
|
|
21
|
+
var current = 0;
|
|
22
|
+
var lastFocus = null;
|
|
23
|
+
|
|
24
|
+
imgs.forEach(function (img, i) {
|
|
25
|
+
img.classList.add("lightbox-trigger");
|
|
26
|
+
img.setAttribute("role", "button");
|
|
27
|
+
img.setAttribute("tabindex", "0");
|
|
28
|
+
img.addEventListener("click", function () { openAt(i); });
|
|
29
|
+
img.addEventListener("keydown", function (e) {
|
|
30
|
+
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); openAt(i); }
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function caption(img) {
|
|
35
|
+
return img.getAttribute("alt") ||
|
|
36
|
+
(img.closest("figure") && img.closest("figure").querySelector("figcaption")
|
|
37
|
+
? img.closest("figure").querySelector("figcaption").textContent : "");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function build() {
|
|
41
|
+
box.innerHTML =
|
|
42
|
+
'<button class="lightbox__close" aria-label="Close (Esc)"><i class="fa-solid fa-xmark"></i></button>' +
|
|
43
|
+
'<span class="lightbox__count"></span>' +
|
|
44
|
+
'<figure class="lightbox__figure">' +
|
|
45
|
+
(imgs.length > 1 ? '<button class="lightbox__btn lightbox__prev" aria-label="Previous"><i class="fa-solid fa-chevron-left"></i></button>' : "") +
|
|
46
|
+
'<img class="lightbox__img" alt="">' +
|
|
47
|
+
(imgs.length > 1 ? '<button class="lightbox__btn lightbox__next" aria-label="Next"><i class="fa-solid fa-chevron-right"></i></button>' : "") +
|
|
48
|
+
'<figcaption class="lightbox__caption"></figcaption>' +
|
|
49
|
+
"</figure>";
|
|
50
|
+
|
|
51
|
+
box.querySelector(".lightbox__close").addEventListener("click", close);
|
|
52
|
+
var p = box.querySelector(".lightbox__prev");
|
|
53
|
+
var n = box.querySelector(".lightbox__next");
|
|
54
|
+
if (p) p.addEventListener("click", function (e) { e.stopPropagation(); show(current - 1); });
|
|
55
|
+
if (n) n.addEventListener("click", function (e) { e.stopPropagation(); show(current + 1); });
|
|
56
|
+
box.addEventListener("click", function (e) {
|
|
57
|
+
if (e.target === box || e.target.classList.contains("lightbox__figure")) close();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function show(i) {
|
|
62
|
+
current = (i + imgs.length) % imgs.length;
|
|
63
|
+
var src = imgs[current];
|
|
64
|
+
var full = src.getAttribute("data-full") || src.currentSrc || src.src;
|
|
65
|
+
var big = box.querySelector(".lightbox__img");
|
|
66
|
+
big.src = full;
|
|
67
|
+
big.alt = caption(src);
|
|
68
|
+
box.querySelector(".lightbox__caption").textContent = caption(src);
|
|
69
|
+
var count = box.querySelector(".lightbox__count");
|
|
70
|
+
count.textContent = imgs.length > 1 ? (current + 1) + " / " + imgs.length : "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function openAt(i) {
|
|
74
|
+
lastFocus = document.activeElement;
|
|
75
|
+
if (!box.dataset.built) { build(); box.dataset.built = "1"; }
|
|
76
|
+
show(i);
|
|
77
|
+
box.classList.add("is-open");
|
|
78
|
+
box.setAttribute("aria-hidden", "false");
|
|
79
|
+
document.body.style.overflow = "hidden";
|
|
80
|
+
box.querySelector(".lightbox__close").focus();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function close() {
|
|
84
|
+
box.classList.remove("is-open");
|
|
85
|
+
box.setAttribute("aria-hidden", "true");
|
|
86
|
+
document.body.style.overflow = "";
|
|
87
|
+
if (lastFocus && lastFocus.focus) lastFocus.focus();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
document.addEventListener("keydown", function (e) {
|
|
91
|
+
if (!box.classList.contains("is-open")) return;
|
|
92
|
+
if (e.key === "Escape") close();
|
|
93
|
+
else if (e.key === "ArrowLeft") show(current - 1);
|
|
94
|
+
else if (e.key === "ArrowRight") show(current + 1);
|
|
95
|
+
});
|
|
96
|
+
})();
|
data/assets/js/main.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/* DocSteer — core interactions (no dependencies) */
|
|
2
|
+
(function () {
|
|
3
|
+
"use strict";
|
|
4
|
+
var DocSteer = window.DocSteer || {};
|
|
5
|
+
var root = document.documentElement;
|
|
6
|
+
var $ = function (s, c) { return (c || document).querySelector(s); };
|
|
7
|
+
var $$ = function (s, c) { return Array.prototype.slice.call((c || document).querySelectorAll(s)); };
|
|
8
|
+
|
|
9
|
+
/* ---------- Dark / light mode ---------- */
|
|
10
|
+
function currentMode() {
|
|
11
|
+
return root.getAttribute("data-mode") ||
|
|
12
|
+
(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
|
13
|
+
}
|
|
14
|
+
var modeToggle = $("#modeToggle");
|
|
15
|
+
if (modeToggle) {
|
|
16
|
+
modeToggle.addEventListener("click", function () {
|
|
17
|
+
var next = currentMode() === "dark" ? "light" : "dark";
|
|
18
|
+
root.setAttribute("data-mode", next);
|
|
19
|
+
try { localStorage.setItem("docsteer-mode", next); } catch (e) {}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* ---------- Skin picker ---------- */
|
|
24
|
+
var picker = $("#skinPicker");
|
|
25
|
+
if (picker) {
|
|
26
|
+
var pbtn = picker.querySelector("button[aria-haspopup]");
|
|
27
|
+
var setActive = function () {
|
|
28
|
+
var cur = root.getAttribute("data-skin");
|
|
29
|
+
$$(".skin-picker__opt", picker).forEach(function (o) {
|
|
30
|
+
o.setAttribute("aria-checked", o.dataset.skinSet === cur);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
pbtn.addEventListener("click", function (e) {
|
|
34
|
+
e.stopPropagation();
|
|
35
|
+
var open = picker.classList.toggle("is-open");
|
|
36
|
+
pbtn.setAttribute("aria-expanded", open);
|
|
37
|
+
setActive();
|
|
38
|
+
});
|
|
39
|
+
$$(".skin-picker__opt", picker).forEach(function (o) {
|
|
40
|
+
o.addEventListener("click", function () {
|
|
41
|
+
root.setAttribute("data-skin", o.dataset.skinSet);
|
|
42
|
+
try { localStorage.setItem("docsteer-skin", o.dataset.skinSet); } catch (e) {}
|
|
43
|
+
picker.classList.remove("is-open");
|
|
44
|
+
pbtn.setAttribute("aria-expanded", "false");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
document.addEventListener("click", function () {
|
|
48
|
+
picker.classList.remove("is-open");
|
|
49
|
+
pbtn.setAttribute("aria-expanded", "false");
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* ---------- Mobile sidebar drawer ---------- */
|
|
54
|
+
var sidebar = $("#sidebar");
|
|
55
|
+
var sTog = $("#sidebarToggle");
|
|
56
|
+
var scrim = $("#sidebarScrim");
|
|
57
|
+
function closeSidebar() {
|
|
58
|
+
if (!sidebar) return;
|
|
59
|
+
sidebar.classList.remove("is-open");
|
|
60
|
+
if (scrim) scrim.hidden = true;
|
|
61
|
+
if (sTog) sTog.setAttribute("aria-expanded", "false");
|
|
62
|
+
document.body.style.overflow = "";
|
|
63
|
+
}
|
|
64
|
+
if (sTog && sidebar) {
|
|
65
|
+
sTog.addEventListener("click", function () {
|
|
66
|
+
var open = sidebar.classList.toggle("is-open");
|
|
67
|
+
if (scrim) scrim.hidden = !open;
|
|
68
|
+
sTog.setAttribute("aria-expanded", open);
|
|
69
|
+
document.body.style.overflow = open ? "hidden" : "";
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (scrim) scrim.addEventListener("click", closeSidebar);
|
|
73
|
+
$$(".sidebar__link").forEach(function (l) { l.addEventListener("click", closeSidebar); });
|
|
74
|
+
|
|
75
|
+
/* ---------- Collapsible sidebar groups ---------- */
|
|
76
|
+
$$(".sidebar__group-title").forEach(function (btn) {
|
|
77
|
+
btn.addEventListener("click", function () {
|
|
78
|
+
var group = btn.closest(".sidebar__group");
|
|
79
|
+
var collapsed = group.classList.toggle("is-collapsed");
|
|
80
|
+
btn.setAttribute("aria-expanded", !collapsed);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/* ---------- Heading anchors + TOC ---------- */
|
|
85
|
+
var content = $("#doc-content") || $(".doc-content");
|
|
86
|
+
if (content) {
|
|
87
|
+
var heads = $$("h2, h3", content).filter(function (h) { return h.id || h.textContent.trim(); });
|
|
88
|
+
heads.forEach(function (h) {
|
|
89
|
+
if (!h.id) {
|
|
90
|
+
h.id = h.textContent.trim().toLowerCase()
|
|
91
|
+
.replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
92
|
+
}
|
|
93
|
+
var a = document.createElement("a");
|
|
94
|
+
a.href = "#" + h.id;
|
|
95
|
+
a.className = "heading-anchor";
|
|
96
|
+
a.setAttribute("aria-label", "Link to this section");
|
|
97
|
+
a.innerHTML = '<i class="fa-solid fa-link" aria-hidden="true"></i>';
|
|
98
|
+
h.appendChild(a);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
var tocNav = $("#tocNav");
|
|
102
|
+
if (DocSteer.toc && DocSteer.toc.enabled && tocNav && heads.length >= (DocSteer.toc.min || 2)) {
|
|
103
|
+
var frag = document.createDocumentFragment();
|
|
104
|
+
heads.forEach(function (h) {
|
|
105
|
+
var link = document.createElement("a");
|
|
106
|
+
link.href = "#" + h.id;
|
|
107
|
+
link.textContent = h.firstChild ? h.firstChild.textContent.trim() : h.textContent.trim();
|
|
108
|
+
link.className = "lvl-" + (h.tagName === "H3" ? "3" : "2");
|
|
109
|
+
link.dataset.target = h.id;
|
|
110
|
+
frag.appendChild(link);
|
|
111
|
+
});
|
|
112
|
+
tocNav.appendChild(frag);
|
|
113
|
+
|
|
114
|
+
var tocLinks = $$("a", tocNav);
|
|
115
|
+
var spy = new IntersectionObserver(function (entries) {
|
|
116
|
+
entries.forEach(function (en) {
|
|
117
|
+
if (!en.isIntersecting) return;
|
|
118
|
+
tocLinks.forEach(function (l) {
|
|
119
|
+
l.classList.toggle("is-active", l.dataset.target === en.target.id);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}, { rootMargin: "-80px 0px -70% 0px", threshold: 0 });
|
|
123
|
+
heads.forEach(function (h) { spy.observe(h); });
|
|
124
|
+
|
|
125
|
+
tocLinks.forEach(function (l) {
|
|
126
|
+
l.addEventListener("click", function () {
|
|
127
|
+
tocLinks.forEach(function (x) { x.classList.remove("is-active"); });
|
|
128
|
+
l.classList.add("is-active");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
} else if (tocNav) {
|
|
132
|
+
var tocAside = $("#toc");
|
|
133
|
+
if (tocAside) tocAside.style.display = "none";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* ---------- Code blocks: header bar, language label, copy ---------- */
|
|
138
|
+
var LANG_NAMES = {
|
|
139
|
+
js: "JavaScript", javascript: "JavaScript", ts: "TypeScript", typescript: "TypeScript",
|
|
140
|
+
rb: "Ruby", ruby: "Ruby", py: "Python", python: "Python", sh: "Shell", bash: "Bash",
|
|
141
|
+
shell: "Shell", console: "Terminal", powershell: "PowerShell", yaml: "YAML", yml: "YAML",
|
|
142
|
+
json: "JSON", html: "HTML", css: "CSS", scss: "SCSS", sass: "Sass", md: "Markdown",
|
|
143
|
+
markdown: "Markdown", liquid: "Liquid", diff: "Diff", sql: "SQL", go: "Go", rust: "Rust",
|
|
144
|
+
java: "Java", php: "PHP", xml: "XML", toml: "TOML", ini: "INI", text: "", plaintext: ""
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
function langOf(el) {
|
|
148
|
+
var host = el.closest("[class*='language-']") || el;
|
|
149
|
+
var m = /language-([\w+#-]+)/.exec(host.className || "");
|
|
150
|
+
if (!m) return "";
|
|
151
|
+
var key = m[1].toLowerCase();
|
|
152
|
+
return LANG_NAMES.hasOwnProperty(key) ? LANG_NAMES[key] : m[1];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (content) {
|
|
156
|
+
$$("div.highlighter-rouge, div.highlight, figure.highlight", content).forEach(function (block) {
|
|
157
|
+
// Only wrap the outermost Rouge container
|
|
158
|
+
if (block.closest(".code-wrap")) return;
|
|
159
|
+
if (block.parentNode.closest && block.parentNode.closest(".highlighter-rouge")) return;
|
|
160
|
+
|
|
161
|
+
var wrap = document.createElement("div");
|
|
162
|
+
wrap.className = "code-wrap";
|
|
163
|
+
block.parentNode.insertBefore(wrap, block);
|
|
164
|
+
|
|
165
|
+
var bar = document.createElement("div");
|
|
166
|
+
bar.className = "code-wrap__bar";
|
|
167
|
+
var label = langOf(block);
|
|
168
|
+
bar.innerHTML = '<span class="code-wrap__lang">' + (label || "Code") + "</span>";
|
|
169
|
+
wrap.appendChild(bar);
|
|
170
|
+
wrap.appendChild(block);
|
|
171
|
+
|
|
172
|
+
if (!DocSteer.codeCopy || !navigator.clipboard) return;
|
|
173
|
+
|
|
174
|
+
var IDLE = '<i class="fa-regular fa-copy" aria-hidden="true"></i><span>Copy</span>';
|
|
175
|
+
var DONE = '<i class="fa-solid fa-check" aria-hidden="true"></i><span>Copied</span>';
|
|
176
|
+
var btn = document.createElement("button");
|
|
177
|
+
btn.className = "code-copy";
|
|
178
|
+
btn.type = "button";
|
|
179
|
+
btn.setAttribute("aria-label", "Copy code to clipboard");
|
|
180
|
+
btn.innerHTML = IDLE;
|
|
181
|
+
bar.appendChild(btn);
|
|
182
|
+
|
|
183
|
+
btn.addEventListener("click", function () {
|
|
184
|
+
var code = block.querySelector("code") || block.querySelector("pre") || block;
|
|
185
|
+
navigator.clipboard.writeText(code.innerText.replace(/\n+$/, "")).then(function () {
|
|
186
|
+
btn.classList.add("is-done");
|
|
187
|
+
btn.innerHTML = DONE;
|
|
188
|
+
setTimeout(function () {
|
|
189
|
+
btn.classList.remove("is-done");
|
|
190
|
+
btn.innerHTML = IDLE;
|
|
191
|
+
}, 1800);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
/* ---------- Tables: wrap so wide ones scroll, not the page ---------- */
|
|
197
|
+
$$("table", content).forEach(function (t) {
|
|
198
|
+
if (t.closest(".table-scroll")) return;
|
|
199
|
+
var box = document.createElement("div");
|
|
200
|
+
box.className = "table-scroll";
|
|
201
|
+
t.parentNode.insertBefore(box, t);
|
|
202
|
+
box.appendChild(t);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* ---------- External links ---------- */
|
|
207
|
+
$$(".doc-content a[href^='http']").forEach(function (a) {
|
|
208
|
+
if (a.hostname !== location.hostname) {
|
|
209
|
+
a.setAttribute("target", "_blank");
|
|
210
|
+
a.setAttribute("rel", "noopener noreferrer");
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
/* ---------- FAQ accordion ---------- */
|
|
215
|
+
// Opening and closing is native <details> behaviour; this only adds the
|
|
216
|
+
// "expand all" control and makes #question deep links actually open the item.
|
|
217
|
+
var faq = $("#faq");
|
|
218
|
+
if (faq) {
|
|
219
|
+
var faqItems = $$(".faq-item", faq);
|
|
220
|
+
|
|
221
|
+
var openFromHash = function () {
|
|
222
|
+
var id = decodeURIComponent(location.hash.slice(1));
|
|
223
|
+
if (!id) return;
|
|
224
|
+
var target = document.getElementById(id);
|
|
225
|
+
if (!target || !target.classList.contains("faq-item")) return;
|
|
226
|
+
target.open = true;
|
|
227
|
+
target.scrollIntoView({ block: "start" });
|
|
228
|
+
};
|
|
229
|
+
openFromHash();
|
|
230
|
+
window.addEventListener("hashchange", openFromHash);
|
|
231
|
+
|
|
232
|
+
// Keep the URL pointing at the question being opened, without piling up
|
|
233
|
+
// history entries the Back button would have to walk through.
|
|
234
|
+
faqItems.forEach(function (d) {
|
|
235
|
+
var sum = $("summary", d);
|
|
236
|
+
if (!sum) return;
|
|
237
|
+
sum.addEventListener("click", function () {
|
|
238
|
+
if (!d.open && d.id) history.replaceState(null, "", "#" + d.id);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
var faqAll = $("#faqToggleAll");
|
|
243
|
+
if (faqAll) {
|
|
244
|
+
faqAll.addEventListener("click", function () {
|
|
245
|
+
var expand = faqAll.dataset.expanded !== "true";
|
|
246
|
+
faqItems.forEach(function (d) { d.open = expand; });
|
|
247
|
+
faqAll.dataset.expanded = expand;
|
|
248
|
+
$("span", faqAll).textContent = expand ? "Collapse all" : "Expand all";
|
|
249
|
+
$("i", faqAll).className = expand
|
|
250
|
+
? "fa-solid fa-angles-up"
|
|
251
|
+
: "fa-solid fa-angles-down";
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* ---------- Back to top ---------- */
|
|
257
|
+
var toTop = $("#toTop");
|
|
258
|
+
if (toTop) {
|
|
259
|
+
var onScroll = function () {
|
|
260
|
+
var show = window.scrollY > 600;
|
|
261
|
+
toTop.hidden = !show;
|
|
262
|
+
toTop.classList.toggle("is-visible", show);
|
|
263
|
+
};
|
|
264
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
265
|
+
onScroll();
|
|
266
|
+
toTop.addEventListener("click", function () {
|
|
267
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* ---------- Esc closes drawers ---------- */
|
|
272
|
+
document.addEventListener("keydown", function (e) {
|
|
273
|
+
if (e.key === "Escape") closeSidebar();
|
|
274
|
+
});
|
|
275
|
+
})();
|