Skip to content

Commit 135db02

Browse files
committed
add lunr search
1 parent fce1820 commit 135db02

File tree

26 files changed

+584
-50
lines changed

26 files changed

+584
-50
lines changed

packages/hyperbook/build.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import {
1212
HyperbookContext,
1313
Navigation,
1414
} from "@hyperbook/types";
15+
import lunr from "lunr";
1516
import { process as hyperbookProcess } from "@hyperbook/markdown";
1617

17-
const ASSETS_FOLDER = "__hyperbook_assets";
18+
export const ASSETS_FOLDER = "__hyperbook_assets";
1819

1920
export async function runBuildProject(
2021
project: Hyperproject,
@@ -129,6 +130,7 @@ async function runBuild(
129130
pagesAndSections.sections,
130131
pagesAndSections.pages,
131132
);
133+
const searchDocuments: any[] = [];
132134

133135
let bookFiles = await vfile.listForFolder(root, "book");
134136
if (filter) {
@@ -147,6 +149,7 @@ async function runBuild(
147149
navigation,
148150
};
149151
const result = await hyperbookProcess(file.markdown.content, ctx);
152+
searchDocuments.push(...(result.data.searchDocuments || []));
150153
for (let directive of Object.keys(result.data.directives || {})) {
151154
directives.add(directive);
152155
}
@@ -204,6 +207,7 @@ async function runBuild(
204207
navigation,
205208
};
206209
const result = await hyperbookProcess(file.markdown.content, ctx);
210+
searchDocuments.push(...(result.data.searchDocuments || []));
207211
for (let directive of Object.keys(result.data.directives || {})) {
208212
directives.add(directive);
209213
}
@@ -309,5 +313,33 @@ async function runBuild(
309313
}
310314
process.stdout.write("\n");
311315

316+
if (hyperbookJson.search) {
317+
const documents: Record<string, any> = {};
318+
const idx = lunr(function () {
319+
this.ref("href");
320+
this.field("description");
321+
this.field("keywords");
322+
this.field("heading");
323+
this.field("content");
324+
325+
searchDocuments.forEach((doc) => {
326+
const href = baseCtx.makeUrl(doc.href, "book");
327+
const docWithBase = {
328+
...doc,
329+
href,
330+
};
331+
this.add(docWithBase);
332+
documents[href] = docWithBase;
333+
});
334+
});
335+
336+
const js = `
337+
const LUNR_INDEX = ${JSON.stringify(idx)};
338+
const SEARCH_DOCUMENTS = ${JSON.stringify(documents)};
339+
`;
340+
341+
await fs.writeFile(path.join(rootOut, ASSETS_FOLDER, "search.js"), js);
342+
}
343+
312344
console.log(`${chalk.green(`[${prefix}]`)} Build success: ${rootOut}`);
313345
}

packages/hyperbook/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { getPkgManager } from "./helpers/get-pkg-manager";
99
import { hyperproject } from "@hyperbook/fs";
1010
import { runNew } from "./new";
1111
import packageJson from "./package.json";
12-
import { deprecate } from "util";
1312

1413
const program = new Command();
1514

packages/hyperbook/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,27 @@
3636
"@types/archiver": "6.0.2",
3737
"@types/async-retry": "1.4.8",
3838
"@types/cross-spawn": "6.0.6",
39+
"@types/lunr": "^2.3.7",
3940
"@types/prompts": "2.4.9",
4041
"@types/tar": "6.1.13",
4142
"@types/ws": "^8.5.12",
4243
"@vercel/ncc": "0.38.1",
4344
"archiver": "7.0.1",
4445
"async-retry": "1.3.3",
4546
"chalk": "5.2.0",
47+
"chokidar": "3.6.0",
4648
"commander": "12.1.0",
4749
"cpy": "11.0.1",
4850
"cross-spawn": "7.0.3",
51+
"domutils": "^3.1.0",
4952
"got": "12.6.0",
53+
"htmlparser2": "^9.1.0",
54+
"lunr": "^2.3.9",
55+
"mime": "^4.0.4",
5056
"prompts": "2.4.2",
5157
"rimraf": "5.0.7",
5258
"tar": "6.2.1",
5359
"update-check": "1.5.4",
54-
"chokidar": "3.6.0",
55-
"mime": "^4.0.4",
5660
"ws": "^8.18.0"
5761
},
5862
"dependencies": {

packages/markdown/assets/client.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,60 @@ var hyperbook = (function () {
3333
const tocDrawerEl = document.getElementById("toc-drawer");
3434
tocDrawerEl.open = !tocDrawerEl.open;
3535
}
36+
// search
37+
38+
const searchInputEl = document.getElementById("search-input");
39+
searchInputEl.addEventListener("keypress", (event) => {
40+
if (event.key === "Enter") {
41+
event.preventDefault();
42+
search();
43+
}
44+
});
45+
46+
function searchToggle() {
47+
const searchDrawerEl = document.getElementById("search-drawer");
48+
searchDrawerEl.open = !searchDrawerEl.open;
49+
}
50+
51+
function search() {
52+
const resultsEl = document.getElementById("search-results");
53+
resultsEl.innerHTML = "";
54+
const query = searchInputEl.value;
55+
const idx = window.lunr.Index.load(LUNR_INDEX);
56+
const documents = SEARCH_DOCUMENTS;
57+
const results = idx.search(query);
58+
for (let result of results) {
59+
const doc = documents[result.ref];
60+
61+
const container = document.createElement("a");
62+
container.href = doc.href;
63+
container.classList.add("search-result");
64+
const heading = document.createElement("div");
65+
heading.textContent = doc.heading;
66+
heading.classList.add("search-result-heading");
67+
const content = document.createElement("div");
68+
content.classList.add("search-result-content");
69+
const href = document.createElement("div");
70+
href.classList.add("search-result-href");
71+
href.textContent = doc.href;
72+
if (doc.content.length > 200) {
73+
content.textContent = doc.content.slice(0, 197) + "...";
74+
} else {
75+
content.textContent = doc.content;
76+
}
77+
78+
container.appendChild(heading);
79+
container.appendChild(content);
80+
container.appendChild(href);
81+
resultsEl.appendChild(container);
82+
}
83+
}
3684

3785
function qrcodeOpen() {
3886
const qrCodeDialog = document.getElementById("qrcode-dialog");
3987
const qrcodeEls = qrCodeDialog.getElementsByClassName("make-qrcode");
4088
const urlEls = qrCodeDialog.getElementsByClassName("url");
4189
const qrcodeEl = qrcodeEls[0];
42-
const urlEl = urlEls[0];
4390
const qrcode = new window.QRCode({
4491
content: window.location.href,
4592
padding: 0,
@@ -113,6 +160,8 @@ var hyperbook = (function () {
113160
toggleBookmark,
114161
navToggle,
115162
tocToggle,
163+
searchToggle,
164+
search,
116165
qrcodeOpen,
117166
qrcodeClose,
118167
};

packages/markdown/assets/content.css

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,106 @@ figure {
377377
cursor: pointer;
378378
}
379379

380+
#search-toggle {
381+
background-color: var(--color-text);
382+
width: 24px;
383+
height: 24px;
384+
cursor: pointer;
385+
margin-right: 16px;
386+
mask-size: 100%;
387+
mask-repeat: no-repeat;
388+
mask-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgY2xhc3M9ImZlYXRoZXIgZmVhdGhlci1zZWFyY2giIGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iOCIvPjxsaW5lIHgxPSIyMSIgeDI9IjE2LjY1IiB5MT0iMjEiIHkyPSIxNi42NSIvPjwvc3ZnPg==')
389+
}
390+
391+
#search-toggle:hover {
392+
background-color: var(--color-brand);
393+
}
394+
395+
#search-drawer {
396+
overflow-y: auto;
397+
background: var(--color-nav);
398+
}
399+
400+
#search-drawer .search-input {
401+
display: flex;
402+
margin-top: 16px;
403+
margin-bottom: 16px;
404+
gap: 8px;
405+
border: 1px solid var(--color-nav-border);
406+
background-color: var(--color-nav);
407+
padding: 8px;
408+
border-radius: 4px;
409+
align-items: center;
410+
justify-content: center;
411+
}
412+
413+
#search-drawer .search-input .search-icon {
414+
background-color: var(--color-text);
415+
width: 24px;
416+
height: 24px;
417+
mask-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgY2xhc3M9ImZlYXRoZXIgZmVhdGhlci1zZWFyY2giIGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iOCIvPjxsaW5lIHgxPSIyMSIgeDI9IjE2LjY1IiB5MT0iMjEiIHkyPSIxNi42NSIvPjwvc3ZnPg==')
418+
}
419+
420+
#search-drawer .search-input input {
421+
flex: 1;
422+
background: none;
423+
color: var(--color-text);
424+
border: none;
425+
}
426+
427+
#search-drawer .search-input .search-button {
428+
background-color: var(--color-text);
429+
width: 24px;
430+
height: 24px;
431+
cursor: pointer;
432+
mask-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgY2xhc3M9ImZlYXRoZXIgZmVhdGhlci1hcnJvdy1yaWdodCIgZmlsbD0ibm9uZSIgaGVpZ2h0PSIyNCIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48bGluZSB4MT0iNSIgeDI9IjE5IiB5MT0iMTIiIHkyPSIxMiIvPjxwb2x5bGluZSBwb2ludHM9IjEyIDUgMTkgMTIgMTIgMTkiLz48L3N2Zz4=')
433+
}
434+
435+
#search-results {
436+
display: flex;
437+
flex-direction: column;
438+
gap: 20px;
439+
}
440+
441+
#search-results .search-result {
442+
display: flex;
443+
gap: 4px;
444+
flex-direction: column;
445+
text-decoration: none;
446+
color: var(--color-text);
447+
font-size: 1rem;
448+
border-radius: 8px;
449+
background-color: var(--color-nav-border);
450+
padding: 8px;
451+
}
452+
453+
#search-results .search-result-heading {
454+
font-size: 1.2rem;
455+
font-weight: 500;
456+
}
457+
458+
#search-results .search-result-content {
459+
text-overflow: ellipsis;
460+
font-size: 0.8rem;
461+
}
462+
463+
#search-results .search-result-href {
464+
text-overflow: ellipsis;
465+
overflow: hidden;
466+
text-wrap: nowrap;
467+
font-size: 0.8rem;
468+
font-weight: 500;
469+
}
470+
471+
.search-drawer-content {
472+
display: flex;
473+
flex-direction: column;
474+
padding: 8px;
475+
height: calc(var(--app-height));
476+
overflow-y: auto;
477+
gap: 8px;
478+
}
479+
380480
#toc-drawer {
381481
overflow-y: auto;
382482
background: var(--color-nav);
@@ -385,6 +485,7 @@ figure {
385485
.toc-drawer-content {
386486
display: flex;
387487
flex-direction: column;
488+
height: calc(var(--app-height));
388489
}
389490

390491
.toc-drawer-content>nav {

packages/markdown/assets/shell.css

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,13 @@ li {
2424
margin: 0;
2525
}
2626

27-
#nav-drawer {
28-
background: var(--color-nav);
29-
border-right-color: var(--color-nav-border);
30-
}
31-
3227
side-drawer {
3328
position: absolute;
34-
display: block;
35-
visibility: hidden;
36-
max-width: 0;
37-
max-height: 0;
3829
}
3930

40-
side-drawer[open] {
41-
visibility: visible;
42-
max-width: 350px;
43-
max-height: initial;
31+
#nav-drawer {
32+
background: var(--color-nav);
33+
border-right-color: var(--color-nav-border);
4434
}
4535

4636
.sidebar>.author,
@@ -77,6 +67,7 @@ header.inverted {
7767

7868
.branding {
7969
color: var(--color-brand-text);
70+
flex: 1;
8071
}
8172

8273
.branding:hover {

packages/markdown/devBuild.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ const markdown = await fs.readFile("dev.md", "utf8");
66

77
const result = await process(markdown, {
88
root: "",
9-
qrcode: true,
109
config: {
1110
name: "Hyperbook Dokumenation",
1211
description: "Dokumentation für Hyperbook erstellt mit Hyperbook",
12+
qrcode: true,
13+
search: true,
1314
author: {
1415
name: "OpenPatch",
1516
url: "https://openpatch.org",

packages/markdown/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"decircular": "^1.0.0",
4343
"handlebars": "^4.7.8",
4444
"hast-util-from-html": "^2.0.3",
45+
"hast-util-to-text": "^4.0.2",
4546
"is-obj": "^3.0.0",
4647
"js-base64": "^3.7.7",
4748
"mdast-util-directive": "^3.0.0",
@@ -65,6 +66,7 @@
6566
"remark-unwrap-images": "4.0.0",
6667
"sort-keys": "^5.1.0",
6768
"unified": "^11.0.5",
69+
"unist-util-find-after": "^5.0.0",
6870
"unist-util-visit": "^5.0.0",
6971
"unist-util-visit-parents": "^6.0.1"
7072
},
@@ -80,6 +82,7 @@
8082
"chalk": "^5.3.0",
8183
"chokidar": "3.6.0",
8284
"live-server": "^1.2.2",
85+
"lunr": "^2.3.9",
8386
"mermaid": "11.3.0",
8487
"ncp": "^2.0.0",
8588
"scratchblocks": "^3.6.4",

packages/markdown/postbuild.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ async function postbuild() {
7979
"hyperbook-excalidraw.umd.js",
8080
),
8181
},
82+
{
83+
src: path.join("./node_modules", "lunr", "lunr.min.js"),
84+
dst: path.join("./dist", "assets", "lunr.min.js"),
85+
},
8286
];
8387

8488
for (let asset of assets) {

0 commit comments

Comments
 (0)