-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
138 lines (116 loc) · 48.4 KB
/
main.js
File metadata and controls
138 lines (116 loc) · 48.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* vault-cleaner plugin */
var P=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var J=(M,F)=>{for(var e in F)P(M,e,{get:F[e],enumerable:!0})},W=(M,F,e,t)=>{if(F&&typeof F=="object"||typeof F=="function")for(let n of H(F))!_.call(M,n)&&n!==e&&P(M,n,{get:()=>F[n],enumerable:!(t=V(F,n))||t.enumerable});return M};var G=M=>W(P({},"__esModule",{value:!0}),M);var Q={};J(Q,{default:()=>I});module.exports=G(Q);var x=require("obsidian"),U=`\u5DE5\u5177/\u5B9E\u6D4B
\u5DE5\u5177/\u6559\u7A0B
\u5DE5\u5177/\u60F3\u6CD5
\u9009\u9898/\u6807\u9898
\u9009\u9898/\u5927\u7EB2
\u9009\u9898/\u7D20\u6750
\u5F85\u529E/\u7D27\u6025
\u5F85\u529E/\u7F13\u529E
\u7075\u611F/\u53D8\u73B0
\u7075\u611F/\u5DE5\u4F5C\u6D41
\u7075\u611F/\u5176\u4ED6`,Y={modelSource:"ollama",ollamaModel:"qwen2.5:3b",qwenApiKey:"",qwenModel:"qwen-plus",zhipuApiKey:"",zhipuModel:"glm-4-flash",deepseekApiKey:"",deepseekModel:"deepseek-chat",doubaoApiKey:"",doubaoModel:"",kimiApiKey:"",kimiModel:"moonshot-v1-8k",excludeFolders:"01_QuickNotes/\u65E5\u8BB0,00_Merged",similarityThreshold:.6,topNPairs:20,mergedFolder:"00_Merged",tagLibrary:U,autoRunHours:0,titleAccepted:[]},L="vault-cleaner-view",I=class extends x.Plugin{constructor(){super(...arguments);this.autoRunTimer=null}async onload(){await this.loadSettings(),this.registerView(L,e=>new q(e,this)),this.addRibbonIcon("scissors","Vault Groomer",()=>this.activateView()),this.addCommand({id:"open-vault-cleaner",name:"\u6253\u5F00 Vault Groomer",callback:()=>this.activateView()}),this.addSettingTab(new N(this.app,this)),this.scheduleAutoRun()}async onunload(){this.autoRunTimer!==null&&window.clearInterval(this.autoRunTimer)}scheduleAutoRun(){if(this.autoRunTimer!==null&&window.clearInterval(this.autoRunTimer),this.settings.autoRunHours>0){let e=this.settings.autoRunHours*60*60*1e3;this.autoRunTimer=window.setInterval(async()=>{var r;new x.Notice("\u7B14\u8BB0\u5E93\u6574\u7406\u5DE5\u5177\uFF1A\u5B9A\u65F6\u6253\u6807\u7B7E\u5F00\u59CB\u2026");let n=(r=this.app.workspace.getLeavesOfType(L)[0])==null?void 0:r.view;n?await n.runAutoTag():new x.Notice("\u5B9A\u65F6\u6253\u6807\u7B7E\uFF1A\u8BF7\u5148\u6253\u5F00\u6574\u7406\u5DE5\u5177\u9762\u677F")},e)}}async activateView(){let{workspace:e}=this.app,t=e.getLeavesOfType(L)[0];t||(t=e.getRightLeaf(!1),await t.setViewState({type:L,active:!0})),e.revealLeaf(t)}async loadSettings(){this.settings=Object.assign({},Y,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}},q=class extends x.ItemView{constructor(e,t){super(e);this.currentTab="tagger";this.isRunning=!1;this.stopRequested=!1;this.plugin=t}getViewType(){return L}getDisplayText(){return"\u7B14\u8BB0\u5E93\u6574\u7406\u5DE5\u5177"}getIcon(){return"archive"}async onOpen(){this.render()}async onClose(){}async runAutoTag(){let e=this.plugin.settings.tagLibrary.split(`
`).map(d=>d.trim()).filter(Boolean),n=this.app.vault.getMarkdownFiles().filter(d=>{var s;let l=(s=this.app.metadataCache.getFileCache(d))==null?void 0:s.frontmatter;if(!l)return!0;let i=l.tags;return i?Array.isArray(i)?i.filter(Boolean).length===0:typeof i=="string"?i.trim()==="":!1:!0}),r=0;for(let d of n)try{let l=await this.app.vault.read(d),i=await this.callAI(this.buildTaggerPrompt(l,e)),s=i==null?void 0:i.match(/\{[\s\S]*\}/);if(s){let a=JSON.parse(s[0]);await this.writeFrontmatter(d,l,a.summary||"",a.tags||[]),r++}}catch(l){}new x.Notice(`\u5B9A\u65F6\u6253\u6807\u7B7E\u5B8C\u6210\uFF0C\u5199\u5165 ${r} \u7BC7`)}buildTaggerPrompt(e,t){return`\u8BF7\u5206\u6790\u4E0B\u9762\u8FD9\u7BC7\u7B14\u8BB0\uFF0C\u8FD4\u56DE JSON \u683C\u5F0F\u3002
\u6807\u7B7E\u5FC5\u987B\u4E25\u683C\u4ECE\u4EE5\u4E0B\u5217\u8868\u4E2D\u9009\u62E9 1-2 \u4E2A\uFF0C\u6700\u591A\u53EF\u4EE5\u65B0\u589E 1 \u4E2A\u4E0D\u5728\u5217\u8868\u4E2D\u7684\u6807\u7B7E\uFF1A
${t.join("\u3001")}
\u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u5176\u4ED6\u5185\u5BB9\u3002
\u7B14\u8BB0\u5185\u5BB9\uFF1A
${e.slice(0,2e3)}
\u8FD4\u56DE\u683C\u5F0F\uFF1A{"summary": "1-2\u53E5\u4E2D\u6587\u6458\u8981", "tags": ["\u6807\u7B7E1", "\u6807\u7B7E2"]}`}render(){let{containerEl:e}=this;e.empty(),e.addClass("vault-cleaner");let t=e.createEl("style");t.textContent=`
.vault-cleaner { padding: 12px; font-size: 13px; }
.vc-tabs-row { display: flex; gap: 3px; margin-bottom: 3px; background: var(--background-secondary); border-radius: 8px; padding: 3px; }
.vc-tab { flex: 1; text-align: center; padding: 5px 2px; border-radius: 5px; cursor: pointer; color: var(--text-muted); font-size: 11px; transition: all 0.15s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vc-tab.active { background: var(--background-primary); color: var(--text-normal); box-shadow: 0 1px 4px rgba(0,0,0,0.15); }
.vc-section { margin-bottom: 14px; }
.vc-card { background: var(--background-secondary); border-radius: 8px; padding: 12px; margin-bottom: 10px; border: 1px solid var(--background-modifier-border); }
.vc-note-name { font-weight: 600; margin-bottom: 4px; font-size: 12px; }
.vc-note-path { font-size: 10px; color: var(--text-faint); font-family: monospace; margin-bottom: 6px; }
.vc-note-preview { font-size: 11px; color: var(--text-muted); line-height: 1.6; max-height: 60px; overflow: hidden; }
.vc-summary { font-size: 12px; color: var(--text-muted); line-height: 1.6; margin-bottom: 10px; }
.vc-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }
.vc-tag { display: flex; align-items: center; gap: 4px; padding: 2px 9px; border-radius: 20px; font-size: 11px; background: var(--background-modifier-hover); color: var(--text-muted); border: 1px solid var(--background-modifier-border); }
.vc-tag.keep { background: rgba(80,160,80,0.1); color: #5aa05a; border-color: rgba(80,160,80,0.3); }
.vc-tag.new { background: rgba(200,120,60,0.1); color: #c8783c; border-color: rgba(200,120,60,0.3); }
.vc-tag-remove { cursor: pointer; opacity: 0.5; font-size: 10px; line-height: 1; }
.vc-tag-remove:hover { opacity: 1; }
.vc-add-tag { display: flex; gap: 6px; margin-bottom: 8px; }
.vc-add-tag input { flex: 1; font-size: 11px; }
.vc-btn-row { display: flex; gap: 6px; justify-content: flex-end; margin-top: 10px; flex-wrap: wrap; }
.vc-btn { padding: 5px 12px; border-radius: 6px; font-size: 11px; cursor: pointer; border: 1px solid var(--background-modifier-border); background: var(--background-secondary); color: var(--text-muted); }
.vc-btn.primary { background: var(--interactive-accent); color: var(--text-on-accent); border-color: var(--interactive-accent); }
.vc-btn.danger { background: rgba(200,80,80,0.15); color: #c85050; border-color: rgba(200,80,80,0.3); }
.vc-btn.success { background: rgba(80,160,80,0.15); color: #5aa05a; border-color: rgba(80,160,80,0.3); }
.vc-progress { margin: 8px 0; }
.vc-progress-bar { height: 3px; background: var(--background-modifier-border); border-radius: 2px; overflow: hidden; margin-bottom: 4px; }
.vc-progress-fill { height: 100%; background: var(--interactive-accent); border-radius: 2px; transition: width 0.3s; }
.vc-progress-text { font-size: 10px; color: var(--text-faint); font-family: monospace; }
.vc-log { font-size: 11px; color: var(--text-muted); font-family: monospace; background: var(--background-secondary); border-radius: 6px; padding: 8px; max-height: 80px; overflow-y: auto; line-height: 1.6; white-space: pre-wrap; margin-bottom: 8px; }
.vc-sim-score { font-family: monospace; font-weight: 600; font-size: 13px; margin-bottom: 6px; }
.vc-sim-actions { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 8px; }
.vc-model-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.vc-model-pills { display: flex; gap: 4px; }
.vc-pill { padding: 3px 10px; border-radius: 20px; font-size: 11px; cursor: pointer; border: 1px solid var(--background-modifier-border); color: var(--text-muted); background: var(--background-secondary); }
.vc-pill.active { border-color: var(--interactive-accent); color: var(--interactive-accent); background: rgba(var(--interactive-accent-rgb),0.1); }
.vc-status { font-size: 10px; color: var(--text-faint); font-family: monospace; margin-top: 4px; }
.vc-file-list { max-height: 200px; overflow-y: auto; background: var(--background-secondary); border-radius: 6px; padding: 6px; border: 1px solid var(--background-modifier-border); margin-bottom: 8px; }
.vc-file-item { display: flex; align-items: center; gap: 6px; padding: 3px 4px; font-size: 11px; border-radius: 4px; }
.vc-file-item:hover { background: var(--background-modifier-hover); }
.vc-file-item input[type=checkbox] { margin: 0; cursor: pointer; flex-shrink: 0; }
.vc-search-input { width: 100%; font-size: 12px; padding: 6px 8px; border-radius: 6px; border: 1px solid var(--background-modifier-border); background: var(--background-secondary); color: var(--text-normal); margin-bottom: 8px; box-sizing: border-box; }
.vc-health-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--background-modifier-border); font-size: 12px; }
.vc-health-row:last-child { border-bottom: none; }
.vc-health-num { font-weight: 700; font-size: 20px; color: var(--interactive-accent); font-family: monospace; }
.vc-merged-preview { font-size: 11px; color: var(--text-muted); background: var(--background-primary); border-radius: 6px; padding: 10px; max-height: 160px; overflow-y: auto; line-height: 1.6; white-space: pre-wrap; margin-bottom: 8px; border: 1px solid var(--background-modifier-border); }
.vc-title-old { font-size: 11px; color: var(--text-faint); text-decoration: line-through; margin-bottom: 2px; }
.vc-title-new { font-size: 13px; font-weight: 600; color: var(--text-normal); margin-bottom: 6px; }
`,this.renderModelBar(e);let n=[{id:"tagger",label:"\u{1F4DD} \u667A\u80FD\u6807\u7B7E"},{id:"similarity",label:"\u{1F50D} \u76F8\u4F3C\u68C0\u6D4B"},{id:"manual-merge",label:"\u{1F517} \u624B\u52A8\u5408\u5E76"}],r=[{id:"orphan",label:"\u{1F33F} \u5B64\u7ACB\u7B14\u8BB0"},{id:"broken",label:"\u26A0\uFE0F \u65AD\u94FE"},{id:"search",label:"\u{1F50E} \u641C\u7D22"},{id:"title",label:"\u270F\uFE0F \u6807\u9898\u4F18\u5316"},{id:"health",label:"\u{1F4CA} \u5065\u5EB7\u62A5\u544A"}],d={tagger:"\u667A\u80FD\u6807\u7B7E\uFF1AAI\u81EA\u52A8\u5206\u6790\u5185\u5BB9\u5E76\u6253\u6807\u7B7E",similarity:"\u76F8\u4F3C\u68C0\u6D4B\uFF1A\u626B\u63CF\u5168\u5E93\u627E\u51FA\u5185\u5BB9\u76F8\u8FD1\u7684\u7B14\u8BB0\u5BF9","manual-merge":"\u624B\u52A8\u5408\u5E76\uFF1A\u52FE\u9009\u591A\u7BC7\u7B14\u8BB0\uFF0CAI\u751F\u6210\u6458\u8981\u540E\u5408\u5E76",orphan:"\u5B64\u7ACB\u7B14\u8BB0\uFF1A\u627E\u51FA\u65E0\u6807\u7B7E\u3001\u65E0\u94FE\u63A5\u7684\u7B14\u8BB0",broken:"\u65AD\u94FE\uFF1A\u626B\u63CF\u6240\u6709\u5931\u6548\u7684\u53CC\u94FE\u5F15\u7528",search:"\u641C\u7D22\uFF1A\u7528\u5173\u952E\u8BCD\u627E\u76F8\u5173\u7B14\u8BB0",title:"\u6807\u9898\u4F18\u5316\uFF1AAI\u5EFA\u8BAE\u66F4\u51C6\u786E\u7684\u6807\u9898",health:"\u5065\u5EB7\u62A5\u544A\uFF1A\u4E00\u952E\u67E5\u770B\u5168\u5E93\u72B6\u6001\u6982\u89C8"},l=s=>{let a=e.createDiv("vc-tabs-row");s.forEach(o=>{let g=a.createDiv("vc-tab"+(this.currentTab===o.id?" active":""));g.setText(o.label),g.title=d[o.id],g.onclick=()=>{this.currentTab=o.id,this.render()}})};l(n),l(r);let i=e.createDiv();switch(i.style.marginTop="10px",this.currentTab){case"tagger":this.renderTagger(i);break;case"similarity":this.renderSimilarity(i);break;case"manual-merge":this.renderManualMerge(i);break;case"orphan":this.renderOrphan(i);break;case"broken":this.renderBroken(i);break;case"search":this.renderSearch(i);break;case"title":this.renderTitle(i);break;case"health":this.renderHealth(i);break}}renderModelBar(e){let t=e.createDiv("vc-model-bar"),n=[{key:"ollama",label:"\u672C\u5730 Ollama"},{key:"qwen",label:"\u5343\u95EE"},{key:"zhipu",label:"\u667A\u8C31"},{key:"deepseek",label:"DeepSeek"},{key:"doubao",label:"\u8C46\u5305"},{key:"kimi",label:"Kimi"}],r=t.createEl("select");r.style.cssText="font-size:12px;padding:3px 8px;border-radius:6px;border:1px solid var(--background-modifier-border);background:var(--background-secondary);color:var(--text-normal);cursor:pointer;",n.forEach(({key:l,label:i})=>{let s=r.createEl("option");s.value=l,s.text=i,this.plugin.settings.modelSource===l&&(s.selected=!0)}),r.onchange=async()=>{this.plugin.settings.modelSource=r.value,await this.plugin.saveSettings(),this.render()};let d=t.createDiv("vc-status");if(this.plugin.settings.modelSource==="ollama")d.setText("\u25CF Ollama "+this.plugin.settings.ollamaModel);else{let l=this.plugin.settings.modelSource,s=!!{qwen:this.plugin.settings.qwenApiKey,zhipu:this.plugin.settings.zhipuApiKey,deepseek:this.plugin.settings.deepseekApiKey,doubao:this.plugin.settings.doubaoApiKey,kimi:this.plugin.settings.kimiApiKey}[l];d.setText(s?"\u25CF API Key \u5DF2\u914D\u7F6E":"\u26A0\uFE0F \u672A\u586B\u5199 API Key\uFF0C\u8BF7\u524D\u5F80\u63D2\u4EF6\u8BBE\u7F6E\u9875"),d.style.color=s?"":"#c85050"}}renderTagger(e){let t=e.createEl("button",{cls:"vc-btn primary",text:"\u25B6 \u5F00\u59CB\u5206\u6790"}),n=e.createDiv("vc-log");n.setText("\u70B9\u51FB\u300C\u5F00\u59CB\u5206\u6790\u300D\u542F\u52A8\u2026");let r=e.createDiv("vc-progress"),d=r.createDiv("vc-progress-bar").createDiv("vc-progress-fill");d.style.width="0%";let l=r.createDiv("vc-progress-text"),i=e.createDiv(),s=e.createEl("button",{cls:"vc-btn danger",text:"\u23F9 \u505C\u6B62"});s.style.display="none",t.onclick=async()=>{this.isRunning||(this.isRunning=!0,this.stopRequested=!1,t.style.display="none",s.style.display="",i.empty(),await this.runTagger(n,d,l,i),this.isRunning=!1,t.style.display="",s.style.display="none")},s.onclick=()=>{this.stopRequested=!0,n.setText("\u6B63\u5728\u505C\u6B62\u2026")}}async runTagger(e,t,n,r){let d=this.plugin.settings.tagLibrary.split(`
`).map(c=>c.trim()).filter(Boolean),l=this.app.vault.getMarkdownFiles(),i=l.filter(c=>{var k;let h=(k=this.app.metadataCache.getFileCache(c))==null?void 0:k.frontmatter;if(!h)return!0;let v=h.tags;return v?Array.isArray(v)?v.filter(Boolean).length===0:typeof v=="string"?v.trim()==="":!1:!0});e.setText(`\u5171 ${l.length} \u7BC7\u7B14\u8BB0\uFF0C${i.length} \u7BC7\u9700\u8981\u5206\u6790`);let s=await this.getAllTags(),a=[],o=0;for(let c=0;c<i.length&&!this.stopRequested;c++){let h=i[c];t.style.width=Math.round(c/i.length*100)+"%",n.setText(`${c+1} / ${i.length}`),e.setText(`\u5206\u6790\u4E2D\uFF1A${h.name}`);let v=await this.app.vault.read(h),k="";try{k=await this.callAI(this.buildTaggerPrompt(v,d))}catch(E){o++;continue}if(!k){o++;continue}let f="",w=[];try{let E=k.match(/\{[\s\S]*\}/);if(E){let T=JSON.parse(E[0]);f=T.summary||"",w=T.tags||[]}}catch(E){o++;continue}a.push({file:h,content:v,summary:f,tags:w})}if(t.style.width="100%",n.setText("\u5206\u6790\u5B8C\u6210"),e.setText(`\u5206\u6790\u5B8C\u6210\uFF1A${a.length} \u7BC7\u6709\u7ED3\u679C${o>0?`\uFF0C${o} \u7BC7\u5931\u8D25`:""}`),a.length===0)return;r.empty();let g=r.createDiv();g.style.cssText="display:flex;gap:6px;align-items:center;margin-bottom:8px;";let u=g.createEl("button",{cls:"vc-btn",text:"\u5168\u9009"}),b=g.createEl("button",{cls:"vc-btn",text:"\u53D6\u6D88\u5168\u9009"}),D=g.createSpan();D.style.cssText="font-size:11px;color:var(--text-muted);margin-left:6px;";let y=[],m=()=>{let c=y.filter(h=>h.checked).length;D.setText(`\u5DF2\u9009 ${c} / ${a.length} \u7BC7`)};a.forEach(({file:c,summary:h,tags:v})=>{let k=r.createDiv("vc-card"),f=k.createDiv();f.style.cssText="display:flex;align-items:flex-start;gap:8px;margin-bottom:6px;";let w=f.createEl("input",{type:"checkbox"});w.checked=!0,w.style.marginTop="2px",w.onchange=m,y.push(w);let E=f.createDiv();E.style.flex="1",E.createDiv("vc-note-name").setText(c.name),E.createDiv("vc-note-path").setText(c.path),k.createDiv("vc-summary").setText("\u6458\u8981\uFF1A"+h);let T=k.createDiv("vc-tags");v.forEach($=>{T.createDiv("vc-tag "+(s.includes($)?"keep":"new")).createSpan().setText($)})}),m(),u.onclick=()=>{y.forEach(c=>c.checked=!0),m()},b.onclick=()=>{y.forEach(c=>c.checked=!1),m()};let p=r.createEl("button",{cls:"vc-btn primary",text:"\u2713 \u5199\u5165\u9009\u4E2D"});p.style.marginTop="10px",p.onclick=async()=>{p.disabled=!0;let c=0;for(let h=0;h<a.length;h++){if(!y[h].checked)continue;let{file:v,content:k,summary:f,tags:w}=a[h];try{await this.writeFrontmatter(v,k,f,w),c++}catch(E){}}new x.Notice(`\u5DF2\u5199\u5165 ${c} \u7BC7`),p.disabled=!1}}renderSimilarity(e){let t=e.createEl("button",{cls:"vc-btn primary",text:"\u25B6 \u5F00\u59CB\u68C0\u6D4B"}),n=e.createDiv("vc-log");n.setText("\u70B9\u51FB\u300C\u5F00\u59CB\u68C0\u6D4B\u300D\u542F\u52A8\u2026");let r=e.createDiv("vc-progress"),d=r.createDiv("vc-progress-bar").createDiv("vc-progress-fill");d.style.width="0%";let l=r.createDiv("vc-progress-text"),i=e.createDiv(),s=e.createEl("button",{cls:"vc-btn danger",text:"\u23F9 \u505C\u6B62"});s.style.display="none",t.onclick=async()=>{this.isRunning||(this.isRunning=!0,this.stopRequested=!1,t.style.display="none",s.style.display="",await this.runSimilarity(n,d,l,i),this.isRunning=!1,t.style.display="",s.style.display="none")},s.onclick=()=>{this.stopRequested=!0}}async runSimilarity(e,t,n,r){var u,b,D,y;let d=this.plugin.settings.excludeFolders.split(",").map(m=>m.trim()).filter(Boolean),l=this.app.vault.getMarkdownFiles().filter(m=>!d.some(p=>m.path.startsWith(p)));e.setText(`\u5171 ${l.length} \u7BC7\u7B14\u8BB0\u53C2\u4E0E\u68C0\u6D4B\uFF0C\u6B63\u5728\u8BA1\u7B97\u76F8\u4F3C\u5EA6\u2026`);let i=[];for(let m of l){let p=await this.app.vault.read(m);i.push(this.extractBody(p).slice(0,2e3))}let s=this.computeSimilarPairs(l,i,this.plugin.settings.similarityThreshold,this.plugin.settings.topNPairs);if(s.length===0){e.setText(`\u672A\u53D1\u73B0\u76F8\u4F3C\u5EA6\u8D85\u8FC7 ${this.plugin.settings.similarityThreshold} \u7684\u7B14\u8BB0\u5BF9`);return}e.setText(`\u53D1\u73B0 ${s.length} \u5BF9\u5019\u9009\uFF0C\u6B63\u5728\u9759\u9ED8\u5206\u6790\u2026`);let a=await this.getAllTags(),o=[];for(let m=0;m<s.length&&!this.stopRequested;m++){let{fileA:p,fileB:c,score:h}=s[m];t.style.width=Math.round(m/s.length*100)+"%",n.setText(`\u5206\u6790\u4E2D ${m+1} / ${s.length}`);let v=this.extractBody(await this.app.vault.read(p)),k=this.extractBody(await this.app.vault.read(c));if(this.isVersionPair(p.name,c.name)){o.push({fileA:p,fileB:c,score:h,state:"version",reason:"\u68C0\u6D4B\u5230\u7248\u672C\u8FED\u4EE3\u5173\u7CFB\uFF0C\u5DF2\u8DF3\u8FC7",suggestion:"",bodyA:v,bodyB:k});continue}let f=!0,w="\u65E0",E="\u65E0";try{let T=await this.callAI(`\u5224\u65AD\u4E24\u7BC7\u7B14\u8BB0\u662F\u5426\u8BED\u4E49\u76F8\u4F3C\uFF08\u5185\u5BB9\u9AD8\u5EA6\u91CD\u53E0\u6216\u91CD\u590D\uFF09\u3002
\u7B14\u8BB0A\uFF1A
${v.slice(0,800)}
\u7B14\u8BB0B\uFF1A
${k.slice(0,800)}
\u53EA\u8FD4\u56DE JSON\uFF1A{"is_similar": true\u6216false, "reason": "\u4E00\u53E5\u8BDD\u7406\u7531", "suggestion": "\u5EFA\u8BAE\u5408\u5E76/\u5EFA\u8BAE\u5220\u9664B/\u5EFA\u8BAE\u5220\u9664A/\u5EFA\u8BAE\u4FDD\u7559"}`);if(T){let $=T.match(/\{[\s\S]*\}/);if($){let S=JSON.parse($[0]);f=S.is_similar!==!1,w=S.reason||"\u65E0",E=S.suggestion||"\u65E0"}}}catch(T){w=`AI \u8BF7\u6C42\u5931\u8D25\uFF1A${(T==null?void 0:T.message)||T}`,f=!1}o.push({fileA:p,fileB:c,score:h,state:f?"pending":"skip",reason:w,suggestion:E,bodyA:v,bodyB:k})}t.style.width="100%",n.setText("\u5206\u6790\u5B8C\u6210");let g=o.filter(m=>m.state==="pending").length;e.setText(`\u68C0\u6D4B\u5B8C\u6210\uFF0C\u5171 ${o.length} \u5BF9\uFF0C\u5176\u4E2D ${g} \u5BF9\u9700\u8981\u5904\u7406`),r.empty();for(let m of o){let p=this.renderSimCard(r,m.fileA,m.fileB,m.score,m.state,m.state==="pending"?`${m.reason} | \u5EFA\u8BAE\uFF1A${m.suggestion}`:m.reason,a);m.state==="pending"&&((u=p.querySelector(".sim-merge"))==null||u.addEventListener("click",async()=>{var c;await this.mergeFiles(m.fileA,m.fileB,m.bodyA,m.bodyB,a),(c=p.querySelector(".sim-actions"))==null||c.remove(),p.createDiv("vc-status").setText("\u2705 \u5DF2\u5408\u5E76")}),(b=p.querySelector(".sim-del-a"))==null||b.addEventListener("click",async()=>{var c;await this.confirmDelete(m.fileA),(c=p.querySelector(".sim-actions"))==null||c.remove(),p.createDiv("vc-status").setText("\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 A")}),(D=p.querySelector(".sim-del-b"))==null||D.addEventListener("click",async()=>{var c;await this.confirmDelete(m.fileB),(c=p.querySelector(".sim-actions"))==null||c.remove(),p.createDiv("vc-status").setText("\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 B")}),(y=p.querySelector(".sim-skip"))==null||y.addEventListener("click",()=>{var c;(c=p.querySelector(".sim-actions"))==null||c.remove(),p.createDiv("vc-status").setText("\u2014 \u5DF2\u8DF3\u8FC7")}))}}renderSimCard(e,t,n,r,d,l,i){let s=e.createDiv("vc-card");if(s.createDiv({cls:"vc-sim-score"}).setText(`${Math.round(r*100)}% \u76F8\u4F3C`),s.createDiv({cls:"vc-note-name"}).setText("A\uFF1A"+t.name),s.createDiv({cls:"vc-note-path"}).setText(t.path),s.createDiv({cls:"vc-note-name"}).setText("B\uFF1A"+n.name),s.createDiv({cls:"vc-note-path"}).setText(n.path),s.createDiv({cls:"vc-summary"}).setText(l),d==="pending"){let a=s.createDiv("vc-sim-actions sim-actions");a.createEl("button",{cls:"vc-btn primary sim-merge",text:"\u5408\u5E76"}),a.createEl("button",{cls:"vc-btn sim-del-a",text:"\u5220\u9664 A"}),a.createEl("button",{cls:"vc-btn sim-del-b",text:"\u5220\u9664 B"}),a.createEl("button",{cls:"vc-btn sim-skip",text:"\u8DF3\u8FC7"})}else d==="version"?s.createDiv("vc-status").setText("\u26A0\uFE0F \u7248\u672C\u8FED\u4EE3\uFF0C\u5DF2\u8DF3\u8FC7"):s.createDiv("vc-status").setText("\u2014 \u4E0D\u76F8\u4F3C\uFF0C\u5DF2\u8DF3\u8FC7");return s}renderManualMerge(e){let t=new Set,n=this.app.vault.getMarkdownFiles().sort((c,h)=>c.name.localeCompare(h.name)),r=e.createEl("input",{cls:"vc-search-input",placeholder:"\u641C\u7D22\u7B14\u8BB0\u540D\u2026"}),d=e.createDiv();d.style.cssText="display:flex;gap:6px;margin-bottom:6px;";let l=d.createEl("button",{cls:"vc-btn",text:"\uFF0B \u6DFB\u52A0\u5F53\u524D\u6253\u5F00\u7684\u6587\u4EF6"}),i=d.createEl("button",{cls:"vc-btn",text:"\u5168\u9009"}),s=d.createEl("button",{cls:"vc-btn",text:"\u53D6\u6D88\u5168\u9009"});l.onclick=()=>{let c=this.app.workspace.getActiveFile();c?(t.add(c.path),o(),g.setText(`\u5DF2\u9009 ${t.size} \u7BC7`),new x.Notice(`\u5DF2\u6DFB\u52A0\uFF1A${c.name}`)):new x.Notice("\u5F53\u524D\u6CA1\u6709\u6253\u5F00\u7684\u6587\u4EF6")};let a=e.createDiv("vc-file-list"),o=()=>{a.empty();let c=r.value.toLowerCase();n.filter(h=>h.name.toLowerCase().includes(c)).forEach(h=>{let v=a.createDiv("vc-file-item"),k=v.createEl("input",{type:"checkbox"});k.checked=t.has(h.path),k.onchange=()=>{k.checked?t.add(h.path):t.delete(h.path),g.setText(`\u5DF2\u9009 ${t.size} \u7BC7`)},v.createSpan({text:h.name})})};o(),r.oninput=()=>o(),i.onclick=()=>{let c=r.value.toLowerCase();n.filter(h=>h.name.toLowerCase().includes(c)).forEach(h=>t.add(h.path)),o(),g.setText(`\u5DF2\u9009 ${t.size} \u7BC7`)},s.onclick=()=>{t.clear(),o(),g.setText("\u5DF2\u9009 0 \u7BC7")};let g=e.createDiv("vc-status");g.style.marginBottom="8px",g.setText("\u5DF2\u9009 0 \u7BC7");let u=e.createDiv("vc-log");u.setText("\u9009\u597D\u7B14\u8BB0\u540E\u70B9\u300C\u751F\u6210\u603B\u7ED3\u300D");let b=e.createDiv(),D=e.createDiv("vc-btn-row"),y=D.createEl("button",{cls:"vc-btn primary",text:"\u751F\u6210\u603B\u7ED3"}),m=D.createEl("button",{cls:"vc-btn success",text:"\u786E\u8BA4\u5408\u5E76"});m.style.display="none";let p="";y.onclick=async()=>{if(t.size<2){new x.Notice("\u8BF7\u81F3\u5C11\u9009 2 \u7BC7\u7B14\u8BB0");return}u.setText("\u6B63\u5728\u751F\u6210\u603B\u7ED3\u2026"),y.disabled=!0;let c=n.filter(v=>t.has(v.path)),h=[];for(let v of c){let k=await this.app.vault.read(v);h.push(`\u3010${v.name}\u3011
${this.extractBody(k).slice(0,1200)}`)}try{p=await this.callAI(`\u4F60\u662F\u77E5\u8BC6\u6574\u7406\u52A9\u624B\u3002\u4E0B\u9762\u6709 ${c.length} \u7BC7\u7B14\u8BB0\uFF0C\u6574\u5408\u6210\u4E00\u7BC7\u7ED3\u6784\u6E05\u6670\u7684\u7B14\u8BB0\uFF0C\u53BB\u6389\u91CD\u590D\u90E8\u5206\uFF0C\u4FDD\u7559\u6240\u6709\u6709\u4EF7\u503C\u7684\u4FE1\u606F\u3002
${h.join(`
---
`)}
\u76F4\u63A5\u8F93\u51FA\u5408\u5E76\u540E\u7684\u7B14\u8BB0\u6B63\u6587\uFF0C\u7528 Markdown \u683C\u5F0F\u3002`),b.empty(),b.createDiv("vc-merged-preview").setText(p),m.style.display="",u.setText("\u603B\u7ED3\u751F\u6210\u5B8C\u6BD5\uFF0C\u786E\u8BA4\u540E\u70B9\u300C\u786E\u8BA4\u5408\u5E76\u300D")}catch(v){u.setText(`\u274C \u751F\u6210\u5931\u8D25\uFF1A${(v==null?void 0:v.message)||v}`)}y.disabled=!1},m.onclick=async()=>{if(!p)return;let c=n.filter(A=>t.has(A.path)),h=this.plugin.settings.tagLibrary.split(`
`).map(A=>A.trim()).filter(Boolean),v="",k=[];try{let A=await this.callAI(`\u5206\u6790\u4EE5\u4E0B\u7B14\u8BB0\uFF0C\u8FD4\u56DEJSON\u3002\u6807\u7B7E\u4ECE\u4EE5\u4E0B\u5217\u8868\u4E2D\u90091-2\u4E2A\uFF1A
${h.join("\u3001")}
\u7B14\u8BB0\uFF1A
${p.slice(0,1500)}
\u8FD4\u56DE\uFF1A{"summary":"...","tags":["\u6807\u7B7E1"]}`),B=A==null?void 0:A.match(/\{[\s\S]*\}/);if(B){let C=JSON.parse(B[0]);v=C.summary||"",k=C.tags||[]}}catch(A){}let f=k.map(A=>` - ${A}`).join(`
`),E=(v?`---
summary: ${v}
tags:
${f}
---
`:"")+p,T=this.plugin.settings.mergedFolder||"00_Merged",$=c.map(A=>A.basename).join("+"),S=`${T}/${$}_merged.md`;this.app.vault.getAbstractFileByPath(T)||await this.app.vault.createFolder(T),await this.app.vault.create(S,E),new x.Notice(`\u2705 \u5408\u5E76\u6587\u4EF6\u5DF2\u4FDD\u5B58\uFF1A${S}`),u.setText(`\u2705 \u5DF2\u4FDD\u5B58\u5230 ${S}`),m.style.display="none",new j(this.app,c,async A=>{if(A)for(let B of c)await this.safeDelete(B)}).open()}}renderOrphan(e){e.createDiv("vc-summary").setText("\u5B64\u7ACB\u7B14\u8BB0\uFF1A\u65E0\u6807\u7B7E + \u6CA1\u6709\u88AB\u5176\u4ED6\u7B14\u8BB0\u5F15\u7528 + \u6CA1\u6709\u5F15\u7528\u5176\u4ED6\u7B14\u8BB0");let t=e.createEl("button",{cls:"vc-btn primary",text:"\u25B6 \u5F00\u59CB\u626B\u63CF"}),n=e.createDiv("vc-log");n.setText("\u70B9\u51FB\u300C\u5F00\u59CB\u626B\u63CF\u300D\u542F\u52A8\u2026");let r=e.createDiv();t.onclick=async()=>{var i;t.disabled=!0,n.setText("\u626B\u63CF\u4E2D\u2026"),r.empty();let d=this.app.vault.getMarkdownFiles(),l=[];for(let s of d){let a=this.app.metadataCache.getFileCache(s),o=!!((i=a==null?void 0:a.frontmatter)!=null&&i.tags&&a.frontmatter.tags.length>0),g=!!(a!=null&&a.links&&a.links.length>0),u=this.getBacklinks(s).length>0;!o&&!g&&!u&&l.push(s)}n.setText(`\u626B\u63CF\u5B8C\u6210\uFF0C\u53D1\u73B0 ${l.length} \u7BC7\u5B64\u7ACB\u7B14\u8BB0`),l.forEach(s=>{let a=r.createDiv("vc-card");a.createDiv("vc-note-name").setText(s.name),a.createDiv("vc-note-path").setText(s.path);let o=a.createDiv("vc-sim-actions");o.createEl("button",{cls:"vc-btn danger",text:"\u5220\u9664"}).onclick=async()=>{await this.confirmDelete(s),a.remove()},o.createEl("button",{cls:"vc-btn",text:"\u8DF3\u8FC7"}).onclick=()=>a.remove(),o.createEl("button",{cls:"vc-btn success",text:"\u52A0\u5165\u6253\u6807\u7B7E\u961F\u5217"}).onclick=()=>{this.currentTab="tagger",this.render(),new x.Notice(`${s.name} \u672A\u5E26\u6807\u7B7E\uFF0C\u8BF7\u5728\u6253\u6807\u7B7E\u9762\u677F\u5904\u7406`)}}),t.disabled=!1}}renderBroken(e){e.createDiv("vc-summary").setText("\u626B\u63CF\u6240\u6709 [[\u53CC\u94FE]]\uFF0C\u627E\u51FA\u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u6587\u4EF6\u7684\u6B7B\u94FE");let t=e.createEl("button",{cls:"vc-btn primary",text:"\u25B6 \u5F00\u59CB\u626B\u63CF"}),n=e.createDiv("vc-log");n.setText("\u70B9\u51FB\u300C\u5F00\u59CB\u626B\u63CF\u300D\u542F\u52A8\u2026");let r=e.createDiv();t.onclick=async()=>{t.disabled=!0,n.setText("\u626B\u63CF\u4E2D\u2026"),r.empty();let d=this.app.vault.getMarkdownFiles(),l=new Set(d.map(a=>a.basename)),i=[];for(let a of d){let o=this.app.metadataCache.getFileCache(a);for(let g of(o==null?void 0:o.links)||[]){let u=g.link.split("#")[0].split("|")[0].trim();u&&!l.has(u)&&i.push({file:a,link:g.link})}}if(n.setText(`\u626B\u63CF\u5B8C\u6210\uFF0C\u53D1\u73B0 ${i.length} \u4E2A\u6B7B\u94FE`),i.length===0){t.disabled=!1;return}let s=new Map;i.forEach(a=>{let o=s.get(a.file.path)||[];o.push(a),s.set(a.file.path,o)}),s.forEach((a,o)=>{let g=d.find(b=>b.path===o),u=r.createDiv("vc-card");u.createDiv("vc-note-name").setText(g.name),u.createDiv("vc-note-path").setText(g.path),a.forEach(b=>{let D=u.createDiv();D.style.cssText="font-size:11px;color:#c85050;margin-bottom:3px;",D.setText(`\u26A0\uFE0F [[${b.link}]] \u627E\u4E0D\u5230\u5BF9\u5E94\u6587\u4EF6`)})}),t.disabled=!1}}renderSearch(e){e.createDiv("vc-summary").setText("\u7528\u5173\u952E\u8BCD\u6216\u4E00\u53E5\u8BDD\u63CF\u8FF0\uFF0C\u627E\u51FA\u76F8\u5173\u7B14\u8BB0");let t=e.createEl("input",{cls:"vc-search-input",placeholder:"\u8F93\u5165\u641C\u7D22\u5185\u5BB9\u2026"}),n=e.createEl("button",{cls:"vc-btn primary",text:"\u{1F50E} \u641C\u7D22"});n.style.marginBottom="10px";let r=e.createDiv("vc-log");r.setText("\u8F93\u5165\u5185\u5BB9\u540E\u70B9\u641C\u7D22");let d=e.createDiv(),l=async()=>{let i=t.value.trim();if(!i)return;r.setText("\u641C\u7D22\u4E2D\u2026"),d.empty();let s=this.app.vault.getMarkdownFiles(),a=[];for(let p of s){let c=await this.app.vault.read(p);a.push(this.extractBody(c).slice(0,2e3))}let o=this.buildTFIDF([i,...a]),g=o[0],u=o.slice(1),b=(p,c)=>{let h=0;return Object.keys(p).forEach(v=>h+=(p[v]||0)*(c[v]||0)),h},D=p=>Math.sqrt(Object.values(p).reduce((c,h)=>c+h*h,0)),y=D(g),m=u.map((p,c)=>{let h=D(p),v=y===0||h===0?0:b(g,p)/(y*h);return{file:s[c],score:v}}).filter(p=>p.score>0).sort((p,c)=>c.score-p.score).slice(0,15);r.setText(`\u627E\u5230 ${m.length} \u6761\u76F8\u5173\u7B14\u8BB0`),m.forEach(({file:p,score:c})=>{let h=d.createDiv("vc-card");h.style.cursor="pointer",h.createDiv("vc-note-name").setText(p.name),h.createDiv("vc-note-path").setText(p.path),h.createDiv("vc-status").setText(`\u76F8\u5173\u5EA6\uFF1A${Math.round(c*100)}%`),h.onclick=()=>this.app.workspace.openLinkText(p.path,"",!1)})};n.onclick=l,t.onkeydown=i=>{i.key==="Enter"&&l()}}renderTitle(e){e.createDiv("vc-summary").setText("AI \u6839\u636E\u7B14\u8BB0\u5185\u5BB9\u5EFA\u8BAE\u66F4\u51C6\u786E\u7684\u6807\u9898\uFF0C\u4F60\u6765\u51B3\u5B9A\u8981\u4E0D\u8981\u6539");let t=e.createEl("input",{cls:"vc-search-input",placeholder:"\u641C\u7D22\u6587\u4EF6\u540D\u2026"}),n=e.createDiv();n.style.cssText="display:flex;gap:6px;margin-bottom:6px;";let r=n.createEl("button",{cls:"vc-btn",text:"\u5168\u9009"}),d=n.createEl("button",{cls:"vc-btn",text:"\u53D6\u6D88\u5168\u9009"}),l=new Set,i=this.app.vault.getMarkdownFiles().sort((f,w)=>f.name.localeCompare(w.name)),s=e.createDiv("vc-file-list"),a=()=>{s.empty();let f=t.value.toLowerCase();i.filter(w=>w.name.toLowerCase().includes(f)).forEach(w=>{let E=s.createDiv("vc-file-item"),T=E.createEl("input",{type:"checkbox"});T.checked=l.has(w.path),T.onchange=()=>{T.checked?l.add(w.path):l.delete(w.path),o.setText(`\u5DF2\u9009 ${l.size} \u7BC7`)},E.createSpan({text:w.name})})};a(),t.oninput=()=>a(),r.onclick=()=>{let f=t.value.toLowerCase();i.filter(w=>w.name.toLowerCase().includes(f)).forEach(w=>l.add(w.path)),a(),o.setText(`\u5DF2\u9009 ${l.size} \u7BC7`)},d.onclick=()=>{l.clear(),a(),o.setText("\u5DF2\u9009 0 \u7BC7")};let o=e.createDiv("vc-status");o.style.marginBottom="8px",o.setText("\u5DF2\u9009 0 \u7BC7");let g=e.createEl("button",{cls:"vc-btn primary",text:"\u25B6 \u5F00\u59CB\u5206\u6790"}),u=e.createDiv("vc-log");u.setText("\u9009\u597D\u6587\u4EF6\u540E\u70B9\u300C\u5F00\u59CB\u5206\u6790\u300D");let b=e.createDiv("vc-progress"),D=b.createDiv("vc-progress-bar").createDiv("vc-progress-fill");D.style.width="0%";let y=b.createDiv("vc-progress-text"),m=e.createDiv();m.style.cssText="display:flex;justify-content:flex-end;margin-bottom:6px;";let p=m.createEl("button",{cls:"vc-btn success",text:"\u2713 \u4E00\u952E\u5168\u90E8\u91C7\u7528"});p.style.display="none";let c=e.createDiv();c.style.cssText="max-height:420px;overflow-y:auto;padding-right:2px;";let h=e.createEl("button",{cls:"vc-btn danger",text:"\u23F9 \u505C\u6B62"});h.style.display="none",this.plugin.settings.titleAccepted||(this.plugin.settings.titleAccepted=[]);let v=new Set(this.plugin.settings.titleAccepted),k=[];p.onclick=async()=>{p.disabled=!0;for(let f of[...k]){if(!f.card.isConnected)continue;let w=f.file.path.replace(f.file.name,f.suggested+".md");try{await this.app.vault.rename(f.file,w),f.actions.remove(),f.card.createDiv("vc-status").setText("\u2705 \u5DF2\u91CD\u547D\u540D"),v.add(f.file.path)}catch(E){}}this.plugin.settings.titleAccepted=[...v],await this.plugin.saveSettings(),p.disabled=!1},g.onclick=async()=>{if(this.isRunning)return;if(l.size===0){new x.Notice("\u8BF7\u5148\u9009\u62E9\u8981\u5206\u6790\u7684\u6587\u4EF6");return}this.isRunning=!0,this.stopRequested=!1,g.style.display="none",h.style.display="",c.empty(),k.length=0,p.style.display="none";let f=i.filter(E=>l.has(E.path)&&!v.has(E.path));u.setText(`\u5171 ${f.length} \u7BC7\u5F85\u5206\u6790`);let w=0;for(let E=0;E<f.length&&!this.stopRequested;E++){let T=f[E];D.style.width=Math.round(E/f.length*100)+"%",y.setText(`${E+1} / ${f.length}`),u.setText(`\u5206\u6790\u4E2D\uFF1A${T.name}`);let $=await this.app.vault.read(T),S="";try{S=(await this.callAI(`\u6839\u636E\u4EE5\u4E0B\u7B14\u8BB0\u5185\u5BB9\uFF0C\u7ED9\u8FD9\u7BC7\u7B14\u8BB0\u8D77\u4E00\u4E2A\u7B80\u77ED\u3001\u51C6\u786E\u3001\u4E2D\u6587\u7684\u6807\u9898\uFF08\u4E0D\u8D85\u8FC720\u5B57\uFF0C\u4E0D\u8981\u52A0\u4E66\u540D\u53F7\u6216\u5F15\u53F7\uFF09\u3002
\u5F53\u524D\u6807\u9898\uFF1A${T.basename}
\u7B14\u8BB0\u5185\u5BB9\uFF1A
${this.extractBody($).slice(0,1e3)}
\u53EA\u8FD4\u56DE\u6807\u9898\u672C\u8EAB\u3002`)).trim().replace(/^["'《「]|["'》」]$/g,"")}catch(R){continue}if(!S||S===T.basename)continue;w++;let A=c.createDiv("vc-card");A.createDiv("vc-title-old").setText("\u539F\u6807\u9898\uFF1A"+T.basename),A.createDiv("vc-title-new").setText("\u5EFA\u8BAE\uFF1A"+S),A.createDiv("vc-note-path").setText(T.path);let B=A.createDiv("vc-sim-actions"),C={file:T,suggested:S,card:A,actions:B};k.push(C),B.createEl("button",{cls:"vc-btn success",text:"\u2713 \u91C7\u7528"}).onclick=async()=>{let R=T.path.replace(T.name,S+".md");try{await this.app.vault.rename(T,R),A.createDiv("vc-status").setText("\u2705 \u5DF2\u91CD\u547D\u540D"),B.remove(),v.add(T.path),this.plugin.settings.titleAccepted=[...v],await this.plugin.saveSettings()}catch(z){new x.Notice(`\u91CD\u547D\u540D\u5931\u8D25\uFF1A${(z==null?void 0:z.message)||z}`)}},B.createEl("button",{cls:"vc-btn",text:"\u8DF3\u8FC7"}).onclick=()=>{A.remove();let R=k.indexOf(C);R>-1&&k.splice(R,1)}}D.style.width="100%",y.setText("\u5B8C\u6210"),u.setText(`\u5206\u6790\u5B8C\u6210\uFF0C${w} \u7BC7\u6709\u6807\u9898\u4F18\u5316\u5EFA\u8BAE`),w>0&&(p.style.display=""),this.isRunning=!1,g.style.display="",h.style.display="none"},h.onclick=()=>{this.stopRequested=!0}}renderHealth(e){e.createDiv("vc-summary").setText("\u4E00\u952E\u751F\u6210\u6574\u4E2A\u7B14\u8BB0\u5E93\u7684\u5065\u5EB7\u6982\u51B5");let t=e.createEl("button",{cls:"vc-btn primary",text:"\u{1F4CA} \u751F\u6210\u62A5\u544A"}),n=e.createDiv("vc-log");n.setText("\u70B9\u51FB\u300C\u751F\u6210\u62A5\u544A\u300D\u5F00\u59CB\u7EDF\u8BA1\u2026");let r=e.createDiv();t.onclick=async()=>{var y;t.disabled=!0,n.setText("\u7EDF\u8BA1\u4E2D\uFF0C\u8BF7\u7A0D\u5019\u2026"),r.empty();let d=this.app.vault.getMarkdownFiles(),l=d.length,i=new Set(d.map(m=>m.basename)),s=0,a=0,o=0;for(let m of d){let p=this.app.metadataCache.getFileCache(m),c=!!((y=p==null?void 0:p.frontmatter)!=null&&y.tags&&p.frontmatter.tags.length>0);c||s++;let h=!!(p!=null&&p.links&&p.links.length>0),v=this.getBacklinks(m).length>0;!c&&!h&&!v&&a++;for(let k of(p==null?void 0:p.links)||[]){let f=k.link.split("#")[0].split("|")[0].trim();if(f&&!i.has(f)){o++;break}}}let g=[];for(let m of d){let p=await this.app.vault.read(m);g.push(this.extractBody(p).slice(0,500))}let u=this.computeSimilarPairs(d,g,.8,999).length;n.setText("\u62A5\u544A\u751F\u6210\u5B8C\u6BD5");let b=r.createDiv("vc-card");[{label:"\u7B14\u8BB0\u603B\u6570",value:l,note:""},{label:"\u65E0\u6807\u7B7E\u7B14\u8BB0",value:s,note:`\u5360 ${Math.round(s/l*100)}%`},{label:"\u5B64\u7ACB\u7B14\u8BB0",value:a,note:"\u65E0\u6807\u7B7E + \u65E0\u5185\u94FE"},{label:"\u542B\u6B7B\u94FE\u7684\u6587\u4EF6",value:o,note:"\u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684\u7B14\u8BB0"},{label:"\u9AD8\u5EA6\u91CD\u590D\u5BF9\uFF08\u226580%\uFF09",value:u,note:"\u5EFA\u8BAE\u5408\u5E76\u6216\u5220\u9664"}].forEach(({label:m,value:p,note:c})=>{let h=b.createDiv("vc-health-row"),v=h.createDiv(),k=v.createDiv({text:m});if(k.style.fontWeight="500",c){let f=v.createDiv({text:c});f.style.cssText="font-size:10px;color:var(--text-faint);"}h.createDiv("vc-health-num").setText(String(p))}),t.disabled=!1}}async mergeFiles(e,t,n,r,d){new x.Notice("\u6B63\u5728\u5408\u5E76\uFF0C\u8BF7\u7A0D\u5019\u2026");let l=this.plugin.settings.tagLibrary.split(`
`).map(y=>y.trim()).filter(Boolean),i=await this.callAI(`\u4F60\u662F\u77E5\u8BC6\u6574\u7406\u52A9\u624B\u3002\u4E0B\u9762\u6709\u4E24\u7BC7\u5185\u5BB9\u76F8\u4F3C\u7684\u7B14\u8BB0\uFF0C\u53BB\u6389\u91CD\u590D\u90E8\u5206\uFF0C\u6574\u5408\u6210\u4E00\u7BC7\u7ED3\u6784\u6E05\u6670\u7684\u7B14\u8BB0\uFF0C\u4FDD\u7559\u6240\u6709\u6709\u4EF7\u503C\u7684\u4FE1\u606F\u3002
\u7B14\u8BB0A\uFF1A
${n.slice(0,1500)}
\u7B14\u8BB0B\uFF1A
${r.slice(0,1500)}
\u76F4\u63A5\u8F93\u51FA\u5408\u5E76\u540E\u7684\u7B14\u8BB0\u6B63\u6587\uFF0C\u7528 Markdown \u683C\u5F0F\u3002`);if(!i){new x.Notice("\u5408\u5E76\u5931\u8D25");return}let s="",a=[];try{let y=await this.callAI(`\u5206\u6790\u4EE5\u4E0B\u7B14\u8BB0\uFF0C\u8FD4\u56DEJSON\u3002\u6807\u7B7E\u4ECE\u4EE5\u4E0B\u5217\u8868\u4E2D\u90091-2\u4E2A\uFF1A
${l.join("\u3001")}
\u7B14\u8BB0\uFF1A
${i.slice(0,1500)}
\u8FD4\u56DE\uFF1A{"summary":"...","tags":["\u6807\u7B7E1"]}`),m=y==null?void 0:y.match(/\{[\s\S]*\}/);if(m){let p=JSON.parse(m[0]);s=p.summary||"",a=p.tags||[]}}catch(y){}let o=a.map(y=>` - ${y}`).join(`
`),u=(s?`---
summary: ${s}
tags:
${o}
---
`:"")+i,b=this.plugin.settings.mergedFolder||"00_Merged",D=`${b}/${e.basename}+${t.basename}_merged.md`;this.app.vault.getAbstractFileByPath(b)||await this.app.vault.createFolder(b),await this.app.vault.create(D,u),new x.Notice(`\u2705 \u5408\u5E76\u6587\u4EF6\u5DF2\u4FDD\u5B58\uFF1A${D}`),new O(this.app,e,t,async y=>{y&&(await this.safeDelete(e),await this.safeDelete(t))}).open()}async confirmDelete(e){new K(this.app,e,async()=>{await this.safeDelete(e)}).open()}async safeDelete(e){let t=this.getBacklinks(e);if(t.length>0){new x.Notice(`\u26A0\uFE0F ${e.name} \u88AB ${t.length} \u7BC7\u7B14\u8BB0\u5F15\u7528\uFF0C\u8BF7\u5148\u5904\u7406\u5185\u94FE`);return}await this.app.vault.trash(e,!0),new x.Notice(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664\uFF1A${e.name}`)}getBacklinks(e){let t=[],n=e.basename;for(let r of this.app.vault.getMarkdownFiles()){if(r.path===e.path)continue;let d=this.app.metadataCache.getFileCache(r);((d==null?void 0:d.links)||[]).some(l=>l.link===n||l.link.startsWith(n+"|"))&&t.push(r)}return t}async writeFrontmatter(e,t,n,r){let d=r.map(l=>` - ${l}`).join(`
`);await this.app.vault.modify(e,`---
summary: ${n}
tags:
${d}
---
`+t)}extractBody(e){return e.replace(/^---[\s\S]*?---\s*\n/,"").split(`
`).filter(t=>!t.match(/^#{1,6}\s/)).join(`
`).trim()}isVersionPair(e,t){let n=/([\s_\-]?[vV]\d+([._]\d+)*|[\s_\-]?\d+\.\d+|[\s_\-]版[\s_\-]?\d+([._]\d+)*|[\s_\-]\d+|[\s_\-](旧|新|最新|老|旧版|新版|最新版|old|new|latest|final|backup|bak|copy|副本|备份))$/i,r=/^(最新版|最新|新版|旧版|旧|老版|老|备份|副本|latest|new|old|backup)[\s_\-]?/i,d=s=>s.replace(/\.md$/,"").replace(r,"").replace(n,"").trim(),l=d(e),i=d(t);return l===i&&l!==""}async getAllTags(){let e=new Set;return this.app.vault.getMarkdownFiles().forEach(t=>{var r,d;let n=(d=(r=this.app.metadataCache.getFileCache(t))==null?void 0:r.frontmatter)==null?void 0:d.tags;Array.isArray(n)&&n.forEach(l=>e.add(l))}),[...e].sort()}buildTFIDF(e){let t=i=>{let s=[],a=i.split(/\s+/).filter(Boolean);for(let o of a)if(/[\u4e00-\u9fff]/.test(o))for(let g=0;g<o.length;g++)s.push(o[g]),g<o.length-1&&s.push(o[g]+o[g+1]);else s.push(o.toLowerCase());return s},n=e.map(i=>{let s=t(i),a={};s.forEach(g=>a[g]=(a[g]||0)+1);let o=s.length||1;return Object.keys(a).forEach(g=>a[g]/=o),a}),r={};n.forEach(i=>Object.keys(i).forEach(s=>r[s]=(r[s]||0)+1));let d=n.length,l={};return Object.keys(r).forEach(i=>l[i]=Math.log((d+1)/(r[i]+1))),n.map(i=>{let s={};return Object.keys(i).forEach(a=>s[a]=i[a]*(l[a]||0)),s})}computeSimilarPairs(e,t,n,r){let d=this.buildTFIDF(t),l=(a,o)=>{let g=0;return Object.keys(a).forEach(u=>g+=(a[u]||0)*(o[u]||0)),g},i=a=>Math.sqrt(Object.values(a).reduce((o,g)=>o+g*g,0)),s=[];for(let a=0;a<d.length;a++)for(let o=a+1;o<d.length;o++){let g=i(d[a]),u=i(d[o]);if(g===0||u===0)continue;let b=l(d[a],d[o])/(g*u);b>=n&&s.push({fileA:e[a],fileB:e[o],score:b})}return s.sort((a,o)=>o.score-a.score).slice(0,r)}async callAI(e){var r,d,l,i;let t=this.plugin.settings,n=async(s,a,o)=>{var b,D,y;let u=await(await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({model:a,messages:[{role:"user",content:e}]})})).json();return((y=(D=(b=u==null?void 0:u.choices)==null?void 0:b[0])==null?void 0:D.message)==null?void 0:y.content)||""};try{if(t.modelSource==="ollama")return(await(await fetch("http://127.0.0.1:11434/api/generate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:t.ollamaModel,prompt:e,stream:!1})})).json()).response||"";if(t.modelSource==="qwen"){let a=await(await fetch("https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.qwenApiKey}`},body:JSON.stringify({model:t.qwenModel,input:{messages:[{role:"user",content:e}]},parameters:{result_format:"message"}})})).json();return((i=(l=(d=(r=a==null?void 0:a.output)==null?void 0:r.choices)==null?void 0:d[0])==null?void 0:l.message)==null?void 0:i.content)||""}else return t.modelSource==="zhipu"?await n("https://open.bigmodel.cn/api/paas/v4/chat/completions",t.zhipuModel||"glm-4-flash",t.zhipuApiKey):t.modelSource==="deepseek"?await n("https://api.deepseek.com/v1/chat/completions",t.deepseekModel||"deepseek-chat",t.deepseekApiKey):t.modelSource==="doubao"?await n("https://ark.volces.com/api/v3/chat/completions",t.doubaoModel,t.doubaoApiKey):await n("https://api.moonshot.cn/v1/chat/completions",t.kimiModel||"moonshot-v1-8k",t.kimiApiKey)}catch(s){throw console.error("AI \u8BF7\u6C42\u5931\u8D25",s),s}}},O=class extends x.Modal{constructor(e,t,n,r){super(e);this.fileA=t,this.fileB=n,this.onConfirm=r}onOpen(){let{contentEl:e}=this;e.createEl("h3").setText("\u5408\u5E76\u5B8C\u6210"),e.createEl("p").setText("\u662F\u5426\u5220\u9664\u539F\u6765\u4E24\u4E2A\u6587\u4EF6\uFF1F"),e.createEl("p",{cls:"vc-note-path"}).setText(this.fileA.path),e.createEl("p",{cls:"vc-note-path"}).setText(this.fileB.path);let t=e.createDiv({cls:"vc-btn-row"});t.createEl("button",{cls:"vc-btn",text:"\u4FDD\u7559\u539F\u6587\u4EF6"}).onclick=()=>{this.onConfirm(!1),this.close()},t.createEl("button",{cls:"vc-btn danger",text:"\u5220\u9664\u539F\u6587\u4EF6"}).onclick=()=>{this.onConfirm(!0),this.close()}}onClose(){this.contentEl.empty()}},j=class extends x.Modal{constructor(e,t,n){super(e);this.files=t,this.onConfirm=n}onOpen(){let{contentEl:e}=this;e.createEl("h3").setText("\u5408\u5E76\u5B8C\u6210"),e.createEl("p").setText(`\u662F\u5426\u5220\u9664\u539F\u6765 ${this.files.length} \u4E2A\u6587\u4EF6\uFF1F`),this.files.forEach(n=>e.createEl("p",{cls:"vc-note-path"}).setText(n.path));let t=e.createDiv({cls:"vc-btn-row"});t.createEl("button",{cls:"vc-btn",text:"\u4FDD\u7559\u539F\u6587\u4EF6"}).onclick=()=>{this.onConfirm(!1),this.close()},t.createEl("button",{cls:"vc-btn danger",text:"\u5220\u9664\u539F\u6587\u4EF6"}).onclick=()=>{this.onConfirm(!0),this.close()}}onClose(){this.contentEl.empty()}},K=class extends x.Modal{constructor(e,t,n){super(e);this.file=t,this.onConfirm=n}onOpen(){let{contentEl:e}=this;e.createEl("h3").setText("\u786E\u8BA4\u5220\u9664"),e.createEl("p").setText(this.file.path);let t=e.createDiv({cls:"vc-btn-row"});t.createEl("button",{cls:"vc-btn",text:"\u53D6\u6D88"}).onclick=()=>this.close(),t.createEl("button",{cls:"vc-btn danger",text:"\u786E\u8BA4\u5220\u9664"}).onclick=()=>{this.onConfirm(),this.close()}}onClose(){this.contentEl.empty()}},N=class extends x.PluginSettingTab{constructor(e,t){super(e,t);this.plugin=t}display(){let{containerEl:e}=this;e.empty(),e.createEl("h2").setText("Vault Groomer \xB7 \u8BBE\u7F6E");let t=e.createEl("p");t.setText("\u5F53\u524D\u7248\u672C\uFF1Av1.4.0"),t.style.cssText="font-size:12px;color:var(--text-muted);margin-top:-8px;margin-bottom:12px;",new x.Setting(e).setName("\u672C\u5730 Ollama \u6A21\u578B").setDesc("\u4F8B\u5982 qwen2.5:3b").addText(u=>u.setValue(this.plugin.settings.ollamaModel).onChange(async b=>{this.plugin.settings.ollamaModel=b,await this.plugin.saveSettings()})),e.createEl("h3").setText("\u5404\u5E73\u53F0 API \u914D\u7F6E");let n={\u5343\u95EE:{keyField:"qwenApiKey",modelField:"qwenModel",modelPlaceholder:"qwen-plus / qwen-max / qwen-turbo"},\u667A\u8C31:{keyField:"zhipuApiKey",modelField:"zhipuModel",modelPlaceholder:"glm-4-flash"},DeepSeek:{keyField:"deepseekApiKey",modelField:"deepseekModel",modelPlaceholder:"deepseek-chat"},\u8C46\u5305:{keyField:"doubaoApiKey",modelField:"doubaoModel",modelPlaceholder:"\u586B endpoint \u5BF9\u5E94\u6A21\u578B\u540D"},Kimi:{keyField:"kimiApiKey",modelField:"kimiModel",modelPlaceholder:"moonshot-v1-8k / moonshot-v1-32k"}},r=Object.keys(n)[0],l=new x.Setting(e).setName("API Key").controlEl.createEl("input",{type:"password"});l.style.cssText="width:100%;background:var(--background-secondary);color:var(--text-normal);border:1px solid var(--background-modifier-border);border-radius:4px;padding:4px 8px;";let i=new x.Setting(e).setName("\u6A21\u578B\u540D"),s=i.controlEl.createEl("input");s.style.cssText="width:100%;background:var(--background-secondary);color:var(--text-normal);border:1px solid var(--background-modifier-border);border-radius:4px;padding:4px 8px;";let a=()=>{let u=n[r];l.value=this.plugin.settings[u.keyField]||"",s.value=this.plugin.settings[u.modelField]||"",i.setDesc(u.modelPlaceholder)};l.onchange=async()=>{let u=n[r];this.plugin.settings[u.keyField]=l.value,await this.plugin.saveSettings()},s.onchange=async()=>{let u=n[r];this.plugin.settings[u.modelField]=s.value,await this.plugin.saveSettings()},new x.Setting(e).setName("\u9009\u62E9\u5E73\u53F0").addDropdown(u=>{Object.keys(n).forEach(b=>u.addOption(b,b)),u.setValue(r).onChange(b=>{r=b,a()})}),a(),new x.Setting(e).setName("\u6392\u9664\u6587\u4EF6\u5939").setDesc("\u9017\u53F7\u5206\u9694\uFF0C\u76F8\u5BF9\u4E8E\u5E93\u6839\u76EE\u5F55").addText(u=>u.setValue(this.plugin.settings.excludeFolders).onChange(async b=>{this.plugin.settings.excludeFolders=b,await this.plugin.saveSettings()})),new x.Setting(e).setName("\u5408\u5E76\u6587\u4EF6\u5B58\u653E\u4F4D\u7F6E").setDesc("\u5408\u5E76\u540E\u7684\u7B14\u8BB0\u653E\u5230\u54EA\u4E2A\u6587\u4EF6\u5939\uFF0C\u9ED8\u8BA4 00_Merged").addText(u=>u.setValue(this.plugin.settings.mergedFolder).onChange(async b=>{this.plugin.settings.mergedFolder=b||"00_Merged",await this.plugin.saveSettings()})),new x.Setting(e).setName("\u76F8\u4F3C\u5EA6\u9608\u503C").setDesc("0.1 ~ 1.0\uFF0C\u8D8A\u9AD8\u8D8A\u4E25\u683C").addText(u=>u.setValue(String(this.plugin.settings.similarityThreshold)).onChange(async b=>{this.plugin.settings.similarityThreshold=parseFloat(b)||.6,await this.plugin.saveSettings()})),new x.Setting(e).setName("\u6700\u591A\u5904\u7406\u5BF9\u6570").setDesc("\u6BCF\u6B21\u76F8\u4F3C\u68C0\u6D4B\u6700\u591A\u5904\u7406\u591A\u5C11\u5BF9").addText(u=>u.setValue(String(this.plugin.settings.topNPairs)).onChange(async b=>{this.plugin.settings.topNPairs=parseInt(b)||20,await this.plugin.saveSettings()})),new x.Setting(e).setName("\u5B9A\u65F6\u81EA\u52A8\u6253\u6807\u7B7E\uFF08\u5C0F\u65F6\uFF09").setDesc("\u6BCF\u9694\u591A\u5C11\u5C0F\u65F6\u81EA\u52A8\u7ED9\u65E0\u6807\u7B7E\u7B14\u8BB0\u6253\u6807\u7B7E\uFF0C0 = \u5173\u95ED").addText(u=>u.setValue(String(this.plugin.settings.autoRunHours)).onChange(async b=>{this.plugin.settings.autoRunHours=parseInt(b)||0,await this.plugin.saveSettings(),this.plugin.scheduleAutoRun()})),e.createEl("h3").setText("\u6807\u7B7E\u5E93");let o=e.createEl("p");o.setText("\u6BCF\u884C\u4E00\u4E2A\u6807\u7B7E\uFF0C\u6253\u6807\u7B7E\u65F6 AI \u53EA\u80FD\u4ECE\u8FD9\u91CC\u9009\uFF08\u6700\u591A\u65B0\u589E 1 \u4E2A\uFF09\u3002\u53EF\u968F\u65F6\u7F16\u8F91\u3002"),o.style.cssText="font-size:12px;color:var(--text-muted);margin-bottom:8px;";let g=e.createEl("textarea");g.value=this.plugin.settings.tagLibrary,g.style.cssText="width:100%;height:200px;font-size:12px;font-family:monospace;border-radius:6px;padding:8px;background:var(--background-secondary);color:var(--text-normal);border:1px solid var(--background-modifier-border);box-sizing:border-box;resize:vertical;",g.onchange=async()=>{this.plugin.settings.tagLibrary=g.value,await this.plugin.saveSettings()}}};