Skip to content

Commit

Permalink
+search function
Browse files Browse the repository at this point in the history
  • Loading branch information
AjaniBilby committed Apr 14, 2024
1 parent 8c460a8 commit a817c56
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 32 deletions.
6 changes: 5 additions & 1 deletion builder/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CreateFolderPage, CreatePage } from "./page";
import { readdir, stat, mkdir } from "fs/promises";
import { readdir, stat, mkdir, writeFile } from "fs/promises";
import { CreateToolbar } from "./toolbar";
import * as Search from "./search";

console.info("Building Docs...");

Expand Down Expand Up @@ -30,6 +31,9 @@ async function BuildDir(path: string) {
await CreateFolderPage(toolbar, path);

await Promise.all(folders.map(BuildDir));


writeFile("./public/search.json", Search.Jsonify());
}


Expand Down
34 changes: 29 additions & 5 deletions builder/page.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,50 @@
import { readFile, writeFile } from "fs/promises";
import { Path2Name, Reroute } from "./helper";
import { AddIndex } from "./search";


const search = `<div id="search">
<div>
<input id="search-input"
type="text" name="q"
placeholder="type..."
aria-labelledby="Search"
autoComplete="off"
/>
<div class="placeholder">Search</div>
</div>
<div class="results"></div>
</div>`;

export async function CreatePage(toolbar: string, path: string) {
const extIndex = path.lastIndexOf(".");
const ext = path.slice(extIndex);
if (ext != ".md") return;

const name = Path2Name(path);
const href = Reroute(path);

const data = await readFile(path, "utf8");
const { html, type } = RenderPage(path, data);

AddIndex({ href, name, text: data });

const document = `<!DOCTYPE html>
<html>
<head>
<title>${Path2Name(path)}</title>
<title>${name}</title>
<link rel="stylesheet" href="/main.css"/>
<script src="/index.js"></script>
</head>
<body>
${toolbar}
<div class="dashboard">
<div class="entry" style="view-transition-name: ${Reroute(path).replaceAll("/", "_")}" data-src="${Reroute(path)}">`
+ html
+`</div>
${search}
<div class="stash">
<div class="entry" style="view-transition-name: ${href.replaceAll("/", "_")}" data-src="${href}">`
+ html
+`</div>
</div>
</div>
</body>
</html>`;
Expand All @@ -42,7 +65,7 @@ export async function CreateFolderPage(toolbar: string, path: string) {
</head>
<body>
${toolbar}
<div class="dashboard"></div>
<div class="dashboard">${search}<div class="stash"></div></div>
</body>
</html>`;

Expand All @@ -57,6 +80,7 @@ function RenderPage(path: string, data: string) {

const html = `<div class="expander" onclick="Expander(event)">`
+ `<span class="comment">${pathFrag.slice(2, -1).join("/")}</span>`
+ `<a title="Open Folder" href="/${pathFrag.slice(2, -1).join("/")}" folder>🔗</a>`
+ `<div class="close" onclick="CloseEntry(event, this);">Close</div>`
+ `</div>`
+ `<div>`
Expand Down
10 changes: 10 additions & 0 deletions builder/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type SearchItem = { href: string, name: string, text: string };
const index = new Array<SearchItem>();

export function AddIndex(item: SearchItem) {
index.push(item);
}

export function Jsonify() {
return JSON.stringify(index);
}
2 changes: 1 addition & 1 deletion builder/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function CreateToolbar(href: string, folders: string[], files: string[])
}
);

return `<div class="toolbar" style="display: flex; flex-direction: column;">
return `<div class="toolbar">
${href !== "./docs" ? `<a href="/" parent folder> Index</a>` : ""}
${parents.map(x =>
`<a href="${x.path}" parent folder>
Expand Down
14 changes: 9 additions & 5 deletions client/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TransitionStart } from "./helper";
import * as Search from "./search";

const parser = new DOMParser();

Expand Down Expand Up @@ -28,8 +29,8 @@ function AnyClick(ev: MouseEvent) {
}

async function OpenEntry(href: string, caller?: HTMLElement) {
const dashboard = document.querySelector(".dashboard");
if (!dashboard) throw new Error("Missing dashboard element");
const stash = document.querySelector(".stash");
if (!stash) throw new Error("Missing stash element");

const existing = FindOpenEntry(href);

Expand All @@ -45,8 +46,8 @@ async function OpenEntry(href: string, caller?: HTMLElement) {
await TransitionStart();
if (existing) existing.remove();
if (caller) caller.style.removeProperty('view-transition-name');
dashboard.insertBefore(entry, dashboard.firstChild);
dashboard.scrollTo({top: 0});
stash.insertBefore(entry, stash.firstChild);
stash.scrollTo({top: 0});

const title = doc.querySelector("title")?.innerText || document.title;
history.replaceState({}, title, href);
Expand All @@ -66,7 +67,7 @@ function FindOpenEntry(href: string) {

async function OpenFolder(href: string) {
const current = document.querySelector(".toolbar");
if (!current) throw new Error("Missing dashboard element");
if (!current) throw new Error("Missing stash element");

const req = await fetch(href);
if (!req.ok) throw new Error(`Failed to load ${href}`);
Expand Down Expand Up @@ -99,6 +100,7 @@ function Save() {

async function Expander(ev: MouseEvent) {
if (!(ev.target instanceof HTMLElement)) return;
if (ev.target instanceof HTMLAnchorElement) return;
const elm = ev.target.closest(".entry");
if (!elm) return;

Expand Down Expand Up @@ -136,6 +138,8 @@ async function Startup() {
for (const page of pages) {
await OpenEntry(page);
}

Search.Bind();
}

window.addEventListener("load", Startup);
82 changes: 82 additions & 0 deletions client/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import lunr from "lunr";

function Focus(ev: FocusEvent) {
if (!(ev.target instanceof HTMLInputElement)) return;
ev.target.value = "";
}

let searchElm: HTMLInputElement;
let loading = false;
let timer: NodeJS.Timeout;
let index: lunr.Index;
const naming = new Map<string, string>();

async function PreloadIndex() {
if (loading) return;
loading = true;

const req = await fetch("/search.json");
if (!req.ok) throw new Error(req.statusText);

const json = await req.json();
console.log("Loaded search index", json);

index = lunr(function () {
this.ref('name');
this.ref('href');
this.field('name');
this.field('text');

for (const item of json) {
naming.set(item.href, item.name);
this.add(item);
}
});
}

function Keypress(ev: KeyboardEvent) {
if (!(ev.target instanceof HTMLInputElement)) return;

PreloadIndex();

if (timer) clearTimeout(timer);
timer = setTimeout(Search, 200);
}

function Search() {
if (!index) {
timer = setTimeout(Search, 400);
return;
};

const res = index.search(searchElm.value+"*");
console.log(res);

const results = document.querySelector("#search .results");
if (!results) return;
results.innerHTML = "";

for (const opt of res) {
const elm = document.createElement("a");
elm.className = "result";
elm.innerText = naming.get(opt.ref) || "Unknown";
elm.href = opt.ref;
elm.setAttribute("entry", "true");

const ctx = document.createElement("span");
ctx.innerText = opt.ref.split("/").slice(0, -1).join("/");
ctx.className = "comment";
elm.appendChild(ctx);

results.appendChild(elm);
}
}

export function Bind() {
const elm = document.getElementById("search-input");
if (!(elm instanceof HTMLInputElement)) throw new Error("Missing search box");
searchElm = elm;

searchElm.addEventListener("keyup", Keypress);
searchElm.addEventListener("focus", Focus);
}
Loading

0 comments on commit a817c56

Please sign in to comment.