site: a lit hero, new slogans, the wallpapers, and a better reader

Home:
- The hero stands on a light from the upper left and a pool of the
  accent, mixed from Niveus tokens so it follows the theme. The mark sits
  in an .nv-tile key under a slogan: "Every layer its own -- from the
  first instruction to the last pixel."
- New section headings: "A teaching kernel that outgrew its lessons",
  "Measured, not marketed", "One ISO. One stick. Your disks untouched."
- A Wallpapers section with the four new ones, each downloadable as a
  4K JPEG or as SVG. The four-card grids are two across, so none is left
  alone on its row.

Docs reader:
- An index filter, a breadcrumb with reading time and source path,
  code blocks framed with a language strip and a Copy key, previous and
  next keys at the foot, a reading-progress hairline, and the contents
  entry lit for the section on screen.
- The index lists the documents it was missing: scheduler, FAT, audio,
  LDLS, debugging and the hardware report.
- On a phone the page is no longer wider than the screen (a pre block
  was sizing the grid's auto column), and the index folds away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-23 15:56:43 +02:00
commit 7af579a20d
4 changed files with 421 additions and 19 deletions

View file

@ -26,21 +26,27 @@
["architecture/README.md", "The system, layer by layer"],
["architecture/boot.md", "Boot"],
["architecture/kernel.md", "Kernel"],
["architecture/scheduler.md", "Scheduler"],
["architecture/syscalls.md", "Syscalls"],
["architecture/filesystem.md", "The VFS"],
["architecture/schfs.md", "SchFS"],
["architecture/fat.md", "FAT"],
["architecture/terminals.md", "Virtual terminals"],
["architecture/devices.md", "Device model"],
["architecture/usb.md", "USB"],
["architecture/graphics.md", "Graphics"],
["architecture/audio.md", "Audio"],
["architecture/networking.md", "Networking"],
["architecture/init.md", "init"]
["architecture/init.md", "init"],
["architecture/ldls.md", "Linux drivers (LDLS)"],
["architecture/debugging.md", "Debugging"]
]},
{ group: "Guides", items: [
["guides/building.md", "Building"],
["guides/testing.md", "Testing"],
["guides/layout.md", "Tree layout"],
["guides/adding-a-driver.md", "Adding a driver"]
["guides/adding-a-driver.md", "Adding a driver"],
["guides/hardware-report.md", "Reporting hardware"]
]},
{ group: "Plan", items: [
["plan/README.md", "What plan/ is for"],
@ -57,7 +63,7 @@
["history/moving-to-linux.md", "Moving to Linux"]
]},
{ group: "Branding", items: [
["branding/README.md", "The three marks"]
["branding/README.md", "Shift, and the wallpapers"]
]}
];
@ -178,9 +184,17 @@
i++;
while (i < L.length && !/^\s*```/.test(L[i])) buf.push(L[i++]);
i++;
out.push('<pre class="nv-code doc__pre"' +
/* A frame rather than a bare block: a title strip naming the language,
and a button that copies it, because the thing a reader most often
wants from a code block in these docs is to paste it somewhere. */
out.push('<figure class="codeframe">' +
'<figcaption class="codeframe__bar">' +
'<span class="codeframe__lang">' + esc(lang || "text") + "</span>" +
'<button class="nv-button nv-button--ghost nv-button--sm" type="button" data-copy>Copy</button>' +
"</figcaption>" +
'<pre class="nv-code doc__pre"' +
(lang ? ' data-lang="' + esc(lang) + '"' : "") +
"><code>" + esc(buf.join("\n")) + "</code></pre>");
"><code>" + esc(buf.join("\n")) + "</code></pre></figure>");
continue;
}
@ -307,6 +321,77 @@
tocEl.appendChild(ul);
}
/* Every document in index order, with the group it belongs to. */
var FLAT = [];
DOCS.forEach(function (g) {
g.items.forEach(function (it) { FLAT.push({ path: it[0], title: it[1], group: g.group }); });
});
function indexOf(path) {
for (var k = 0; k < FLAT.length; k++) if (FLAT[k].path === path) return k;
return -1;
}
/* Where the reader is, how long it will take, and where the file lives --
above the document, because a page arrived at from a search has no other
way to say which part of the docs it belongs to. */
function header(path, text) {
var at = FLAT[indexOf(path)] || { group: "Docs", title: path };
var words = text.split(/\s+/).length;
var mins = Math.max(1, Math.round(words / 230));
return '<header class="dochead">' +
'<ol class="nv-breadcrumbs">' +
'<li><a href="?doc=README.md">Docs</a></li>' +
"<li>" + esc(at.group) + "</li>" +
'<li aria-current="page">' + esc(at.title) + "</li>" +
"</ol>" +
'<span class="dochead__meta nv-caption nv-text-tertiary">' +
mins + " min read · " +
'<a href="../' + esc(path) + '">' + esc(path) + "</a>" +
"</span>" +
"</header>";
}
/* The next and previous document, as two keys at the foot of the page. A
reader who reaches the end of one has nearly always come to read the
section, not the file. */
function pager(path) {
var k = indexOf(path), html = '<nav class="docpager" aria-label="More documents">';
function key(d, dir) {
return '<a class="nv-card nv-card--interactive docpager__key docpager__key--' + dir +
'" href="' + docHref(d.path) + '">' +
'<span class="nv-overline nv-text-tertiary">' + (dir === "prev" ? "← Previous" : "Next →") + "</span>" +
'<span class="nv-title">' + esc(d.title) + "</span>" +
'<span class="nv-caption nv-text-tertiary">' + esc(d.group) + "</span></a>";
}
if (k > 0) html += key(FLAT[k - 1], "prev");
else html += "<span></span>";
if (k >= 0 && k < FLAT.length - 1) html += key(FLAT[k + 1], "next");
return html + "</nav>";
}
/* Light the contents entry for the section being read. */
var spy;
function watchHeadings() {
if (spy) spy.disconnect();
if (!("IntersectionObserver" in window)) return;
var links = {};
Array.prototype.forEach.call(tocEl.querySelectorAll("a"), function (a) {
links[a.getAttribute("href").slice(1)] = a;
});
spy = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting || !links[e.target.id]) return;
Array.prototype.forEach.call(tocEl.querySelectorAll("a"), function (a) {
a.removeAttribute("aria-current");
});
links[e.target.id].setAttribute("aria-current", "location");
});
}, { rootMargin: "0px 0px -70% 0px" });
Array.prototype.forEach.call(article.querySelectorAll("h2[id], h3[id]"), function (h) {
spy.observe(h);
});
}
function fail(title, body) {
article.innerHTML = "<h1>" + title + "</h1>" + body;
tocEl.hidden = true;
@ -322,8 +407,9 @@
return r.text();
}).then(function (text) {
var headings = [];
article.innerHTML = render(text, path, headings);
article.innerHTML = header(path, text) + render(text, path, headings) + pager(path);
buildToc(headings);
watchHeadings();
article.removeAttribute("aria-busy");
if (headings.length) document.title = headings[0].text + " — SchOS docs";
var hash = location.hash.slice(1);
@ -340,7 +426,7 @@
"<p>Browsers block <code>fetch()</code> on <code>file://</code> URLs, so the " +
"reader cannot load the Markdown. The rest of the site works from a file; " +
"this page does not. Serve the repository root:</p>" +
'<pre class="nv-code doc__pre"><code>cd /home/nexoniarz/Dokumenty/Projects/SchOS\n' +
'<pre class="nv-code doc__pre"><code>cd path/to/SchOS\n' +
"python3 -m http.server 8000</code></pre>" +
"<p>Then open <code>http://localhost:8000/docs/Website/docs.html</code>.</p>" +
'<p>Or read this one directly: <a href="../' + esc(path) + '">' + esc(path) + "</a></p>");
@ -365,6 +451,54 @@
buildNav();
load(pathFromUrl());
/* Fold the index on a narrow screen, and fold it again after picking a
document there -- the choice is made, and the document is what was asked
for. */
var fold = document.getElementById("docindex");
var narrow = window.matchMedia && window.matchMedia("(max-width: 64rem)");
if (fold && narrow && narrow.matches) fold.open = false;
nav.addEventListener("click", function () {
if (fold && narrow && narrow.matches) fold.open = false;
});
/* Filter the index as you type. Groups with nothing left in them go too,
so the heading of an empty section does not sit there implying a match. */
var filter = document.getElementById("docfilter");
if (filter) filter.addEventListener("input", function () {
var q = filter.value.trim().toLowerCase();
Array.prototype.forEach.call(nav.querySelectorAll(".nv-nav__list"), function (ul) {
var any = false;
Array.prototype.forEach.call(ul.querySelectorAll("li"), function (li) {
var hit = !q || li.textContent.toLowerCase().indexOf(q) >= 0;
li.hidden = !hit;
any = any || hit;
});
ul.hidden = !any;
if (ul.previousElementSibling) ul.previousElementSibling.hidden = !any;
});
});
/* Copy a code block. The label says what happened, then goes back. */
article.addEventListener("click", function (ev) {
var b = ev.target.closest ? ev.target.closest("[data-copy]") : null;
if (!b) return;
var code = b.closest(".codeframe").querySelector("code").textContent;
var done = function (ok) {
b.textContent = ok ? "Copied" : "Select and copy";
setTimeout(function () { b.textContent = "Copy"; }, 1600);
};
if (navigator.clipboard) navigator.clipboard.writeText(code).then(function () { done(true); }, function () { done(false); });
else done(false);
});
/* How far down the document the reader is: a hairline under the bar. */
var bar = document.getElementById("readbar");
if (bar) window.addEventListener("scroll", function () {
var h = document.documentElement;
var f = h.scrollHeight > h.clientHeight ? h.scrollTop / (h.scrollHeight - h.clientHeight) : 0;
bar.style.transform = "scaleX(" + Math.min(1, Math.max(0, f)) + ")";
}, { passive: true });
/* Intercept in-page navigation so the reader does not reload its own shell. */
document.addEventListener("click", function (ev) {
var a = ev.target.closest ? ev.target.closest("a") : null;