From 98b2cb93e2c56574313df91b5d9f5a4863657711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Schwarz=C3=A4ugl?= Date: Mon, 5 Jan 2026 05:07:17 +0100 Subject: [PATCH] docs: improve docs page --- SwarselSystems.org | 1282 +++++++++++++++++++++++++------------------- 1 file changed, 743 insertions(+), 539 deletions(-) diff --git a/SwarselSystems.org b/SwarselSystems.org index f9d9fe5..022583a 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -167,312 +167,448 @@ I also add this javascript to add header pinning functionality to the site, usin #+begin_src elisp :noweb yes :exports both :results html " - - " + pinBtn.addEventListener('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + const current = pinnedItems.get(href); + if (!current) return; + + if (current.li) { + if (current.li.parentElement) { + current.li.parentElement.removeChild(current.li); + } + current.li = null; + current.btns.forEach(b => b.textContent = '[pin]'); + saveToStorage(); + } else { + const li = document.createElement('li'); + + const a = document.createElement('a'); + a.href = href; + a.textContent = current.text; + + const removeBtn = document.createElement('button'); + removeBtn.className = 'pin-remove'; + removeBtn.type = 'button'; + removeBtn.textContent = '✕'; + removeBtn.addEventListener('click', () => { + const cur = pinnedItems.get(href); + if (!cur) return; + if (cur.li && cur.li.parentElement) { + cur.li.parentElement.removeChild(cur.li); + } + cur.li = null; + cur.btns.forEach(b => b.textContent = '[pin]'); + saveToStorage(); + }); + + li.appendChild(a); + li.appendChild(removeBtn); + pinnedList.appendChild(li); + + current.li = li; + current.btns.forEach(b => b.textContent = '[unpin]'); + + sortPinnedList(); + saveToStorage(); + } + }); + } + + loadFromStorage(); + + const tocLinks = document.querySelectorAll('#text-table-of-contents a'); + tocLinks.forEach(link => { + if (link.parentElement && link.parentElement.classList.contains('toc-entry')) { + return; + } + const li = link.closest('li'); + if (!li) return; + + const wrapper = document.createElement('span'); + wrapper.className = 'toc-entry'; + li.insertBefore(wrapper, link); + wrapper.appendChild(link); + + const pinBtn = document.createElement('button'); + pinBtn.className = 'toc-pin-btn'; + pinBtn.type = 'button'; + pinBtn.textContent = '[pin]'; + wrapper.appendChild(pinBtn); + + const href = link.getAttribute('href'); + const text = link.textContent.trim(); + attachPinBehavior(pinBtn, href, text); + }); + + const headers = content.querySelectorAll('h2, h3, h4, h5, h6'); + headers.forEach(header => { + const id = header.getAttribute('id'); + if (!id) return; + if (header.querySelector('.toc-pin-btn')) return; + + const href = '#' + id; + const text = header.textContent.trim(); + + const pinBtn = document.createElement('button'); + pinBtn.className = 'toc-pin-btn'; + pinBtn.type = 'button'; + pinBtn.textContent = '[pin]'; + pinBtn.style.marginLeft = '0.8rem'; + pinBtn.style.fontSize = '0.75em'; + + header.appendChild(pinBtn); + attachPinBehavior(pinBtn, href, text); + }); + + initiallyPinnedHrefs.forEach(href => { + const entry = pinnedItems.get(href); + if (!entry) return; + + const li = document.createElement('li'); + + const a = document.createElement('a'); + a.href = href; + a.textContent = entry.text; + + const removeBtn = document.createElement('button'); + removeBtn.className = 'pin-remove'; + removeBtn.type = 'button'; + removeBtn.textContent = '✕'; + removeBtn.addEventListener('click', () => { + const cur = pinnedItems.get(href); + if (!cur) return; + if (cur.li && cur.li.parentElement) { + cur.li.parentElement.removeChild(cur.li); + } + cur.li = null; + cur.btns.forEach(b => b.textContent = '[pin]'); + saveToStorage(); + }); + + li.appendChild(a); + li.appendChild(removeBtn); + pinnedList.appendChild(li); + + entry.li = li; + entry.btns.forEach(b => b.textContent = '[unpin]'); + }); + + sortPinnedList(); + saveToStorage(); + }); + })(); + + " #+end_src #+RESULTS: @@ -511,7 +647,7 @@ I also add this javascript to add header pinning functionality to the site, usin showBtn = document.createElement('button'); showBtn.id = 'show-pinned-btn'; showBtn.type = 'button'; - showBtn.textContent = 'Show Pinned'; + showBtn.textContent = 'Pinned'; document.body.appendChild(showBtn); } @@ -725,7 +861,7 @@ I also add this javascript to add header pinning functionality to the site, usin attachPinBehavior(pinBtn, href, text); }); - const headers = content.querySelectorAll('h2, h3, h4, h5'); + const headers = content.querySelectorAll('h2, h3, h4, h5, h6'); headers.forEach(header => { const id = header.getAttribute('id'); if (!id) return; @@ -32272,427 +32408,495 @@ This is the stylesheet used by the [[#h:12880c64-229c-4063-9eea-387a97490676][HT #+begin_src css :tangle site/style.css :mkdirp yes html, body { - margin: 0; - padding: 0; - background-color: #1d252c; - color: #b7c5d3; - font-family: "Inter", "Fira Sans", system-ui, sans-serif; - line-height: 1.6; - overflow-x: hidden; + margin: 0; + padding: 0; + background-color: #1d252c; + color: #b7c5d3; + font-family: "Inter", "Fira Sans", system-ui, sans-serif; + line-height: 1.6; + overflow-x: hidden; } body { - display: flex; + display: flex; } #table-of-contents { - position: fixed; - top: 0; - left: 0; - width: 280px; - height: 100vh; - overflow-y: auto; - padding: 1.2rem 1rem; - background-color: #232b32; - border-right: 1px solid #2f3b45; - font-size: 0.9rem; + position: fixed; + top: 0; + left: 0; + width: 280px; + height: 100vh; + overflow-y: auto; + padding: 1.2rem 1rem; + background-color: #232b32; + border-right: 1px solid #2f3b45; + font-size: 0.9rem; + display: flex; + flex-direction: column; } #table-of-contents h2 { - display: none; + display: none; + } + + #toc-search-container { + margin-bottom: 1rem; + position: sticky; + top: 0; + background-color: #232b32; + z-index: 10; + padding-bottom: 0.5rem; + display: flex; + align-items: center; + gap: 0.5rem; + } + + #toc-search-input { + flex: 1; + padding: 0.5rem; + background-color: #1d252c; + border: 1px solid #2f3b45; + color: #b7c5d3; + border-radius: 4px; + font-size: 0.9rem; + box-sizing: border-box; + } + + #toc-search-input:focus { + outline: none; + border-color: #5ec4ff; + } + + #toc-search-clear { + padding: 0.5rem 0.7rem; + background-color: #2f3b45; + border: 1px solid #3a4a56; + color: #b7c5d3; + cursor: pointer; + font-size: 0.85rem; + border-radius: 4px; + transition: background-color 0.2s, color 0.2s; + white-space: nowrap; + } + + #toc-search-clear:hover { + background-color: #3a4a56; + color: #ff6b6b; + } + + .hidden-by-search { + display: none !important; + } + + #text-table-of-contents { + flex: 1; } #text-table-of-contents ul { - list-style: none; - padding-left: 0; + list-style: none; + padding-left: 0; } #text-table-of-contents li { - margin: 0.2rem 0; - position: relative; + margin: 0.2rem 0; + position: relative; } .toc-entry { - display: inline-flex; - align-items: center; + display: inline-flex; + align-items: center; } #text-table-of-contents a { - color: #b7c5d3; - text-decoration: none; + color: #b7c5d3; + text-decoration: none; } #text-table-of-contents a:hover { - color: #5ec4ff; + color: #5ec4ff; } #text-table-of-contents ul ul { - padding-left: 1rem; - border-left: 1px solid #2f3b45; + padding-left: 1rem; + border-left: 1px solid #2f3b45; } #content { - margin-left: 300px; - margin-right: 320px; - padding: 2rem 3rem; - max-width: 1200px; - width: calc(100vw - 620px); - box-sizing: border-box; - transition: margin 0.3s ease, padding 0.3s ease, - width 0.3s ease, max-width 0.3s ease; + margin-left: 300px; + margin-right: 320px; + padding: 2rem 3rem; + max-width: 1200px; + width: calc(100vw - 620px); + box-sizing: border-box; + transition: margin 0.3s ease, padding 0.3s ease, + width 0.3s ease, max-width 0.3s ease; } #content.pinned-hidden { - margin-right: 0; - width: calc(100vw - 300px); + margin-right: 0; + width: calc(100vw - 300px); } h1, h2, h3, h4, h5, h6 { - color: #70e1e8; - font-weight: 500; - margin-top: 2.2rem; + color: #70e1e8; + font-weight: 500; + margin-top: 2.2rem; } - h1 { - font-size: 2rem; - } - - h2 { - font-size: 1.6rem; - } - - h3 { - font-size: 1.3rem; - } - - h4 { - font-size: 1.2rem; - } - - h5 { - font-size: 1.1rem; - } - - h6 { - font-size: 1.0rem; - } + h1 { font-size: 2rem; } + h2 { font-size: 1.6rem; } + h3 { font-size: 1.3rem; } + h4 { font-size: 1.2rem; } + h5 { font-size: 1.1rem; } + h6 { font-size: 1.0rem; } a { - color: #5ec4ff; + color: #5ec4ff; } a:hover { - text-decoration: underline; + text-decoration: underline; } pre, code { - font-family: "Fira Code", monospace; - background-color: #232b32; - color: #b7c5d3; + font-family: "Fira Code", monospace; + background-color: #232b32; + color: #b7c5d3; } pre { - padding: 1rem; - overflow-x: auto; - border: 1px solid #2f3b45; - border-radius: 4px; - max-width: 100%; - box-sizing: border-box; + padding: 1rem; + overflow-x: auto; + border: 1px solid #2f3b45; + border-radius: 4px; + max-width: 100%; + box-sizing: border-box; } code { - padding: 0.15rem 0.3rem; - border-radius: 3px; + padding: 0.15rem 0.3rem; + border-radius: 3px; } table { - border-collapse: collapse; - max-width: 100%; + border-collapse: collapse; + max-width: 100%; } th, td { - border: 1px solid #2f3b45; - padding: 0.5rem 0.8rem; + border: 1px solid #2f3b45; + padding: 0.5rem 0.8rem; } th { - background-color: #232b32; - color: #70e1e8; + background-color: #232b32; + color: #70e1e8; } blockquote { - border-left: 3px solid #5ec4ff; - margin-left: 0; - padding-left: 1rem; - color: #718ca1; + border-left: 3px solid #5ec4ff; + margin-left: 0; + padding-left: 1rem; + color: #718ca1; } #pinned-panel { - position: fixed; - top: 0; - right: 0; - width: 280px; - height: 100vh; - overflow-y: auto; - padding: 1.2rem 1rem; - padding-bottom: 5rem; - background-color: #232b32; - border-left: 1px solid #2f3b45; - font-size: 0.9rem; - box-sizing: border-box; - transition: transform 0.3s ease; + position: fixed; + top: 0; + right: 0; + width: 280px; + height: 100vh; + overflow-y: auto; + padding: 1.2rem 1rem; + padding-bottom: 5rem; + background-color: #232b32; + border-left: 1px solid #2f3b45; + font-size: 0.9rem; + box-sizing: border-box; + transition: transform 0.3s ease; } #pinned-panel.hidden { - transform: translateX(100%); + transform: translateX(100%); } #pinned-panel-header { - display: flex; - justify-content: space-between; - align-items: center; + display: flex; + justify-content: space-between; + align-items: center; } #pinned-panel h2 { - margin: 0; - font-size: 1rem; - color: #70e1e8; - font-weight: 500; + margin: 0; + font-size: 1rem; + color: #70e1e8; + font-weight: 500; } #toggle-pinned-btn { - background: none; - border: none; - color: #718ca1; - cursor: pointer; - font-size: 1.2rem; - padding: 0; - line-height: 1; + background: none; + border: none; + color: #718ca1; + cursor: pointer; + font-size: 1.2rem; + padding: 0; + line-height: 1; } #toggle-pinned-btn:hover { - color: #5ec4ff; + color: #5ec4ff; } #pinned-list { - list-style: none; - padding-left: 0; + list-style: none; + padding-left: 0; } #pinned-list li { - margin: 0.5rem 0; - display: flex; - justify-content: space-between; - align-items: center; + margin: 0.5rem 0; + display: flex; + justify-content: space-between; + align-items: center; } #pinned-list a { - color: #b7c5d3; - text-decoration: none; - flex: 1; + color: #b7c5d3; + text-decoration: none; + flex: 1; } #pinned-list a:hover { - color: #5ec4ff; + color: #5ec4ff; } .pin-remove { - background: none; - border: none; - color: #718ca1; - cursor: pointer; - font-size: 0.9rem; - padding: 0 0.3rem; + background: none; + border: none; + color: #718ca1; + cursor: pointer; + font-size: 0.9rem; + padding: 0 0.3rem; } .pin-remove:hover { - color: #ff6b6b; + color: #ff6b6b; } .toc-pin-btn { - opacity: 0; - visibility: hidden; - transition: opacity 0.2s, visibility 0.2s; - cursor: pointer; - margin-left: 0.4rem; - font-size: 0.85rem; - color: #718ca1; - background: none; - border: none; - padding: 0; + opacity: 0; + visibility: hidden; + transition: opacity 0.2s, visibility 0.2s; + cursor: pointer; + margin-left: 0.4rem; + font-size: 0.85rem; + color: #718ca1; + background: none; + border: none; + padding: 0; } .toc-pin-btn:hover { - color: #5ec4ff; + color: #5ec4ff; } #text-table-of-contents .toc-entry:hover .toc-pin-btn { - opacity: 1; - visibility: visible; + opacity: 1; + visibility: visible; } #show-pinned-btn { - position: fixed; - top: 4.5rem; - right: 1rem; - background-color: #232b32; - border: 1px solid #2f3b45; - color: #b7c5d3; - cursor: pointer; - padding: 0.5rem 0.8rem; - font-size: 0.9rem; - border-radius: 4px; - display: none; - z-index: 1000; + position: fixed; + top: 4.5rem; + right: 1rem; + background-color: #232b32; + border: 1px solid #2f3b45; + color: #b7c5d3; + cursor: pointer; + padding: 0.5rem 0.8rem; + font-size: 0.9rem; + border-radius: 4px; + display: none; + z-index: 1000; } #show-pinned-btn:hover { - background-color: #2f3b45; - color: #5ec4ff; + background-color: #2f3b45; + color: #5ec4ff; } #show-pinned-btn.visible { - display: block; + display: block; } #mobile-toc-toggle, #mobile-pinned-toggle { - position: fixed; - top: 1rem; - z-index: 1100; - padding: 0.5rem 0.8rem; - border-radius: 4px; - border: 1px solid #2f3b45; - background-color: #232b32; - color: #b7c5d3; - font-size: 0.8rem; - cursor: pointer; - display: none; + position: fixed; + top: 1rem; + z-index: 1100; + padding: 0.5rem 0.8rem; + border-radius: 4px; + border: 1px solid #2f3b45; + background-color: #232b32; + color: #b7c5d3; + font-size: 0.8rem; + cursor: pointer; + display: none; } #mobile-toc-toggle { - left: 1rem; + left: 1rem; } #mobile-pinned-toggle { - right: 1rem; + right: 1rem; } #mobile-toc-toggle:hover, #mobile-pinned-toggle:hover { - background-color: #2f3b45; - color: #5ec4ff; + background-color: #2f3b45; + color: #5ec4ff; } @media (max-width: 1600px) { - #content { - max-width: 100%; - } + #content { + max-width: 100%; + } } @media (max-width: 1300px) { - #mobile-pinned-toggle { - display: block; - } + #mobile-pinned-toggle { + display: block; + } - #pinned-panel { - display: block !important; - position: fixed; - top: 0; - right: 0; - width: 260px; - max-width: 80vw; - height: 100vh; - overflow-y: auto; - transform: translateX(100%); - transition: transform 0.25s ease; - z-index: 1000; - } + #pinned-panel { + display: block !important; + position: fixed; + top: 0; + right: 0; + width: 260px; + max-width: 80vw; + height: 100vh; + overflow-y: auto; + transform: translateX(100%); + transition: transform 0.25s ease; + z-index: 1000; + } - #pinned-panel.mobile-visible { - transform: translateX(0); - } + #pinned-panel.mobile-visible { + transform: translateX(0); + } - #show-pinned-btn { - display: none !important; - } + #show-pinned-btn { + display: none !important; + } - #content, - #content.pinned-hidden { - margin-right: 0; - width: calc(100vw - 300px); - max-width: 100%; - padding: 1.8rem 2.2rem; - } + #content, + #content.pinned-hidden { + margin-right: 0; + width: calc(100vw - 300px); + max-width: 100%; + padding: 1.8rem 2.2rem; + } } @media (max-width: 1000px) { - #mobile-toc-toggle, - #mobile-pinned-toggle { - display: block; - } + #mobile-toc-toggle, + #mobile-pinned-toggle { + display: block; + } - #table-of-contents { - display: block; - position: fixed; - top: 0; - left: 0; - width: 260px; - max-width: 80vw; - height: 100vh; - overflow-y: auto; - transform: translateX(-100%); - transition: transform 0.25s ease; - z-index: 1000; - } + #table-of-contents { + display: block; + position: fixed; + top: 0; + left: 0; + width: 260px; + max-width: 80vw; + height: 100vh; + overflow-y: auto; + transform: translateX(-100%); + transition: transform 0.25s ease; + z-index: 1000; + } - #table-of-contents.mobile-visible { - transform: translateX(0); - } + #table-of-contents.mobile-visible { + transform: translateX(0); + } - #pinned-panel { - display: block !important; - position: fixed; - top: 0; - right: 0; - width: 260px; - max-width: 80vw; - height: 100vh; - overflow-y: auto; - transform: translateX(100%); - transition: transform 0.25s ease; - z-index: 1000; - } + #pinned-panel { + display: block !important; + position: fixed; + top: 0; + right: 0; + width: 260px; + max-width: 80vw; + height: 100vh; + overflow-y: auto; + transform: translateX(100%); + transition: transform 0.25s ease; + z-index: 1000; + } - #pinned-panel.mobile-visible { - transform: translateX(0); - } + #pinned-panel.mobile-visible { + transform: translateX(0); + } - #content, - #content.pinned-hidden { - margin-left: 0; - margin-right: 0; - width: 100vw; - max-width: 100%; - padding: 1.5rem 1.25rem; - } + #content, + #content.pinned-hidden { + margin-left: 0; + margin-right: 0; + width: 100vw; + max-width: 100%; + padding: 1.5rem 1.25rem; + } + #toc-search-container { + top: 3rem; + margin-top: 2.5rem; + } + } + + .heading-link { + opacity: 0; + visibility: hidden; + transition: opacity 0.2s, visibility 0.2s; + margin-left: 0.5rem; + color: #718ca1; + text-decoration: none; + font-size: 0.8em; + vertical-align: middle; + } + + .heading-link:hover { + color: #5ec4ff; + text-decoration: none; + } + + h1:hover .heading-link, + h2:hover .heading-link, + h3:hover .heading-link, + h4:hover .heading-link, + h5:hover .heading-link, + h6:hover .heading-link { + opacity: 1; + visibility: visible; } @media (max-width: 700px) { - #content { - padding: 1.2rem 1rem; - } + #content { + padding: 1.2rem 1rem; + } } .darkmode-layer, .darkmode-toggle { - z-index: 500; + z-index: 500; } html, body { - overflow-x: hidden; + overflow-x: hidden; } h1 .toc-pin-btn, @@ -32701,9 +32905,9 @@ This is the stylesheet used by the [[#h:12880c64-229c-4063-9eea-387a97490676][HT h4 .toc-pin-btn, h5 .toc-pin-btn, h6 .toc-pin-btn { - opacity: 0; - visibility: hidden; - transition: opacity 0.2s, visibility 0.2s; + opacity: 0; + visibility: hidden; + transition: opacity 0.2s, visibility 0.2s; } h1:hover .toc-pin-btn, @@ -32712,32 +32916,32 @@ This is the stylesheet used by the [[#h:12880c64-229c-4063-9eea-387a97490676][HT h4:hover .toc-pin-btn, h5:hover .toc-pin-btn, h6:hover .toc-pin-btn { - opacity: 1; - visibility: visible; + opacity: 1; + visibility: visible; } #clear-all-pins-btn { - width: 100%; - margin: 0.5rem 0; - padding: 0.4rem 0.6rem; - background-color: #2f3b45; - border: 1px solid #3a4a56; - color: #b7c5d3; - cursor: pointer; - font-size: 0.85rem; - border-radius: 4px; - transition: background-color 0.2s, color 0.2s; + width: 100%; + margin: 0.5rem 0; + padding: 0.4rem 0.6rem; + background-color: #2f3b45; + border: 1px solid #3a4a56; + color: #b7c5d3; + cursor: pointer; + font-size: 0.85rem; + border-radius: 4px; + transition: background-color 0.2s, color 0.2s; } #clear-all-pins-btn:hover { - background-color: #3a4a56; - color: #ff6b6b; + background-color: #3a4a56; + color: #ff6b6b; } @media (max-width: 1000px) { - body.mobile-panel-open { - overflow: hidden; - } + body.mobile-panel-open { + overflow: hidden; + } } #+end_src ** justfile