補完windowとあいまい検索を組み合わせるテスト
https://gyazo.com/dac42e26f1f497c43432695fd24bee72
code:js
(async () => {
const projectName = 'programming-notes';
const pageTitle = '補完windowとあいまい検索を組み合わせるテスト';
const promises = [
import(/api/code/${projectName}/scrapbox-suggest-container/test-dom.js),
//import(/api/code/${projectName}/scrapbox-suggest-container/test-dark-theme.js),
import(/api/code/${projectName}/scrapbox-suggest-container/script.js),
];
const promise = import(/api/code/${projectName}/${pageTitle}/test1-project-list.js);
const worker = new Worker(/api/code/${projectName}/${pageTitle}/test1-worker.js);
worker.postMessage({type: 'fetch', projects: (await promise).projects});
// 入力補完windowを作る
const suggestBox = document.createElement('suggest-container');
editor.append(suggestBox);
// とりあえず、cursorに追随するだけにする
const observer = new MutationObserver(() =>{
suggestBox.show({
top: parseInt(cursor.style.top) + 14,
left: parseInt(cursor.style.left),
});
});
observer.observe(cursor, {attributes: true});
// あいまい検索して、候補を入力補完windowに追加する
window.search = (word, {limit = 30, timeout = 10000,} = {}) => {
// 時間がかかるようであればLoading表示をする
const timer = setTimeout(() => {
const image = /paper-dark-dark|default-dark/
.test(document.head.parentElement.dataset.projectTheme) ?
suggestBox.pushFirst({text: 'Loading...', image,});
}, 1000);
worker.postMessage({type: 'search', word, limit, timeout});
worker.addEventListener('message', ({data: {links}}) => {
clearTimeout(timer);
suggestBox.clear();
suggestBox.push(...links.flat().map(link => {
return {
text: link,
link: https://scrapbox.io${link},
onClick: () => window.open(https://scrapbox.io${link}),
};
}));
}, {once: true});
};
})();
入力補完に使うscrapbox projects
code:test1-project-list.js
export const projects = [
'hub',
'villagepump',
'motoso',
'shokai',
'nishio',
/*'masui',
'rakusai',
'yuiseki',
'june29',
'ucdktr2016',
'rashitamemo',
'thinkandcreateteck',
'customize',
'scrapboxlab',
'scrasobox',
'foldrr',
'scrapbox-drinkup',
'public-mrsekut',
'mrsekut-p',
'marshmallow-rm',
'wkpmm',
'sushitecture',
'nwtgck',
'dojineko',
'kadoyau',
'inteltank',
'sta',
'kn1cht',
'miyamonz',
'rmaruon',
'MISONLN41',
'yuta0801',
'choiyakiBox',
'choiyaki-hondana',
'spud-oimo',
'keroxp',
'aioilight',*/
];
worker code
code:test1-worker.js
const pageTitle = '補完windowとあいまい検索を組み合わせるテスト';
self.importScripts('/api/code/programming-notes/WebWorker用asearch/script.js');
// 検索候補
const list = [];
self.addEventListener('message', message => {
switch (message.data.type) {
case 'search':
search(message.data);
break;
case 'fetch':
fetch_(message.data.projects);
break;
}
});
async function search({word, limit, timeout}) {
//_log(start searching for ${word}...: limit = ${limit});
const result = fuzzySearch({
query: word.split('/').join(' '),
source: list,
limit, timeout,
});
//_log('finished: %o', result);
self.postMessage({links: result,});
}
async function fetch_(projects) {
_log('Loading links from %o', projects);
const result = (await Promise.all(projects
.map(project => fetchExternalLinks(project))
)).flat();
_log(Finish loading ${result.length} links from %o, projects);
list.push(...result);
}
async function fetchExternalLinks(project) {
let followingId = null;
let temp = [];
_log(Start loading links from ${project}...);
do {
_log(Loading links from ${project}: followingId = ${followingId});
const json = await (!followingId ?
fetch(/api/pages/${project}/search/titles) :
fetch(/api/pages/${project}/search/titles?followingId=${followingId})
).then(res => {
followingId = res.headers.get('X-Following-Id');
return res.json();
});
} while(followingId);
_log(Loaded ${links.length} links from /${project});
return links;
}
// debug用
function _log(msg, ...objects) {
console.log([search-worker @${pageTitle}/test1-worker.js] ${msg}, ...objects);
}