-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
5 lines (4 loc) · 24.6 KB
/
Copy pathmain.js
File metadata and controls
5 lines (4 loc) · 24.6 KB
1
2
3
4
5
var Y=Object.create;var D=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var X=Object.getPrototypeOf,ee=Object.prototype.hasOwnProperty;var te=(m,d)=>{for(var e in d)D(m,e,{get:d[e],enumerable:!0})},_=(m,d,e,t)=>{if(d&&typeof d=="object"||typeof d=="function")for(let s of Q(d))!ee.call(m,s)&&s!==e&&D(m,s,{get:()=>d[s],enumerable:!(t=Z(d,s))||t.enumerable});return m};var W=(m,d,e)=>(e=m!=null?Y(X(m)):{},_(d||!m||!m.__esModule?D(e,"default",{value:m,enumerable:!0}):e,m)),se=m=>_(D({},"__esModule",{value:!0}),m);var oe={};te(oe,{default:()=>B});module.exports=se(oe);var l=require("obsidian");var T=require("obsidian"),b="kjarni-similar-notes",$=class extends T.ItemView{constructor(e,t){super(e);this.currentFile=null;this.debounceTimer=null;this.searchVersion=0;this.results=[];this.plugin=t}getViewType(){return b}getDisplayText(){return"Similar Notes"}getIcon(){return"git-compare"}async onOpen(){let e=this.containerEl.children[1];e.empty(),e.addClass("kjarni-similar-container");let t=e.createDiv({cls:"kjarni-similar-header"});t.createEl("span",{text:"Similar Notes",cls:"kjarni-similar-title"});let s=t.createEl("button",{cls:"kjarni-similar-refresh clickable-icon"});(0,T.setIcon)(s,"refresh-cw"),s.setAttribute("aria-label","Refresh"),s.addEventListener("click",()=>{this.currentFile&&this.findSimilar(this.currentFile,!0)}),e.createDiv({cls:"kjarni-similar-results"}),e.createDiv({cls:"kjarni-similar-status"}),this.renderEmpty("Open a note to see similar notes"),this.registerEvent(this.app.workspace.on("active-leaf-change",n=>{if(!n)return;let r=n.view;if(r.getViewType()==="markdown"){let a=r.file;a instanceof T.TFile&&a.extension==="md"&&this.onActiveFileChange(a)}})),this.registerEvent(this.app.vault.on("modify",n=>{n instanceof T.TFile&&n===this.currentFile&&this.plugin.settings.similarAutoUpdate&&(this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.findSimilar(n,!0)},2e3))}));let i=this.app.workspace.getActiveFile();i&&this.onActiveFileChange(i)}async onClose(){this.searchVersion++,this.debounceTimer&&clearTimeout(this.debounceTimer)}onActiveFileChange(e){var t;((t=this.currentFile)==null?void 0:t.path)!==e.path&&(this.currentFile=e,this.results=[],this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.findSimilar(e,!1)},500))}async findSimilar(e,t=!1){if(!this.plugin.indexReady){this.renderEmpty("Index not ready yet...");return}this.currentFile=e,this.searchVersion++;let s=this.searchVersion;this.renderLoading(e.basename);try{let i=await this.app.vault.cachedRead(e);if(i.trim().length===0){this.renderEmpty("Empty note");return}let n=i.slice(0,500),r=performance.now(),a=await this.plugin.doSearch(n,20),c=performance.now()-r;if(this.searchVersion!==s)return;let p=a.filter(o=>o.source!==e.path),k=new Map;for(let o of p){let h=k.get(o.source);(!h||o.score>h.score)&&k.set(o.source,o)}let f=Array.from(k.values()).sort((o,h)=>h.score-o.score).slice(0,this.plugin.settings.searchLimit);if(this.results=f,this.searchVersion!==s)return;if(this.renderResults(e.basename,this.results,c),this.plugin.hasReranker&&f.length>1){let o=this.getStatusEl();o&&(o.textContent=`${f.length} similar notes \xB7 ${c.toFixed(0)}ms \xB7 refining...`,o.addClass("kjarni-refining"));let h=f.map(S=>S.text),w=performance.now(),v=await this.plugin.doRerank(n,h,f.length),E=performance.now()-w;if(this.searchVersion!==s)return;v.length>0&&(this.results=v.map(S=>f[S.index]),this.renderResults(e.basename,this.results,c)),o&&(o.textContent=`${this.results.length} similar notes \xB7 ${c.toFixed(0)}ms \xB7 ${E.toFixed(0)}ms rerank`,o.removeClass("kjarni-refining"))}}catch(i){console.error("Kjarni: Similar notes search failed:",i),this.renderEmpty("Search failed")}}getResultsEl(){return this.containerEl.querySelector(".kjarni-similar-results")}getStatusEl(){return this.containerEl.querySelector(".kjarni-similar-status")}renderEmpty(e){let t=this.getResultsEl();if(!t)return;t.empty();let s=t.createDiv({cls:"kjarni-similar-empty"}),i=s.createDiv({cls:"kjarni-similar-empty-icon"});(0,T.setIcon)(i,"file-search"),s.createEl("p",{text:e});let n=this.getStatusEl();n&&(n.textContent="")}renderLoading(e){let t=this.getResultsEl();if(!t)return;t.empty(),t.createDiv({cls:"kjarni-similar-loading"}).createEl("p",{text:`Finding notes similar to "${e}"...`});let i=this.getStatusEl();i&&(i.textContent="Searching...")}renderResults(e,t,s){let i=this.getResultsEl();if(!i)return;if(i.empty(),t.length===0){this.renderEmpty("No similar notes found");return}for(let r of t){let a=Math.round(Math.max(0,Math.min(100,r.score*100))),c=i.createDiv({cls:"kjarni-similar-item"});c.addEventListener("click",E=>{this.app.workspace.openLinkText(r.source,"",!1)}),c.addEventListener("auxclick",E=>{E.button===1&&this.app.workspace.openLinkText(r.source,"",!0)}),c.addEventListener("mouseover",E=>{this.app.workspace.trigger("hover-link",{event:E,source:b,hoverParent:c,targetEl:c,linktext:r.source})});let p=c.createDiv({cls:"kjarni-similar-item-title"}),k=p.createEl("span",{cls:"kjarni-similar-item-name"});k.textContent=r.source.replace(/\.md$/,"");let f=p.createEl("span",{cls:"kjarni-similar-item-score"});f.textContent=`${a}%`;let h=c.createDiv({cls:"kjarni-similar-bar-container"}).createDiv({cls:"kjarni-similar-bar-fill"});h.style.width=`${a}%`;let w=r.text.length>120?r.text.slice(0,120)+"\u2026":r.text;c.createDiv({cls:"kjarni-similar-item-snippet",text:w});let v=c.createEl("button",{cls:"kjarni-similar-link-btn clickable-icon",attr:{"aria-label":"Insert link to this note"}});(0,T.setIcon)(v,"link"),v.addEventListener("click",E=>{E.stopPropagation(),this.insertLink(r.source)})}let n=this.getStatusEl();n&&(n.textContent=`${t.length} similar notes \xB7 ${s.toFixed(0)}ms`)}insertLink(e){var r;let t=this.app.workspace.getActiveViewOfType(this.app.workspace.constructor.prototype.constructor),s=(r=this.app.workspace.activeEditor)==null?void 0:r.editor;if(!s){navigator.clipboard.writeText(`[[${e.replace(/\.md$/,"")}]]`);return}let i=`[[${e.replace(/\.md$/,"")}]]`,n=s.getCursor();s.replaceRange(i,n),s.setCursor({line:n.line,ch:n.ch+i.length})}};var g=W(require("fs")),y=W(require("path")),A="https://kjarni.ai/models",V={encoder:{filename:"encoder.kjq",url:`${A}/all-MiniLM-L6-v2-q8.kjq`,size:"22 MB"},reranker:{filename:"reranker.kjq",url:`${A}/ms-marco-MiniLM-L-6-v2-q8.kjq`,size:"22 MB"}},R=2,ie=3e3,ne=5e3,U={chunkSize:1e3,chunkOverlap:200,searchLimit:10,rerankerEnabled:!0,debugLogging:!1,similarPanelOnStartup:!1,similarAutoUpdate:!0};function re(m){return m.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function ae(m,d){if(d.length===0)return m;let e=d.map(s=>s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")),t=new RegExp(`(${e.join("|")})`,"gi");return m.replace(t,'<mark class="kjarni-highlight">$1</mark>')}var B=class extends l.Plugin{constructor(){super(...arguments);this.settings=U;this.pluginDir="";this.searchWorker=null;this.hasReranker=!1;this.indexReady=!1;this.indexing=!1;this.statusBarEl=null;this.nextId=1;this.searchPending=new Map;this.encoderWorkers=[];this.encoderPending=new Map;this.pendingUpdates=new Set;this.pendingDeletes=new Set;this.updateTimer=null;this.deleteTimer=null;this.wasmBytesCache=null;this.encoderBytesCache=null}async activateSimilarView(){let e=this.app.workspace.getLeavesOfType(b);if(e.length){this.app.workspace.revealLeaf(e[0]);return}let t=this.app.workspace.getRightLeaf(!1);t&&(await t.setViewState({type:b,active:!0}),this.app.workspace.revealLeaf(t))}async onload(){await this.loadSettings(),this.statusBarEl=this.addStatusBarItem(),this.statusBarEl.setText("");let e=this.app.vault.adapter.basePath;this.pluginDir=y.join(e,".obsidian","plugins","kjarni-search"),this.registerView(b,t=>new $(t,this)),this.addCommand({id:"open-similar-notes",name:"Show similar notes",callback:()=>this.activateSimilarView()}),this.addCommand({id:"open-search",name:"Search vault",callback:()=>this.openSearch(),hotkeys:[{modifiers:["Mod","Shift"],key:"k"}]}),this.addCommand({id:"reindex-vault",name:"Reindex vault",callback:()=>this.reindexVault()}),this.addRibbonIcon("search","Kjarni Search",()=>this.openSearch()),this.addSettingTab(new M(this.app,this)),this.registerEvent(this.app.vault.on("modify",t=>{t instanceof l.TFile&&t.extension==="md"&&this.queueFileUpdate(t.path)})),this.registerEvent(this.app.vault.on("create",t=>{t instanceof l.TFile&&t.extension==="md"&&this.queueFileUpdate(t.path)})),this.registerEvent(this.app.vault.on("delete",t=>{t instanceof l.TFile&&t.extension==="md"&&this.queueFileDelete(t.path)})),this.registerEvent(this.app.vault.on("rename",(t,s)=>{t instanceof l.TFile&&t.extension==="md"&&(this.queueFileDelete(s),this.queueFileUpdate(t.path))})),this.initializeAsync()}onunload(){var e;this.updateTimer&&clearTimeout(this.updateTimer),this.deleteTimer&&clearTimeout(this.deleteTimer),this.stopEncoderPool(),(e=this.searchWorker)==null||e.terminate(),this.searchWorker=null,this.app.workspace.detachLeavesOfType(b)}createBlobWorker(e){let t=y.join(this.pluginDir,e),s=g.readFileSync(t,"utf8"),i=new Blob([s],{type:"application/javascript"}),n=URL.createObjectURL(i);return new Worker(n)}postToSearch(e,t){return new Promise((s,i)=>{if(!this.searchWorker){i(new Error("Search worker not started"));return}let n=this.nextId++;e.id=n,this.searchPending.set(n,{resolve:s,reject:i}),t?this.searchWorker.postMessage(e,t):this.searchWorker.postMessage(e)})}postToEncoder(e,t,s){return new Promise((i,n)=>{let r=this.nextId++;t.id=r,this.encoderPending.set(r,{resolve:i,reject:n}),s?e.postMessage(t,s):e.postMessage(t)})}async setWorkerLogging(e){try{await this.postToSearch({type:"set_logging",enabled:e})}catch(t){}}startSearchWorker(){this.searchWorker=this.createBlobWorker("worker.js"),this.searchWorker.onmessage=e=>{let t=e.data;if(t.id&&this.searchPending.has(t.id)){let{resolve:s,reject:i}=this.searchPending.get(t.id);this.searchPending.delete(t.id),t.type==="error"?i(new Error(t.error)):s(t);return}t.type==="error"&&console.error("Kjarni search worker:",t.error)},this.searchWorker.onerror=e=>{console.error("Kjarni search worker crashed:",e),new l.Notice(`Kjarni: Worker error \u2014 ${e.message}`)}}async startEncoderPool(){if(!this.wasmBytesCache||!this.encoderBytesCache)throw new Error("WASM/encoder bytes not loaded");this.encoderWorkers=[];for(let e=0;e<R;e++){let t=this.createBlobWorker("encoder-worker.js");t.onmessage=n=>{let r=n.data;if(r.type!=="encode_progress"&&r.id&&this.encoderPending.has(r.id)){let{resolve:a,reject:c}=this.encoderPending.get(r.id);this.encoderPending.delete(r.id),r.type==="error"?c(new Error(r.error)):a(r)}},t.onerror=n=>{console.error(`Kjarni encoder worker ${e} crashed:`,n)};let s=this.wasmBytesCache.slice(0),i=this.encoderBytesCache.slice(0);await this.postToEncoder(t,{type:"init",wasmBytes:s,encoderBytes:i},[s,i]),this.settings.debugLogging&&await this.postToEncoder(t,{type:"set_logging",enabled:!0}),this.encoderWorkers.push(t)}}stopEncoderPool(){for(let e of this.encoderWorkers)e.terminate();this.encoderWorkers=[]}async downloadModel(e,t){var i;let s=y.join(t,e.filename);if(g.existsSync(s))return s;(i=this.statusBarEl)==null||i.setText(`Kjarni: downloading ${e.filename} (${e.size})...`),new l.Notice(`Kjarni: Downloading ${e.filename} (${e.size})...`);try{let n=await(0,l.requestUrl)({url:e.url,method:"GET"});return g.mkdirSync(t,{recursive:!0}),g.writeFileSync(s,Buffer.from(n.arrayBuffer)),s}catch(n){throw g.existsSync(s)&&g.unlinkSync(s),new Error(`Failed to download ${e.filename}: ${n}`)}}async ensureModels(){var t;let e=y.join(this.pluginDir,"models");try{return await this.downloadModel(V.encoder,e),this.settings.rerankerEnabled&&await this.downloadModel(V.reranker,e),(t=this.statusBarEl)==null||t.setText(""),!0}catch(s){return console.error("Kjarni: Model download failed:",s),new l.Notice(`Kjarni: ${s}`,1e4),!1}}async initializeAsync(){var e,t,s;try{if(!this.checkFiles()||!await this.ensureModels())return;(e=this.statusBarEl)==null||e.setText("Kjarni: loading engine...");let i=g.readFileSync(y.join(this.pluginDir,"kjarni_wasm_bg.wasm")),n=g.readFileSync(y.join(this.pluginDir,"models","encoder.kjq"));this.wasmBytesCache=i.buffer.slice(i.byteOffset,i.byteOffset+i.byteLength),this.encoderBytesCache=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength),this.startSearchWorker();let r=null;if(this.settings.rerankerEnabled){let h=y.join(this.pluginDir,"models","reranker.kjq");if(g.existsSync(h)){let w=g.readFileSync(h);r=w.buffer.slice(w.byteOffset,w.byteOffset+w.byteLength)}}let a=null,c=0,p=y.join(this.pluginDir,"index.idx");if(g.existsSync(p)){let h=g.readFileSync(p);a=h.buffer.slice(h.byteOffset,h.byteOffset+h.byteLength),c=g.statSync(p).mtimeMs}let k=[this.wasmBytesCache.slice(0),this.encoderBytesCache.slice(0)],f={type:"init",wasmBytes:k[0],encoderBytes:k[1]};if(r){let h=r.slice(0);f.rerankerBytes=h,k.push(h)}if(a){let h=a.slice(0);f.indexBytes=h,k.push(h)}let o=await this.postToSearch(f,k);this.hasReranker=o.hasReranker,await this.setWorkerLogging(this.settings.debugLogging),o.hasIndex?(this.indexReady=!0,(t=this.statusBarEl)==null||t.setText(`Kjarni: ${o.docCount} chunks`),new l.Notice(`Kjarni: Loaded index (${o.docCount} chunks)`),await this.catchUpChangedFiles(c)):await this.buildFullIndex()}catch(i){console.error("Kjarni: Initialization failed:",i),new l.Notice(`Kjarni: Failed to initialize \u2014 ${i}`),(s=this.statusBarEl)==null||s.setText("Kjarni: init failed")}}checkFiles(){let e=y.join(this.pluginDir,"kjarni_wasm_bg.wasm"),t=y.join(this.pluginDir,"worker.js"),s=y.join(this.pluginDir,"encoder-worker.js"),i=[];return g.existsSync(e)||i.push("pkg/kjarni_wasm_bg.wasm"),g.existsSync(t)||i.push("worker.js"),g.existsSync(s)||i.push("encoder-worker.js"),i.length>0?(new l.Notice(`Kjarni: Missing files:
${i.join(`
`)}
Reinstall the plugin.`,1e4),!1):!0}async buildFullIndex(){var e,t,s,i,n,r,a,c;if(!this.indexing){this.indexing=!0;try{let p=Date.now(),k=this.app.vault.getMarkdownFiles().sort((u,x)=>u.path.localeCompare(x.path));(e=this.statusBarEl)==null||e.setText(`Kjarni: reading ${k.length} files...`);let f=[];for(let u of k){let x=await this.app.vault.cachedRead(u);x.trim().length>0&&f.push({text:x,path:u.path})}let o=f.length;if(o===0){(t=this.statusBarEl)==null||t.setText("Kjarni: no files to index");return}(s=this.statusBarEl)==null||s.setText(`Kjarni: starting ${R} encoder workers...`),await this.startEncoderPool();let h=Array.from({length:R},()=>[]);for(let u=0;u<f.length;u++)h[u%R].push(f[u]);let w=new Array(R).fill(0),v=0,E=u=>{var K;let x=u.data;if(x.type==="encode_progress"){w[x.workerId]=x.indexed;let I=w.reduce((G,J)=>G+J,0),z=((Date.now()-p)/1e3).toFixed(0),H=(I/(Date.now()-p)*1e3).toFixed(1);(K=this.statusBarEl)==null||K.setText(`Kjarni: encoding ${I}/${o} files \xB7 ${z}s \xB7 ${H} files/s`)}};for(let u of this.encoderWorkers)u.addEventListener("message",E);(i=this.statusBarEl)==null||i.setText(`Kjarni: encoding 0/${o} files...`);let S=h.map((u,x)=>u.length===0?Promise.resolve({chunks:[],skipped:0}):this.postToEncoder(this.encoderWorkers[x],{type:"encode_batch",files:u,workerId:x})),q=await Promise.all(S);for(let u of this.encoderWorkers)u.removeEventListener("message",E);this.stopEncoderPool();let j=[],L=0;for(let u of q)u.chunks&&(j=j.concat(u.chunks)),L+=u.skipped||0;v=j.length,(n=this.statusBarEl)==null||n.setText(`Kjarni: building index from ${v} chunks...`),await this.postToSearch({type:"build_start"});let F=1e3;for(let u=0;u<j.length;u+=F){let x=j.slice(u,u+F);await this.postToSearch({type:"add_encoded_chunks",chunks:x}),(r=this.statusBarEl)==null||r.setText(`Kjarni: indexing ${Math.min(u+F,j.length)}/${v} chunks...`)}let C=await this.postToSearch({type:"build_finish"});if(C.indexBytes){let u=y.join(this.pluginDir,"index.idx");g.writeFileSync(u,Buffer.from(C.indexBytes))}this.indexReady=!0;let O=((Date.now()-p)/1e3).toFixed(1),N=L>0?` (${L} skipped)`:"";(a=this.statusBarEl)==null||a.setText(`Kjarni: ${C.docCount} chunks \xB7 ${o} files`),new l.Notice(`Kjarni: Indexed ${C.docCount} chunks from ${o} files in ${O}s${N}`)}catch(p){console.error("Kjarni: Indexing failed:",p),new l.Notice(`Kjarni: Indexing failed \u2014 ${p}`),(c=this.statusBarEl)==null||c.setText("Kjarni: indexing failed"),this.stopEncoderPool()}finally{this.indexing=!1}}}async catchUpChangedFiles(e){var n;let t=this.app.vault.getMarkdownFiles(),s=t.filter(r=>r.stat.mtime>e);if(s.length===0)return;if(s.length>t.length*.5){new l.Notice(`Kjarni: ${s.length} files changed \u2014 rebuilding...`),await this.buildFullIndex();return}(n=this.statusBarEl)==null||n.setText(`Kjarni: updating ${s.length} changed files...`);let i=0;for(let r of s)try{let a=await this.app.vault.cachedRead(r);await this.postToSearch({type:"update_file",text:a,path:r.path}),i++}catch(a){console.warn(`Kjarni: Failed to update ${r.path}: ${a}`)}i>0&&(await this.requestSaveIndex(),new l.Notice(`Kjarni: Updated ${i} changed files`))}queueFileUpdate(e){this.indexReady&&(this.pendingDeletes.delete(e),this.pendingUpdates.add(e),this.updateTimer&&clearTimeout(this.updateTimer),this.updateTimer=setTimeout(()=>this.flushUpdates(),ie))}queueFileDelete(e){this.indexReady&&(this.pendingUpdates.delete(e),this.pendingDeletes.add(e),this.deleteTimer&&clearTimeout(this.deleteTimer),this.deleteTimer=setTimeout(()=>this.flushDeletes(),ne))}async flushUpdates(){var i;if(this.pendingUpdates.size===0)return;let e=[...this.pendingUpdates];this.pendingUpdates.clear();let t=0,s=0;for(let n of e){let r=this.app.vault.getAbstractFileByPath(n);if(r instanceof l.TFile)try{let a=await this.app.vault.cachedRead(r);s=(await this.postToSearch({type:"update_file",text:a,path:n})).docCount,t++}catch(a){console.warn(`Kjarni: Failed to update ${n}: ${a}`)}}t>0&&(this.requestSaveIndex(),(i=this.statusBarEl)==null||i.setText(`Kjarni: ${s} chunks \xB7 updated ${t} files`),setTimeout(()=>{var n;(n=this.statusBarEl)==null||n.setText(`Kjarni: ${s} chunks`)},3e3))}async flushDeletes(){var s;if(this.pendingDeletes.size===0)return;let e=[...this.pendingDeletes];this.pendingDeletes.clear();let t=0;for(let i of e)try{t=(await this.postToSearch({type:"remove_file",path:i})).docCount}catch(n){console.warn(`Kjarni: Failed to remove ${i}: ${n}`)}this.requestSaveIndex(),(s=this.statusBarEl)==null||s.setText(`Kjarni: ${t} chunks`)}async requestSaveIndex(){var e;try{let t=await this.postToSearch({type:"save_index"});if(t.indexBytes){let s=y.join(this.pluginDir,"index.idx");g.writeFileSync(s,Buffer.from(t.indexBytes))}(e=this.statusBarEl)==null||e.setText(`Kjarni: ${t.docCount} chunks`)}catch(t){console.error("Kjarni: Failed to save index:",t)}}async reindexVault(){if(this.indexing){new l.Notice("Kjarni: Already indexing...");return}this.pendingUpdates.clear(),this.pendingDeletes.clear(),this.updateTimer&&clearTimeout(this.updateTimer),this.deleteTimer&&clearTimeout(this.deleteTimer),await this.buildFullIndex()}openSearch(){if(!this.indexReady){new l.Notice("Kjarni: Index not ready. Please wait for indexing to complete.");return}new P(this.app,this).open()}async doSearch(e,t){if(!e.trim())return[];try{return(await this.postToSearch({type:"search",query:e,limit:t})).results||[]}catch(s){return console.error("Kjarni: Search failed:",s),[]}}async doRerank(e,t,s){if(!this.hasReranker||t.length===0)return[];try{return(await this.postToSearch({type:"rerank",query:e,docs:t,limit:s})).results||[]}catch(i){return console.error("Kjarni: Rerank failed:",i),[]}}async loadSettings(){this.settings=Object.assign({},U,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}},P=class extends l.Modal{constructor(e,t){super(e);this.inputEl=null;this.resultsEl=null;this.statusEl=null;this.debounceTimer=null;this.searchVersion=0;this.lastDisplayed=[];this.rerankTimer=null;this.plugin=t}onOpen(){let{contentEl:e,modalEl:t}=this;t.addClass("kjarni-search-modal");let s=e.createDiv({cls:"kjarni-header"}),i=s.createEl("span",{cls:"kjarni-icon"});i.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>',this.inputEl=s.createEl("input",{cls:"kjarni-input",attr:{type:"text",placeholder:"Search your vault...",spellcheck:"false"}}),s.createEl("span",{cls:"kjarni-kbd",text:"esc"}),this.resultsEl=e.createDiv({cls:"kjarni-results"}),this.statusEl=e.createDiv({cls:"kjarni-status"}),this.inputEl.addEventListener("input",()=>{this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>this.runSearch(),300)}),this.inputEl.addEventListener("keydown",n=>{n.key==="Escape"?this.close():n.key==="Enter"&&this.openResult(0)}),this.inputEl.focus()}async runSearch(){var a,c;let e=(c=(a=this.inputEl)==null?void 0:a.value)==null?void 0:c.trim();if(!e||e.length<3||!this.resultsEl){this.resultsEl&&this.resultsEl.empty(),this.statusEl&&(this.statusEl.textContent="");return}this.searchVersion++;let t=this.searchVersion;this.rerankTimer&&clearTimeout(this.rerankTimer);let s=performance.now(),i=await this.plugin.doSearch(e,this.plugin.settings.searchLimit*2),n=performance.now()-s;if(this.searchVersion!==t)return;let r=i.slice(0,this.plugin.settings.searchLimit);this.lastDisplayed=r,this.renderResults(r),this.statusEl&&(this.statusEl.textContent=`${r.length} results \xB7 ${n.toFixed(0)}ms`,this.statusEl.removeClass("kjarni-refining")),this.plugin.hasReranker&&r.length>0&&e.length>=5&&(this.rerankTimer=setTimeout(()=>{this.doRerank(e,r,t,n)},1e3))}async doRerank(e,t,s,i){if(this.searchVersion!==s)return;this.statusEl&&(this.statusEl.textContent=`${t.length} results \xB7 ${i.toFixed(0)}ms \xB7 refining...`,this.statusEl.addClass("kjarni-refining"));let n=t.slice(0,5).map(p=>p.text),r=performance.now(),a=await this.plugin.doRerank(e,n,5),c=performance.now()-r;if(this.searchVersion===s){if(a.length>0){let p=a.map(o=>t[o.index]),k=new Set(a.map(o=>o.index)),f=t.filter((o,h)=>h>=5||!k.has(h));this.lastDisplayed=[...p,...f],this.renderResults(this.lastDisplayed)}this.statusEl&&(this.statusEl.textContent=`${t.length} results \xB7 ${i.toFixed(0)}ms search \xB7 ${c.toFixed(0)}ms rerank \xB7 refined`,this.statusEl.removeClass("kjarni-refining"),this.statusEl.addClass("kjarni-refined"),setTimeout(()=>{var p;return(p=this.statusEl)==null?void 0:p.removeClass("kjarni-refined")},2e3))}}renderResults(e){var n,r;if(!this.resultsEl)return;if(this.resultsEl.empty(),e.length===0){this.resultsEl.createDiv({cls:"kjarni-empty",text:"No results found"});return}let s=(((r=(n=this.inputEl)==null?void 0:n.value)==null?void 0:r.trim())||"").toLowerCase().split(/\s+/).filter(a=>a.length>=2),i=Math.max(...e.map(a=>a.score),.001);e.forEach((a,c)=>{let p=a.text.length>200?a.text.slice(0,200)+"\u2026":a.text,k=Math.round(a.score/i*100),f=this.resultsEl.createDiv({cls:"kjarni-result"});f.addEventListener("click",()=>this.openResult(c));let o=f.createDiv({cls:"kjarni-result-title"});o.createEl("span",{text:a.source.replace(/\.md$/,"")});let h=o.createEl("span",{cls:"kjarni-score"});h.textContent=`${k}%`;let w=f.createDiv({cls:"kjarni-result-text"});w.innerHTML=ae(re(p),s)})}openResult(e){let t=this.lastDisplayed[e];if(!t)return;this.app.vault.getAbstractFileByPath(t.source)instanceof l.TFile&&(this.app.workspace.openLinkText(t.source,"",!1),this.close())}onClose(){this.searchVersion++,this.debounceTimer&&clearTimeout(this.debounceTimer),this.rerankTimer&&clearTimeout(this.rerankTimer),this.contentEl.empty()}},M=class extends l.PluginSettingTab{constructor(d,e){super(d,e),this.plugin=e}display(){let{containerEl:d}=this;d.empty(),d.createEl("h2",{text:"Kjarni Search"}),new l.Setting(d).setName("Search results limit").setDesc("Maximum number of results to show").addSlider(e=>e.setLimits(5,30,5).setValue(this.plugin.settings.searchLimit).setDynamicTooltip().onChange(async t=>{this.plugin.settings.searchLimit=t,await this.plugin.saveSettings()})),new l.Setting(d).setName("Reranker").setDesc("Use cross-encoder reranking for better result quality").addToggle(e=>e.setValue(this.plugin.settings.rerankerEnabled).onChange(async t=>{this.plugin.settings.rerankerEnabled=t,await this.plugin.saveSettings()})),new l.Setting(d).setName("Chunk size").setDesc("Maximum characters per chunk when indexing (default 1000)").addText(e=>e.setValue(String(this.plugin.settings.chunkSize)).onChange(async t=>{let s=parseInt(t);!isNaN(s)&&s>100&&(this.plugin.settings.chunkSize=s,await this.plugin.saveSettings())})),new l.Setting(d).setName("Reindex vault").setDesc("Rebuild the search index from scratch").addButton(e=>e.setButtonText("Reindex").onClick(async()=>{await this.plugin.reindexVault()})),d.createEl("h3",{text:"Developer"}),new l.Setting(d).setName("Debug logging").setDesc("Log detailed timing info to the developer console. Requires Obsidian restart.").addToggle(e=>e.setValue(this.plugin.settings.debugLogging).onChange(async t=>{this.plugin.settings.debugLogging=t,await this.plugin.saveSettings(),await this.plugin.setWorkerLogging(t)}))}};