diff --git a/apps/server/src/services/export/zip/share_theme.ts b/apps/server/src/services/export/zip/share_theme.ts index bc02dff12..10a366f15 100644 --- a/apps/server/src/services/export/zip/share_theme.ts +++ b/apps/server/src/services/export/zip/share_theme.ts @@ -9,12 +9,15 @@ import type BNote from "../../../becca/entities/bnote.js"; import type BBranch from "../../../becca/entities/bbranch.js"; import { getShareThemeAssetDir } from "../../../routes/assets"; import { convert as convertToText } from "html-to-text"; +import becca from "../../../becca/becca"; const shareThemeAssetDir = getShareThemeAssetDir(); interface SearchIndexEntry { + id: string; title: string; content: string; + path: string; } export default class ShareThemeExportProvider extends ZipExportProvider { @@ -72,9 +75,14 @@ export default class ShareThemeExportProvider extends ZipExportProvider { } // Prepare search index. - this.searchIndex.set(note?.noteId, { - title: title, - content: searchContent + this.searchIndex.set(note.noteId, { + id: note.noteId, + title, + content: searchContent, + path: note.getBestNotePath() + .map(noteId => noteId !== "root" && becca.getNote(noteId)?.title) + .filter(noteId => noteId) + .join(" / ") }); } diff --git a/packages/share-theme/package.json b/packages/share-theme/package.json index fdfa253d1..13dac7f02 100644 --- a/packages/share-theme/package.json +++ b/packages/share-theme/package.json @@ -22,12 +22,14 @@ ], "license": "Apache-2.0", "dependencies": { + "boxicons": "2.1.4", + "fuse.js": "7.1.0", "katex": "0.16.25", - "mermaid": "11.12.0", - "boxicons": "2.1.4" + "mermaid": "11.12.0" }, "devDependencies": { "@digitak/esrun": "3.2.26", + "@triliumnext/ckeditor5": "workspace:*", "@types/swagger-ui": "5.21.1", "@typescript-eslint/eslint-plugin": "8.46.2", "@typescript-eslint/parser": "8.46.2", @@ -35,7 +37,6 @@ "esbuild": "0.25.11", "eslint": "9.38.0", "highlight.js": "11.11.1", - "typescript": "5.9.3", - "@triliumnext/ckeditor5": "workspace:*" + "typescript": "5.9.3" } } diff --git a/packages/share-theme/src/scripts/modules/search.ts b/packages/share-theme/src/scripts/modules/search.ts index 768b9421f..cc0777443 100644 --- a/packages/share-theme/src/scripts/modules/search.ts +++ b/packages/share-theme/src/scripts/modules/search.ts @@ -2,7 +2,6 @@ import debounce from "../common/debounce"; import parents from "../common/parents"; import parseHTML from "../common/parsehtml"; - interface SearchResults { results: SearchResult[]; } @@ -10,7 +9,7 @@ interface SearchResults { interface SearchResult { id: string; title: string; - score: number; + score?: number; path: string; } @@ -30,12 +29,10 @@ export default function setupSearch() { searchInput.addEventListener("keyup", debounce(async () => { // console.log("CHANGE EVENT"); - const ancestor = document.body.dataset.ancestorNoteId; const query = searchInput.value; if (query.length < 3) return; - const resp = await fetch(`api/notes?search=${query}&ancestorNoteId=${ancestor}`); - const json = await resp.json() as SearchResults; - const results = json.results.slice(0, 5); + const resp = await fetchResults(query); + const results = resp.results.slice(0, 5); const lines = [`
`]; for (const result of results) { lines.push(buildResultItem(result)); @@ -62,3 +59,29 @@ export default function setupSearch() { if (existing) existing.remove(); }); } + +async function fetchResults(query: string): Promise { + if ((window as any).glob.isStatic) { + const linkHref = document.head.querySelector("link[rel=stylesheet]")?.getAttribute("href"); + const rootUrl = linkHref?.split("/").slice(0, -2).join("/"); + const searchIndex = await (await fetch(`${rootUrl}/search-index.json`)).json(); + const Fuse = (await import("fuse.js")).default; + const fuse = new Fuse(searchIndex, { + keys: [ + "title", + "content" + ] + }); + + const results = fuse.search(query); + const processedResults = results.map(({ item, score }) => ({ + ...item, + score + })); + return { results: processedResults }; + } else { + const ancestor = document.body.dataset.ancestorNoteId; + const resp = await fetch(`api/notes?search=${query}&ancestorNoteId=${ancestor}`); + return await resp.json() as SearchResults; + } +} diff --git a/packages/share-theme/src/templates/page.ejs b/packages/share-theme/src/templates/page.ejs index 782a88dbf..9b8433b90 100644 --- a/packages/share-theme/src/templates/page.ejs +++ b/packages/share-theme/src/templates/page.ejs @@ -54,6 +54,11 @@ } %> <%= pageTitle %> + "> @@ -114,7 +119,7 @@ content = content.replaceAll(headingRe, (...match) => {
- <% if (hasTree && !isStatic) { %> + <% if (hasTree) { %>
"> diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ca3f39de..df172fbba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1335,6 +1335,9 @@ importers: boxicons: specifier: 2.1.4 version: 2.1.4 + fuse.js: + specifier: 7.1.0 + version: 7.1.0 katex: specifier: 0.16.25 version: 0.16.25 @@ -8097,6 +8100,10 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + fuse.js@7.1.0: + resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} + engines: {node: '>=10'} + futoin-hkdf@1.5.3: resolution: {integrity: sha512-SewY5KdMpaoCeh7jachEWFsh1nNlaDjNHZXWqL5IGwtpEYHTgkr2+AMCgNwKWkcc0wpSYrZfR7he4WdmHFtDxQ==} engines: {node: '>=8'} @@ -15040,8 +15047,6 @@ snapshots: '@ckeditor/ckeditor5-core': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-code-block@47.1.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)': dependencies: @@ -15103,8 +15108,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 '@ckeditor/ckeditor5-watchdog': 47.1.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)': dependencies: @@ -15269,8 +15272,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-editor-classic@47.1.0': dependencies: @@ -15298,8 +15299,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-editor-multi-root@47.1.0': dependencies: @@ -15552,6 +15551,8 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-line-height@47.1.0': dependencies: @@ -15794,6 +15795,8 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-restricted-editing@47.1.0': dependencies: @@ -15838,6 +15841,8 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-slash-command@47.1.0': dependencies: @@ -16046,6 +16051,8 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) es-toolkit: 1.39.5 + transitivePeerDependencies: + - supports-color '@codemirror/autocomplete@6.18.6': dependencies: @@ -23661,6 +23668,8 @@ snapshots: functions-have-names@1.2.3: {} + fuse.js@7.1.0: {} + futoin-hkdf@1.5.3: {} fuzzy@0.1.3: {}