diff --git a/docs/build.ts b/docs/build.ts
new file mode 100644
index 0000000..acfb7d1
--- /dev/null
+++ b/docs/build.ts
@@ -0,0 +1,70 @@
+import { Renderer } from "@libs/markdown";
+import HighlightPlugin from "@libs/markdown/plugins/highlighting";
+import { walk } from "@std/fs";
+import * as pathTools from "@std/path";
+
+function injectIntoTemplate(name: string, rendered: string): string {
+ let content = [
+ "",
+ "",
+ "
",
+ [
+ `${name}`,
+ '',
+ '',
+ ],
+ "",
+ "",
+ [
+ rendered,
+ ],
+ "",
+ "",
+ ];
+ while (content.some((x) => Array.isArray(x))) {
+ content = content.flat();
+ }
+ return content.join("");
+}
+
+async function renderMarkdown() {
+ const renderer = await Renderer.with({
+ plugins: [HighlightPlugin],
+ });
+ for await (
+ const entry of walk("src", { exts: [".md"], includeDirs: false })
+ ) {
+ const parsed = pathTools.parse(entry.path);
+ parsed.dir = parsed.dir.replace(/^src[\\/]?/, "");
+ parsed.dir = pathTools.join("-", parsed.dir);
+ parsed.ext = ".html";
+ parsed.base = `${parsed.name}${parsed.ext}`;
+
+ await Deno.mkdir(parsed.dir, { recursive: true });
+ const content = await renderer.render(await Deno.readTextFile(entry.path));
+ await Deno.writeTextFile(
+ pathTools.format(parsed),
+ injectIntoTemplate(parsed.name, content),
+ );
+ }
+}
+
+async function copyStaticFiles() {
+ for await (const entry of walk("static", { includeDirs: false })) {
+ const parsed = pathTools.parse(entry.path);
+ parsed.dir = parsed.dir.replace(/^static[\\/]?/, "");
+ parsed.dir = pathTools.join("-", parsed.dir);
+
+ await Deno.mkdir(parsed.dir, { recursive: true });
+ await Deno.copyFile(entry.path, pathTools.format(parsed));
+ }
+}
+
+async function main() {
+ await renderMarkdown();
+ await copyStaticFiles();
+}
+
+if (import.meta.main) {
+ main();
+}
diff --git a/docs/deno.jsonc b/docs/deno.jsonc
index 07a5bdc..4192d83 100644
--- a/docs/deno.jsonc
+++ b/docs/deno.jsonc
@@ -7,5 +7,8 @@
"@libs/markdown": "jsr:@libs/markdown@^2.0.4",
"@std/fs": "jsr:@std/fs@^1.0.19",
"@std/path": "jsr:@std/path@^1.1.2"
+ },
+ "tasks": {
+ "build": "deno run -A build.ts"
}
}
diff --git a/docs/render.ts b/docs/render.ts
deleted file mode 100644
index e208212..0000000
--- a/docs/render.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { Renderer } from "@libs/markdown";
-import HighlightPlugin from "@libs/markdown/plugins/highlighting";
-import { walk } from "@std/fs";
-import * as pathTools from "@std/path";
-
-function injectIntoTemplate(name: string, rendered: string): string {
- let content = [
- "",
- "",
- "",
- [
- `${name}`,
- ],
- "",
- "",
- [
- rendered,
- "",
- ],
- "",
- "",
- ];
- while (content.some((x) => Array.isArray(x))) {
- content = content.flat();
- }
- return content.join("");
-}
-
-async function main() {
- const renderer = await Renderer.with({
- plugins: [HighlightPlugin],
- });
- for await (
- const entry of walk("src", {
- exts: [".md"],
- })
- ) {
- const parsed = pathTools.parse(entry.path);
- parsed.dir = parsed.dir.replace(/^src[\\/]?/, "");
- parsed.dir = pathTools.join("-", parsed.dir);
- parsed.ext = ".html";
- parsed.base = `${parsed.name}${parsed.ext}`;
-
- await Deno.mkdir(parsed.dir, { recursive: true });
- const content = await renderer.render(await Deno.readTextFile(entry.path));
- await Deno.writeTextFile(
- pathTools.format(parsed),
- injectIntoTemplate(parsed.name, content),
- );
- }
-}
-
-if (import.meta.main) {
- main();
-}
diff --git a/docs/src/gamelib.md b/docs/src/gamelib.md
index de0c55a..efba5af 100644
--- a/docs/src/gamelib.md
+++ b/docs/src/gamelib.md
@@ -1,5 +1,9 @@
# Gamelib
+## Contents
+
+
+
## Models
### `Color`
@@ -22,8 +26,8 @@ lib.drawRect(100, 0, 100, 100, lib.rgb(192, 127, 172));
A type of string.
-Represents JavaScript's `KeyboardEvent.key`
-([link](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)) property.
+Represents JavaScript's
+[`KeyboardEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) property.
Example:
diff --git a/docs/static/header_ids.js b/docs/static/header_ids.js
new file mode 100644
index 0000000..8e4e0f4
--- /dev/null
+++ b/docs/static/header_ids.js
@@ -0,0 +1,61 @@
+function renderTableOfContents() {
+ const root = document.querySelector("table-of-contents");
+
+ const list = document.createElement("ul");
+
+ const headers = document.querySelectorAll("h2,h3,h4,h5,h6");
+
+ const ancestors = [];
+ for (const header of headers) {
+ const depth = parseInt(header.tagName.slice(1)) - 1;
+ while (ancestors.length >= depth) {
+ ancestors.pop();
+ }
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+ a.textContent = header.textContent;
+ a.href = `#${header.id}`;
+ const ul = document.createElement("ul");
+ li.replaceChildren(a, ul);
+ const mostRecent = ancestors[ancestors.length - 1];
+ if (mostRecent === undefined) {
+ list.appendChild(li);
+ } else {
+ mostRecent.appendChild(li);
+ }
+ ancestors.push(ul);
+ }
+
+ root.replaceChildren(list);
+}
+
+function slugify(elements) {
+ console.log(elements);
+ return elements
+ .map((element, i) => {
+ if (i === 0) return null;
+ const content = element.textContent.trim();
+ if (content.startsWith("lib.")) {
+ const [name] = content.match(/^lib\.[^\(]+/);
+ return name.trim();
+ }
+ return content;
+ })
+ .filter((x) => x !== null)
+ .join("-");
+}
+function attachIdToHeaders() {
+ const headers = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
+ const ancestors = [];
+ for (const header of headers) {
+ const depth = parseInt(header.tagName.slice(1));
+ while (ancestors.length >= depth) {
+ ancestors.pop();
+ }
+ ancestors.push(header);
+ if (depth > 1) header.id = slugify(ancestors);
+ }
+}
+
+attachIdToHeaders();
+renderTableOfContents();