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:
parent
1fd73740f8
commit
7af579a20d
4 changed files with 421 additions and 19 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue