mirror of
https://github.com/tcsenpai/obsidiangarden_netlify.git
synced 2025-06-03 19:40:03 +00:00
Local flexsearch (#160)
* Switch to using local search * Switch from lunr to FlexSearch * Cache searchindex in localstorage * Link unresolved links to 404 page in build step
This commit is contained in:
parent
2899392b9e
commit
48b7162298
12
.eleventy.js
12
.eleventy.js
@ -194,6 +194,11 @@ module.exports = function (eleventyConfig) {
|
|||||||
|
|
||||||
eleventyConfig.setLibrary("md", markdownLib);
|
eleventyConfig.setLibrary("md", markdownLib);
|
||||||
|
|
||||||
|
eleventyConfig.addFilter("isoDate", function (date) {
|
||||||
|
return date && date.toISOString();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
eleventyConfig.addFilter("link", function (str) {
|
eleventyConfig.addFilter("link", function (str) {
|
||||||
return (
|
return (
|
||||||
str &&
|
str &&
|
||||||
@ -240,9 +245,10 @@ module.exports = function (eleventyConfig) {
|
|||||||
deadLink = true;
|
deadLink = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<a class="internal-link ${
|
if(deadLink){
|
||||||
deadLink ? "is-unresolved" : ""
|
return `<a class="internal-link is-unresolved" href="/404">${title}</a>`;
|
||||||
}" ${deadLink ? "" : 'data-note-icon="' + noteIcon + '"'} href="${permalink}${headerLinkPath}">${title}</a>`;
|
}
|
||||||
|
return `<a class="internal-link data-note-icon="${noteIcon}" href="${permalink}${headerLinkPath}">${title}</a>`;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
//vercel search function
|
|
||||||
const searchHandler = require('../netlify/functions/search/search.js').handler;
|
|
||||||
async function vercelSearch(request, response) {
|
|
||||||
let event = {queryStringParameters: request.query};
|
|
||||||
|
|
||||||
let searchResponse = await searchHandler(event);
|
|
||||||
|
|
||||||
return response.status(200).json(JSON.parse(searchResponse.body));
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.default = vercelSearch;
|
|
@ -1,49 +0,0 @@
|
|||||||
const lunrjs = require("lunr");
|
|
||||||
|
|
||||||
const handler = async (event) => {
|
|
||||||
try {
|
|
||||||
const search = event.queryStringParameters.term;
|
|
||||||
if (!search) throw "Missing term query parameter";
|
|
||||||
|
|
||||||
const data = require("./data.json");
|
|
||||||
const indexJson = require("./index.json");
|
|
||||||
const index = lunrjs.Index.load(indexJson);
|
|
||||||
console.log("index made");
|
|
||||||
|
|
||||||
let results =
|
|
||||||
search[0] == "#" && search.length > 1
|
|
||||||
? index.search(`tags:${search.substring(1)}`)
|
|
||||||
: index.search(search + "*");
|
|
||||||
|
|
||||||
results.forEach((r) => {
|
|
||||||
r.title = data[r.ref].title;
|
|
||||||
r.content = truncate(data[r.ref].content, 400);
|
|
||||||
r.date = data[r.ref].date;
|
|
||||||
r.url = data[r.ref].url;
|
|
||||||
r.tags = data[r.ref].tags.filter(
|
|
||||||
(x) => x != "gardenEntry" && x != "note"
|
|
||||||
); //Note is automatically added by 11ty. GardenEntry is used internally to mark the home page
|
|
||||||
|
|
||||||
delete r.ref;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify(results),
|
|
||||||
// // more keys you can return:
|
|
||||||
// headers: { "headerName": "headerValue", ... },
|
|
||||||
// isBase64Encoded: true,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return { statusCode: 500, body: error.toString() };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function truncate(str, size) {
|
|
||||||
//first, remove HTML
|
|
||||||
str = str.replace(/<.*?>/g, "");
|
|
||||||
if (str.length < size) return str;
|
|
||||||
return str.substring(0, size - 3) + "...";
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { handler };
|
|
@ -10,8 +10,7 @@
|
|||||||
"build:eleventy": "cross-env ELEVENTY_ENV=prod NODE_OPTIONS=--max-old-space-size=4096 eleventy",
|
"build:eleventy": "cross-env ELEVENTY_ENV=prod NODE_OPTIONS=--max-old-space-size=4096 eleventy",
|
||||||
"build:sass": "sass src/site/styles:dist/styles --style compressed",
|
"build:sass": "sass src/site/styles:dist/styles --style compressed",
|
||||||
"get-theme": "node src/site/get-theme.js",
|
"get-theme": "node src/site/get-theme.js",
|
||||||
"build": "npm-run-all get-theme build:*",
|
"build": "npm-run-all get-theme build:*"
|
||||||
"postbuild": "node src/site/lunr-index.js"
|
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
@ -3,7 +3,15 @@
|
|||||||
"src/site/styles/style.css",
|
"src/site/styles/style.css",
|
||||||
"src/site/index.njk",
|
"src/site/index.njk",
|
||||||
"src/site/index.11tydata.js",
|
"src/site/index.11tydata.js",
|
||||||
"src/site/_data/filetree.js"
|
"src/site/_data/filetree.js",
|
||||||
|
"api/search.js",
|
||||||
|
"netlify/functions/search/search.js",
|
||||||
|
"src/site/versionednote.njk",
|
||||||
|
"src/site/_includes/layouts/versionednote.njk",
|
||||||
|
"src/site/lunr-index.js",
|
||||||
|
"src/site/_data/versionednotes.js",
|
||||||
|
"src/site/lunr.njk"
|
||||||
|
|
||||||
],
|
],
|
||||||
"filesToAdd": [
|
"filesToAdd": [
|
||||||
"src/site/styles/custom-style.scss",
|
"src/site/styles/custom-style.scss",
|
||||||
@ -27,7 +35,6 @@
|
|||||||
"src/site/404.njk",
|
"src/site/404.njk",
|
||||||
"src/site/sitemap.njk",
|
"src/site/sitemap.njk",
|
||||||
"src/site/feed.njk",
|
"src/site/feed.njk",
|
||||||
"src/site/versionednote.njk",
|
|
||||||
"src/site/styles/style.scss",
|
"src/site/styles/style.scss",
|
||||||
"src/site/styles/digital-garden-base.scss",
|
"src/site/styles/digital-garden-base.scss",
|
||||||
"src/site/styles/obsidian-base.scss",
|
"src/site/styles/obsidian-base.scss",
|
||||||
@ -35,7 +42,6 @@
|
|||||||
"src/site/notes/notes.11tydata.js",
|
"src/site/notes/notes.11tydata.js",
|
||||||
"src/site/_includes/layouts/note.njk",
|
"src/site/_includes/layouts/note.njk",
|
||||||
"src/site/_includes/layouts/index.njk",
|
"src/site/_includes/layouts/index.njk",
|
||||||
"src/site/_includes/layouts/versionednote.njk",
|
|
||||||
"src/site/_includes/components/notegrowthhistory.njk",
|
"src/site/_includes/components/notegrowthhistory.njk",
|
||||||
"src/site/_includes/components/pageheader.njk",
|
"src/site/_includes/components/pageheader.njk",
|
||||||
"src/site/_includes/components/linkPreview.njk",
|
"src/site/_includes/components/linkPreview.njk",
|
||||||
@ -51,9 +57,6 @@
|
|||||||
"src/site/_includes/components/calloutScript.njk",
|
"src/site/_includes/components/calloutScript.njk",
|
||||||
"src/site/_includes/components/lucideIcons.njk",
|
"src/site/_includes/components/lucideIcons.njk",
|
||||||
"src/site/_includes/components/timestamps.njk",
|
"src/site/_includes/components/timestamps.njk",
|
||||||
"src/site/lunr-index.js",
|
|
||||||
"src/site/lunr.njk",
|
|
||||||
"src/site/_data/versionednotes.js",
|
|
||||||
"src/site/_data/meta.js",
|
"src/site/_data/meta.js",
|
||||||
"src/site/_data/dynamics.js",
|
"src/site/_data/dynamics.js",
|
||||||
"src/site/img/outgoing.svg",
|
"src/site/img/outgoing.svg",
|
||||||
@ -61,10 +64,9 @@
|
|||||||
"src/helpers/utils.js",
|
"src/helpers/utils.js",
|
||||||
"src/helpers/filetreeUtils.js",
|
"src/helpers/filetreeUtils.js",
|
||||||
"src/helpers/linkUtils.js",
|
"src/helpers/linkUtils.js",
|
||||||
"netlify/functions/search/search.js",
|
|
||||||
"src/site/get-theme.js",
|
"src/site/get-theme.js",
|
||||||
"api/search.js",
|
|
||||||
"src/site/_data/eleventyComputed.js",
|
"src/site/_data/eleventyComputed.js",
|
||||||
"src/site/graph.njk"
|
"src/site/graph.njk",
|
||||||
|
"src/site/search-index.njk"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -5,5 +5,5 @@ const { userComputed } = require("../../helpers/userUtils");
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
graph: (data) => getGraph(data),
|
graph: (data) => getGraph(data),
|
||||||
filetree: (data) => getFileTree(data),
|
filetree: (data) => getFileTree(data),
|
||||||
userComputed: (data) => userComputed(data),
|
userComputed: (data) => userComputed(data)
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ const fs = require("fs");
|
|||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const { globSync } = require("glob");
|
const { globSync } = require("glob");
|
||||||
|
|
||||||
module.exports = async () => {
|
module.exports = async (data) => {
|
||||||
let baseUrl = process.env.SITE_BASE_URL || "";
|
let baseUrl = process.env.SITE_BASE_URL || "";
|
||||||
if (baseUrl && !baseUrl.startsWith("http")) {
|
if (baseUrl && !baseUrl.startsWith("http")) {
|
||||||
baseUrl = "https://" + baseUrl;
|
baseUrl = "https://" + baseUrl;
|
||||||
@ -67,7 +67,8 @@ module.exports = async () => {
|
|||||||
baseTheme: process.env.BASE_THEME || "dark",
|
baseTheme: process.env.BASE_THEME || "dark",
|
||||||
siteName: process.env.SITE_NAME_HEADER || "Digital Garden",
|
siteName: process.env.SITE_NAME_HEADER || "Digital Garden",
|
||||||
siteBaseUrl: baseUrl,
|
siteBaseUrl: baseUrl,
|
||||||
styleSettingsCss
|
styleSettingsCss,
|
||||||
|
buildDate: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return meta;
|
return meta;
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
require('dotenv').config();
|
|
||||||
const { Octokit } = require("@octokit/core");
|
|
||||||
const githubToken = process.env.GH_TOKEN;
|
|
||||||
const octokit = new Octokit({ auth: githubToken });
|
|
||||||
const markdownIt = require("markdown-it");
|
|
||||||
const md = markdownIt({
|
|
||||||
html: true,
|
|
||||||
}).use(function(md) {
|
|
||||||
//https://github.com/DCsunset/markdown-it-mermaid-plugin
|
|
||||||
const origRule = md.renderer.rules.fence.bind(md.renderer.rules);
|
|
||||||
md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
|
|
||||||
const token = tokens[idx];
|
|
||||||
if (token.info === 'mermaid') {
|
|
||||||
const code = token.content.trim();
|
|
||||||
return `<pre class="mermaid">${code}</pre>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other languages
|
|
||||||
return origRule(tokens, idx, options, env, slf);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = async function() {
|
|
||||||
if (!process.env.ENABLE_VERSION_HISTORY) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
//list all files
|
|
||||||
const noteFolder = 'src/site/notes';
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
const files = fs.readdirSync(noteFolder).filter(file => file.endsWith(".md")).map(file => noteFolder + '/' + file);
|
|
||||||
const notes = [];
|
|
||||||
for (const filePath of files) {
|
|
||||||
const fileCommits = await octokit.request(`GET /repos/{owner}/{repo}/commits?path=${encodeURI(filePath)}`, {
|
|
||||||
owner: process.env.GH_USERNAME,
|
|
||||||
repo: process.env.GH_REPO_NAME
|
|
||||||
})
|
|
||||||
if (filePath.indexOf("digital garden") > -1) {
|
|
||||||
console.log(fileCommits);
|
|
||||||
}
|
|
||||||
for (const commit of fileCommits.data) {
|
|
||||||
|
|
||||||
const sha = commit.sha
|
|
||||||
const fileData = await octokit.request(`GET /repos/{owner}/{repo}/contents/${encodeURI(filePath)}?ref=${sha}`, {
|
|
||||||
owner: process.env.GH_USERNAME,
|
|
||||||
repo: process.env.GH_REPO_NAME
|
|
||||||
});
|
|
||||||
const content = Buffer.from(fileData.data.content, 'base64').toString('utf8');
|
|
||||||
const segments = filePath.split("/");
|
|
||||||
const name = segments[segments.length - 1].replace(".md", "");
|
|
||||||
const date = commit.commit.author.date;
|
|
||||||
let markdown = ''
|
|
||||||
try {
|
|
||||||
markdown = md.render(content);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
const note = { content: markdown, title: name, fullTitle: name + " - " + sha, sha: sha, date: date };
|
|
||||||
|
|
||||||
notes.push(note);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return notes;
|
|
||||||
}
|
|
@ -1,21 +1,33 @@
|
|||||||
|
<script src="https://cdn.jsdelivr.net/npm/flexsearch@0.7.21/dist/flexsearch.bundle.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', init, false);
|
document.addEventListener('DOMContentLoaded', init, false);
|
||||||
document.addEventListener('DOMContentLoaded', setCorrectShortcut, false);
|
document.addEventListener('DOMContentLoaded', setCorrectShortcut, false);
|
||||||
|
|
||||||
window.toggleSearch=function(){
|
window.toggleSearch = function () {
|
||||||
if(document.getElementById('globalsearch').classList.contains('active')){
|
if (document.getElementById('globalsearch').classList.contains('active')) {
|
||||||
document.getElementById('globalsearch').classList.remove('active');
|
document
|
||||||
}else{
|
.getElementById('globalsearch')
|
||||||
document.getElementById('globalsearch').classList.add('active');
|
.classList
|
||||||
document.getElementById('term').focus();
|
.remove('active');
|
||||||
|
} else {
|
||||||
|
document
|
||||||
|
.getElementById('globalsearch')
|
||||||
|
.classList
|
||||||
|
.add('active');
|
||||||
|
document
|
||||||
|
.getElementById('term')
|
||||||
|
.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.toggleTagSearch=function(evt) {
|
window.toggleTagSearch = function (evt) {
|
||||||
console.log(evt.textContent);
|
console.log(evt.textContent);
|
||||||
const term = evt.textContent;
|
const term = evt.textContent;
|
||||||
if(term){
|
if (term) {
|
||||||
window.document.getElementById('term').value = term.trim();
|
window
|
||||||
|
.document
|
||||||
|
.getElementById('term')
|
||||||
|
.value = term.trim();
|
||||||
window.toggleSearch();
|
window.toggleSearch();
|
||||||
window.search();
|
window.search();
|
||||||
}
|
}
|
||||||
@ -66,26 +78,83 @@
|
|||||||
|
|
||||||
function debounce(func, wait, immediate) {
|
function debounce(func, wait, immediate) {
|
||||||
var timeout;
|
var timeout;
|
||||||
return function() {
|
return function () {
|
||||||
var context = this, args = arguments;
|
var context = this,
|
||||||
var later = function() {
|
args = arguments;
|
||||||
|
var later = function () {
|
||||||
timeout = null;
|
timeout = null;
|
||||||
if (!immediate) func.apply(context, args);
|
if (!immediate)
|
||||||
};
|
func.apply(context, args);
|
||||||
|
};
|
||||||
var callNow = immediate && !timeout;
|
var callNow = immediate && !timeout;
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
timeout = setTimeout(later, wait);
|
timeout = setTimeout(later, wait);
|
||||||
if (callNow) func.apply(context, args);
|
if (callNow)
|
||||||
};
|
func.apply(context, args);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function setCorrectShortcut(){
|
function setCorrectShortcut() {
|
||||||
if(navigator.platform.toUpperCase().indexOf('MAC')>=0){
|
if (navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
|
||||||
document.querySelectorAll(".search-keys").forEach(x=>x.innerHTML = "⌘ + K");
|
document
|
||||||
|
.querySelectorAll(".search-keys")
|
||||||
|
.forEach(x => x.innerHTML = "⌘ + K");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createIndex(posts) {
|
||||||
|
const encoder = (str) => str
|
||||||
|
.toLowerCase()
|
||||||
|
.split(/([^a-z]|[^\x00-\x7F])/)
|
||||||
|
const contentIndex = new FlexSearch.Document({
|
||||||
|
cache: true,
|
||||||
|
charset: "latin:extra",
|
||||||
|
optimize: true,
|
||||||
|
index: [
|
||||||
|
{
|
||||||
|
field: "content",
|
||||||
|
tokenize: "reverse",
|
||||||
|
encode: encoder
|
||||||
|
}, {
|
||||||
|
field: "title",
|
||||||
|
tokenize: "forward",
|
||||||
|
encode: encoder
|
||||||
|
}, {
|
||||||
|
field: "tags",
|
||||||
|
tokenize: "forward",
|
||||||
|
encode: encoder
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
posts.forEach((p, idx) => {
|
||||||
|
contentIndex.add({
|
||||||
|
id: idx, title: p.title, content: p.content, //Change to removeHTML
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return contentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
|
//init offline search index
|
||||||
|
|
||||||
|
const searchIndexDate = '{{meta.buildDate|isoDate}}';
|
||||||
|
let shouldFetch = true;
|
||||||
|
if(localStorage.getItem("searchIndex")) {
|
||||||
|
let {date, docs}= JSON.parse(localStorage.getItem('searchIndex'));
|
||||||
|
if(date === searchIndexDate){
|
||||||
|
shouldFetch = false;
|
||||||
|
let index = createIndex(docs);
|
||||||
|
window.docs = docs
|
||||||
|
window.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(shouldFetch){
|
||||||
|
let docs = await(await fetch('/searchIndex.json?v={{meta.buildDate|isoDate}}')).json();
|
||||||
|
let index = createIndex(docs);
|
||||||
|
localStorage.setItem("searchIndex", JSON.stringify({date: '{{meta.buildDate|isoDate}}', docs}));
|
||||||
|
window.docs = docs
|
||||||
|
window.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
//open searchmodal when ctrl + k is pressed, cmd + k on mac
|
//open searchmodal when ctrl + k is pressed, cmd + k on mac
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
@ -94,7 +163,10 @@
|
|||||||
toggleSearch();
|
toggleSearch();
|
||||||
}
|
}
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
document.getElementById('globalsearch').classList.remove('active');
|
document
|
||||||
|
.getElementById('globalsearch')
|
||||||
|
.classList
|
||||||
|
.remove('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
//navigate search results with arrow keys
|
//navigate search results with arrow keys
|
||||||
@ -103,54 +175,70 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let active = document.querySelector('.searchresult.active');
|
let active = document.querySelector('.searchresult.active');
|
||||||
if (active) {
|
if (active) {
|
||||||
active.classList.remove('active');
|
active
|
||||||
|
.classList
|
||||||
|
.remove('active');
|
||||||
if (active.nextElementSibling) {
|
if (active.nextElementSibling) {
|
||||||
active.nextElementSibling.classList.add('active');
|
active
|
||||||
|
.nextElementSibling
|
||||||
|
.classList
|
||||||
|
.add('active');
|
||||||
} else {
|
} else {
|
||||||
document.querySelector('.searchresult').classList.add('active');
|
document
|
||||||
|
.querySelector('.searchresult')
|
||||||
|
.classList
|
||||||
|
.add('active');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
document.querySelector('.searchresult').classList.add('active');
|
document
|
||||||
|
.querySelector('.searchresult')
|
||||||
|
.classList
|
||||||
|
.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentActive = document.querySelector('.searchresult.active');
|
let currentActive = document.querySelector('.searchresult.active');
|
||||||
if (currentActive) {
|
if (currentActive) {
|
||||||
currentActive .scrollIntoView({
|
currentActive.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});
|
||||||
behavior: 'smooth',
|
|
||||||
block: 'nearest',
|
|
||||||
inline: 'start',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e.key === 'ArrowUp') {
|
if (e.key === 'ArrowUp') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let active = document.querySelector('.searchresult.active');
|
let active = document.querySelector('.searchresult.active');
|
||||||
if (active) {
|
if (active) {
|
||||||
active.classList.remove('active');
|
active
|
||||||
|
.classList
|
||||||
|
.remove('active');
|
||||||
if (active.previousElementSibling) {
|
if (active.previousElementSibling) {
|
||||||
active.previousElementSibling.classList.add('active');
|
active
|
||||||
|
.previousElementSibling
|
||||||
|
.classList
|
||||||
|
.add('active');
|
||||||
} else {
|
} else {
|
||||||
document.querySelectorAll('.searchresult').forEach((el) => {
|
document
|
||||||
if (!el.nextElementSibling) {
|
.querySelectorAll('.searchresult')
|
||||||
el.classList.add('active');
|
.forEach((el) => {
|
||||||
}
|
if (!el.nextElementSibling) {
|
||||||
});
|
el
|
||||||
|
.classList
|
||||||
|
.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
document.querySelectorAll('.searchresult').forEach((el) => {
|
document
|
||||||
if (el.nextElementSibling) {
|
.querySelectorAll('.searchresult')
|
||||||
el.classList.add('active');
|
.forEach((el) => {
|
||||||
}
|
if (el.nextElementSibling) {
|
||||||
});
|
el
|
||||||
|
.classList
|
||||||
|
.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentActive = document.querySelector('.searchresult.active');
|
let currentActive = document.querySelector('.searchresult.active');
|
||||||
if (currentActive) {
|
if (currentActive) {
|
||||||
currentActive .scrollIntoView({
|
currentActive.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});
|
||||||
behavior: 'smooth',
|
|
||||||
block: 'nearest',
|
|
||||||
inline: 'start',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -158,7 +246,9 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let active = document.querySelector('.searchresult.active');
|
let active = document.querySelector('.searchresult.active');
|
||||||
if (active) {
|
if (active) {
|
||||||
window.location.href = active.querySelector("a").href;
|
window.location.href = active
|
||||||
|
.querySelector("a")
|
||||||
|
.href;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,13 +277,15 @@
|
|||||||
.trim();
|
.trim();
|
||||||
if (!search)
|
if (!search)
|
||||||
return;
|
return;
|
||||||
if(search == lastSearch) return;
|
if (search == lastSearch)
|
||||||
|
return;
|
||||||
console.log(`search for ${search}`);
|
console.log(`search for ${search}`);
|
||||||
window.lastSearch = search;
|
window.lastSearch = search;
|
||||||
|
|
||||||
resultsDiv.innerHTML = loadingSvg;
|
resultsDiv.innerHTML = loadingSvg;
|
||||||
let searchRequest = await fetch(`/api/search?term=${encodeURIComponent(search)}`);
|
//let searchRequest = await fetch(`/api/search?term=${encodeURIComponent(search)}`);
|
||||||
let results = await searchRequest.json();
|
//let results = await searchRequest.json();
|
||||||
|
let results = offlineSearch(search);
|
||||||
let resultsHTML = '';
|
let resultsHTML = '';
|
||||||
if (!results.length) {
|
if (!results.length) {
|
||||||
let resultParagraph = document.createElement("p");
|
let resultParagraph = document.createElement("p");
|
||||||
@ -229,4 +321,59 @@
|
|||||||
resultsHTML += '</div>';
|
resultsHTML += '</div>';
|
||||||
resultsDiv.innerHTML = resultsHTML;
|
resultsDiv.innerHTML = resultsHTML;
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
function truncate(str, size) {
|
||||||
|
//first, remove HTML
|
||||||
|
str = str.replaceAll(/<[^>]*>/g, '');
|
||||||
|
if (str.length < size)
|
||||||
|
return str;
|
||||||
|
return str.substring(0, size - 3) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
function offlineSearch(searchQuery) {
|
||||||
|
let data = window.docs;
|
||||||
|
|
||||||
|
let isTagSearch = search[0] === "#" && search.length > 1;
|
||||||
|
|
||||||
|
let searchResults = isTagSearch
|
||||||
|
? index.search(searchQuery.substring(1), [
|
||||||
|
{
|
||||||
|
field: "tags"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
: index.search(searchQuery, [
|
||||||
|
{
|
||||||
|
field: "title",
|
||||||
|
limit: 5
|
||||||
|
}, {
|
||||||
|
field: "content",
|
||||||
|
weight: 10
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getByField = (field) => {
|
||||||
|
const results = searchResults.filter((x) => x.field === field)
|
||||||
|
if (results.length === 0) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
return [...results[0].result]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const allIds = new Set([
|
||||||
|
...getByField("title"),
|
||||||
|
...getByField("content")
|
||||||
|
])
|
||||||
|
const dataIds = [...allIds];
|
||||||
|
const finalResults = dataIds.map((id) => {
|
||||||
|
let result = data[id];
|
||||||
|
result.content = truncate(result.content, 400);
|
||||||
|
result.tags = result
|
||||||
|
.tags
|
||||||
|
.filter((x) => x != "gardenEntry" && x != "note"); //Note is automatically added by 11ty. GardenEntry is used internally to mark the home page
|
||||||
|
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
return finalResults;
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>{{ item.title }}</title>
|
|
||||||
{%include "components/pageheader.njk"%}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="content">
|
|
||||||
{{ item.content | safe}}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,22 +0,0 @@
|
|||||||
require("dotenv").config()
|
|
||||||
const lunrjs = require('lunr');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
function createIndex(posts) {
|
|
||||||
return lunrjs(function () {
|
|
||||||
this.ref('id');
|
|
||||||
this.field('title');
|
|
||||||
this.field('content');
|
|
||||||
this.field('date');
|
|
||||||
this.field("tags");
|
|
||||||
|
|
||||||
posts.forEach((p, idx) => {
|
|
||||||
p.id = idx;
|
|
||||||
this.add(p);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = require('../../netlify/functions/search/data.json');
|
|
||||||
const index = createIndex(data);
|
|
||||||
require('fs').writeFileSync(path.join(__dirname, '../../netlify/functions/search/index.json'), JSON.stringify(index));
|
|
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
permalink: netlify/functions/search/data.json
|
permalink: /searchIndex.json
|
||||||
permalinkBypassOutputDir: true
|
|
||||||
eleventyExcludeFromCollections: true
|
eleventyExcludeFromCollections: true
|
||||||
---
|
---
|
||||||
[{% for post in collections.note %}
|
[{% for post in collections.note %}
|
||||||
@ -11,4 +10,4 @@ eleventyExcludeFromCollections: true
|
|||||||
"content": {{ post.templateContent | striptags(true) | link | jsonify | safe }},
|
"content": {{ post.templateContent | striptags(true) | link | jsonify | safe }},
|
||||||
"tags": [{{post.templateContent | link | searchableTags | safe }} {% if post.data.tags %}{% for tag in post.data.tags %}"{{tag|validJson}}"{% if not loop.last %},{% endif %}{% endfor %}{% endif %}]
|
"tags": [{{post.templateContent | link | searchableTags | safe }} {% if post.data.tags %}{% for tag in post.data.tags %}"{{tag|validJson}}"{% if not loop.last %},{% endif %}{% endfor %}{% endif %}]
|
||||||
}{% if not loop.last %},{% endif %}
|
}{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}]
|
{% endfor %}]
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
layout: layouts/versionednote.njk
|
|
||||||
tags: versionednote
|
|
||||||
pagination:
|
|
||||||
data: versionednotes
|
|
||||||
size: 1
|
|
||||||
alias: item
|
|
||||||
addAllPagesToCollections: true
|
|
||||||
permalink: versionednotes/{{item.title}}/{{item.sha}}/index.html
|
|
||||||
---
|
|
Loading…
x
Reference in New Issue
Block a user