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 @@