diff --git a/assets/js/flexsearch.js b/assets/js/flexsearch.js new file mode 100644 index 0000000..dd3f1f9 --- /dev/null +++ b/assets/js/flexsearch.js @@ -0,0 +1,86 @@ +// {{ $searchDataFile := printf "%s.search-data.json" .Language.Lang }} +// {{ $searchData := resources.Get "json/search-data.json" | resources.ExecuteAsTemplate $searchDataFile . | resources.Minify | resources.Fingerprint }} + +(function () { + const searchDataURL = '{{ $searchData.Permalink }}'; + console.log('searchDataURL', searchDataURL); + + const indexConfig = { + tokenize: "full", + cache: 100, + document: { + id: 'id', + store: ['title', 'href', 'section'], + index: ["title", "content"] + }, + context: { + resolution: 9, + depth: 2, + bidirectional: true + } + } + window.flexSearchIndex = new FlexSearch.Document(indexConfig); + + const input = document.getElementById('search-input'); + const results = document.getElementById('search-results'); + + input.addEventListener('focus', init); + input.addEventListener('keyup', search); + + + /** + * Initializes the search functionality by adding the necessary event listeners and fetching the search data. + */ + function init() { + input.removeEventListener('focus', init); // init once + + fetch(searchDataURL).then(resp => resp.json()).then(pages => { + pages.forEach(page => { + window.flexSearchIndex.add(page); + }); + }).then(search); + } + + function search() { + console.log('search', input.value); + while (results.firstChild) { + results.removeChild(results.firstChild); + } + + if (!input.value) { + return; + } + + const searchHits = window.flexSearchIndex.search(input.value, { limit: 5, enrich: true }); + showResults(searchHits); + } + + function showResults(hits) { + console.log('showResults', hits); + const flatResults = new Map(); // keyed by href to dedupe hits + for (const result of hits.flatMap(r => r.result)) { + if (flatResults.has(result.doc.href)) continue; + flatResults.set(result.doc.href, result.doc); + } + console.log('flatResults', flatResults); + const create = (str) => { + const div = document.createElement('div'); + div.innerHTML = str.trim(); + return div.firstChild; + } + const fragment = document.createDocumentFragment(); + + console.log(fragment) + + for (const result of flatResults.values()) { + const li = create(` +
  • + +
    ${result.title}
    +
    +
  • `); + fragment.appendChild(li); + } + results.appendChild(fragment); + } +})(); diff --git a/assets/json/search-data.json b/assets/json/search-data.json new file mode 100644 index 0000000..94f23ef --- /dev/null +++ b/assets/json/search-data.json @@ -0,0 +1,19 @@ +{{- $pages := where .Site.Pages "Kind" "in" (slice "page" "section") -}} +{{- $pages = where $pages "Params.excludeSearch" "!=" true -}} +{{- $pages = where $pages "Content" "!=" "" -}} + +[ + {{ range $index, $page := $pages }} + {{ $pageTitle := $page.LinkTitle | default $page.File.BaseFileName }} + {{ $pageContent := $page.Plain }} + {{ $pageSection := $page.Parent.LinkTitle }} + + {{ if gt $index 0}},{{end}} { + "id": {{ $index }}, + "href": "{{ $page.Permalink }}", + "title": {{ $pageTitle | jsonify }}, + "section": {{ $pageSection | jsonify }}, + "content": {{ $page.Plain | jsonify }} + } + {{- end -}} +] diff --git a/data/icons.yaml b/data/icons.yaml index f8c7f87..b3c50d6 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -34,5 +34,5 @@ warning: cards: -copy: +copy: check: diff --git a/hugo.toml b/hugo.toml index d2b53bb..a12e125 100644 --- a/hugo.toml +++ b/hugo.toml @@ -63,6 +63,10 @@ defaultContentLanguage = 'en' name = 'About' pageRef = '/about' weight = 30 + [[menu.main]] + name = 'Search' + weight = 35 + params = { placeholder = 'Search documentation...' } [[menu.main]] name = 'GitHub' url = 'https://github.com' diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html index d803363..030b128 100644 --- a/layouts/partials/navbar.html +++ b/layouts/partials/navbar.html @@ -3,17 +3,19 @@