docs: update generated docs

This commit is contained in:
nym21
2026-06-14 00:46:07 +02:00
parent c85da92cbc
commit 8614e9eded
639 changed files with 5529 additions and 3472 deletions
+10 -3
View File
@@ -9,20 +9,27 @@ function createContentsItem(section, path) {
const anchor = document.createElement("a");
const children = section.children ?? [];
const sectionPath = [...path, section.title];
const id = createPathId(sectionPath);
anchor.href = `#${createPathId(sectionPath)}`;
anchor.href = `#${id}`;
anchor.append(section.title);
if (children.length) {
const details = document.createElement("details");
const summary = document.createElement("summary");
const list = document.createElement("ol");
for (const child of children) {
list.append(createContentsItem(child, sectionPath));
}
item.append(list);
summary.append(anchor);
details.append(summary, list);
item.append(details);
} else {
item.append(anchor);
}
item.prepend(anchor);
return item;
}
+9 -7
View File
@@ -21,13 +21,19 @@ main.learn {
text-transform: uppercase;
ol {
display: grid;
list-style: none;
margin: 0;
padding: 0;
}
li + li {
margin-block-start: 0.25rem;
summary {
display: block;
list-style: none;
&::-webkit-details-marker {
display: none;
}
}
a {
@@ -40,8 +46,7 @@ main.learn {
color: inherit;
text-decoration: none;
border-radius: 0.25rem;
margin-block: -0.25rem;
margin-inline: calc(-1 * var(--line-indent)) 1rem;
margin-inline: 0 1rem;
padding: 0.25rem;
padding-inline-start: calc(var(--line-width) + var(--line-gap));
@@ -74,9 +79,6 @@ main.learn {
ol ol {
--line-indent: var(--line-step);
margin-block-start: 0.25rem;
margin-inline-start: var(--line-step);
}
ol ol ol {
+115
View File
@@ -0,0 +1,115 @@
const storagePrefix = "bitview:learn-section";
/** @param {string} id */
function getStorageKey(id) {
return `${storagePrefix}:${id}`;
}
/** @param {HTMLElement} section */
function getDetails(section) {
return /** @type {HTMLDetailsElement} */ (
section.querySelector(":scope > details")
);
}
/** @param {HTMLElement} section */
function getHash(section) {
return `#${section.id}`;
}
/**
* @param {HTMLElement} main
* @param {HTMLElement} section
*/
function getNavDetails(main, section) {
const anchor = main.querySelector(`nav a[href="${getHash(section)}"]`);
const summary = anchor?.parentElement;
return summary?.localName === "summary" ? summary.parentElement : null;
}
/**
* @param {HTMLElement} main
* @param {HTMLElement} section
*/
function syncNavSection(main, section) {
const details = getNavDetails(main, section);
if (details) details.toggleAttribute("open", getDetails(section).open);
}
/**
* @param {HTMLElement} main
* @param {HTMLElement} section
*/
function openSection(main, section) {
let current = section;
while (main.contains(current)) {
const details = current.querySelector(":scope > details");
if (details) {
/** @type {HTMLDetailsElement} */ (details).open = true;
syncNavSection(main, current);
}
const parent = current.parentElement?.closest("section[id]");
if (!parent) break;
current = /** @type {HTMLElement} */ (parent);
}
}
/** @param {HTMLElement} main */
export function initSectionDetails(main) {
const sections = [
...main.querySelectorAll("section[id]:not([data-numbered='false'])"),
].map((section) => /** @type {HTMLElement} */ (section));
for (const section of sections) {
const saved = localStorage.getItem(getStorageKey(section.id));
getDetails(section).open = saved === "1";
syncNavSection(main, section);
}
main.addEventListener("toggle", (event) => {
const details = /** @type {HTMLDetailsElement} */ (event.target);
const section = /** @type {HTMLElement} */ (details.parentElement);
if (!section.matches("article section[id]")) return;
localStorage.setItem(getStorageKey(section.id), details.open ? "1" : "0");
syncNavSection(main, section);
main.dispatchEvent(new Event("sectiontoggle"));
}, { capture: true });
/** @param {string} hash */
function openHash(hash) {
const target = document.getElementById(hash.slice(1));
if (target && main.contains(target)) openSection(main, target);
}
/** @param {string} hash */
function toggleHash(hash) {
const target = document.getElementById(hash.slice(1));
if (!target || !main.contains(target)) return false;
const section = /** @type {HTMLElement} */ (target);
const details = getDetails(section);
if (details.open) {
details.open = false;
syncNavSection(main, section);
return false;
}
openSection(main, section);
return true;
}
if (window.location.hash) openHash(window.location.hash);
return {
openHash,
toggleHash,
};
}
+17 -6
View File
@@ -21,18 +21,24 @@ function scrollToTarget(target, behavior) {
/**
* @param {HTMLElement} main
* @param {ScrollBehavior} behavior
* @param {LearnDetails} details
*/
function scrollToCurrentHash(main, behavior) {
const target = getHashTarget(main, window.location.hash);
function scrollToCurrentHash(main, behavior, details) {
const hash = window.location.hash;
const target = getHashTarget(main, hash);
if (target) scrollToTarget(target, behavior);
if (target) {
details.openHash(hash);
scrollToTarget(target, behavior);
}
}
/**
* @param {HTMLElement} main
* @param {(hash: string) => void} onHashNavigate
* @param {LearnDetails} details
*/
export function initHashLinks(main, onHashNavigate) {
export function initHashLinks(main, onHashNavigate, details) {
main.addEventListener("click", (event) => {
if (!isPlainLeftClick(event)) return;
@@ -47,6 +53,9 @@ export function initHashLinks(main, onHashNavigate) {
if (!target) return;
event.preventDefault();
const open = details.toggleHash(url.hash);
if (!open) return;
onHashNavigate(url.hash);
scrollToTarget(target, "smooth");
@@ -57,8 +66,10 @@ export function initHashLinks(main, onHashNavigate) {
window.addEventListener("popstate", () => {
if (main.hidden) return;
scrollToCurrentHash(main, "auto");
scrollToCurrentHash(main, "auto", details);
});
main.addEventListener("pageactive", () => scrollToCurrentHash(main, "auto"));
main.addEventListener("pageactive", () => {
scrollToCurrentHash(main, "auto", details);
});
}
+18 -4
View File
@@ -1,6 +1,7 @@
import { createContents } from "./contents/index.js";
import { sections } from "./data/index.js";
import { createChart } from "./charts/index.js";
import { initSectionDetails } from "./details.js";
import { initHashLinks } from "./hash-links.js";
import { initScrollSpy } from "./scroll-spy.js";
import { createPathId } from "./path.js";
@@ -18,6 +19,8 @@ function createSection(section, path = []) {
const description = document.createElement("p");
const children = section.children ?? [];
const id = createPathId(sectionPath);
/** @type {HTMLElement[]} */
const content = [description];
element.id = id;
if (section.numbered === false) element.dataset.numbered = "false";
@@ -25,11 +28,21 @@ function createSection(section, path = []) {
anchor.append(section.title);
heading.append(anchor);
description.append(section.description);
element.append(heading, description);
if (section.chart) element.append(createChart(section.chart, id));
if (section.chart) content.push(createChart(section.chart, id));
for (const child of children) {
element.append(createSection(child, sectionPath));
content.push(createSection(child, sectionPath));
}
if (section.numbered === false) {
element.append(heading, ...content);
} else {
const details = document.createElement("details");
const summary = document.createElement("summary");
summary.append(heading);
details.append(summary, ...content);
element.append(details);
}
return element;
@@ -45,7 +58,8 @@ export function createLearnPage() {
}
main.append(createContents(sections), article);
const details = initSectionDetails(main);
const navigateToHash = initScrollSpy(main);
initHashLinks(main, navigateToHash);
initHashLinks(main, navigateToHash, details);
return main;
}
+2
View File
@@ -105,6 +105,7 @@ export function initScrollSpy(main) {
const viewportTop = getViewportTop();
for (const section of sections) {
if (!section.getClientRects().length) continue;
if (section.getBoundingClientRect().top > viewportTop) break;
currentSection = section;
@@ -149,6 +150,7 @@ export function initScrollSpy(main) {
alignNavToTop = true;
scheduleUpdate();
});
main.addEventListener("sectiontoggle", scheduleUpdate);
scheduleUpdate();
return navigateToHash;
}
+116 -42
View File
@@ -23,6 +23,19 @@ main.learn {
grid-template-columns: 14rem minmax(0, 1fr);
padding: 0 var(--page-x);
scroll-padding-top: var(--offset);
interpolate-size: allow-keywords;
details::details-content {
height: 0;
overflow: clip;
transition:
height 1s ease,
content-visibility 1s ease allow-discrete;
}
details[open]::details-content {
height: auto;
}
article {
counter-reset: theme;
@@ -79,8 +92,13 @@ main.learn {
margin-top: 8rem;
}
> section:has(> details:not([open])) + section {
margin-top: var(--offset);
}
section[id] {
> :is(h1, h2, h3, h4) {
> :is(h1, h2, h3, h4),
> details > summary > :is(h1, h2, h3, h4) {
a {
position: relative;
display: inline-block;
@@ -115,17 +133,17 @@ main.learn {
}
> section {
> section {
> details > section {
counter-increment: topic;
counter-reset: detail;
scroll-margin-top: var(--offset);
> section {
> details > section {
counter-increment: detail;
counter-reset: subtopic;
scroll-margin-top: var(--offset);
> section {
> details > section {
counter-increment: subtopic;
scroll-margin-top: var(--offset);
}
@@ -134,52 +152,108 @@ main.learn {
}
section[id]:not([data-numbered="false"]) {
> :is(h1, h2, h3, h4) {
position: sticky;
top: var(--offset);
line-height: 1;
background: var(--black);
> details {
> summary {
position: relative;
line-height: 1;
list-style: none;
background: var(--black);
cursor: pointer;
&::-webkit-details-marker {
display: none;
}
&::after {
content: "+";
position: absolute;
top: 50%;
right: 0;
translate: 0 -50%;
color: var(--gray);
font-family: var(--font-mono);
font-size: var(--font-size-sm);
pointer-events: none;
}
> :is(h1, h2, h3, h4) {
line-height: 1;
}
&:has(> h1) {
z-index: 8;
> h1 {
border-bottom: 1px solid var(--gray);
font-size: 3rem;
}
}
&:has(> h2) {
z-index: 7;
> h2 {
border-bottom: 1px dashed var(--gray);
font-size: var(--topic-font-size);
}
}
&:has(> h3) {
z-index: 6;
> h3 {
border-bottom: 1px dotted var(--gray);
font-size: var(--detail-font-size);
}
}
&:has(> h4) {
z-index: 5;
> h4 {
border-bottom: 1px dotted var(--gray);
font-size: var(--subtopic-font-size);
}
}
}
&[open] > summary::after {
content: "-";
}
&[open] > summary {
position: sticky;
top: var(--offset);
> h1 {
padding-bottom: var(--heading-padding-bottom);
}
> h2 {
padding-top: var(--topic-padding-top);
padding-bottom: var(--heading-padding-bottom);
}
> h3 {
padding-top: var(--detail-padding-top);
padding-bottom: var(--detail-padding-bottom);
}
> h4 {
padding-top: var(--subtopic-padding-top);
padding-bottom: var(--subtopic-padding-bottom);
}
}
}
> h1 {
z-index: 8;
padding-bottom: var(--heading-padding-bottom);
border-bottom: 1px solid var(--gray);
font-size: 3rem;
}
> h2 {
z-index: 7;
padding-top: var(--topic-padding-top);
padding-bottom: var(--heading-padding-bottom);
border-bottom: 1px dashed var(--gray);
font-size: var(--topic-font-size);
}
> h3 {
z-index: 6;
padding-top: var(--detail-padding-top);
padding-bottom: var(--detail-padding-bottom);
border-bottom: 1px dotted var(--gray);
font-size: var(--detail-font-size);
}
> h4 {
z-index: 5;
padding-top: var(--subtopic-padding-top);
padding-bottom: var(--subtopic-padding-bottom);
border-bottom: 1px dotted var(--gray);
font-size: var(--subtopic-font-size);
}
> p {
> details > p {
margin-top: 1rem;
color: var(--white);
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
}
> figure {
> details > figure {
margin-top: 2rem;
color: var(--gray);
font-size: var(--font-size-xs);
+5
View File
@@ -6,6 +6,11 @@ declare global {
numbered?: boolean;
children?: LearnSection[];
};
type LearnDetails = {
openHash(hash: string): void;
toggleHash(hash: string): boolean;
};
}
export {};