Skip to content

Commit ec24f58

Browse files
authored
New site, who dis? (#132)
* new site, who dis? * add no toc extension
1 parent e63fce5 commit ec24f58

12 files changed

+515
-19
lines changed

docs/_ext/notoc.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from docutils.parsers.rst import Directive
2+
from docutils import nodes
3+
4+
5+
class NoTocDirective(Directive):
6+
has_content = False
7+
8+
def run(self):
9+
# Create a raw HTML node to add the no-right-toc class to body
10+
html = '<script>document.body.classList.add("no-right-toc");</script>'
11+
return [nodes.raw("", html, format="html")]
12+
13+
14+
def setup(app):
15+
app.add_directive("notoc", NoTocDirective)
16+
17+
return {
18+
"version": "0.1",
19+
"parallel_read_safe": True,
20+
"parallel_write_safe": True,
21+
}

docs/_static/custom.css

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
.grid-item-card .card-img-top {
2+
height: 100%;
3+
object-fit: cover;
4+
width: 100%;
5+
background-color: slategrey;
6+
}
7+
8+
/* Make all cards with this class use flexbox for vertical layout */
9+
.card-with-bottom-text {
10+
display: flex !important;
11+
flex-direction: column !important;
12+
height: 100% !important;
13+
}
14+
15+
/* Style the card content areas */
16+
.card-with-bottom-text .sd-card-body {
17+
display: flex !important;
18+
flex-direction: column !important;
19+
flex-grow: 1 !important;
20+
}
21+
22+
/* Make images not grow or shrink */
23+
.card-with-bottom-text img {
24+
flex-shrink: 0 !important;
25+
margin-bottom: 0.5rem !important;
26+
}
27+
28+
/* Push the last paragraph to the bottom */
29+
.card-with-bottom-text .sd-card-body > p:last-child {
30+
margin-top: auto !important;
31+
padding-top: 0.5rem !important;
32+
text-align: center !important;
33+
}
34+
35+
.img-container img {
36+
object-fit: cover;
37+
width: 100%;
38+
height: 100%;
39+
}
40+
41+
.right-toc {
42+
position: fixed;
43+
top: 90px;
44+
right: 20px;
45+
width: 280px;
46+
font-size: 0.9em;
47+
max-height: calc(100vh - 150px);
48+
background-color: #f8f9fa;
49+
z-index: 100;
50+
border-radius: 6px;
51+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
52+
transition: all 0.3s ease;
53+
border-left: 3px solid #2980b9;
54+
}
55+
56+
.right-toc-header {
57+
display: flex;
58+
justify-content: space-between;
59+
align-items: center;
60+
padding: 12px 15px;
61+
border-bottom: 1px solid #e1e4e5;
62+
}
63+
64+
.right-toc-title {
65+
font-weight: 600;
66+
font-size: 1.1em;
67+
color: #2980b9;
68+
}
69+
70+
.right-toc-buttons {
71+
display: flex;
72+
align-items: center;
73+
}
74+
75+
.right-toc-toggle-btn {
76+
background: none;
77+
border: none;
78+
color: #2980b9;
79+
font-size: 16px;
80+
cursor: pointer;
81+
width: 24px;
82+
height: 24px;
83+
display: flex;
84+
align-items: center;
85+
justify-content: center;
86+
border-radius: 3px;
87+
padding: 0;
88+
transition: background-color 0.2s;
89+
}
90+
91+
.right-toc-toggle-btn:hover {
92+
background-color: rgba(41, 128, 185, 0.1);
93+
}
94+
95+
.right-toc-content {
96+
padding: 15px 15px 15px 20px;
97+
overflow-y: auto;
98+
max-height: calc(100vh - 200px);
99+
}
100+
101+
.right-toc-list {
102+
list-style-type: none;
103+
padding-left: 0;
104+
margin: 0;
105+
}
106+
107+
.right-toc-link {
108+
display: block;
109+
padding: 5px 0;
110+
text-decoration: none;
111+
color: #404040;
112+
border-radius: 4px;
113+
transition: all 0.2s ease;
114+
margin-bottom: 3px;
115+
}
116+
117+
.right-toc-link:hover {
118+
background-color: rgba(41, 128, 185, 0.1);
119+
padding-left: 5px;
120+
color: #2980b9;
121+
}
122+
123+
.right-toc-level-h1 {
124+
font-weight: 600;
125+
font-size: 1em;
126+
}
127+
128+
.right-toc-level-h2 {
129+
padding-left: 1.2em;
130+
font-size: 0.95em;
131+
}
132+
133+
.right-toc-level-h3 {
134+
padding-left: 2.4em;
135+
font-size: 0.9em;
136+
color: #606060;
137+
}
138+
139+
/* Active TOC item highlighting */
140+
.right-toc-link.active {
141+
background-color: rgba(41, 128, 185, 0.15);
142+
color: #2980b9;
143+
font-weight: 500;
144+
padding-left: 5px;
145+
}
146+
147+
/* Collapsed state */
148+
.right-toc-collapsed {
149+
width: auto;
150+
border-left-width: 0;
151+
}
152+
153+
.right-toc-collapsed .right-toc-header {
154+
border-bottom: none;
155+
padding: 8px 12px;
156+
}
157+
158+
/* Scrollbar styling */
159+
.right-toc-content::-webkit-scrollbar {
160+
width: 5px;
161+
}
162+
163+
.right-toc-content::-webkit-scrollbar-track {
164+
background: #f1f1f1;
165+
border-radius: 10px;
166+
}
167+
168+
.right-toc-content::-webkit-scrollbar-thumb {
169+
background: #cdcdcd;
170+
border-radius: 10px;
171+
}
172+
173+
.right-toc-content::-webkit-scrollbar-thumb:hover {
174+
background: #9e9e9e;
175+
}
176+
177+
/* Responsive adjustments */
178+
@media screen and (max-width: 1200px) {
179+
.right-toc {
180+
width: 230px;
181+
}
182+
}
183+
184+
@media screen and (max-width: 992px) {
185+
.right-toc {
186+
display: none; /* Hide on smaller screens */
187+
}
188+
}

docs/_static/custom.js

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
document.addEventListener("DOMContentLoaded", function () {
2+
// Check if current page has opted out of the TOC
3+
if (document.body.classList.contains("no-right-toc")) {
4+
return;
5+
}
6+
7+
const content = document.querySelector(".rst-content");
8+
if (!content) return;
9+
10+
// Find all headers in the main content
11+
const headers = Array.from(
12+
content.querySelectorAll("h1:not(.document-title), h2, h3"),
13+
).filter((header) => !header.classList.contains("no-toc"));
14+
15+
// Only create TOC if there are headers
16+
if (headers.length === 0) return;
17+
18+
// Create TOC container
19+
const toc = document.createElement("div");
20+
toc.className = "right-toc";
21+
toc.innerHTML =
22+
'<div class="right-toc-header">' +
23+
'<div class="right-toc-title">On This Page</div>' +
24+
'<div class="right-toc-buttons">' +
25+
'<button class="right-toc-toggle-btn" title="Toggle TOC visibility">−</button>' +
26+
"</div></div>" +
27+
'<div class="right-toc-content"><ul class="right-toc-list"></ul></div>';
28+
29+
const tocList = toc.querySelector(".right-toc-list");
30+
const tocContent = toc.querySelector(".right-toc-content");
31+
const tocToggleBtn = toc.querySelector(
32+
".right-toc-toggle-btn",
33+
);
34+
35+
// Set up the toggle button
36+
tocToggleBtn.addEventListener("click", function () {
37+
if (tocContent.style.display === "none") {
38+
tocContent.style.display = "block";
39+
tocToggleBtn.textContent = "−";
40+
toc.classList.remove("right-toc-collapsed");
41+
localStorage.setItem("tocVisible", "true");
42+
} else {
43+
tocContent.style.display = "none";
44+
tocToggleBtn.textContent = "+";
45+
toc.classList.add("right-toc-collapsed");
46+
localStorage.setItem("tocVisible", "false");
47+
}
48+
});
49+
50+
// Check saved state
51+
if (localStorage.getItem("tocVisible") === "false") {
52+
tocContent.style.display = "none";
53+
tocToggleBtn.textContent = "+";
54+
toc.classList.add("right-toc-collapsed");
55+
}
56+
57+
// Track used IDs to avoid duplicates
58+
const usedIds = new Set();
59+
60+
// Get all existing IDs in the document
61+
document.querySelectorAll("[id]").forEach((el) => {
62+
usedIds.add(el.id);
63+
});
64+
65+
// Generate unique IDs for headers that need them
66+
headers.forEach((header, index) => {
67+
// If header already has a unique ID, use that
68+
if (header.id && !usedIds.has(header.id)) {
69+
usedIds.add(header.id);
70+
return;
71+
}
72+
73+
// Create a slug from the header text
74+
let headerText = header.textContent || "";
75+
76+
// Clean the text (remove icons and special characters)
77+
headerText = headerText.replace(/\s*\uf0c1\s*$/, "");
78+
headerText = headerText.replace(/\s*[§#]\s*$/, "");
79+
headerText = headerText.trim();
80+
81+
let slug = headerText
82+
.toLowerCase()
83+
.replace(/[^\w\s-]/g, "")
84+
.replace(/\s+/g, "-")
85+
.replace(/--+/g, "-")
86+
.trim();
87+
88+
// Make sure slug is not empty
89+
if (!slug) {
90+
slug = "section";
91+
}
92+
93+
// Ensure the ID is unique
94+
let uniqueId = slug;
95+
let counter = 1;
96+
97+
while (usedIds.has(uniqueId)) {
98+
uniqueId = `${slug}-${counter}`;
99+
counter++;
100+
}
101+
102+
// Set the unique ID and add to our tracking set
103+
header.id = uniqueId;
104+
usedIds.add(uniqueId);
105+
});
106+
107+
// Add entries for each header
108+
headers.forEach((header) => {
109+
const item = document.createElement("li");
110+
const link = document.createElement("a");
111+
112+
link.href = "#" + header.id;
113+
114+
// Get clean text without icons
115+
let headerText = header.textContent || "";
116+
headerText = headerText.replace(/\s*\uf0c1\s*$/, "");
117+
headerText = headerText.replace(/\s*[§#]\s*$/, "");
118+
119+
link.textContent = headerText.trim();
120+
link.className =
121+
"right-toc-link right-toc-level-" +
122+
header.tagName.toLowerCase();
123+
124+
item.appendChild(link);
125+
tocList.appendChild(item);
126+
});
127+
128+
// Add TOC to page
129+
document.body.appendChild(toc);
130+
131+
// Add active link highlighting
132+
const tocLinks = document.querySelectorAll(".right-toc-link");
133+
const headerElements = Array.from(headers);
134+
135+
if (tocLinks.length > 0 && headerElements.length > 0) {
136+
// Highlight the current section on scroll
137+
window.addEventListener(
138+
"scroll",
139+
debounce(function () {
140+
let currentSection = null;
141+
let smallestDistanceFromTop = Infinity;
142+
143+
headerElements.forEach((header) => {
144+
const distance = Math.abs(
145+
header.getBoundingClientRect().top,
146+
);
147+
if (distance < smallestDistanceFromTop) {
148+
smallestDistanceFromTop = distance;
149+
currentSection = header.id;
150+
}
151+
});
152+
153+
tocLinks.forEach((link) => {
154+
link.classList.remove("active");
155+
if (
156+
link.getAttribute("href") === `#${currentSection}`
157+
) {
158+
link.classList.add("active");
159+
}
160+
});
161+
}, 100),
162+
);
163+
}
164+
});
165+
166+
// Debounce function to limit scroll event firing
167+
function debounce(func, wait) {
168+
let timeout;
169+
return function () {
170+
const context = this;
171+
const args = arguments;
172+
clearTimeout(timeout);
173+
timeout = setTimeout(() => func.apply(context, args), wait);
174+
};
175+
}
354 KB
Loading
2.09 MB
Loading
7.1 MB
Loading
438 KB
Loading
669 KB
Loading
157 KB
Loading

0 commit comments

Comments
 (0)