test-repo/.obsidian/plugins/qmd-search/main.js

9 lines
32 KiB
JavaScript

/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var M=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var T=(h,i)=>{for(var e in i)M(h,e,{get:i[e],enumerable:!0})},N=(h,i,e,t)=>{if(i&&typeof i=="object"||typeof i=="function")for(let s of k(i))!q.call(h,s)&&s!==e&&M(h,s,{get:()=>i[s],enumerable:!(t=I(i,s))||t.enumerable});return h};var B=h=>N(M({},"__esModule",{value:!0}),h);var F={};T(F,{default:()=>w});module.exports=B(F);var d=require("obsidian");var y={qmdBinaryPath:"qmd",collectionName:"",indexName:null,fileMask:"**/*.md",debounceMs:45e3,enablePeriodicUpdates:!0,periodicUpdateMinutes:15,defaultSearchMode:"semantic",fallbackOnSemanticFailure:!0,fallbackOnZeroResults:!1,showEmbeddingsBanner:!0,autoGenerateEmbeddings:!0,enableRibbonIcon:!0,enableSearchPane:!1,showScoresInResults:!0,lastIndexUpdateTime:null,lastEmbeddingRunTime:null,lastSearchMode:null,lastError:null};function D(h){return h.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}var E=require("child_process"),P=require("util"),x=(0,P.promisify)(E.exec),u=class extends Error{constructor(i,e,t){super(i),this.name="QMDError",this.type=e,this.stderr=t}},f=class{constructor(i,e,t,s,n){this.commandQueue=[];this.isProcessing=!1;this.currentSearchProcess=null;this.binaryPath=i,this.collectionName=e,this.indexName=t,this.vaultPath=s,this.execAsync=n!=null?n:x}abortSearch(){this.currentSearchProcess&&(this.currentSearchProcess.kill(),this.currentSearchProcess=null)}updateConfig(i,e,t){this.binaryPath=i,this.collectionName=e,this.indexName=t}buildBinaryWithIndex(){let i=`"${this.binaryPath}"`;return this.indexName&&(i+=` --index "${this.indexName}"`),i}buildBaseCommand(i){return`${this.buildBinaryWithIndex()} ${i}`}async queueCommand(i){return new Promise((e,t)=>{let s=async()=>{try{let n=await i();e(n)}catch(n){t(n instanceof Error?n:new Error(String(n)))}};this.commandQueue.push(s),this.processQueue()})}async processQueue(){if(!(this.isProcessing||this.commandQueue.length===0)){for(this.isProcessing=!0;this.commandQueue.length>0;){let i=this.commandQueue.shift();i&&await i()}this.isProcessing=!1}}async execCommand(i){var e,t,s,n;try{let{stdout:r,stderr:o}=await this.execAsync(i,{maxBuffer:10485760,cwd:this.vaultPath});return{stdout:r,stderr:o}}catch(r){let o=r;throw o.code===127||(e=o.message)!=null&&e.includes("not found")?new u(`QMD binary not found at: ${this.binaryPath}`,"not_found",o.stderr):(t=o.stderr)!=null&&t.includes("no collection")?new u(`Collection "${this.collectionName}" not found`,"no_collection",o.stderr):(s=o.stderr)!=null&&s.includes("no embeddings")||(n=o.stderr)!=null&&n.includes("embeddings not found")?new u("Embeddings not generated for this collection","no_embeddings",o.stderr):new u(o.message||"Unknown QMD error","execution_error",o.stderr)}}execSearchCommand(i){return this.execAsync!==x?this.execCommand(i):new Promise((e,t)=>{this.currentSearchProcess=(0,E.exec)(i,{maxBuffer:10*1024*1024,cwd:this.vaultPath},(s,n,r)=>{var o;if(this.currentSearchProcess=null,s){if(s.killed||s.signal==="SIGTERM"){t(new u("Search cancelled","execution_error"));return}let a=s;if(a.code===127||(o=a.message)!=null&&o.includes("not found")){t(new u(`QMD binary not found at: ${this.binaryPath}`,"not_found",r));return}if(r!=null&&r.includes("no collection")){t(new u(`Collection "${this.collectionName}" not found`,"no_collection",r));return}if(r!=null&&r.includes("no embeddings")||r!=null&&r.includes("embeddings not found")){t(new u("Embeddings not generated for this collection","no_embeddings",r));return}t(new u(a.message||"Unknown QMD error","execution_error",r));return}e({stdout:n,stderr:r})})})}async testConnection(){return this.queueCommand(async()=>{try{let i=this.buildBaseCommand("status"),{stdout:e,stderr:t}=await this.execCommand(i);return{success:!0,data:this.parseStatusOutput(e),stderr:t}}catch(i){let e=i;return{success:!1,error:e.message,stderr:e.stderr}}})}parseStatusOutput(i){let e=i.match(/Total:\s+(\d+)\s+files?\s+indexed/i),t=e?parseInt(e[1],10):0,s=i.match(/Vectors:\s+(\d+)\s+embedded/i),n=s?parseInt(s[1],10):0,r=i.includes(this.collectionName)||i.includes(`qmd://${this.collectionName}/`),o=i.toLowerCase().includes("no collections");return{collection:r?this.collectionName:"",path:this.vaultPath,fileCount:t,indexedCount:t,embeddingsCount:n,hasEmbeddings:n>0,hasCollection:r&&!o}}async ensureCollection(i){return this.queueCommand(async()=>{try{let e=`${this.buildBinaryWithIndex()} collection list`;try{let{stdout:n}=await this.execCommand(e);if(n.includes(this.collectionName)||n.includes(`qmd://${this.collectionName}/`))return{success:!0}}catch(n){}let t=`${this.buildBinaryWithIndex()} collection add "${this.vaultPath}" --name "${this.collectionName}" --mask "${i}"`,{stderr:s}=await this.execCommand(t);return{success:!0,stderr:s}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}async updateIndex(){return this.queueCommand(async()=>{try{let i=this.buildBaseCommand("update"),{stderr:e}=await this.execCommand(i);return{success:!0,stderr:e}}catch(i){let e=i;return{success:!1,error:e.message,stderr:e.stderr}}})}async generateEmbeddings(i=!1){return this.queueCommand(async()=>{try{let e=this.buildBaseCommand("embed");i&&(e+=" -f");let{stderr:t}=await this.execCommand(e);return{success:!0,stderr:t}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}parseSearchResults(i){return i.map(e=>{let t=e.file;if(t.startsWith("qmd://")){let n=t.substring(6),r=n.indexOf("/");r!==-1&&(t=n.substring(r+1))}let s=e.snippet;return s&&(s=s.replace(/@@ -\d+,\d+ @@\s*\(\d+ before, \d+ after\)\s*/g,"").trim()),{path:t,score:e.score,title:e.title,snippet:s,docid:e.docid}})}extractJsonArray(i){let e=i.indexOf("[");if(e===-1)return"[]";let t=0,s=e;for(let n=e;n<i.length;n++)if(i[n]==="["&&t++,i[n]==="]"&&t--,t===0){s=n+1;break}return i.substring(e,s)}async semanticSearch(i){return this.queueCommand(async()=>{try{let e=i.replace(/"/g,'\\"'),t=this.buildBaseCommand("vsearch")+` "${e}" --json`,{stdout:s,stderr:n}=await this.execSearchCommand(t);try{let r=this.extractJsonArray(s),o=JSON.parse(r);return{success:!0,data:this.parseSearchResults(o),stderr:n}}catch(r){throw new u("Failed to parse search results","parse_error",s)}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}async keywordSearch(i){return this.queueCommand(async()=>{try{let e=i.replace(/"/g,'\\"'),t=this.buildBaseCommand("search")+` "${e}" --json`,{stdout:s,stderr:n}=await this.execSearchCommand(t);try{let r=this.extractJsonArray(s),o=JSON.parse(r);return{success:!0,data:this.parseSearchResults(o),stderr:n}}catch(r){throw new u("Failed to parse search results","parse_error",s)}}catch(e){let t=e;return{success:!1,error:t.message,stderr:t.stderr}}})}async hasEmbeddings(){var e;let i=await this.testConnection();return i.success&&((e=i.data)==null?void 0:e.hasEmbeddings)===!0}getQueueLength(){return this.commandQueue.length}isBusy(){return this.isProcessing}};var g=require("obsidian"),b=class extends g.SuggestModal{constructor(e,t,s,n){super(e);this.currentQuery="";this.lastCompletedQuery="";this.lastResults=[];this.isSearching=!1;this.searchId=0;this.usedFallback=!1;this.searchError=null;this.progressBar=null;this.searchModeIndicator=null;this.qmd=t,this.settings=s,this.onResultSelect=n.onResultSelect,this.onSearchModeUsed=n.onSearchModeUsed,this.onError=n.onError,this.setPlaceholder("Search your vault with QMD..."),this.setInstructions([{command:"\u2191\u2193",purpose:"Navigate"},{command:"\u21B5",purpose:"Open file"},{command:"esc",purpose:"Close"}]),this.debouncedSearch=(0,g.debounce)(r=>this.performSearch(r),1e3,!1)}async onOpen(){await super.onOpen(),this.injectStatusElements();let e=this.getInputElement();e&&(e.maxLength=50)}injectStatusElements(){let e=this.containerEl.querySelector(".prompt-input-container");e&&(this.progressBar||(this.progressBar=createDiv({cls:"qmd-progress-container qmd-hidden"}),this.progressBar.createDiv({cls:"qmd-progress-bar"}).createDiv({cls:"qmd-progress-bar-fill"}),this.progressBar.createSpan({cls:"qmd-progress-text",text:"Searching..."}),e.insertAdjacentElement("afterend",this.progressBar)),this.searchModeIndicator||(this.searchModeIndicator=createDiv({cls:"qmd-search-mode-indicator qmd-hidden"}),e.appendChild(this.searchModeIndicator)))}showProgressBar(){var e;(e=this.progressBar)==null||e.removeClass("qmd-hidden")}hideProgressBar(){var e;(e=this.progressBar)==null||e.addClass("qmd-hidden")}updateSearchModeIndicator(e,t){if(!this.searchModeIndicator)return;let s=t?"keyword (fallback)":e;this.searchModeIndicator.textContent=s,this.searchModeIndicator.removeClass("qmd-hidden");let n=this.getInputElement();n&&n.addClass("qmd-input-with-pill")}hideSearchModeIndicator(){var t;(t=this.searchModeIndicator)==null||t.addClass("qmd-hidden");let e=this.getInputElement();e&&e.removeClass("qmd-input-with-pill")}getSuggestions(e){return this.currentQuery=e,!e||e.trim().length<2?(this.searchError=null,this.lastCompletedQuery="",this.lastResults=[],this.hideProgressBar(),this.hideSearchModeIndicator(),[]):(this.isSearching&&(this.qmd.abortSearch(),this.searchId++),e!==this.lastCompletedQuery&&this.debouncedSearch(e),this.searchError&&this.lastResults.length===0?[{path:"",score:0,title:`Error: ${this.searchError}`,snippet:"Check that QMD is installed and accessible. Use 'Test QMD' in settings.",searchMode:this.settings.defaultSearchMode,isFallback:!1}]:this.lastResults)}async performSearch(e){var s;if(e!==this.currentQuery)return;let t=++this.searchId;this.isSearching=!0,this.usedFallback=!1,this.searchError=null,this.showProgressBar();try{let n=[],r=this.settings.defaultSearchMode;if(r==="semantic"){let a=await this.qmd.semanticSearch(e);if(t!==this.searchId)return;if(a.success&&a.data){if(a.data.length>0||!this.settings.fallbackOnZeroResults)n=this.mapResults(a.data,"semantic",!1);else if(this.settings.fallbackOnZeroResults){let l=await this.qmd.keywordSearch(e);if(t!==this.searchId)return;l.success&&l.data&&(n=this.mapResults(l.data,"keyword",!0),this.usedFallback=!0,r="keyword")}}else if(this.settings.fallbackOnSemanticFailure){(s=a.error)!=null&&s.includes("embeddings")&&this.showEmbeddingsNotice();let l=await this.qmd.keywordSearch(e);if(t!==this.searchId)return;l.success&&l.data&&(n=this.mapResults(l.data,"keyword",!0),this.usedFallback=!0,r="keyword",new g.Notice("Semantic search unavailable \u2014 using keyword search."))}else this.searchError=a.error||"Search failed",this.onError(this.searchError)}else{let a=await this.qmd.keywordSearch(e);if(t!==this.searchId)return;a.success&&a.data?n=this.mapResults(a.data,"keyword",!1):(this.searchError=a.error||"Search failed",this.onError(this.searchError))}this.lastResults=n,this.lastCompletedQuery=e,this.isSearching=!1,this.hideProgressBar(),n.length>0?this.updateSearchModeIndicator(r,this.usedFallback):this.hideSearchModeIndicator(),this.onSearchModeUsed(r,this.usedFallback);let o=this.getInputElement();if(o&&o.value===e){let a=this.containerEl.querySelector(".suggestion-container"),l=(a==null?void 0:a.scrollTop)||0;o.dispatchEvent(new Event("input")),a&&l>0&&queueMicrotask(()=>{a.scrollTop=l})}}catch(n){if(t!==this.searchId)return;let r=n instanceof Error?n.message:"Unknown error";this.searchError=r,this.onError(r)}finally{t===this.searchId&&(this.isSearching=!1,this.hideProgressBar())}}toSlug(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}buildSlugMap(){let e=new Map;for(let t of this.app.vault.getFiles()){let s=this.toSlug(t.basename);e.has(s)||e.set(s,t);let n=this.toSlug(t.path.replace(/\.md$/i,""));e.has(n)||e.set(n,t)}return e}mapResults(e,t,s){let n=this.buildSlugMap();return e.map(r=>{let o;if(r.title&&(o=this.app.vault.getFiles().find(a=>a.basename===r.title)),!o){let a=this.toSlug(r.path.replace(/\.md$/i,""));o=n.get(a)}return{...r,file:o,searchMode:t,isFallback:s}})}showEmbeddingsNotice(){this.settings.showEmbeddingsBanner&&new g.Notice("Semantic search requires embeddings. Run 'QMD: Generate embeddings' to enable.",1e4)}renderSuggestion(e,t){var o;let s=e.path===""&&((o=e.title)==null?void 0:o.startsWith("Error:")),n=t.createDiv({cls:s?"qmd-search-error":"qmd-search-result"}),r=n.createDiv({cls:"qmd-search-result-title"});if(r.createSpan({text:e.title||e.path.replace(/\.md$/i,"").split("/").pop()||e.path,cls:s?"qmd-search-error-title":"qmd-search-result-name"}),!s&&this.settings.showScoresInResults){let a=Math.round(e.score*100);r.createSpan({text:` (Score: ${a}%)`,cls:"qmd-search-result-score"})}e.snippet&&n.createDiv({text:e.snippet,cls:s?"qmd-search-error-hint":"qmd-search-result-snippet"})}onChooseSuggestion(e,t){var s;e.path===""&&((s=e.title)!=null&&s.startsWith("Error:"))||this.onResultSelect(e)}getInputElement(){return this.containerEl.querySelector("input")}};var m=require("obsidian"),p="qmd-search-view",v=class extends m.ItemView{constructor(e,t,s,n){super(e);this.searchInput=null;this.resultsContainer=null;this.statusContainer=null;this.currentQuery="";this.isSearching=!1;this.qmd=t,this.settings=s,this.onResultSelect=n.onResultSelect,this.onSearchModeUsed=n.onSearchModeUsed,this.onError=n.onError,this.debouncedSearch=(0,m.debounce)(r=>{this.performSearch(r)},300,!0)}getViewType(){return p}getDisplayText(){return"QMD search"}getIcon(){return"search"}async onOpen(){await super.onOpen();let e=this.containerEl.children[1];e.empty(),e.addClass("qmd-search-pane");let s=e.createDiv({cls:"qmd-search-input-container"}).createDiv({cls:"qmd-search-input-wrapper"}),n=s.createSpan({cls:"qmd-search-icon"});(0,m.setIcon)(n,"search");let r=s.createEl("input",{type:"text",placeholder:"Search with QMD...",cls:"qmd-search-input"});r.addEventListener("input",o=>{let a=o.target.value;this.currentQuery=a,a.trim().length>=2?this.debouncedSearch(a):this.clearResults()}),r.addEventListener("keydown",o=>{o.key==="Enter"&&this.currentQuery.trim().length>=2&&this.performSearch(this.currentQuery)}),this.statusContainer=e.createDiv({cls:"qmd-search-status"}),this.resultsContainer=e.createDiv({cls:"qmd-search-results"})}async onClose(){}updateReferences(e,t){this.qmd=e,this.settings=t}async performSearch(e){var t;if(!this.isSearching&&!(!e||e.trim().length<2)){this.isSearching=!0,this.showStatus("Searching...","loading");try{let s=[],n=this.settings.defaultSearchMode,r=!1;if(n==="semantic"){let a=await this.qmd.semanticSearch(e);if(a.success&&a.data){if(a.data.length>0||!this.settings.fallbackOnZeroResults)s=a.data;else if(this.settings.fallbackOnZeroResults){let l=await this.qmd.keywordSearch(e);l.success&&l.data&&(s=l.data,r=!0,n="keyword")}}else if(this.settings.fallbackOnSemanticFailure){(t=a.error)!=null&&t.includes("embeddings")&&this.showEmbeddingsNotice();let l=await this.qmd.keywordSearch(e);l.success&&l.data&&(s=l.data,r=!0,n="keyword",new m.Notice("Semantic search unavailable \u2014 using keyword search."))}else throw new Error(a.error||"Search failed")}else{let a=await this.qmd.keywordSearch(e);if(a.success&&a.data)s=a.data;else throw new Error(a.error||"Search failed")}this.onSearchModeUsed(n,r),this.renderResults(s,n,r);let o=r?`${s.length} results (keyword fallback)`:`${s.length} results (${n})`;this.showStatus(o,"success")}catch(s){let n=s instanceof Error?s.message:"Unknown error";this.onError(n),this.showStatus(`Error: ${n}`,"error")}finally{this.isSearching=!1}}}showEmbeddingsNotice(){this.settings.showEmbeddingsBanner&&new m.Notice("Semantic search requires embeddings. Run 'QMD: Generate embeddings' to enable.",1e4)}renderResults(e,t,s){if(this.resultsContainer){if(this.resultsContainer.empty(),e.length===0){this.resultsContainer.createDiv({text:"No results found",cls:"qmd-search-no-results"});return}for(let n of e){let r=this.resultsContainer.createDiv({cls:"qmd-search-result-item"}),o=this.app.vault.getFiles().find(C=>C.path===n.path||C.path.endsWith(n.path)),a={...n,file:o,searchMode:t,isFallback:s};r.createDiv({cls:"qmd-result-title"}).createSpan({text:n.title||n.path.split("/").pop()||n.path}),n.title&&r.createDiv({text:n.path,cls:"qmd-result-path"}),n.snippet&&r.createDiv({text:n.snippet,cls:"qmd-result-snippet"}),this.settings.showScoresInResults&&r.createDiv({text:`Score: ${n.score.toFixed(3)}`,cls:"qmd-result-score"}),r.addEventListener("click",()=>{this.onResultSelect(a)})}}}clearResults(){this.resultsContainer&&this.resultsContainer.empty(),this.hideStatus()}showStatus(e,t){if(this.statusContainer){if(this.statusContainer.empty(),this.statusContainer.removeClass("qmd-status-loading","qmd-status-success","qmd-status-error"),this.statusContainer.addClass(`qmd-status-${t}`),t==="loading"){let s=this.statusContainer.createDiv({cls:"qmd-pane-loading-row"});s.createSpan({cls:"qmd-pane-spinner"}),s.createSpan({text:e})}else this.statusContainer.setText(e);this.statusContainer.show()}}hideStatus(){this.statusContainer&&this.statusContainer.hide()}};var c=require("obsidian"),S=class extends c.PluginSettingTab{constructor(i,e){super(i,e),this.plugin=e}display(){let{containerEl:i}=this;i.empty(),new c.Setting(i).setName("QMD configuration").setHeading(),new c.Setting(i).setName("QMD binary path").setDesc("Path to the QMD executable. Use 'qmd' if it's in your PATH.").addText(t=>t.setPlaceholder("qmd").setValue(this.plugin.settings.qmdBinaryPath).onChange(async s=>{this.plugin.settings.qmdBinaryPath=s||"qmd",await this.plugin.saveSettings()})),new c.Setting(i).setName("Collection name").setDesc("Name for the QMD collection. Leave empty to use vault name.").addText(t=>t.setPlaceholder("(derived from vault name)").setValue(this.plugin.settings.collectionName).onChange(async s=>{this.plugin.settings.collectionName=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Index name").setDesc("Optional QMD index name override. Leave empty for default.").addText(t=>t.setPlaceholder("(default)").setValue(this.plugin.settings.indexName||"").onChange(async s=>{this.plugin.settings.indexName=s||null,await this.plugin.saveSettings()})),new c.Setting(i).setName("File mask").setDesc("Glob pattern for markdown files to index.").addText(t=>t.setPlaceholder("**/*.md").setValue(this.plugin.settings.fileMask).onChange(async s=>{this.plugin.settings.fileMask=s||"**/*.md",await this.plugin.saveSettings()})),new c.Setting(i).setName("Test QMD connection").setDesc("Verify that QMD is installed and accessible.").addButton(t=>t.setButtonText("Test QMD").setCta().onClick(async()=>{t.setButtonText("Testing..."),t.setDisabled(!0);let s=await this.plugin.testQMDConnection();s.success?(new c.Notice("QMD is working correctly."),s.data&&new c.Notice(`Collection: ${s.data.collection}
Files: ${s.data.fileCount}
Embeddings: ${s.data.hasEmbeddings?"Yes":"No"}`)):new c.Notice(`QMD error: ${s.error}`,1e4),t.setButtonText("Test QMD"),t.setDisabled(!1)})),new c.Setting(i).setName("Indexing & updates").setHeading(),new c.Setting(i).setName("Debounce delay (ms)").setDesc("Wait time after file changes before updating index.").addSlider(t=>t.setLimits(1e3,12e4,1e3).setValue(this.plugin.settings.debounceMs).setDynamicTooltip().onChange(async s=>{this.plugin.settings.debounceMs=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Enable periodic updates").setDesc("Automatically run index updates on a timer.").addToggle(t=>t.setValue(this.plugin.settings.enablePeriodicUpdates).onChange(async s=>{this.plugin.settings.enablePeriodicUpdates=s,await this.plugin.saveSettings(),this.plugin.setupPeriodicUpdates()})),new c.Setting(i).setName("Periodic update interval (minutes)").setDesc("How often to run automatic index updates.").addSlider(t=>t.setLimits(5,120,5).setValue(this.plugin.settings.periodicUpdateMinutes).setDynamicTooltip().onChange(async s=>{this.plugin.settings.periodicUpdateMinutes=s,await this.plugin.saveSettings(),this.plugin.setupPeriodicUpdates()})),new c.Setting(i).setName("Search behavior").setHeading(),new c.Setting(i).setName("Default search mode").setDesc("Primary search method. Semantic uses AI embeddings, keyword uses BM25.").addDropdown(t=>t.addOption("semantic","Semantic (AI)").addOption("keyword","Keyword (BM25)").setValue(this.plugin.settings.defaultSearchMode).onChange(async s=>{this.plugin.settings.defaultSearchMode=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Fallback on semantic failure").setDesc("Use keyword search if semantic search fails or has no embeddings.").addToggle(t=>t.setValue(this.plugin.settings.fallbackOnSemanticFailure).onChange(async s=>{this.plugin.settings.fallbackOnSemanticFailure=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Fallback on zero results").setDesc("Use keyword search if semantic search returns no results.").addToggle(t=>t.setValue(this.plugin.settings.fallbackOnZeroResults).onChange(async s=>{this.plugin.settings.fallbackOnZeroResults=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Show embeddings banner").setDesc("Display a notice when semantic search is unavailable due to missing embeddings.").addToggle(t=>t.setValue(this.plugin.settings.showEmbeddingsBanner).onChange(async s=>{this.plugin.settings.showEmbeddingsBanner=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Auto-generate embeddings").setDesc("Automatically generate embeddings when missing. First run downloads ~3GB of local AI models.").addToggle(t=>t.setValue(this.plugin.settings.autoGenerateEmbeddings).onChange(async s=>{this.plugin.settings.autoGenerateEmbeddings=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("User interface").setHeading(),new c.Setting(i).setName("Show ribbon icon").setDesc("Display QMD search icon in the left sidebar.").addToggle(t=>t.setValue(this.plugin.settings.enableRibbonIcon).onChange(async s=>{this.plugin.settings.enableRibbonIcon=s,await this.plugin.saveSettings(),this.plugin.setupRibbonIcon()})),new c.Setting(i).setName("Enable search pane").setDesc("Allow opening QMD search as a persistent sidebar pane.").addToggle(t=>t.setValue(this.plugin.settings.enableSearchPane).onChange(async s=>{this.plugin.settings.enableSearchPane=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Show scores in results").setDesc("Display numeric relevance scores in search results.").addToggle(t=>t.setValue(this.plugin.settings.showScoresInResults).onChange(async s=>{this.plugin.settings.showScoresInResults=s,await this.plugin.saveSettings()})),new c.Setting(i).setName("Diagnostics").setHeading();let e=i.createDiv({cls:"qmd-diagnostics"});this.renderDiagnostics(e),new c.Setting(i).setName("Actions").setHeading(),new c.Setting(i).setName("Update index now").setDesc("Manually trigger an index update.").addButton(t=>t.setButtonText("Update index").onClick(async()=>{t.setButtonText("Updating..."),t.setDisabled(!0),await this.plugin.updateIndexNow(),t.setButtonText("Update index"),t.setDisabled(!1)})),new c.Setting(i).setName("Generate embeddings").setDesc("Build AI embeddings for semantic search. This may take a while for large vaults.").addButton(t=>t.setButtonText("Generate embeddings").onClick(async()=>{t.setButtonText("Generating..."),t.setDisabled(!0),await this.plugin.generateEmbeddings(!1),t.setButtonText("Generate embeddings"),t.setDisabled(!1)})),new c.Setting(i).setName("Force rebuild embeddings").setDesc("Rebuild all embeddings from scratch. Use if embeddings seem corrupted.").addButton(t=>t.setButtonText("Force rebuild").setWarning().onClick(async()=>{t.setButtonText("Rebuilding..."),t.setDisabled(!0),await this.plugin.generateEmbeddings(!0),t.setButtonText("Force rebuild"),t.setDisabled(!1)})),new c.Setting(i).setName("Ensure collection exists").setDesc("Create the QMD collection if it doesn't exist.").addButton(t=>t.setButtonText("Ensure collection").onClick(async()=>{t.setButtonText("Checking..."),t.setDisabled(!0),await this.plugin.ensureCollection(),t.setButtonText("Ensure collection"),t.setDisabled(!1)}))}renderDiagnostics(i){i.empty();let e=this.plugin.settings,t=[{label:"Last index update",value:e.lastIndexUpdateTime||"Never"},{label:"Last embedding run",value:e.lastEmbeddingRunTime||"Never"},{label:"Last search mode",value:e.lastSearchMode||"N/A"},{label:"Last error",value:e.lastError||"None"}];for(let s of t){let n=i.createDiv({cls:"qmd-diagnostic-row"});n.createSpan({text:s.label+":",cls:"qmd-diagnostic-label"}),n.createSpan({text:s.value,cls:"qmd-diagnostic-value"})}}};var R=require("fs"),Q=require("os"),U=["qmd",`${(0,Q.homedir)()}/.bun/bin/qmd`,"/usr/local/bin/qmd","/opt/homebrew/bin/qmd","/usr/bin/qmd"],w=class extends d.Plugin{constructor(e,t){super(e,t);this.settings=y;this.qmd=null;this.ribbonIcon=null;this.periodicUpdateInterval=null;this.debouncedUpdate=null;this.hasPendingChanges=!1}async onload(){if(await this.loadSettings(),!this.isDesktop()){new d.Notice("QMD requires desktop filesystem access \u2014 plugin disabled on mobile.");return}this.initializeQMD(),this.registerView(p,e=>new v(e,this.qmd,this.settings,{onResultSelect:t=>{this.openSearchResult(t)},onSearchModeUsed:(t,s)=>{this.recordSearchMode(t,s)},onError:t=>{this.recordError(t)}})),this.registerCommands(),this.setupRibbonIcon(),this.setupFileWatchers(),this.setupPeriodicUpdates(),this.addSettingTab(new S(this.app,this)),this.ensureCollectionOnLoad()}onunload(){this.periodicUpdateInterval!==null&&window.clearInterval(this.periodicUpdateInterval)}isDesktop(){return typeof this.app.vault.adapter.getBasePath=="function"}getVaultPath(){return this.app.vault.adapter.getBasePath()}findQMDBinary(){if(this.settings.qmdBinaryPath!=="qmd")return this.settings.qmdBinaryPath;for(let e of U)if(e!=="qmd")try{if((0,R.existsSync)(e))return e}catch(t){}return this.settings.qmdBinaryPath}initializeQMD(){let e=this.getVaultPath(),t=this.settings.collectionName||D(this.app.vault.getName()),s=this.findQMDBinary();s!==this.settings.qmdBinaryPath&&(this.settings.qmdBinaryPath=s,this.saveSettings()),this.qmd=new f(s,t,this.settings.indexName,e)}registerCommands(){this.addCommand({id:"search",name:"Search",callback:()=>this.openSearchModal()}),this.addCommand({id:"open-search-pane",name:"Open search pane",checkCallback:e=>this.settings.enableSearchPane?(e||this.openSearchPane(),!0):!1}),this.addCommand({id:"update-index",name:"Update index now",callback:()=>{this.updateIndexNow()}}),this.addCommand({id:"generate-embeddings",name:"Generate embeddings",callback:()=>{this.generateEmbeddings(!1)}}),this.addCommand({id:"force-rebuild-embeddings",name:"Force rebuild embeddings",callback:()=>{this.generateEmbeddings(!0)}}),this.addCommand({id:"ensure-collection",name:"Ensure collection",callback:()=>{this.ensureCollection()}})}setupRibbonIcon(){this.ribbonIcon&&(this.ribbonIcon.remove(),this.ribbonIcon=null),this.settings.enableRibbonIcon&&(this.ribbonIcon=this.addRibbonIcon("search","QMD search",()=>this.openSearchModal()))}setupFileWatchers(){this.debouncedUpdate=(0,d.debounce)(()=>{this.triggerIndexUpdate()},this.settings.debounceMs,!0);let e=t=>{var s;t instanceof d.TFile&&t.extension==="md"&&(this.hasPendingChanges=!0,(s=this.debouncedUpdate)==null||s.call(this))};this.registerEvent(this.app.vault.on("create",e)),this.registerEvent(this.app.vault.on("modify",e)),this.registerEvent(this.app.vault.on("delete",e)),this.registerEvent(this.app.vault.on("rename",e))}setupPeriodicUpdates(){if(this.periodicUpdateInterval!==null&&(window.clearInterval(this.periodicUpdateInterval),this.periodicUpdateInterval=null),this.settings.enablePeriodicUpdates){let e=this.settings.periodicUpdateMinutes*60*1e3;this.periodicUpdateInterval=window.setInterval(()=>{this.triggerIndexUpdate()},e)}}async triggerIndexUpdate(){if(!this.qmd||!this.hasPendingChanges)return;this.hasPendingChanges=!1;let e=await this.qmd.updateIndex();if(e.success){this.settings.lastIndexUpdateTime=new Date().toISOString(),await this.saveSettings();let t=await this.qmd.generateEmbeddings(!1);t.success?(this.settings.lastEmbeddingRunTime=new Date().toISOString(),await this.saveSettings()):t.error&&this.recordError(t.error)}else e.error&&this.recordError(e.error)}async ensureCollectionOnLoad(){var t,s,n,r;if(!this.qmd)return;let e=await this.qmd.testConnection();if(!e.success){if((t=e.error)!=null&&t.toLowerCase().includes("not found")&&((s=e.error)!=null&&s.toLowerCase().includes("binary"))){new d.Notice("QMD binary not found. Install QMD and configure the path in settings.",15e3),this.recordError(e.error);return}this.recordError(e.error||"Unknown QMD error");return}(n=e.data)!=null&&n.hasCollection||await this.ensureCollection(),(r=e.data)!=null&&r.hasEmbeddings||await this.autoGenerateEmbeddingsIfNeeded()}async autoGenerateEmbeddingsIfNeeded(){var n;if(!this.qmd||!this.settings.autoGenerateEmbeddings)return;let e=await this.qmd.testConnection();if(e.success&&((n=e.data)!=null&&n.hasEmbeddings))return;let t=!this.settings.lastEmbeddingRunTime;t?new d.Notice("QMD: generating embeddings for the first time. This will download ~300MB of AI models and may take several minutes. Semantic search will be available when complete.",15e3):new d.Notice("Generating embeddings for semantic search...",5e3);let s=await this.qmd.generateEmbeddings(!1);s.success?(this.settings.lastEmbeddingRunTime=new Date().toISOString(),await this.saveSettings(),new d.Notice("Embeddings generated. Semantic search is now available.")):(t&&new d.Notice(`Failed to generate embeddings: ${s.error}. Check that QMD is properly installed and you have internet access for model download.`,15e3),this.recordError(s.error||"Failed to auto-generate embeddings"))}async loadSettings(){this.settings=Object.assign({},y,await this.loadData())}async saveSettings(){if(await this.saveData(this.settings),this.qmd){let e=this.settings.collectionName||D(this.app.vault.getName());this.qmd.updateConfig(this.settings.qmdBinaryPath,e,this.settings.indexName)}}async testQMDConnection(){return this.qmd?this.qmd.testConnection():{success:!1,error:"QMD not initialized"}}async updateIndexNow(){if(!this.qmd){new d.Notice("QMD not initialized");return}new d.Notice("Updating QMD index...");let e=await this.qmd.updateIndex();e.success?(this.settings.lastIndexUpdateTime=new Date().toISOString(),await this.saveSettings(),new d.Notice("QMD index updated successfully.")):(new d.Notice(`Failed to update index: ${e.error}`,1e4),this.recordError(e.error||"Unknown error"))}async generateEmbeddings(e){if(!this.qmd){new d.Notice("QMD not initialized");return}let t=e?"Rebuilding":"Generating";new d.Notice(`${t} embeddings... this may take a while.`);let s=await this.qmd.generateEmbeddings(e);s.success?(this.settings.lastEmbeddingRunTime=new Date().toISOString(),await this.saveSettings(),new d.Notice("Embeddings generated successfully.")):(new d.Notice(`Failed to generate embeddings: ${s.error}`,1e4),this.recordError(s.error||"Unknown error"))}async ensureCollection(){if(!this.qmd){new d.Notice("QMD not initialized");return}new d.Notice("Ensuring QMD collection exists...");let e=await this.qmd.ensureCollection(this.settings.fileMask);e.success?new d.Notice("QMD collection is ready."):(new d.Notice(`Failed to ensure collection: ${e.error}`,1e4),this.recordError(e.error||"Unknown error"))}openSearchModal(){if(!this.qmd){new d.Notice("QMD not initialized");return}new b(this.app,this.qmd,this.settings,{onResultSelect:t=>{this.openSearchResult(t)},onSearchModeUsed:(t,s)=>{this.recordSearchMode(t,s)},onError:t=>{this.recordError(t)}}).open()}async openSearchPane(){let e=this.app.workspace.getLeavesOfType(p);if(e.length>0)await this.app.workspace.revealLeaf(e[0]);else{let t=this.app.workspace.getRightLeaf(!1);t&&(await t.setViewState({type:p,active:!0}),await this.app.workspace.revealLeaf(t))}}async openSearchResult(e){if(e.file){let t=this.app.workspace.getLeaf(!1);await t.openFile(e.file),e.line!==void 0&&e.line>0&&setTimeout(()=>{let s=t.view;if(s&&"editor"in s){let n=s.editor,r=e.line-1;n.setCursor({line:r,ch:0}),n.scrollIntoView({from:{line:r},to:{line:r}})}},100)}else new d.Notice(`File not found: ${e.path}`)}async recordSearchMode(e,t){this.settings.lastSearchMode=e,await this.saveSettings()}async recordError(e){this.settings.lastError=`${new Date().toISOString()}: ${e}`,await this.saveSettings()}};