lllin000_PaperForge/paperforge/plugin/main.js

38 lines
137 KiB
JavaScript
Raw Normal View History

"use strict";var ut=Object.create;var Se=Object.defineProperty;var ht=Object.getOwnPropertyDescriptor;var gt=Object.getOwnPropertyNames;var ft=Object.getPrototypeOf,_t=Object.prototype.hasOwnProperty;var mt=(h,g)=>{for(var e in g)Se(h,e,{get:g[e],enumerable:!0})},Ue=(h,g,e,t)=>{if(g&&typeof g=="object"||typeof g=="function")for(let s of gt(g))!_t.call(h,s)&&s!==e&&Se(h,s,{get:()=>g[s],enumerable:!(t=ht(g,s))||t.enumerable});return h};var j=(h,g,e)=>(e=h!=null?ut(ft(h)):{},Ue(g||!h||!h.__esModule?Se(e,"default",{value:h,enumerable:!0}):e,h)),yt=h=>Ue(Se({},"__esModule",{value:!0}),h);var Et={};mt(Et,{default:()=>Re});module.exports=yt(Et);var V=require("obsidian"),I=j(require("fs")),me=j(require("path")),se=require("child_process");var pe="paperforge-status",xe="paperforge",Ge='<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path><line x1="8" y1="7" x2="16" y2="7"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>',W=[{id:"paperforge-sync",title:"Sync Library",desc:"Pull new references from Zotero and generate literature notes",icon:"\u21BB",cmd:"sync",okMsg:"Sync complete"},{id:"paperforge-ocr",title:"Run OCR",desc:"Extract full text and figures from PDFs via PaddleOCR",icon:"\u229E",cmd:"ocr",okMsg:"OCR started"},{id:"paperforge-doctor",title:"Run Doctor",desc:"Verify PaperForge setup \u2014 check configs, Zotero, paths, and index health",icon:"\u2695",cmd:"doctor",okMsg:"Doctor complete"},{id:"paperforge-repair",title:"Repair Issues",desc:"Fix three-way state divergence, path errors, and rebuild index",icon:"\u21BA",cmd:"repair",args:["--fix","--fix-paths"],okMsg:"Repair complete"},{id:"paperforge-ocr-redo",title:"\u91CD\u505AOCR",desc:"Re-run OCR for papers marked ocr_redo: true",icon:"\u21BA",cmd:"ocr",args:["redo"],okMsg:"OCR redo started"}],Ee={vault_path:"",setup_complete:!1,auto_update:!0,auto_update_on_startup:!0,agent_platform:"opencode",language:"",paddleocr_api_key:"",zotero_data_dir:"",python_path:"",features:{memory_layer:!0,vector_db:!1},selected_skill_platform:"opencode",vector_db_api_key:"",vector_db_api_base:"",vector_db_api_model:"text-embedding-3-small",frozen_skills:{},system_dir:"",resources_dir:"",literature_dir:"",base_dir:""};function Xe(h,g){if(!g||!g.note_path)return g;let e=h.vault.getAbstractFileByPath(g.note_path);if(!e)return g;let t=h.metadataCache.getFileCache(e),s=t&&t.frontmatter;if(!s)return g;let n={...g};for(let a of["do_ocr","analyze","ocr_status","deep_reading_status"])Object.prototype.hasOwnProperty.call(s,a)&&(n[a]=s[a]);return n}function Oe(h,g){return h&&{...h,...g}}var Ie={en:{action_running:"Running ",api_key_missing:"Missing",api_key_set:"Entered",btn_install:"Open Setup Wizard",btn_install_desc:"Check whether the environment is ready, then open the step-by-step setup wizard",btn_reconfig:"Reconfigure",btn_reconfig_desc:"Open the setup wizard again to change directories, platform, or API keys",btn_validate:"Validate",check_bbt_fail:"Not detected",check_bbt_ok:"Installed",check_python_fail:"Not found",check_python_ok:"Ready",check_zotero_fail:"Not detected",check_zotero_ok:"Found",complete_export_path:"Save Better BibTeX JSON exports into:",complete_next:"Recommended next steps",complete_step1:"Open Dashboard",complete_step1_desc:'Press Ctrl+P and run "PaperForge: Open Main Panel", or click the PaperForge icon in the left sidebar.',complete_step2:"Sync Literature",complete_step2_desc:"In the main panel, click Sync Library to bring papers from Zotero into Obsidian and generate notes.",complete_step3:"Run OCR",complete_step3_desc:"In the Obsidian Base view, mark do_ocr:true on papers, then run OCR in the main panel.",complete_step4:"Configure Better BibTeX Auto-export",complete_step4_desc:'In Zotero, right-click the library or collection you want to sync -> Export -> Better BibTeX JSON -> enable "Keep updated".',complete_summary:"Saved Configuration",complete_titl
`+d.message,exitCode:-1,elapsed:Date.now()-o})})})}function Ne(){if(Qe)return Me;Qe=!0;try{let h;if(process.platform==="win32"){let g=process.env.ComSpec||"C:\\Windows\\System32\\cmd.exe";h=(0,ne.execFileSync)(g,["/c","where","git"],{timeout:5e3,windowsHide:!0,encoding:"utf-8"})}else h=(0,ne.execFileSync)("which",["git"],{timeout:5e3,encoding:"utf-8"});if(h){let g=h.split(`
`)[0].trim();g&&(Me=X.dirname(g))}}catch(h){}return Me}function de(){let h={...process.env},g=process.platform,e=et.homedir(),t=[],s=Ne();s&&t.push(s),g==="darwin"?t.push("/opt/homebrew/bin","/usr/local/bin","/usr/bin",`${e}/.local/bin`):g==="linux"&&t.push("/usr/local/bin","/usr/bin",`${e}/.local/bin`);let n=h.PATH||"";return h.PATH=[...t,n].filter(Boolean).join(X.delimiter),h}function nt(h){return String(h).toLowerCase().replace(/[^a-z0-9]/g,"").includes("betterbibtex")}function ze(h){if(!h)return!1;try{if(!Y.existsSync(h))return!1;for(let g of Y.readdirSync(h))if(nt(g))return!0}catch(g){}return!1}function Ce(h){if(!h)return!1;try{if(!Y.existsSync(h))return!1;for(let g of Y.readdirSync(h)){let e=X.join(h,g,"extensions");try{if(!Y.existsSync(e))continue;for(let t of Y.readdirSync(e))if(nt(t))return!0}catch(t){}}}catch(g){}return!1}var ue=j(require("fs")),M=j(require("path")),at=require("child_process"),Q=null;function vt(h,g){let e=g||ue,t=M.join(h,"paperforge.json"),s={system_dir:"System",resources_dir:"Resources",literature_dir:"Literature",base_dir:"Bases"};try{if(!e.existsSync(t))return{...s,_warning:"paperforge.json not found; using defaults"};let n=e.readFileSync(t,"utf-8"),a=JSON.parse(n),r=a.vault_config||{};return{system_dir:r.system_dir||a.system_dir||s.system_dir,resources_dir:r.resources_dir||a.resources_dir||s.resources_dir,literature_dir:r.literature_dir||a.literature_dir||s.literature_dir,base_dir:r.base_dir||a.base_dir||s.base_dir,_warning:null}}catch(n){return console.warn("PaperForge: Failed to read paperforge.json, using defaults",n),{...s,_warning:"paperforge.json invalid; using defaults"}}}function ee(h,g){let e=vt(h,g),t=M.join(h,e.system_dir,"PaperForge");return{vault:h,systemDir:t,indexesDir:M.join(t,"indexes"),logsDir:M.join(t,"logs"),dbPath:M.join(t,"indexes","paperforge.db"),memoryStatePath:M.join(t,"indexes","memory-runtime-state.json"),vectorStatePath:M.join(t,"indexes","vector-runtime-state.json"),healthStatePath:M.join(t,"indexes","runtime-health.json"),buildStatePath:M.join(t,"indexes","vector-build-state.json"),orphanStatePath:M.join(t,"indexes","sync-orphan-state.json"),exportsDir:M.join(t,"exports"),ocrDir:M.join(t,"ocr"),pluginDataPath:M.join(h,".obsidian","plugins","paperforge","data.json"),pfJsonPath:M.join(h,"paperforge.json"),configWarning:e._warning}}function $e(h){try{return ue.existsSync(h)?JSON.parse(ue.readFileSync(h,"utf-8")):null}catch(g){return null}}function xt(h){let g=ee(h);return $e(g.memoryStatePath)}function ke(h){let g=ee(h);return $e(g.vectorStatePath)}function je(h){let g=ee(h);return $e(g.healthStatePath)}function it(h){var e;let g=je(h);return!!(g&&((e=g.summary)==null?void 0:e.status)==="ok")}function Fe(h){let g=xt(h);return!g||g.paper_count_db===0?"DB not found. Run paperforge memory build.":"Papers: "+g.paper_count_db+" | "+(g.fresh?"fresh":"stale")}function fe(h){let g=ke(h);return g?g.healthy===!1?"Vector index unreadable - rebuild required":"Chunks: "+g.chunk_count+" | "+g.model+" | "+g.mode:"Status unavailable"}function ae(h,g){if(Q)return Q;if(g&&g.python_path&&g.python_path.trim()){let s=g.python_path.trim();if(ue.existsSync(s))return Q={path:s,source:"manual",extraArgs:[]},Q}let e=[M.join(h,".paperforge-test-venv","Scripts","python.exe"),M.join(h,".venv","Scripts","python.exe"),M.join(h,"venv","Scripts","python.exe")];for(let s=0;s<e.length;s++)if(ue.existsSync(e[s]))return Q={path:e[s],source:"auto-detected",extraArgs:[]},Q;let t=[{path:"py",extraArgs:["-3"]},{path:"python",extraArgs:[]},{path:"python3",extraArgs:[]}];for(let s=0;s<t.length;s++)try{let n=t[s],a=(0,at.execFileSync)(n.path,n.extraArgs.concat(["--version"]),{encoding:"utf-8",timeout:5e3,windowsHide:!0});if(a&&a.toLowerCase().indexOf("python")!==-1)return Q={path:n.path,source:"auto-detected",extraArgs:n.extraArgs},Q}catch(n){}return Q={path:"python",source:"auto-detected",extraArgs:[]},Q}function He(h,g,e){return!h||typeof h!="object"||!Object.prototype.hasOwnProperty.call(h,g)?!!e:!!h[g]}function ot(h,g,e){let t=!He(h,g,e);return h&&typeof h=="object"&&(h[g]=t),t}var Z=require("obsidi
`),this._log(i("install_validating"));let t=this.plugin.settings,s=this._validate();if(s.length>0){this._log(i("validate_fail")+":"),s.forEach(d=>this._log(" \u2717 "+d)),e.disabled=!1,e.textContent=i("install_btn_retry");return}let n=(d,_={})=>new Promise((y,x)=>{let{path:f,extraArgs:E=[]}=A(t.vault_path.trim(),this.plugin.settings,void 0,void 0),S=(0,Pe.spawn)(f,[...E,...d],{cwd:t.vault_path.trim(),env:de(),timeout:12e4,..._}),b="",m="";S.stdout.on("data",v=>{let k=v.toString("utf-8");b+=k,_.logStdout&&this._processSetupOutput(k)}),S.stderr.on("data",v=>{let k=v.toString("utf-8");m+=k,this._log("[stderr] "+k.trim())}),S.on("close",v=>{v===0?y({stdout:b,stderr:m}):x(new Error(m.trim()||b.trim()||`exit code ${v}`))}),S.on("error",v=>x(v))}),a=["-m","paperforge","--vault",t.vault_path.trim(),"setup","--headless","--system-dir",t.system_dir.trim(),"--resources-dir",t.resources_dir.trim(),"--literature-dir",t.literature_dir.trim(),"--base-dir",t.base_dir.trim(),"--agent",t.agent_platform||"opencode"];t.zotero_data_dir&&t.zotero_data_dir.trim()&&a.push("--zotero-data",t.zotero_data_dir.trim()),t.paddleocr_api_key&&t.paddleocr_api_key.trim()&&a.push("--paddleocr-key",t.paddleocr_api_key.trim());try{let d=!0;try{await n(["-c","import paperforge"])}catch(_){d=!1}if(!d){this._log(i("install_bootstrapping"));let _=this.plugin.manifest.version;this._log(`[install] Trying PyPI: pip install paperforge==${_}`);let y=["-m","pip","install","--upgrade"];process.platform!=="win32"&&y.push("--user"),y.push(`paperforge==${_}`);try{await n(y,{logStdout:!0})}catch(x){this._log(`[install] PyPI failed, falling back to git: git+https://...@v${_}`),console.warn("[PaperForge] PyPI install failed, falling back to git:",(r=x.message)==null?void 0:r.slice(0,200));let f=["-m","pip","install","--upgrade"];process.platform!=="win32"&&f.push("--user"),f.push(`git+https://github.com/LLLin000/PaperForge.git@v${_}`),await n(f,{logStdout:!0})}}await n(a,{logStdout:!0,env:de()}),this._log(i("install_complete")),t.setup_complete=!0,await this.plugin.saveSettings(),setTimeout(()=>{this._step=5,this._render()},800)}catch(d){console.error("PaperForge setup failed:",d.message);let _=this._formatSetupError(d.message);this._log(i("install_failed")+_);let y=(o=this._installLog.parentElement)==null?void 0:o.createEl("button",{cls:"paperforge-copy-diag-btn",text:i("error_copy_diagnostic")||"Copy diagnostic"});if(y){let x=d.message,f=((c=(l=this.plugin)==null?void 0:l.settings)==null?void 0:c.python_path)||"auto",E=((u=(p=this.plugin)==null?void 0:p.manifest)==null?void 0:u.version)||"?",S=process.platform+" "+process.arch,b,m;try{b=Ne()||"(not found)"}catch(F){b="(error)"}try{m=A(t.vault_path.trim(),this.plugin.settings,void 0,void 0)}catch(F){m=null}let v=(process.env.PATH||"").length,k=(process.env.PATH||"").toLowerCase().includes("git"),C=["[PaperForge Diagnostic]","Category: "+_,"Plugin version: "+E,"Python: "+f,"Resolved Python: "+((m==null?void 0:m.path)||"?"),"OS: "+S,"Vault path: "+(t.vault_path||"?"),"--- Git ---","Git dir (resolved): "+b,"PATH length: "+v+" chars","PATH contains git: "+k,"--- Raw error ---",x.slice(0,2e3)].join(`
`);y.addEventListener("click",()=>{navigator.clipboard.writeText(C).then(()=>{y.setText(i("error_copied")||"Copied!"),setTimeout(()=>{y.setText(i("error_copy_diagnostic")||"Copy diagnostic")},3e3)}).catch(()=>{new Z.Notice("[!!] Clipboard write failed",6e3)})})}e.disabled=!1,e.textContent=i("install_btn_retry")}}_log(e){this._installLog&&this._installLog.setText(this._installLog.textContent+e+`
`)}_validate(){let e=[],t=this.plugin.settings;return(!t.vault_path||!t.vault_path.trim())&&e.push(i("validate_vault")),(!t.resources_dir||!t.resources_dir.trim())&&e.push(i("validate_resources")),(!t.literature_dir||!t.literature_dir.trim())&&e.push(i("validate_notes")),(!t.base_dir||!t.base_dir.trim())&&e.push(i("validate_base")),(!t.paddleocr_api_key||!t.paddleocr_api_key.trim())&&this._log(" ! "+i("validate_key")+" "+i("optional_later")),(!t.zotero_data_dir||!t.zotero_data_dir.trim())&&this._log(" ! "+i("validate_zotero")+" "+i("optional_later")),e}_processSetupOutput(e){let t=e.split(`
`).filter(Boolean);for(let s of t)if(s.includes("[*]")||s.includes("[OK]")||s.includes("[FAIL]")){let n=s.replace(/^\[\*\].*\d+:?\s*/,"").replace(/^\[OK\]\s*/,"").replace(/^\[FAIL\]\s*/,"");this._log(" "+n)}}_formatSetupError(e){if(process.platform==="darwin"&&/No module named ['"]?paperforge/i.test(e))return"PaperForge not installed \u2014 install Python from Homebrew or python.org (Apple CLT /Library/Developer/CommandLineTools python often fails); then: python3 -m pip install --user git+https://github.com/LLLin000/PaperForge.git";let t=[{match:/pip.*not found|No module named.*pip|command not found.*pip/i,msg:"pip not found"},{match:/command not found|No such file|not recognized/i,msg:"Python not found"},{match:/resolve host|getaddrinfo.*nodename|connect ETIMEDOUT|connect ECONNREFUSED|fetch failed|Network error|ENOTFOUND|ECONNREFUSED|ECONNRESET/i,msg:"Network error"},{match:/certificate verify failed|SSL.*certificate|self.signed.cert|CERTIFICATE_VERIFY_FAILED/i,msg:"SSL certificate error"},{match:/No space left on device|disk full|ENOSPC/i,msg:"Disk full"},{match:/paperforge.*not found|cannot import|ModuleNotFoundError|No module named/i,msg:"PaperForge not installed"},{match:/permission denied|EACCES|EPERM/i,msg:"Permission denied"},{match:/ENOENT/i,msg:"Path not found"},{match:/timeout|timed out/i,msg:"Timeout"}];for(let n of t)if(n.match.test(e))return n.msg;return e.split(`
`).filter(Boolean).slice(0,3).join(" | ").slice(0,200)||"Unknown error"}_stepComplete(e){e.createEl("h2",{text:i("complete_title")});let t=e.createEl("div",{cls:"paperforge-summary"});t.createEl("div",{cls:"paperforge-summary-title",text:i("complete_summary")});let s=this.plugin.settings,n=this.app.vault.adapter.basePath,a=[{label:i("dir_vault"),val:n},{label:i("dir_resources"),val:`${n}/${s.resources_dir}`},{label:i("dir_notes"),val:`${n}/${s.resources_dir}/${s.literature_dir}`},{label:i("dir_base"),val:`${n}/${s.base_dir}`},{label:i("dir_system"),val:`${n}/${s.system_dir}`},{label:"API Key",val:s.paddleocr_api_key?i("api_key_set"):i("api_key_missing")},{label:i("field_zotero_data"),val:s.zotero_data_dir||i("not_set")}];for(let p of a){let u=t.createEl("div",{cls:"paperforge-summary-row"});u.createEl("span",{cls:"paperforge-summary-label",text:p.label}),u.createEl("span",{cls:"paperforge-summary-value",text:p.val})}let r=t.createEl("div",{cls:"paperforge-summary-row"});r.createEl("span",{cls:"paperforge-summary-label",text:"PaperForge"});let o=r.createEl("span",{cls:"paperforge-summary-value",text:"\u2014"});{let p=n,{path:u,extraArgs:d=[]}=A(p,this.plugin.settings,void 0,void 0);(0,Pe.execFile)(u,[...d,"-c","import paperforge; print(paperforge.__version__)"],{cwd:p,timeout:1e4},(_,y)=>{!_&&y&&(o.textContent="v"+y.trim())})}for(let p of a){let u=t.createEl("div",{cls:"paperforge-summary-row"});u.createEl("span",{cls:"paperforge-summary-label",text:p.label}),u.createEl("span",{cls:"paperforge-summary-value",text:p.val})}e.createEl("h3",{text:i("complete_next")});let l=e.createEl("div",{cls:"paperforge-nextsteps"}),c=[[i("complete_step4"),i("complete_step4_desc")],["",`${i("complete_export_path")} ${n}/${s.system_dir}/PaperForge/exports/`],[i("complete_step1"),i("complete_step1_desc")],[i("complete_step2"),i("complete_step2_desc")],[i("complete_step3"),i("complete_step3_desc")]];for(let[p,u]of c){let d=l.createEl("div",{cls:"paperforge-nextstep-item"});p&&d.createEl("strong",{text:p}),d.createEl("span",{text:u})}}};var De=class extends w.PluginSettingTab{constructor(e,t){super(e,t);this._saveTimeout=null;this._pfConfig=null;this._lastSyncTime=null;this._memoryStatusText=null;this._vectorDepsOk=null;this._embedStatusText=null;this._skillsCollapsed={user:!0};this._featurePanelsCollapsed={};this._advCollapsed=!0;this._refreshPending=!1;this._pythonInterpDescEl=null;this._customPathDescEl=null;this._checkEl=null;this.activeTab="setup";this.plugin=t}_refreshPfConfig(){this._pfConfig=this.plugin.readPaperforgeJson()}display(){let{containerEl:e}=this;if(e.empty(),this._refreshPfConfig(),!document.getElementById("paperforge-tab-styles")){let a=document.createElement("style");a.id="paperforge-tab-styles",a.textContent=`
.paperforge-settings-tabs { display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--background-modifier-border); }
.paperforge-settings-tab { padding: 6px 16px; border: none; background: none; cursor: pointer; border-bottom: 2px solid transparent; font-size: 14px; color: var(--text-muted); }
.paperforge-settings-tab--active { color: var(--text-accent); border-bottom-color: var(--text-accent); }
.paperforge-tab-content { display: none; }
.paperforge-tab-content--active { display: block; }
.paperforge-skills-collapse-header { display: flex !important; align-items: center; cursor: pointer; padding: 6px 0 !important; margin: 0 !important; }
.paperforge-skills-collapse-header h4 { margin: 0 !important; }
.paperforge-skills-collapse-content { margin: 0 !important; padding: 0 !important; }
.paperforge-skills-group { margin-bottom: 10px; }
.paperforge-skills-group:last-child { margin-bottom: 0; }
.vertical-tab-content-container { overflow-y: scroll !important; }
`,document.head.appendChild(a)}let t=e.createDiv({cls:"paperforge-settings-tabs"}),s=[{id:"setup",label:i("tab_setup")||"Installation"},{id:"features",label:i("tab_features")||"Features"}],n={};s.forEach(a=>{t.createEl("button",{cls:"paperforge-settings-tab"+(a.id===this.activeTab?" paperforge-settings-tab--active":""),text:a.label}).addEventListener("click",()=>{this.activeTab=a.id,this.display()})}),s.forEach(a=>{n[a.id]=e.createDiv({cls:"paperforge-tab-content"+(a.id===this.activeTab?" paperforge-tab-content--active":"")})}),this.activeTab==="setup"?this._renderSetupTab(n.setup):this._renderFeaturesTab(n.features)}_renderSetupTab(e){let t=this.app.vault.adapter.basePath;this.plugin.settings.vault_path||(this.plugin.settings.vault_path=t,this._debouncedSave()),this.plugin.settings.setup_complete&&(O.existsSync(z.join(t,"paperforge.json"))||(this.plugin.settings.setup_complete=!1,this._debouncedSave())),e.createEl("h2",{text:i("header_title")||"PaperForge"}),e.createEl("p",{text:i("desc"),cls:"paperforge-settings-desc"});let n=e.createEl("div",{cls:"paperforge-setup-bar"}).createEl("span",{cls:"paperforge-setup-label"});this.plugin.settings.setup_complete?(n.setText(i("setup_done")),n.addClass("paperforge-setup-done")):(n.setText(i("setup_pending")),n.addClass("paperforge-setup-pending"));let a=this.app.vault.adapter.basePath,r=A(a,this.plugin.settings,void 0,void 0),o=r.path,l=this.plugin.settings._python_path_stale?"stale":r.source,c=new w.Setting(e).setName(i("field_python_interp")).setDesc(this._getPythonDesc(o,l));this._pythonInterpDescEl=c.descEl;let p=new w.Setting(e).setName(i("field_python_custom")).setDesc("");this._customPathDescEl=p.descEl,p.addText(b=>{b.setPlaceholder("e.g. C:\\Python310\\python.exe").setValue(this.plugin.settings.python_path||"").onChange(m=>{if(this.plugin.settings.python_path=m,this.plugin.saveSettings(),m&&m.trim()){let C=O.existsSync(m.trim());this.plugin.settings._python_path_stale=!C}else this.plugin.settings._python_path_stale=!1;let v=A(this.app.vault.adapter.basePath,this.plugin.settings,void 0,void 0),k=this.plugin.settings._python_path_stale?"stale":v.source;this._pythonInterpDescEl&&(this._pythonInterpDescEl.textContent=this._getPythonDesc(v.path,k))})}),p.addButton(b=>{b.setButtonText(i("btn_validate")).onClick(()=>this._validatePythonOverride())}),e.createEl("h3",{text:i("runtime_health")}),e.createEl("p",{text:i("runtime_health_desc"),cls:"paperforge-settings-desc"});let u=new w.Setting(e).setName("PaperForge").setDesc(i("runtime_health_checking")),d=u.descEl.createEl("span",{cls:"paperforge-runtime-badge"}),_=null;u.addButton(b=>{_=b,b.setButtonText(i("runtime_health_sync")).setDisabled(!0).onClick(()=>this._syncRuntime(b))});{let b=this.app.vault.adapter.basePath,{path:m,extraArgs:v=[]}=A(b,this.plugin.settings,void 0,void 0),k=this.plugin.manifest.version||"?";(0,H.execFile)(m,[...v,"-c","import paperforge; print(paperforge.__version__)"],{cwd:b,timeout:1e4},(C,F)=>{let R=this.plugin.settings.setup_complete,B=!C&&F?F.trim():null,N=B?`${i("runtime_health_plugin_ver").replace("{0}",k)} \u2192 ${i("runtime_health_package_ver").replace("{0}",B)}`:R?`Plugin v${k} \u2192 Python package not installed. Click "Sync Runtime" to install.`:`Plugin v${k} \u2192 Not configured. Please open the setup wizard first.`;u.setDesc(N),B===k?(d.setText(i("runtime_health_match")),d.className="paperforge-runtime-badge match",_&&_.setDisabled(!0)):B?(d.setText(i("runtime_health_mismatch")),d.className="paperforge-runtime-badge mismatch",_&&_.setDisabled(!1)):(d.setText(R?"Not installed":"Setup needed"),d.className="paperforge-runtime-badge missing",_&&_.setDisabled(!1))})}e.createEl("h3",{text:i("section_prep")}),e.createEl("p",{text:i("section_prep_desc"),cls:"paperforge-settings-desc"});let y=e.createEl("div",{cls:"paperforge-guide"}),x=[["prep_python","prep_python_desc"],["prep_zotero","prep_zotero_desc"],["prep_bbt","prep_bbt_desc"],["prep_key","prep_key_desc"]];for(let[b,m]of x){let v=y.createEl("div",{cls:"paperforge-guide-item"});v.createEl("strong",{text:i(b)}),v.createEl("sp
`),R=F.findIndex(K=>/^description:/.test(K)),B="";if(R>=0){let K=F[R].match(/^description:\s*(.+)$/);if(K&&K[1]&&K[1]!==">"&&K[1]!=="|-"&&K[1]!=="|")B=K[1].trim();else{for(let J=R+1;J<F.length&&(/^\s{2,}/.test(F[J])||F[J].trim()==="");J++)B+=F[J].trim()+" ";B=B.trim()}}let N=k.match(/^source:\s*(.+)$/m),re=k.match(/^disable-model-invocation:\s*(.+)$/m),L=k.match(/^version:\s*(.+)$/m),q={name:C?C[1].trim():m.name,desc:B,source:N?N[1].trim():"user",disabled:re&&re[1].trim()==="true",version:L?L[1].trim():"",path:v,content:k,dirName:m.name};q.source==="paperforge"?l.push(q):c.push(q)});let p=e.createEl("div");p.style.cssText="background:var(--background-secondary); border-radius:8px; padding:12px 12px 10px; margin:8px 0 16px;";let u=(m,v,k)=>{if(v.length===0)return;let C=p.createEl("div",{cls:"paperforge-skills-group"}),F=C.createEl("div",{cls:"paperforge-skills-collapse-header"}),R=C.createEl("div",{cls:"paperforge-skills-collapse-content"}),B=F.createEl("span",{text:"\u25BC",cls:"paperforge-skills-arrow"});B.style.cssText="display:inline-block; font-size:10px; margin-right:6px; transition:transform 0.2s; transform:rotate(0deg);",F.createEl("h4",{text:`${m} (${v.length})`,cls:"paperforge-skills-subheader"}),v.forEach(L=>{let q=L.name+(L.version?" v"+L.version:""),K=k?" [system]":" [user]",J=L.desc||"",oe=new w.Setting(R).setName(q+K).setDesc(J);oe.settingEl.style.opacity=L.disabled?"0.4":"1",oe.addToggle(ge=>{ge.setValue(!L.disabled).onChange(ye=>{let le=!ye,be=L.content.match(/^disable-model-invocation:\s*(.+)$/m)?L.content.replace(/^disable-model-invocation:\s*.+$/m,`disable-model-invocation: ${le}`):L.content.replace(/^(---\r?\n)/,`$1disable-model-invocation: ${le}
`);O.writeFileSync(L.path,be,"utf-8"),L.disabled=le,L.content=be,oe.settingEl.style.opacity=L.disabled?"0.4":"1"})})});let N=k?"system":"user";(this._skillsCollapsed[N]||!1)&&(R.style.display="none",B.style.transform="rotate(-90deg)"),F.addEventListener("click",()=>{R.style.display!=="none"?(R.style.display="none",B.style.transform="rotate(-90deg)"):(R.style.display="",B.style.transform="rotate(0deg)"),this._skillsCollapsed[N]=R.style.display==="none"})};u("System Skills",l,!0),u("User Skills",c,!1),l.length===0&&c.length===0&&p.createEl("p",{text:`No skills found in ${n[r]}. Run setup to deploy skills.`,cls:"setting-item-description"}),this._advCollapsed===void 0&&(this._advCollapsed=!0);let d=e.createEl("div",{cls:"paperforge-collapsible-header"});d.style.cssText="cursor:pointer; display:flex; align-items:center; gap:8px; padding:8px 0; user-select:none;";let _=d.createEl("span",{text:"\u25B6",cls:"paperforge-collapsible-arrow"});_.style.cssText="display:inline-block; transition:transform 0.2s; font-size:10px; transform:"+(this._advCollapsed?"rotate(0deg)":"rotate(90deg)")+";";let y=d.createEl("span",{text:"Advanced"});y.style.cssText="font-size:16px; font-weight:700; line-height:1.4;";let x=d.createEl("span",{text:"Memory + Vector DB + Embedding"});x.style.cssText="font-size:12px; color:var(--text-muted); margin-left:10px;";let f=e.createEl("div",{cls:"paperforge-collapsible-content"});f.style.display=this._advCollapsed?"none":"",d.addEventListener("click",()=>{this._advCollapsed=!this._advCollapsed,f.style.display=this._advCollapsed?"none":"",_.style.transform=this._advCollapsed?"rotate(0deg)":"rotate(90deg)"}),f.createEl("h4",{text:"Memory Layer"});let E=f.createEl("div",{cls:"paperforge-desc-box"});E.style.cssText="padding:8px 12px; margin:0 0 12px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",E.setText(i("feat_memory_desc"));let S=f.createEl("div",{cls:"paperforge-memory-status"});S.style.cssText="display:flex; align-items:center; padding:8px 12px; margin:8px 0; background:var(--background-secondary); border-radius:4px;";let b=this.app.vault.adapter.basePath;this.plugin._lastSyncTime&&!this._lastSyncTime&&(this._lastSyncTime=this.plugin._lastSyncTime),this._memoryStatusText===null&&(this._memoryStatusText=Fe(b)),this._renderMemoryStatusText(S,this._memoryStatusText,this._lastSyncTime),this._renderVectorSection(f)}_renderVectorSection(e){var l;e.createEl("h4",{text:"Vector Database"}),this.plugin.settings.features||(this.plugin.settings.features={memory_layer:!0,vector_db:!1});let t=e.createEl("div",{cls:"paperforge-desc-box"});if(t.style.cssText="padding:8px 12px; margin:0 0 8px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",t.setText(i("feat_vector_desc")),new w.Setting(e).setName(i("feat_vector_enable")).setDesc(i("feat_vector_enable_desc")).addToggle(c=>{c.setValue(!!this.plugin.settings.features.vector_db).onChange(p=>{this.plugin.settings.features.vector_db=p,this.plugin.saveSettings(),this._vectorDepsOk=null,this._embedStatusText=null,this.display()})}),!this.plugin.settings.features.vector_db)return;let s=this.app.vault.adapter.basePath,n=e.createEl("div",{cls:"paperforge-skills-collapse-header"});n.style.cssText="display:flex; align-items:center; cursor:pointer; padding:6px 0 2px; margin:0;";let a=n.createEl("span",{text:"\u25BC"});a.style.cssText="display:inline-block; font-size:10px; margin-right:6px; transition:transform 0.2s;",n.createEl("span",{text:i("feat_vector_config_label"),cls:""}).style.cssText="font-size:12px; color:var(--text-muted);";let r=e.createEl("div",{cls:"paperforge-vector-config"}),o=c=>{r.style.display=c?"none":"",a.style.transform=c?"rotate(-90deg)":"rotate(0deg)"};if(o(He(this._featurePanelsCollapsed,"vectorConfig",!1)),n.addEventListener("click",()=>{let c=ot(this._featurePanelsCollapsed,"vectorConfig",!1);o(c)}),this._vectorDepsOk===!0){this._renderVectorReady(r,s);return}if(this._vectorDepsOk===!1){this._renderVectorNoDeps(r);return}i
`);for(let R of F)if(R.startsWith("EMBED_START:"))this.plugin._embedProgress.total=parseInt(R.split(":")[1])||0;else if(R.startsWith("EMBED_PROGRESS:")){let B=R.split(":");this.plugin._embedProgress.current=parseInt(B[1])||0,this.plugin._embedProgress.key=B[3]||""}else R.startsWith("EMBED_DONE")&&(this.plugin._embedProcess=null,this.plugin._embedProgress.current=this.plugin._embedProgress.total);this.display()},onStderr:C=>{this.plugin._embedStderr||(this.plugin._embedStderr=""),this.plugin._embedStderr+=C.toString("utf-8")},onError:C=>{this.plugin._embedProcess=null,new w.Notice(i("feat_build_failed")+": "+(C.message||C)),this.display()},onClose:C=>{if(this.plugin._embedProcess=null,C===0)this.plugin._embedProgress.current=this.plugin._embedProgress.total,this.plugin.saveSettings(),this._embedStatusText=fe(t),new w.Notice(i("feat_build_complete"));else{this._embedStatusText=null;let F=(this.plugin._embedStderr||"").slice(0,200);new w.Notice(i("feat_build_failed")+(F?": "+F:""),8e3)}this.plugin._embedStderr="",this.display(),this._refreshSnapshots(t)}}),this.display()};if(E){let m=n.createEl("div");m.style.cssText="padding:8px 12px; margin:8px 0; background:var(--background-modifier-warning); border-radius:4px; font-size:12px; display:flex; align-items:center; justify-content:space-between;",m.createEl("span",{text:i("feat_vector_corrupted")});let v=m.createEl("button",{text:i("feat_vector_rebuild_force_btn")});v.className="mod-cta",v.addEventListener("click",()=>S("--force"))}f&&!E&&r.createEl("span",{text:x.chunk_count+" chunks embedded",cls:"setting-item-description"});let b=r.createEl("button");if(b.setText(f?i("feat_rebuild_btn"):i("feat_build_btn")),b.addClass("mod-cta"),b.addEventListener("click",()=>S("--resume")),!E&&f){let m=r.createEl("button");m.setText(i("feat_vector_rebuild_force_btn")),m.style.marginLeft="6px",m.addEventListener("click",()=>S("--force"))}}})()}_getCurrentModelKey(){return this.plugin.settings.vector_db_api_model||"text-embedding-3-small"}_parseEmbedStatus(e){let t={};return e&&(e.split(`
`).forEach(s=>{let n=s.match(/^\s*([^:]+):\s*(.*)/);n&&(t[n[1].trim()]=n[2].trim())}),t.db_exists!==void 0&&(t.db_exists=t.db_exists==="True"),t.chunk_count!==void 0&&(t.chunk_count=parseInt(t.chunk_count,10)||0)),t}_getPythonDesc(e,t){return t==="stale"?`[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`:t==="manual"?`${e} (manual)`:`${e} (auto-detected)`}_refreshPythonInterpDesc(e,t){let s=this._pythonInterpDescEl;s&&(t==="stale"?s.textContent=`[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`:t==="manual"?s.textContent=`${e} (manual)`:s.textContent=`${e} (auto-detected)`)}_validatePythonOverride(){let e=this.plugin.settings.python_path?this.plugin.settings.python_path.trim():"",t=this._customPathDescEl;if(!e){let s="\u8BF7\u8F93\u5165\u8DEF\u5F84 / Enter a path first";t&&(t.innerHTML=`<span style="color:var(--text-error)">\u2717 ${s}</span>`),new w.Notice(s);return}if(!O.existsSync(e)){let s="\u8DEF\u5F84\u4E0D\u5B58\u5728 / Path does not exist";t&&(t.innerHTML=`<span style="color:var(--text-error)">\u2717 ${s}</span>`),new w.Notice(s,4e3);return}try{O.accessSync(e,O.constants.X_OK)}catch(s){let n="\u4E0D\u53EF\u6267\u884C / Not executable";t&&(t.innerHTML=`<span style="color:var(--text-error)">\u2717 ${n}</span>`),new w.Notice(n,4e3);return}(0,H.execFile)(e,["--version"],{timeout:8e3},(s,n)=>{if(s||!n){let l="\u65E0\u6CD5\u8FD0\u884C / Cannot run";t&&(t.innerHTML=`<span style="color:var(--text-error)">\u2717 ${l}</span>`),new w.Notice(l,4e3);return}let a=n.match(/Python (\d+)\.(\d+)/);if(!a){let l="\u65E0\u6CD5\u89E3\u6790\u7248\u672C / Cannot parse version";t&&(t.innerHTML=`<span style="color:var(--text-error)">\u2717 ${l}</span>`),new w.Notice(l,4e3);return}let r=parseInt(a[1],10),o=parseInt(a[2],10);if(r<3||r===3&&o<10){let l="Python \u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u8981 3.10+ / Python version too low, need 3.10+";t&&(t.innerHTML=`<span style="color:var(--text-error)">\u2717 ${l}</span>`),new w.Notice(l,4e3);return}(0,H.execFile)(e,["-m","pip","--version"],{timeout:8e3},l=>{if(l){let c=`\u2713 Python ${r}.${o} \u6709\u6548\uFF0C\u4F46\u672A\u68C0\u6D4B\u5230 pip / Valid, but pip not found`;t&&(t.innerHTML=`<span style="color:var(--text-warning)">\u26A0 ${c}</span>`),new w.Notice(c,4e3)}else{let c=`\u2713 Python ${r}.${o} \u6709\u6548 / Valid`;t&&(t.innerHTML=`<span style="color:var(--text-accent)">${c}</span>`),new w.Notice(c,4e3)}})})}_syncRuntime(e){let t=this.app.vault.adapter.basePath,{path:s,extraArgs:n=[]}=A(t,this.plugin.settings,void 0,void 0),a=this.plugin.manifest.version,r=st(s,a,n);e.setDisabled(!0),e.setButtonText(i("runtime_health_syncing"));let o=(c,p)=>(console.log(`[PaperForge] Sync Runtime: trying ${p}`),rt(r.cmd,c,t,r.timeout,void 0,de())),l=()=>{let c="opencode";try{let _=O.readFileSync(z.join(t,"paperforge.json"),"utf-8"),y=JSON.parse(_);y.agent_key&&(c=y.agent_key)}catch(_){}let p=[...n,"-c",'from paperforge.services.skill_deploy import deploy_skills; from pathlib import Path; r=deploy_skills(vault=Path(r"'+t.replace(/\\/g,"\\\\")+'"), agent_key="'+c+'", overwrite=True); print("skills deployed" if r["skill_deployed"] else "skills skipped", flush=True)'],u=(0,H.spawn)(s,p,{cwd:t,timeout:3e4,windowsHide:!0}),d="";u.stdout.on("data",_=>{d+=_.toString("utf-8")}),u.on("close",_=>{console.log(`[PaperForge] Skill deploy: ${d.trim()} (exit ${_})`)})};o(r.pypiArgs,"PyPI").then(c=>{if(c.exitCode===0){console.log("[PaperForge] Sync Runtime: installed via PyPI"),l(),new w.Notice(i("runtime_health_sync_done").replace("{0}",a),5e3),this.display();return}console.warn("[PaperForge] Sync Runtime: PyPI failed, falling back to git..."),o(r.gitArgs,"git").then(p=>{p.exitCode===0?(console.log("[PaperForge] Sync Runtime: installed via git"),l(),new w.Notice(i("runtime_health_sync_done").replace("{0}",a),5e3),this.display()):(e.setDisabled(!1),e.setButtonText(i("runtime_health_sync")),console.error("[PaperForge] git fallback stderr:",p.stderr),new w.Notice(i("runtime_health_sync_fail").replace("{0}","pip exit code "+p.exitCode),8e3))})})}_debo
`));let f=l.some(E=>!E.ok);this._checkEl.className=`paperforge-message msg-${f?"error":"ok"}`}let x=l.filter(f=>!f.ok);x.length>0&&new w.Notice(`[!!] \u672A\u901A\u8FC7: ${x.map(f=>f.label).join(", ")}`,6e3),e()})}};var D=require("obsidian"),ie=j(require("fs")),te=j(require("path")),he=require("child_process");var _e=class extends D.ItemView{constructor(e){super(e);this._currentMode=null;this._currentDomain=null;this._currentPaperKey=null;this._currentPaperEntry=null;this._currentFilePath=null;this._cachedItems=null;this._modeSubscribers=[];this._leafChangeTimer=null;this._ocrPrivacyShown=!1;this._cachedStats=null;this._techDetailsExpanded=!1;this._paperforgeVersion="";this._dashboardPermissions={};this._headerTitle=null;this._versionBadge=null;this._messageEl=null;this._metricsEl=null;this._ocrSection=null;this._ocrEmpty=null;this._ocrBadge=null;this._ocrTrack=null;this._ocrCounts=null;this._driftBannerEl=null;this._currentMode=null,this._currentDomain=null,this._currentPaperKey=null,this._currentPaperEntry=null,this._currentFilePath=null,this._cachedItems=null,this._modeSubscribers=[],this._leafChangeTimer=null,this._ocrPrivacyShown=!1}getViewType(){return pe}getDisplayText(){return"PaperForge"}getIcon(){return xe}async onOpen(){this._buildPanel(),this._modeSubscribers=[],this._leafChangeTimer=null,this._setupEventSubscriptions(),this._fetchVersion(),this._detectAndSwitch()}async onClose(){if(this._modeSubscribers&&this._modeSubscribers.length>0){for(let e of this._modeSubscribers)e.event==="active-leaf-change"?this.app.workspace.off("active-leaf-change",e.ref):e.event==="modify"&&this.app.vault.off("modify",e.ref);this._modeSubscribers=[]}this._leafChangeTimer&&(clearTimeout(this._leafChangeTimer),this._leafChangeTimer=null),this._cachedItems=null,this._cachedStats=null}_buildPanel(){let e=this.containerEl;e.empty(),e.addClass("paperforge-status-panel");let t=e.createEl("div",{cls:"paperforge-header"}),s=t.createEl("div",{cls:"paperforge-header-left"});s.createEl("div",{cls:"paperforge-header-logo",text:"P"}),this._modeContextEl=s.createEl("div",{cls:"paperforge-mode-context"}),this._headerTitle=s.createEl("h3",{cls:"paperforge-header-title",text:"PaperForge"}),this._versionBadge=s.createEl("span",{cls:"paperforge-header-badge",text:"v\u2014"});let n=t.createEl("button",{cls:"paperforge-header-refresh",attr:{"aria-label":"Refresh"}});n.innerHTML="\u21BB",n.addEventListener("click",()=>{this._invalidateIndex(),this._detectAndSwitch()}),this._messageEl=e.createEl("div",{cls:"paperforge-message"}),this._contentEl=e.createEl("div",{cls:"paperforge-content-area"})}_fetchVersion(){var r,o;let e=this.app.vault.adapter.basePath,t=this.app.plugins.plugins.paperforge,s=((r=t==null?void 0:t.manifest)==null?void 0:r.version)||"?",{path:n,extraArgs:a=[]}=A(e,(o=t==null?void 0:t.settings)!=null?o:null,void 0,void 0);tt(n,s,e,1e4,void 0).then(l=>{if(l.status==="not-installed")return;let c=l.pyVersion||"";this._paperforgeVersion=c.startsWith("v")?c:"v"+c,this._versionBadge&&this._versionBadge.setText(this._paperforgeVersion),this._driftBannerEl&&s&&this._paperforgeVersion!=="v"+s.replace(/^v/,"")?(this._driftBannerEl.style.display="block",this._driftBannerEl.setText(i("dashboard_drift_warning").replace("{0}",this._paperforgeVersion).replace("{1}","v"+s.replace(/^v/,"")))):this._driftBannerEl&&(this._driftBannerEl.style.display="none")})}_fetchStats(e){var r;if(!this._metricsEl)return;if(!e&&!this._cachedStats)this._metricsEl.empty(),this._metricsEl.createEl("div",{cls:"paperforge-status-loading",text:"Loading..."});else if(e&&!this._cachedStats)return;let t=this.app.vault.adapter.basePath,s=this.app.plugins.plugins.paperforge,{path:n,extraArgs:a=[]}=A(t,(r=s==null?void 0:s.settings)!=null?r:null,void 0,void 0);(0,he.execFile)(n,[...a,"-m","paperforge","dashboard","--json"],{cwd:t,timeout:3e4},(o,l)=>{if(!o)try{let c=JSON.parse(l);if(c.ok&&c.data){let p=this._normalizeDashboardData(c.data);this._cachedStats=p,this._metricsEl.empty(),this._renderStats(p),this._renderOcr(p),this._dashboardPermissions=c.data.permissions||{};return}}catc
Make sure paperforge is installed and in your PATH.`});return}try{let y=JSON.parse(_);this._cachedStats=y,this._metricsEl.empty(),this._renderStats(y),this._renderOcr(y)}catch(y){this._cachedStats||this._metricsEl.createEl("div",{cls:"paperforge-status-error",text:"Invalid response from paperforge status."})}})}}_renderSkeleton(e){e.addClass("paperforge-loading")}_renderEmptyState(e,t){e.createEl("div",{cls:"paperforge-empty-state",text:t||"No data"})}_buildMetricBar(e,t,s){if(s<=0)return;let n=Math.min(100,t/s*100);e.createEl("div",{cls:"paperforge-metric-progress"}).createEl("div",{cls:"paperforge-metric-progress-fill",attr:{style:`width:${n.toFixed(1)}%`}})}_loadIndex(){var a;let e=this.app.vault.adapter.basePath,t=this.app.plugins.plugins.paperforge,s=((a=t==null?void 0:t.settings)==null?void 0:a.system_dir)||"System",n=te.join(e,s,"PaperForge","indexes","formal-library.json");try{let r=ie.readFileSync(n,"utf-8");return JSON.parse(r)}catch(r){return null}}_getCachedIndex(){if(!this._cachedItems){let e=this._loadIndex();this._cachedItems=e?e.items||[]:[]}return this._cachedItems}_findEntry(e){if(!e)return null;let t=this._getCachedIndex().find(s=>s.zotero_key===e)||null;return Xe(this.app,t)}_patchCachedEntry(e,t){if(!e||!this._cachedItems)return;let s=this._cachedItems.findIndex(n=>n.zotero_key===e);s!==-1&&(this._cachedItems[s]=Oe(this._cachedItems[s],t))}_filterByDomain(e){return e?this._getCachedIndex().filter(t=>t.domain===e):[]}_renderStats(e){var a;if(this._versionBadge&&this._versionBadge.setText(this._paperforgeVersion||(e.version?"v"+e.version:"v\u2014")),!e||typeof e.total_papers=="undefined"){this._metricsEl&&this._renderSkeleton(this._metricsEl);return}if(!this._metricsEl)return;this._metricsEl.removeClass("paperforge-loading");let t=e.total_papers||0,s=e.formal_notes||0,n=[{value:t,label:"Papers",color:"var(--color-cyan)",barMax:0},{value:s,label:"Formal Notes",color:"var(--color-blue)",barMax:t},{value:e.exports||0,label:"Exports",color:"var(--color-purple)",barMax:0}];for(let r of n){let o=this._metricsEl.createEl("div",{cls:"paperforge-metric-card"});o.style.setProperty("--metric-color",r.color),o.createEl("div",{cls:"paperforge-metric-value",text:((a=r.value)==null?void 0:a.toString())||"\u2014"}),o.createEl("div",{cls:"paperforge-metric-label",text:r.label}),r.barMax>0&&this._buildMetricBar(o,r.value,r.barMax)}}_renderOcr(e){if(!this._ocrSection)return;let t=e.ocr||{},s=t.total||0;if(s===0){this._ocrSection.style.display="none";return}this._ocrSection.style.display="block",this._ocrEmpty&&(this._ocrEmpty.style.display="none");let n=t.done||0,a=t.pending||0,r=t.processing||0,o=t.failed||0;if(this._ocrBadge&&(this._ocrBadge.removeClass("active","idle"),r>0?(this._ocrBadge.addClass("active"),this._ocrBadge.setText("Processing")):a>0?(this._ocrBadge.addClass("idle"),this._ocrBadge.setText("Pending")):(this._ocrBadge.addClass("idle"),this._ocrBadge.setText("Idle"))),this._ocrTrack){this._ocrTrack.empty(),r>0?this._ocrTrack.addClass("paperforge-processing"):this._ocrTrack.removeClass("paperforge-processing");let l=[{cls:"pending",count:a},{cls:"active",count:r},{cls:"done",count:n},{cls:"failed",count:o}];for(let c of l)if(c.count>0){let p=(c.count/s*100).toFixed(1);this._ocrTrack.createEl("div",{cls:`paperforge-progress-seg ${c.cls}`,attr:{style:`width:${p}%`}})}}if(this._ocrCounts){this._ocrCounts.empty();let l=[{cls:"pending",value:a,label:"Pending"},{cls:"active",value:r,label:"Processing"},{cls:"done",value:n,label:"Done"},{cls:"failed",value:o,label:"Failed"}];for(let c of l){let p=this._ocrCounts.createEl("div",{cls:"paperforge-ocr-count"});p.createEl("div",{cls:"paperforge-ocr-count-value",text:c.value.toString()}),p.createEl("div",{cls:"paperforge-ocr-count-label",text:c.label})}}}_renderLifecycleStepper(e,t,s){if(!t||!s){this._renderSkeleton(e);return}let n=[{key:"indexed",label:"Indexed"},{key:"pdf_ready",label:"PDF Ready"},{key:"fulltext_ready",label:"Fulltext Ready"},{key:"deep_read_done",label:"Deep Read"}],a=e.createEl("div",{cls:"paperforge-lifecycle-stepper"}),r=!1;for(let o of n){let l=
`);d!==-1&&d<u&&(u=d);let _=c.slice(0,u).trim();return _.startsWith("**")&&(_=_.slice(2)),_.endsWith("**")&&(_=_.slice(0,-2)),_||null}}let a=s.indexOf(`
`);if(a===-1)return null;let r=s.slice(a+1).split(`
`)[0].trim();return!r||r.startsWith("###")||r.startsWith("##")?null:r.length>300?r.slice(0,300)+"...":r}_renderRecentDiscussionCard(e,t){let s=e.createEl("div",{cls:"paperforge-discussion-card"});if(s.style.display="none",!t.note_path)return;let n=t.note_path.lastIndexOf("/"),r=(n!==-1?t.note_path.substring(0,n):".")+"/ai/discussion.md";this.app.vault.adapter.exists(r).then(o=>{if(o)return this.app.vault.adapter.read(r)}).then(async o=>{if(!o)return;let l=this._parseDiscussionMD(o);if(!l||l.length===0)return;s.style.display="block",s.createEl("div",{cls:"paperforge-discussion-header"}).createEl("span",{cls:"paperforge-discussion-title",text:"\u6700\u8FD1\u8BA8\u8BBA"});for(let u of l){let d=s.createEl("div",{cls:"paperforge-discussion-item"}),_=d.createEl("div",{cls:"paperforge-discussion-q"});_.createEl("span",{cls:"paperforge-discussion-q-label",text:"\u63D0\u95EE\uFF1A"}),_.createEl("span",{cls:"paperforge-discussion-q-text",text:u.question});let y=d.createEl("div",{cls:"paperforge-discussion-a"}),x=!1;if(u.answer&&u.answer.length>500&&(x=!0,y.classList.add("paperforge-discussion-a-collapsed")),await D.MarkdownRenderer.render(this.app,u.answer||"",y,r,this),x){let f=!1;d.style.cursor="pointer",d.addEventListener("click",()=>{f=!f,y.classList.toggle("paperforge-discussion-a-collapsed",!f),y.classList.toggle("paperforge-discussion-a-expanded",f)})}}s.createEl("a",{cls:"paperforge-discussion-viewall",text:"\u67E5\u770B\u5168\u90E8\u8BA8\u8BBA \u2192"}).addEventListener("click",u=>{u.preventDefault(),this.app.vault.getAbstractFileByPath(r)?this.app.workspace.openLinkText(r,""):new D.Notice("\u8BA8\u8BBA\u6587\u4EF6\u5C1A\u672A\u751F\u6210")})}).catch(o=>{console.error("PaperForge: discussion.md read error",r,o.message)})}_parseDiscussionMD(e){let t=e.split(/\n## /).slice(1);if(t.length===0)return null;let s=t[t.length-1],n=[],a=s.split(/\*\*\u95EE\u9898:\*\*/).slice(1);for(let r of a){let o=r.match(/\*\*\u89E3\u7B54:\*\*/);if(!o)continue;let l=r.substring(0,o.index).trim(),c=r.substring(o.index+3+4).trim();n.push({question:l,answer:c})}return n.slice(-3)}_renderPaperTechnicalDetails(e,t){let s=this._currentPaperKey,n=e.createEl("div",{cls:"paperforge-technical-details"}),a=n.createEl("button",{cls:"paperforge-technical-details-toggle"}),r=n.createEl("div",{cls:"paperforge-technical-details-body"});r.style.display="none",this._techDetailsExpanded?(r.style.display="block",a.setText("\u6280\u672F\u8BE6\u60C5 \u25BE")):a.setText("\u6280\u672F\u8BE6\u60C5 \u25B8"),a.addEventListener("click",()=>{let u=r.style.display!=="none";r.style.display=u?"none":"block",a.setText(u?"\u6280\u672F\u8BE6\u60C5 \u25B8":"\u6280\u672F\u8BE6\u60C5 \u25BE"),this._techDetailsExpanded=!u});let o=r.createEl("div",{cls:"paperforge-workflow-toggles"}),l=[{key:"do_ocr",label:"OCR",hint:"\u52A0\u5165 OCR"},{key:"analyze",label:"\u7CBE\u8BFB",hint:"\u6807\u8BB0\u7CBE\u8BFB"}];for(let u of l){let d=o.createEl("label",{cls:"paperforge-workflow-toggle"}),_=d.createEl("input",{type:"checkbox",cls:"paperforge-workflow-checkbox"});_.checked=t[u.key]===!0,d.createEl("span",{cls:"paperforge-workflow-toggle-label",text:u.label}),d.createEl("span",{cls:"paperforge-workflow-toggle-hint",text:u.hint}),_.addEventListener("change",async()=>{let y=t.note_path?this.app.vault.getAbstractFileByPath(t.note_path):null;if(!y){new D.Notice("[!!] Note file not found",6e3);return}let x=_.checked;await this.app.fileManager.processFrontMatter(y,f=>{f[u.key]=x}),this._patchCachedEntry(s,{[u.key]:x}),this._currentPaperEntry=Oe(this._currentPaperEntry,{[u.key]:x})})}let c=t.health||{},p=[["PDF Health",c.pdf_health||"\u2014"],["OCR Status",t.ocr_status||"\u2014"],["Asset Health",c.asset_health||"\u2014"],["Note Path",t.note_path||"\u2014"],["Fulltext Path",t.fulltext_path||"\u2014"]];for(let[u,d]of p){let _=r.createEl("div",{cls:"paperforge-technical-row"});_.createEl("span",{cls:"paperforge-technical-label",text:u}),_.createEl("span",{cls:"paperforge-technical-value",text:String(d)})}}_renderNextStepCard(e,t,s){var l,c;let n=t.next_step||"ready",a={sync:{label:"Sync Needed",text:"T
`).filter(Boolean);for(let f of x){let E=f.trim();E&&(c.push(E),this._showMessage(c.slice(-8).join(`
`),"running"))}}),l.stderr.on("data",y=>{let x=y.toString("utf-8").split(`
`).filter(Boolean);for(let f of x){if(f.includes("\r")||f.includes("%")||f.includes("\u2588"))continue;let E=f.trim();E&&!E.match(/^\d+%|^\|/)&&(c.push(E),this._showMessage(c.slice(-8).join(`
`),"running"))}}),l.on("close",y=>{clearInterval(u),t.removeClass("running");let x=((Date.now()-p)/1e3).toFixed(1);if(y!==0){let f=c.slice(-3).join(" | ")||"exit code "+y;(e.cmd==="repair"||e.cmd==="ocr")&&y===1?(this._showMessage("[WARN] "+f,"running"),new D.Notice("[WARN] "+e.cmd+" partial: "+f,8e3),this._fetchStats(!0)):(this._showMessage("[!!] "+f,"error"),new D.Notice("[!!] "+e.cmd+" failed: "+f,8e3))}else if(e.needsKey||e.needsFilter){let f=c.join(`
`);if(f.trim())try{JSON.parse(f),navigator.clipboard.writeText(f).then(()=>{let E=`${x}s \u2014 ${f.length} chars copied`;this._showMessage("[OK] "+e.title+": "+E,"ok"),new D.Notice("[OK] "+e.okMsg+" \u2014 "+f.length+" chars")}).catch(E=>{this._showMessage("[!!] Clipboard write failed: "+E.message,"error"),new D.Notice("[!!] Clipboard error",6e3)})}catch(E){this._showMessage("[!!] Invalid JSON from "+e.title,"error"),new D.Notice("[!!] "+e.title+" returned invalid JSON: "+E.message.slice(0,100),8e3)}else this._showMessage("[!!] No output from context command","error"),new D.Notice("[!!] Context command returned empty output",8e3);this._fetchStats(!0)}else{let E=c.filter(b=>b.match(/updated \d+/)).pop()||c[c.length-1]||"",S=`${x}s \u2014 ${E}`;this._showMessage("[OK] "+e.title+": "+S,"ok"),new D.Notice("[OK] "+e.okMsg),this._contentEl&&this._contentEl.removeClass("switching"),this._cachedStats=null;try{this._fetchStats(!1)}catch(b){console.log("[PF] fetchStats error:",b)}console.log("[PF] close cmd="+e.cmd+" id="+e.id),e.cmd==="sync"&&Te(this.app,this.app.plugins.plugins.paperforge,s)}}),l.on("error",y=>{t.removeClass("running"),this._contentEl&&this._contentEl.removeClass("switching"),this._showMessage("[!!] "+y.message,"error"),new D.Notice("[!!] Cannot start: "+y.message,8e3)})}_showMessage(e,t){this._messageEl&&(this._messageEl.setText(e),this._messageEl.className=`paperforge-message msg-${t}`)}_renderModeHeader(e){if(!this._modeContextEl)return;this._modeContextEl.empty();let t=this._modeContextEl.createEl("span",{cls:"paperforge-mode-badge"}),s="";switch(e){case"global":t.addClass("global"),t.setText("Global"),this._headerTitle&&this._headerTitle.setText("PaperForge");break;case"paper":t.addClass("paper"),t.setText("Paper"),this._headerTitle&&this._headerTitle.setText("Paper"),this._currentPaperEntry&&this._currentPaperEntry.title?s=this._currentPaperEntry.title:this._currentPaperKey?(s=this._currentPaperKey,this._modeContextEl.createEl("span",{cls:"paperforge-mode-warning",text:"Not found in index"})):s="Unknown paper";break;case"collection":t.addClass("collection"),t.setText("Collection"),this._headerTitle&&this._headerTitle.setText("Collection"),s=this._currentDomain||"Unknown Domain";break}s&&this._modeContextEl.createEl("span",{cls:"paperforge-mode-name",text:s})}_setupEventSubscriptions(){let e=this.app.workspace.on("active-leaf-change",()=>{this._leafChangeTimer&&clearTimeout(this._leafChangeTimer),this._leafChangeTimer=setTimeout(()=>{let s=this._resolveModeForFile(this.app.workspace.getActiveFile()),n=s.mode,a=s.filePath;this._currentMode===n&&this._currentFilePath===a||this._detectAndSwitch()},300)});this._modeSubscribers.push({event:"active-leaf-change",ref:e});let t=this.app.vault.on("modify",s=>{s&&s.path&&s.path.endsWith("formal-library.json")&&(this._invalidateIndex(),this._refreshCurrentMode())});this._modeSubscribers.push({event:"modify",ref:t})}static async open(e){let t=e.app.workspace.getLeavesOfType(pe);if(t.length>0){e.app.workspace.revealLeaf(t[0]);return}let s=e.app.workspace.getRightLeaf(!1);s&&(await s.setViewState({type:pe,active:!0}),e.app.workspace.revealLeaf(s))}};var Re=class extends V.Plugin{constructor(){super(...arguments);this._lastExportMtime=0;this._lastOcrMtimes={};this._autoSyncRunning=!1;this._lastSyncTime=null;this._pollTimer=null;this._embedProcess=null;this._embedProgress={current:0,total:0,key:""};this._embedStderr="";this._memoryStatusText=null}async onload(){await this.loadSettings(),this.saveSettings(),Ye(this.app),this.registerView(pe,t=>new _e(t));try{(0,V.addIcon)(xe,Ge)}catch(t){}this.addRibbonIcon(xe,"PaperForge Dashboard",()=>_e.open(this)),W.find(t=>t.id==="paperforge-ocr-redo")&&this.addRibbonIcon("reset","PaperForge: \u91CD\u505AOCR",()=>{let t=this.app.vault.adapter.basePath;new V.Notice("PaperForge: \u91CD\u505AOCR starting...");let{path:s,extraArgs:n}=A(t,this.settings,void 0,void 0);(0,se.execFile)(s,[...n,"-m","paperforge","ocr","redo"],{cwd:t,timeout:6e5},(a,r,o)=>{if(a){new V.Notice("PaperForge: \u91CD\u505AOCR failed");return}new V.Notice("PaperForge:
`)[0].slice(0,80)}`)})}});this.settings.auto_update_on_startup===!0&&this.settings.setup_complete&&setTimeout(()=>this._autoUpdate(),3e3),this._startFilePolling(),this._firstLaunchSnapshotMigration()}_firstLaunchSnapshotMigration(){let e=this.app.vault.adapter.basePath;if(!e)return;let s=ee(e).memoryStatePath;if(!I.existsSync(s)){let n=A(e,this.settings,void 0,void 0);[["runtime-health","--json"],["memory","status","--json"],["embed","status","--json"]].forEach(r=>{let o=[...n.extraArgs,"-m","paperforge","--vault",e,...r];(0,se.execFile)(n.path,o,{cwd:e,timeout:6e4,windowsHide:!0},()=>{})})}}_autoUpdate(){let e=this.app.vault.adapter.basePath,{path:t,extraArgs:s=[]}=A(e,this.settings,void 0,void 0),n=this.manifest.version,a=`paperforge==${n}`,r=`git+https://github.com/LLLin000/PaperForge.git@${n}`,o=(l,c)=>{(0,se.spawn)(t,[...s,"-m","pip","install","--upgrade",l],{cwd:e,timeout:12e4,env:de()}).on("close",u=>c(u===0))};(0,se.execFile)(t,[...s,"-c","import paperforge; print(paperforge.__version__)"],{cwd:e,timeout:1e4},(l,c)=>{let p=d=>{console.log(`[PaperForge] Auto-update: trying PyPI (paperforge==${n})`),o(a,_=>{if(_){console.log("[PaperForge] Auto-update: installed via PyPI"),new V.Notice(`[OK] PaperForge CLI ${d}`,5e3);return}console.warn("[PaperForge] Auto-update: PyPI failed, falling back to git..."),o(r,y=>{y&&(console.log("[PaperForge] Auto-update: installed via git"),new V.Notice(`[OK] PaperForge CLI ${d} (via git)`,5e3))})})};if(l){p("installed");return}let u=c.trim();u!==n&&p(`${u} -> ${n}`)})}_startFilePolling(){let e=this.app.vault.adapter.basePath;this._pollTimer=setInterval(()=>{this._checkExports(e),this._checkOcr(e)},12e4)}_checkExports(e){if(this._autoSyncRunning)return;let t=ee(e).exportsDir;if(!I.existsSync(t))return;let s=0;try{I.readdirSync(t).forEach(n=>{if(!n.endsWith(".json"))return;let a=I.statSync(me.join(t,n));a.mtimeMs>s&&(s=a.mtimeMs)})}catch(n){return}s>this._lastExportMtime&&(this._lastExportMtime=s,this._autoSync(e))}_autoSync(e){if(this._autoSyncRunning)return;this._autoSyncRunning=!0;let t=A(e,this.settings,void 0,void 0);if(!t.path){this._autoSyncRunning=!1;return}let s=`"${t.path}" -m paperforge --vault "${e}" sync`;(0,se.exec)(s,{timeout:12e4,encoding:"utf-8"},(n,a,r)=>{this._autoSyncRunning=!1,this._memoryStatusText=null,n||(this._lastSyncTime=new Date().toLocaleTimeString());try{let o=ee(e).exportsDir,l=0;I.readdirSync(o).forEach(c=>{c.endsWith(".json")&&(l=Math.max(l,I.statSync(me.join(o,c)).mtimeMs))}),this._lastExportMtime=l}catch(o){}})}_checkOcr(e){if(this._autoSyncRunning)return;let t=ee(e).ocrDir;if(I.existsSync(t))try{I.readdirSync(t,{withFileTypes:!0}).forEach(s=>{if(!s.isDirectory())return;let n=me.join(t,s.name,"meta.json");if(!I.existsSync(n))return;let a=I.statSync(n),r=this._lastOcrMtimes[s.name]||0;if(a.mtimeMs<=r||(this._lastOcrMtimes[s.name]=a.mtimeMs,this._autoSyncRunning))return;this._autoSyncRunning=!0;let o=A(e,this.settings,void 0,void 0);if(!o.path){this._autoSyncRunning=!1;return}let l=`"${o.path}" -m paperforge --vault "${e}" sync`;(0,se.exec)(l,{timeout:3e4,encoding:"utf-8"},()=>{this._autoSyncRunning=!1,this._memoryStatusText=null})})}catch(s){}}readPaperforgeJson(){let e=this.app.vault.adapter.basePath,t=me.join(e,"paperforge.json"),s={system_dir:"System",resources_dir:"Resources",literature_dir:"Literature",base_dir:"Bases"};try{if(!I.existsSync(t))return s;let n=I.readFileSync(t,"utf-8"),a=JSON.parse(n),r=a.vault_config||{};return{system_dir:r.system_dir||a.system_dir||s.system_dir,resources_dir:r.resources_dir||a.resources_dir||s.resources_dir,literature_dir:r.literature_dir||a.literature_dir||s.literature_dir,base_dir:r.base_dir||a.base_dir||s.base_dir}}catch(n){return console.warn("PaperForge: Failed to read paperforge.json, using defaults",n),s}}savePaperforgeJson(e){let t=this.app.vault.adapter.basePath,s=me.join(t,"paperforge.json"),n={};try{I.existsSync(s)&&(n=JSON.parse(I.readFileSync(s,"utf-8")))}catch(r){console.warn("PaperForge: Failed to read paperforge.json for update",r)}(!n.vault_config||typeof n.vault_config!="object")&&(n.vault_config=