1 import { create, insert, insertBatch, search } from '@lyrasearch/lyra';
2 import { map } from 'lodash';
3 import { OutlineNode } from 'outline';
4 import keyboardJS from 'keyboardjs';
8 <div class="modal-content" id="search">
9 <input type="text" id="search-query" placeholder="enter fuzzy search terms">
10 <ul id="search-results">
19 state: 'ready' | 'notready'
23 this.state = 'notready';
26 async createIndex(schema: Record<string, any>) {
27 this.db = await create({
34 keyboardJS.withContext('search', () => {
35 keyboardJS.bind('escape', e => {
36 document.querySelector('.modal').remove();
37 keyboardJS.setContext('navigation');
40 keyboardJS.bind('down', e => {
41 document.getElementById('search-query').blur();
42 const el = document.querySelector('.search-result.selected');
43 if(el.nextElementSibling) {
44 el.classList.remove('selected');
45 el.nextElementSibling.classList.add('selected');
49 keyboardJS.bind('up', () => {
50 const el = document.querySelector('.search-result.selected');
51 if(el.previousElementSibling) {
52 el.classList.remove('selected');
53 el.previousElementSibling.classList.add('selected');
57 keyboardJS.bind('enter', e => {
58 const el = document.querySelector('.search-result.selected');
59 const docId = el.getAttribute('data-id');
61 document.querySelector('.modal').remove();
62 keyboardJS.setContext('navigation');
64 if(this.onTermSelection) {
65 this.onTermSelection(docId);
70 keyboardJS.withContext('navigation', () => {
71 keyboardJS.bind('shift + f', e => {
75 document.querySelector('body').innerHTML += searchModal;
76 const el = document.getElementById('search-query');
78 el.addEventListener('keyup', this.debounceSearch.bind(this));
79 keyboardJS.setContext('search');
84 debounceSearch(e: KeyboardEvent) {
86 clearInterval(this.debounce);
89 const el = e.target as HTMLTextAreaElement;
90 const query = el.value.toString().trim();
93 this.debounce = setTimeout(() => {
94 this.displaySearch(query, e);
99 async displaySearch(terms: string, e: KeyboardEvent) {
103 const res = await this.search(terms);
105 const resultContainer = document.getElementById('search-results');
107 if(res.hits.length === 0) {
108 resultContainer.innerHTML = '<li><em>No Results</em></li>';
112 const html = res.hits.map((doc, idx) => {
113 const content = doc.document.content.toString();
114 const display = content.substring(0, 100);
117 <li class="search-result ${idx === 0 ? 'selected' : ''}" data-id="${doc.id}">${display}${content.length > display.length ? '...': ''}</li>
121 resultContainer.innerHTML = html.join("\n");
124 indexDoc(doc: Record<string, any>) {
125 return insert(this.db, doc)
128 indexBatch(docs: Record<string, OutlineNode>) {
129 return insertBatch(this.db, map(docs, doc => doc as any));
132 search(term: string) {
133 return search(this.db, {
135 properties: ["content"]