diff --git a/builder/page.ts b/builder/page.ts
index 7080601..ff5dffd 100644
--- a/builder/page.ts
+++ b/builder/page.ts
@@ -80,12 +80,14 @@ export async function CreateFolderPage(toolbar: string, path: string) {
function RenderPage(path: string, data: string) {
- const pathFrag = path.split("/");
+ const pathFrag = path.split("/").slice(2);
const { summary, details } = IngestPage(data);
const html = `
`
- + ``
- + `
🔗`
+ + ``
+ + `
🔗`
+ + `
📂`
+ + `
`
+ `
Close
`
+ `
`
+ `${summary.text}
`
@@ -97,7 +99,7 @@ function RenderPage(path: string, data: string) {
+ summary.params.map(p => ``
+`${p.name}`
+`: ${p.type}`
- + ``
+ + ``
+`
`).join("")
+ ``
+ (summary.type == "function"
@@ -105,7 +107,7 @@ function RenderPage(path: string, data: string) {
+ `${summary.returns.map(p => `
`
+`${p.name}`
+`: ${p.type}`
- + ``
+ + ``
+`
`).join("")}
`
) : "}")
+ ``
diff --git a/client/index.ts b/client/index.ts
index c24d604..0165a6d 100644
--- a/client/index.ts
+++ b/client/index.ts
@@ -28,7 +28,7 @@ function AnyClick(ev: MouseEvent) {
}
}
-async function OpenEntry(href: string, caller?: HTMLElement) {
+async function OpenEntry(href: string, caller?: HTMLElement, pushEnd: boolean = false) {
const stash = document.querySelector(".stash");
if (!stash) throw new Error("Missing stash element");
@@ -43,10 +43,12 @@ async function OpenEntry(href: string, caller?: HTMLElement) {
if (!entry) throw new Error("Route is missing div.entry");
if (!existing && caller) caller.style.setProperty('view-transition-name', href.replaceAll("/", "_"));
- await TransitionStart();
+ if (!pushEnd) await TransitionStart();
if (existing) existing.remove();
if (caller) caller.style.removeProperty('view-transition-name');
- stash.insertBefore(entry, stash.firstChild);
+
+ if (pushEnd) stash.appendChild(entry);
+ else stash.insertBefore(entry, stash.firstChild);
stash.scrollTo({top: 0});
const title = doc.querySelector("title")?.innerText || document.title;
@@ -55,6 +57,8 @@ async function OpenEntry(href: string, caller?: HTMLElement) {
Save();
}
+(window as any).OpenEntry = OpenEntry;
+
function FindOpenEntry(href: string) {
for (const div of document.body.querySelectorAll(".entry")) {
@@ -89,8 +93,7 @@ async function OpenFolder(href: string) {
function Save() {
const pages = [ ...document.body.querySelectorAll(".entry") ]
.map(x => x.getAttribute("data-src"))
- .filter(x => x)
- .reverse();
+ .filter(x => x);
localStorage.setItem("open-pages", pages.join("\n"));
}
@@ -137,7 +140,7 @@ async function Startup() {
const pages = (localStorage.getItem('open-pages') || "").split("\n");
for (const page of pages) {
- await OpenEntry(page);
+ await OpenEntry(page, undefined, true);
}
Search.Bind();
diff --git a/client/search.ts b/client/search.ts
index 2cc38a5..733a0d8 100644
--- a/client/search.ts
+++ b/client/search.ts
@@ -5,6 +5,7 @@ function Focus(ev: FocusEvent) {
ev.target.value = "";
}
+let resultsElm: HTMLDivElement;
let searchElm: HTMLInputElement;
let loading = false;
let timer: NodeJS.Timeout;
@@ -19,7 +20,7 @@ async function PreloadIndex() {
if (!req.ok) throw new Error(req.statusText);
const json = await req.json();
- console.log("Loaded search index", json);
+ console.info("Loaded search index", json);
index = lunr(function () {
this.ref('href');
@@ -37,6 +38,25 @@ async function PreloadIndex() {
function Keypress(ev: KeyboardEvent) {
if (!(ev.target instanceof HTMLInputElement)) return;
+ if (ev.key === "Enter") {
+ ev.stopImmediatePropagation();
+ ev.stopPropagation();
+ ev.preventDefault();
+ OpenSelection();
+ return;
+ }
+
+ let move = 0;
+ if (ev.key === "ArrowDown") move = -1;
+ if (ev.key === "ArrowUp") move = 1;
+ if (move !== 0) {
+ ev.stopImmediatePropagation();
+ ev.stopPropagation();
+ ev.preventDefault();
+ MoveSelection(move);
+ return;
+ }
+
PreloadIndex();
if (timer) clearTimeout(timer);
@@ -50,11 +70,9 @@ function Search() {
};
const res = index.search(searchElm.value+"*");
- console.log(res);
- const results = document.querySelector("#search .results");
- if (!results) return;
- results.innerHTML = "";
+ if (!resultsElm) return;
+ resultsElm.innerHTML = "";
for (const opt of res) {
const elm = document.createElement("a");
@@ -68,14 +86,37 @@ function Search() {
ctx.className = "comment";
elm.appendChild(ctx);
- results.appendChild(elm);
+ resultsElm.appendChild(elm);
}
}
+function MoveSelection(delta: number) {
+ const i = [...resultsElm.children].findIndex(v => v.hasAttribute("selected"));
+ if (0 <= i) resultsElm.children[i].removeAttribute("selected");
+
+ const j = Math.min(Math.max(0, i-delta), resultsElm.children.length-1);
+ resultsElm.children[j].setAttribute("selected", "true");
+}
+
+function OpenSelection() {
+ const i = Math.max(0, [...resultsElm.children].findIndex(v => v.hasAttribute("selected")));
+
+ const item = resultsElm.children[i];
+ if (!item) return;
+
+ item.removeAttribute("selected");
+ (window as any).OpenEntry(item.getAttribute("href") || "", item);
+ searchElm.blur();
+}
+
export function Bind() {
- const elm = document.getElementById("search-input");
- if (!(elm instanceof HTMLInputElement)) throw new Error("Missing search box");
- searchElm = elm;
+ const a = document.querySelector("#search .results");
+ if (!(a instanceof HTMLDivElement)) throw new Error("Missing search results div");
+ resultsElm = a;
+
+ const b = document.getElementById("search-input");
+ if (!(b instanceof HTMLInputElement)) throw new Error("Missing search box");
+ searchElm = b;
searchElm.addEventListener("keyup", Keypress);
searchElm.addEventListener("focus", Focus);
diff --git a/public/main.css b/public/main.css
index c615a08..59f88bb 100644
--- a/public/main.css
+++ b/public/main.css
@@ -10,9 +10,22 @@ body {
min-height: 100vh;
font-size: 0.8rem;
- background-color: #272822;
+ background-color: var(--bg);
font-family: Fira Code;
- color: #f8f8f2;
+ color: var(--fg);
+
+ --bg: #272822;
+ --bg_h: #3e3d32;
+ --bg_c: #75715e;
+ --fg: #f8f8f2;
+ --yellow: #e6db74;
+ --orange: #fd971f;
+ --red: #f92672;
+ --magenta: #fd5ff0;
+ --violet: #ae81ff;
+ --blue: #66d9ef;
+ --cyan: #a1efe4;
+ --green: #a6e22e;
}
a, a:visited {
@@ -43,7 +56,7 @@ a, a:visited {
margin-top: 0;
margin-left: 0;
- background-color: #3e3d32;
+ background-color: var(--bg_h);
padding: 3px 10px;
}
.toolbar a[folder][parent]::before {
@@ -90,14 +103,14 @@ a[folder][parent] + a[folder]:not([parent]) {
.entry .expander {
display: flex;
align-items: center;
- gap: 5px;
+ gap: 10px;
user-select: none;
}
.entry .expander::before {
content: "+";
font-size: 1.3rem;
- margin-right: 0.2rem;
+ margin-right: 0rem;
cursor: pointer;
}
.entry[open] .expander::before {
@@ -106,7 +119,6 @@ a[folder][parent] + a[folder]:not([parent]) {
.entry .expander a {
text-decoration: none;
- flex-grow: 1;
}
.entry .close {
@@ -121,6 +133,12 @@ a[folder][parent] + a[folder]:not([parent]) {
.entry[open] .details {
display: block;
}
+.entry .inline-details {
+ display: none;
+}
+.entry[open] .inline-details {
+ display: inline;
+}
.entry .context {
font-style: italic;
@@ -134,9 +152,9 @@ a[folder][parent] + a[folder]:not([parent]) {
font-family: Fira Code;
font-size: 0.8rem;
- color: #f8f8f2;
+ color: var(--fg);
- background-color: #3e3d32;
+ background-color: var(--bg_h);
border-radius: 5px;
}
@@ -146,13 +164,6 @@ a[folder][parent] + a[folder]:not([parent]) {
gap: 0.6rem;
}
-.entry .comment {
- display: none;
-}
-.entry[open] .comment {
- display: inline;
-}
-
.entry[open] .cluster {
display: flex;
flex-direction: column;
@@ -165,21 +176,21 @@ a[folder][parent] + a[folder]:not([parent]) {
.name {
- color: #a6e22e;
+ color: var(--green);
}
.argument {
- color: #fd971f;
+ color: var(--orange);
font-style: italic;
}
.type {
- color: #66d9ef !important;
+ color: var(--blue) !important;
font-style: italic;
}
.comment {
- color: #75715e;
+ color: var(--bg_c);
font-style: italic;
}
@@ -288,4 +299,7 @@ a[folder][parent] + a[folder]:not([parent]) {
#search .result .comment {
font-style: normal;
margin-left: 0.5rem;
+}
+#search .result[selected] {
+ background-color: var(--bg_h);
}
\ No newline at end of file