generated at
scrapbox-incremental-fulltext-search

sh
deno run --allow-net --allow-read --allow-write --allow-run --allow-env --unstable https://scrapbox.io/api/code/takker/UserScriptをbundleするDeno_script/build.ts https://scrapbox.io/api/code/programming-notes/scrapbox-incremental-fulltext-search/sample.js --bundle --minify --external=../htm@3.0.4%2Fpreact/script.js --external=../preact@10.5.13/hooks.js --charset=utf8 | xsel
script.js
import{html as j,render as H}from"../htm@3.0.4%2Fpreact/script.js";function S(r,o=0,{immediate:s=!0}={}){if(typeof r!="function")throw new Error("argument is not function.");let e,n=!1,t=o>0?()=>new Promise(a=>setTimeout(()=>a(),o)):()=>{},c=async()=>{if(await t(),!e){n=!1;return}let{parameters:a,resolve:i}=e;e=void 0,i({result:await r(...a),executed:!0}),await c()};return(...a)=>new Promise(async i=>{if(n){e?.resolve?.({executed:!1}),e={parameters:a,resolve:i};return}n=!0,s?i({result:await r(...a),executed:!0}):(e?.resolve?.({executed:!1}),e={parameters:a,resolve:i}),await c()})}import{useState as d,useMemo as y,useEffect as $}from"../preact@10.5.13/hooks.js";import{useState as W,useEffect as D,useCallback as J}from"../preact@10.5.13/hooks.js";function C(r,{delay:o},s){let[e,n]=W(!1),t=J(r,s);return D(()=>{(async()=>{let c=setTimeout(()=>n(!0),o);await t(),clearTimeout(c),n(!1)})()},[t,o,...s]),{loading:e}}import{html as K}from"../htm@3.0.4%2Fpreact/script.js";var P=({onClose:r})=>K`<div class="background" onClick="${r}"/>`,L=` .background { position: fixed; top: 0; right: 0; width: 100%; height: 100%; background-color: var(--modal-bg, rgba(0, 0, 0, 0.4)); z-index: 89999; } `;import{html as E}from"../htm@3.0.4%2Fpreact/script.js";import{useRef as Q,useCallback as Y}from"../preact@10.5.13/hooks.js";var I=({projects:r,selectedProject:o,onSelect:s})=>{let e=Q(null),n=Y(t=>{t.preventDefault(),t.stopPropagation(),e.current.selectedIndex=t.deltaY<0?Math.max(e.current.selectedIndex-1,0):Math.min(e.current.selectedIndex+1,e.current.length),s?.(e.current.value)},[]);return E` <select ref="${e}" value="${o}" onChange="${({target:{value:t}})=>s(t)}" onWheel="${n}"> ${r.map(t=>E`<option key="${t.id}" value="${t.name}">${t.displayName}</option>`)} </select> `};import{html as G}from"../htm@3.0.4%2Fpreact/script.js";var F=({error:r})=>r&&G` <div class="error">${r}</div> `,M=` .error { display: block; padding: 15px; margin: 20px; text-align: center; border: 1px solid #ebccd1; background-color: #f2dede; color: #a94442; } .error::before { font: normal normal normal 14px/1 FontAwesome; content: '\f071'; margin-right: .3em; } `;var V=r=>r.toLowerCase().replaceAll(" ","_").replace("/","%2F"),X=({watchList:r})=>{let[o,s]=d(!1),[e,n]=d(scrapbox.Project.name),[t,c]=d(""),[a,i]=d(!0),m=y(()=>a?t:"",[a,t]),[x,b]=d(!1),{loading:u,items:l}=Z({project:e,query:t}),{searching:f,error:h,projects:g}=_({query:m,watchList:r,includeWatchList:x});$(()=>h&&i(!0),[h]);let w=p=>n(p),A=({target:{value:p}})=>{i(!1),c(p)},v=()=>s(!1),R=({key:p})=>{p==="Escape"&&v()},q=({ctrlKey:p,shiftKey:k,altKey:B,metaKey:O,target:T})=>{T.target==="_blank"||p||k||B||O||v()},U=()=>{i(!0)};return $(()=>scrapbox.PageMenu.addItem({title:"Fulltext Search",image:"https://raw.githubusercontent.com/nota/kamon/master/svg/search.svg",onClick:()=>s(!0)}),[]),o&&j` <${P} onClose="${v}"/> <div class="container" onKeydownCapture="${R}"> <div class="search-form"> <${I} projects="${g}" selectedProject="${e}" onSelect="${w}" /> <input type="text" value="${t}" onInput="${A}" /> <button type="button" onClick="${U}" disabled="${a}"> ${a?`${f?"Searching...: ":""}Found ${g.length} projects`:"Search for all projects"} </button> <input type="checkbox" value="${!x}" onChange="${({target:p})=>b(p.value)}" /> <label>Search besides watch list</label> <span class="info"> ${u?`Searching for ${t}...`:`${l.length} results`} </span> <${F} error="${h}" /> </div> ${l.length>0&&j` <ul class="dropdown"> ${l.map(p=>j`<li key="${p.title}"> <a href="/${p.project}/${V(p.title)}" target="${p.project===scrapbox.Project.name?"":"_blank"}" rel="${p.project===scrapbox.Project.name?"route":"noopener noreferrer"}" onClick="${q}"> ${p.title} <div class="description"> ${p.lines.map(k=>j`<span>${k}</span>`)} </div> </a> </li>`)} </ul> `} </div>`};function Z({project:r,query:o}){let[s,e]=d([]),n=y(()=>S(async(c,a)=>{if(a===""||c===""){e([]);return}try{let i=await fetch(`/api/pages/${c}/search/query?q=${encodeURIComponent(a)}`),{pages:m}=await i.json();e(m.map(({title:x,words:b,lines:u})=>({project:c,title:x,words:b,lines:u})))}catch(i){console.error(i),e([])}},500,{immediate:!1}),[]),{loading:t}=C(async()=>await n(r,o),{delay:1500},[n,r,o]);return{loading:t,items:s}}function _({query:r,watchList:o,includeWatchList:s}){let[e,n]=d([]),t=y(()=>{let u=e.map(({id:l})=>l);return o.filter(({id:l})=>!u.some(f=>f===l))},[e,o]),[c,a]=d([]),[i,m]=d(!1),[x,b]=d(void 0);return $(()=>(async()=>{let u=await fetch("/api/projects");if(!u.ok)return[];let l=await u.json();n(l.projects?.map?.(({id:f,name:h,displayName:g})=>({id:f,name:h,displayName:g}))??[])})(),[]),$(()=>(async()=>{if(b(void 0),r===""){a([...e,...t]);return}m(!0),a([]);try{{let u=await fetch(`/api/projects/search/query?q=${r}`),l=await u.json();if(!u.ok)throw Error(l.message);a(l.projects)}if(s){let u=Math.floor(t.length/100)+1;for(let l=0;l<u;l++){let f=new URLSearchParams;f.append("q",r),t.slice(l*100,100+l*100).forEach(({id:w})=>f.append("ids",w));let h=await fetch(`/api/projects/search/watch-list?${f.toString()}`),g=await h.json();if(!h.ok)throw Error(g.message);a(w=>[...w,...g.projects])}}}catch(u){b(u.toString())}finally{m(!1)}})(),[r,t,e,s]),{searching:i,error:x,projects:c}}var ee=` .container { display: block; position: fixed; width: calc(100% - 20px); top: 5vh; left: 10px; color: var(--incremental-fulltext-search-text-color, #4a4a4a); z-index: 90000; } span { margin-right: .5em; } .search-form { width: inherit; border-radius: 5px; padding: 0 10px; border: transparent; box-shadow: none; font-size: 14px; color: var(--search-form-text-color, rgba(255,255,255,0.35)); background-color: var(--search-form-bg, rgba(255,255,255,0.15)); } .info { display: block; } .dropdown { max-height: 80vh; flex-direction: column; width: 100%; padding: 5px 0; margin: 2px 0 0; list-style: none; font-size: 14px; font-weight: normal; line-height: 28px; text-align: left; border: 1px solid rgba(0,0,0,0.15); border-radius: 4px; background-clip: padding-box; background-color: var(--incremental-fulltext-search-result-bg, #fefefe); white-space: nowrap; overflow-x: hidden; overflow-y: auto; text-overflow: ellipsis; } a { display: block; padding: 3px 20px; clear: both; align-items: center; font-weight:normal; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; text-decoration: none; text-overflow: ellipsis; color: var(--incremental-fulltext-search-text-color, #262626); background-color: var(--incremental-fulltext-search-result-bg, #f5f5f5); } a:hover { text-decoration: none; color: var(--incremental-fulltext-search-hover-text-color, #262626); background-color: var(--incremental-fulltext-search-result-hover-bg, #f5f5f5); } a:focus { color: var(--incremental-fulltext-search-hover-text-color, #262626); background-color: var(--incremental-fulltext-search-result-hover-bg, #f5f5f5); outline: 0; box-shadow: 0 0px 0px 3px rgba(102,175,233,0.6); border-color: #66afe9; transition: border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s; } .description { display: block; margin-top: 0.5em; color: var(--incremental-fulltext-search-description-text-color, gray); font-size: 12px; line-height: 14px; max-height: 28px; overflow: hidden; text-overflow: ellipsis; } ${L} ${M} `;function N(r){let o=document.createElement("div");o.dataset.userscriptName="incremental-fulltext-search-form",o.attachShadow({mode:"open"}),document.body.append(o),H(j` <style> :host { --incremental-fulltext-search-text-color: var(--page-text-color, #4a4a4a); --incremental-fulltext-search-description-text-color: var(--card-description-color, gray); --incremental-fulltext-search-result-bg: var(--page-bg, #fefefe); } ${ee} </style> <${X} watchList="${r}"/> `,o.shadowRoot)}async function z(r){let o=Math.floor(r.length/100)+1,e=(await Promise.all([...Array(o).keys()].map(async t=>{let c=new URLSearchParams;r.slice(t*100,100+t*100).forEach(m=>c.append("ids",m));let a=await fetch(`/api/projects?${c.toString()}`),{projects:i}=await a.json();return i}))).flat();return[...new Set(e.map(({id:t})=>t))].map(t=>e.find(c=>c.id===t))}(async()=>{let r=Object.keys(JSON.parse(localStorage.getItem("projectsLastAccessed"))),o=[...await z(r)].map(({id:s,name:e,displayName:n})=>({id:s,name:e,displayName:n})).sort((s,e)=>s.displayName.localeCompare(e.displayName));N(o)})();

#2021-06-08 15:33:35
#2021-06-05 13:30:47
#2021-06-01 17:09:54
#2021-05-31 01:28:12